History

imported initial core file

Paul Batchelor committed 5 years ago

Commit 25f3cd21 parent f5248861 file · +510 −0Browse files

Unified · Split

core.org+510−0
@@ -0,0 +1,510 @@
1+#+TITLE: Core
2+* Overview
3+This document describes and outlines a small API for
4+sndkit. It is primarily designed to be a small layer on top
5+of Patchwerk, a library used to build up directed audio
6+graphs. A large portion of the functionality is based on
7+what is found in runt-patchwerk. Just simpler.
8+
9+In addition to maintaining an instance of patchwerk, the
10+sndkit API provides a stack and a register system. The
11+stack is the primary means of sharing data between nodes.
12+Registers are for more persitently used values.
13+* Tangled files
14+=core.h= and =core.c=.
15+
16+#+NAME: core.h
17+#+BEGIN_SRC c :tangle core.h
18+#ifndef SKPW_H
19+#define SKPW_H
20+
21+#ifndef SKFLT
22+#define SKFLT float
23+#endif
24+
25+<<typedefs>>
26+#ifdef SK_CORE_PRIV
27+<<structs>>
28+<<core>>
29+#endif
30+<<macros>>
31+<<funcdefs>>
32+#endif
33+#+END_SRC
34+
35+#+NAME: core.c
36+#+BEGIN_SRC c :tangle core.c
37+#include <stdlib.h>
38+#include <math.h>
39+#include "patchwerk.h"
40+#define SK_CORE_PRIV
41+#include "core.h"
42+<<enums>>
43+<<funcs>>
44+#+END_SRC
45+* Engine Management
46+All this behavior is defined in a struct called =sk_core=.
47+
48+Creating/freeing is done with =sk_core_new= and
49+=sk_core_del=.
50+
51+#+NAME: funcdefs
52+#+BEGIN_SRC c
53+sk_core * sk_core_new(int sr);
54+void sk_core_del(sk_core *core);
55+#+END_SRC
56+
57+#+NAME: funcs
58+#+BEGIN_SRC c
59+sk_core * sk_core_new(int sr)
60+{
61+ sk_core *core;
62+ pw_patch *patch;
63+
64+ core = malloc(sizeof(sk_core));
65+ core->patch = malloc(pw_patch_size());
66+
67+ patch = core->patch;
68+ pw_patch_init(patch, 64);
69+ pw_patch_alloc(patch, 8, 10);
70+ pw_patch_srate_set(patch, sr);
71+
72+ sk_stack_init(&core->stack);
73+ return core;
74+}
75+#+END_SRC
76+
77+#+NAME: funcs
78+#+BEGIN_SRC c
79+void sk_core_del(sk_core *core)
80+{
81+ if (core == NULL) return;
82+
83+ pw_patch_destroy(core->patch);
84+ pw_patch_free_nodes(core->patch);
85+ free(core->patch);
86+
87+ free(core);
88+ core = NULL;
89+}
90+#+END_SRC
91+
92+#+NAME: typedefs
93+#+BEGIN_SRC c
94+typedef struct sk_core sk_core;
95+#+END_SRC
96+
97+#+NAME: core
98+#+BEGIN_SRC c
99+struct sk_core {
100+ pw_patch *patch;
101+ sk_stack stack;
102+};
103+#+END_SRC
104+
105+A internal block of audio can be computed with
106+=sk_core_compute=. Usually this size is 64 samples.
107+
108+#+NAME: funcdefs
109+#+BEGIN_SRC c
110+void sk_core_compute(sk_core *core);
111+#+END_SRC
112+
113+#+NAME: funcs
114+#+BEGIN_SRC c
115+void sk_core_compute(sk_core *core)
116+{
117+ pw_patch_compute(core->patch);
118+}
119+#+END_SRC
120+
121+Building up nodes involves interacting with the patchwerk
122+API. To get the top level struct of that opaquely, use
123+=sk_core_patch=.
124+
125+#+NAME: funcdefs
126+#+BEGIN_SRC c
127+pw_patch * sk_core_patch(sk_core *core);
128+#+END_SRC
129+
130+#+NAME: funcs
131+#+BEGIN_SRC c
132+pw_patch * sk_core_patch(sk_core *core)
133+{
134+ return core->patch;
135+}
136+#+END_SRC
137+
138+The function =sk_core_seconds_to_blocks= converts
139+seconds to a number of render blocks.
140+
141+#+NAME: funcdefs
142+#+BEGIN_SRC c
143+size_t sk_core_seconds_to_blocks(sk_core *core, SKFLT secs);
144+#+END_SRC
145+
146+#+NAME: funcs
147+#+BEGIN_SRC c
148+size_t sk_core_seconds_to_blocks(sk_core *core, SKFLT secs)
149+{
150+ size_t nblocks;
151+ int sr;
152+
153+ sr = pw_patch_srate_get(core->patch);
154+ nblocks = floor((sr * secs) / 64) + 1;
155+
156+ return nblocks;
157+}
158+#+END_SRC
159+* Stack
160+** Core Data Types
161+An item on a stack is contained in a thing known as a
162+=stacklet=. A stacklet has integer for a type flag, a
163+floating point number for numerical values, and a generic
164+pointer for C structures.
165+
166+#+NAME: typedefs
167+#+BEGIN_SRC c
168+typedef struct sk_stacklet sk_stacklet;
169+#+END_SRC
170+
171+#+NAME: structs
172+#+BEGIN_SRC c
173+struct sk_stacklet {
174+ int type;
175+ SKFLT f;
176+ void *ptr;
177+};
178+<<stack_struct>>
179+#+END_SRC
180+
181+A stacklet is initialized with the function
182+=sk_stacklet_init=.
183+
184+#+NAME: funcdefs
185+#+BEGIN_SRC c
186+void sk_stacklet_init(sk_stacklet *s);
187+#+END_SRC
188+
189+#+NAME: funcs
190+#+BEGIN_SRC c
191+void sk_stacklet_init(sk_stacklet *s)
192+{
193+ s->type = SK_TYPE_NONE;
194+ s->f = 0;
195+ s->ptr = NULL;
196+}
197+#+END_SRC
198+
199+An array of stacklets forms the foundation of a =sk_stack=.
200+
201+#+NAME: typedefs
202+#+BEGIN_SRC c
203+typedef struct sk_stack sk_stack;
204+#+END_SRC
205+
206+The stack will be hard coded to be 16.
207+
208+An integer =pos= is used to keep track of position.
209+
210+#+NAME: stack_struct
211+#+BEGIN_SRC c
212+#define SK_STACKSIZE 16
213+struct sk_stack {
214+ sk_stacklet stack[SK_STACKSIZE];
215+ int pos;
216+};
217+#+END_SRC
218+
219+A stack is initialized with =sk_stack_init=.
220+
221+#+NAME: funcdefs
222+#+BEGIN_SRC c
223+void sk_stack_init(sk_stack *s);
224+#+END_SRC
225+
226+The position is set to be negative, indicating an empty
227+stack.
228+
229+#+NAME: funcs
230+#+BEGIN_SRC c
231+void sk_stack_init(sk_stack *s)
232+{
233+ int i;
234+
235+ for (i = 0; i < SK_STACKSIZE; i++) {
236+ sk_stacklet_init(&s->stack[i]);
237+ }
238+
239+ s->pos = -1;
240+}
241+#+END_SRC
242+** Types
243+The typeflag currently supports the following types:
244+
245+#+NAME: enums
246+#+BEGIN_SRC c
247+enum {
248+ SK_TYPE_NONE,
249+ SK_TYPE_CONSTANT,
250+ SK_TYPE_CABLE,
251+ SK_TYPE_TABLE,
252+ SK_TYPE_GENERIC
253+};
254+#+END_SRC
255+** Push/Pop
256+Push and pop are the core operations for the stack. Both
257+return non-zero values on error.
258+
259+=sk_stack_pop= will pop a value off the stack and save it
260+to the stacklet variable =s=.
261+
262+=sk_stack_push= will push an initialized stacklet to the
263+stack, and save that value to stacklet variable =s= to
264+be filled with some item.
265+
266+#+NAME: funcdefs
267+#+BEGIN_SRC c
268+int sk_stack_pop(sk_stack *stack, sk_stacklet **out);
269+int sk_stack_push(sk_stack *stack, sk_stacklet **out);
270+#+END_SRC
271+
272+#+NAME: funcs
273+#+BEGIN_SRC c
274+int sk_stack_pop(sk_stack *stack, sk_stacklet **out)
275+{
276+ sk_stacklet *s;
277+ /* no items on stack */
278+ if (stack->pos < 0) return 1;
279+
280+ /* stack overflow */
281+ if (stack->pos >= SK_STACKSIZE) return 2;
282+
283+ s = &stack->stack[stack->pos];
284+ stack->pos--;
285+
286+ *out = s;
287+ return 0;
288+}
289+#+END_SRC
290+
291+#+NAME: funcs
292+#+BEGIN_SRC c
293+int sk_stack_push(sk_stack *stack, sk_stacklet **out)
294+{
295+ sk_stacklet *s;
296+
297+ if (stack->pos >= (SK_STACKSIZE - 1)) return 1;
298+ stack->pos++;
299+ s = &stack->stack[stack->pos];
300+
301+ sk_stacklet_init(s);
302+ *out = s;
303+ return 0;
304+}
305+#+END_SRC
306+* Parameters and Cables
307+=sndkit_param= is an abstraction used to deal with
308+patchwerk cables, and is designed to link up with the
309+sndkit stack and patchwerk nodes.
310+
311+A sndkit parameter can either be a patchwerk cable from a
312+node or a constant value. If it is cable, it will properly
313+manage the buffer stack in patchwerk. If it is a constant,
314+it will only manipulate the sndkit stack.
315+** Struct
316+A parameter is stored in a struct called =sk_param=.
317+
318+#+NAME: typedefs
319+#+BEGIN_SRC c
320+typedef struct {
321+ char type;
322+ union {
323+ pw_cable *c;
324+ SKFLT f;
325+ } data;
326+} sk_param;
327+#+END_SRC
328+** Getting a Parameter
329+Get a parameter from the core stack via =sk_param_get=.
330+
331+#+NAME: funcdefs
332+#+BEGIN_SRC c
333+int sk_param_get(sk_core *core, sk_param *p);
334+#+END_SRC
335+
336+Getting a parameter is a matter of popping from the stack
337+and checking the type. A constant will set the constant
338+value and flag in the param struct. A cable will set the
339+cable value and flag in the param struct, and will also pop
340+from the buffer stack.
341+
342+#+NAME: funcs
343+#+BEGIN_SRC c
344+int sk_param_get(sk_core *core, sk_param *p)
345+{
346+ sk_stack *stk;
347+ sk_stacklet *s;
348+ int rc;
349+
350+ stk = &core->stack;
351+
352+ rc = sk_stack_pop(stk, &s);
353+ SK_ERROR_CHECK(rc);
354+
355+ if (s->type == SK_TYPE_CONSTANT) {
356+ p->type = 0;
357+ p->data.f = s->f;
358+ } else if (s->type == SK_TYPE_CABLE) {
359+ p->type = 1;
360+ p->data.c = (pw_cable *)s->ptr;
361+ pw_cable_pop(p->data.c);
362+ } else {
363+ /* Wrong type! */
364+ return 1;
365+ }
366+
367+ return 0;
368+}
369+#+END_SRC
370+** Setting a Parameter
371+Set a parameter with =sk_param_set=.
372+
373+#+NAME: funcdefs
374+#+BEGIN_SRC c
375+int sk_param_set(sk_core *core,
376+ pw_node *node,
377+ sk_param *p,
378+ int cid);
379+#+END_SRC
380+
381+#+NAME: funcs
382+#+BEGIN_SRC c
383+int sk_param_set(sk_core *core,
384+ pw_node *node,
385+ sk_param *p,
386+ int cid)
387+{
388+ pw_cable *c;
389+
390+ pw_node_get_cable(node, cid, &c);
391+ if (p->type == 0) {
392+ pw_cable_set_value(c, p->data.f);
393+ } else {
394+ int rc;
395+ rc = pw_cable_connect(p->data.c, c);
396+ SK_PW_ERROR_CHECK(rc);
397+ }
398+ return 0;
399+}
400+#+END_SRC
401+
402+Setting a parameter will properly assign the internal value
403+to a cable of a node. This node's cable is referenced by
404+its index position. A constant parameter will set the node
405+cable as a consant. A cable parameter will be connected to
406+the node cable.
407+** Pushing Constants
408+Constant values can be pushed to the stack with the
409+function =sk_core_constant=.
410+
411+#+NAME: funcdefs
412+#+BEGIN_SRC c
413+int sk_core_constant(sk_core *core, SKFLT x);
414+#+END_SRC
415+
416+#+NAME: funcs
417+#+BEGIN_SRC c
418+int sk_core_constant(sk_core *core, SKFLT x)
419+{
420+ int rc;
421+ sk_stacklet *s;
422+ sk_stack *stk;
423+
424+ stk = &core->stack;
425+
426+ rc = sk_stack_push(stk, &s);
427+ SK_ERROR_CHECK(rc);
428+
429+ s->type = SK_TYPE_CONSTANT;
430+ s->f = x;
431+
432+ return 0;
433+}
434+#+END_SRC
435+** Pushing Output
436+An signal cable from a node is pushed to the stack via
437+=sk_param_out=.
438+
439+#+NAME: funcdefs
440+#+BEGIN_SRC c
441+int sk_param_out(sk_core *core,
442+ pw_node *node,
443+ int cid);
444+#+END_SRC
445+
446+Cables need to be pushed in the order they are created in
447+the patch. The onus is on the developers of the node to
448+make sure this is done properly. Don't worry,
449+this is less tricky than it sounds. If done correctly, this
450+process can be mostly automated or abstracted away.
451+
452+#+NAME: funcs
453+#+BEGIN_SRC c
454+int sk_param_out(sk_core *core,
455+ pw_node *node,
456+ int cid)
457+{
458+ pw_cable *c;
459+ sk_stacklet *s;
460+ sk_stack *stk;
461+ int rc;
462+
463+ stk = &core->stack;
464+
465+ rc = sk_stack_push(stk, &s);
466+ SK_ERROR_CHECK(rc);
467+ rc = pw_node_get_cable(node, cid, &c);
468+ SK_PW_ERROR_CHECK(rc);
469+
470+ s->type = SK_TYPE_CABLE;
471+ s->ptr = c;
472+
473+ return 0;
474+}
475+#+END_SRC
476+
477+=sk_param_out= will take an output cable of a node
478+(referenced by index), and push it onto the sndkit stack.
479+It will also push the cable's buffer onto the stack.
480+* Error Checking
481+=SK_ERROR_CHECK= is a convenient macro used that will
482+check an error code and exit if it is non-zero.
483+
484+#+NAME: macros
485+#+BEGIN_SRC c
486+#define SK_ERROR_CHECK(rc) if (rc) return rc;
487+#+END_SRC
488+
489+#+NAME: macros
490+#+BEGIN_SRC c
491+#define SK_PW_ERROR_CHECK(rc) if(rc != PW_OK) {\
492+ fprintf(stderr, "Error: %s\n", pw_error(rc));\
493+ return 1;\
494+}
495+#+END_SRC
496+* Tables
497+A small abstraction for dealing with tables that are managed
498+by patchwerk.
499+* Registers
500+** TODO regset/regget
501+** TODO regmrk
502+marks a register as being used
503+** TODO regclr
504+clears the register, making it free to be claimed.
505+** TODO regnxt
506+returns the next free register
507+* Buffer Operations
508+** TODO bdup
509+** TODO bhold/bunhold
510+** TODO bdrop

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t