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
3This document describes and outlines a small API for
4sndkit. It is primarily designed to be a small layer on top
5of Patchwerk, a library used to build up directed audio
6graphs. A large portion of the functionality is based on
7what is found in runt-patchwerk. Just simpler.
8
9In addition to maintaining an instance of patchwerk, the
10sndkit API provides a stack and a register system. The
11stack is the primary means of sharing data between nodes.
12Registers 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
46All this behavior is defined in a struct called =sk_core=.
47
48Creating/freeing is done with =sk_core_new= and
49=sk_core_del=.
50
51#+NAME: funcdefs
52#+BEGIN_SRC c
53sk_core * sk_core_new(int sr);
54void sk_core_del(sk_core *core);
55#+END_SRC
56
57#+NAME: funcs
58#+BEGIN_SRC c
59sk_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
79void 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
94typedef struct sk_core sk_core;
95#+END_SRC
96
97#+NAME: core
98#+BEGIN_SRC c
99struct sk_core {
100 pw_patch *patch;
101 sk_stack stack;
102};
103#+END_SRC
104
105A 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
110void sk_core_compute(sk_core *core);
111#+END_SRC
112
113#+NAME: funcs
114#+BEGIN_SRC c
115void sk_core_compute(sk_core *core)
116{
117 pw_patch_compute(core->patch);
118}
119#+END_SRC
120
121Building up nodes involves interacting with the patchwerk
122API. To get the top level struct of that opaquely, use
123=sk_core_patch=.
124
125#+NAME: funcdefs
126#+BEGIN_SRC c
127pw_patch * sk_core_patch(sk_core *core);
128#+END_SRC
129
130#+NAME: funcs
131#+BEGIN_SRC c
132pw_patch * sk_core_patch(sk_core *core)
133{
134 return core->patch;
135}
136#+END_SRC
137
138The function =sk_core_seconds_to_blocks= converts
139seconds to a number of render blocks.
140
141#+NAME: funcdefs
142#+BEGIN_SRC c
143size_t sk_core_seconds_to_blocks(sk_core *core, SKFLT secs);
144#+END_SRC
145
146#+NAME: funcs
147#+BEGIN_SRC c
148size_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
161An item on a stack is contained in a thing known as a
162=stacklet=. A stacklet has integer for a type flag, a
163floating point number for numerical values, and a generic
164pointer for C structures.
165
166#+NAME: typedefs
167#+BEGIN_SRC c
168typedef struct sk_stacklet sk_stacklet;
169#+END_SRC
170
171#+NAME: structs
172#+BEGIN_SRC c
173struct sk_stacklet {
174 int type;
175 SKFLT f;
176 void *ptr;
177};
178<<stack_struct>>
179#+END_SRC
180
181A stacklet is initialized with the function
182=sk_stacklet_init=.
183
184#+NAME: funcdefs
185#+BEGIN_SRC c
186void sk_stacklet_init(sk_stacklet *s);
187#+END_SRC
188
189#+NAME: funcs
190#+BEGIN_SRC c
191void 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
199An array of stacklets forms the foundation of a =sk_stack=.
200
201#+NAME: typedefs
202#+BEGIN_SRC c
203typedef struct sk_stack sk_stack;
204#+END_SRC
205
206The stack will be hard coded to be 16.
207
208An integer =pos= is used to keep track of position.
209
210#+NAME: stack_struct
211#+BEGIN_SRC c
212#define SK_STACKSIZE 16
213struct sk_stack {
214 sk_stacklet stack[SK_STACKSIZE];
215 int pos;
216};
217#+END_SRC
218
219A stack is initialized with =sk_stack_init=.
220
221#+NAME: funcdefs
222#+BEGIN_SRC c
223void sk_stack_init(sk_stack *s);
224#+END_SRC
225
226The position is set to be negative, indicating an empty
227stack.
228
229#+NAME: funcs
230#+BEGIN_SRC c
231void 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
243The typeflag currently supports the following types:
244
245#+NAME: enums
246#+BEGIN_SRC c
247enum {
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
256Push and pop are the core operations for the stack. Both
257return non-zero values on error.
258
259=sk_stack_pop= will pop a value off the stack and save it
260to the stacklet variable =s=.
261
262=sk_stack_push= will push an initialized stacklet to the
263stack, and save that value to stacklet variable =s= to
264be filled with some item.
265
266#+NAME: funcdefs
267#+BEGIN_SRC c
268int sk_stack_pop(sk_stack *stack, sk_stacklet **out);
269int sk_stack_push(sk_stack *stack, sk_stacklet **out);
270#+END_SRC
271
272#+NAME: funcs
273#+BEGIN_SRC c
274int 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
293int 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
308patchwerk cables, and is designed to link up with the
309sndkit stack and patchwerk nodes.
310
311A sndkit parameter can either be a patchwerk cable from a
312node or a constant value. If it is cable, it will properly
313manage the buffer stack in patchwerk. If it is a constant,
314it will only manipulate the sndkit stack.
315** Struct
316A parameter is stored in a struct called =sk_param=.
317
318#+NAME: typedefs
319#+BEGIN_SRC c
320typedef 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
329Get a parameter from the core stack via =sk_param_get=.
330
331#+NAME: funcdefs
332#+BEGIN_SRC c
333int sk_param_get(sk_core *core, sk_param *p);
334#+END_SRC
335
336Getting a parameter is a matter of popping from the stack
337and checking the type. A constant will set the constant
338value and flag in the param struct. A cable will set the
339cable value and flag in the param struct, and will also pop
340from the buffer stack.
341
342#+NAME: funcs
343#+BEGIN_SRC c
344int 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
371Set a parameter with =sk_param_set=.
372
373#+NAME: funcdefs
374#+BEGIN_SRC c
375int 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
383int 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
402Setting a parameter will properly assign the internal value
403to a cable of a node. This node's cable is referenced by
404its index position. A constant parameter will set the node
405cable as a consant. A cable parameter will be connected to
406the node cable.
407** Pushing Constants
408Constant values can be pushed to the stack with the
409function =sk_core_constant=.
410
411#+NAME: funcdefs
412#+BEGIN_SRC c
413int sk_core_constant(sk_core *core, SKFLT x);
414#+END_SRC
415
416#+NAME: funcs
417#+BEGIN_SRC c
418int 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
436An signal cable from a node is pushed to the stack via
437=sk_param_out=.
438
439#+NAME: funcdefs
440#+BEGIN_SRC c
441int sk_param_out(sk_core *core,
442 pw_node *node,
443 int cid);
444#+END_SRC
445
446Cables need to be pushed in the order they are created in
447the patch. The onus is on the developers of the node to
448make sure this is done properly. Don't worry,
449this is less tricky than it sounds. If done correctly, this
450process can be mostly automated or abstracted away.
451
452#+NAME: funcs
453#+BEGIN_SRC c
454int 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.
479It will also push the cable's buffer onto the stack.
480* Error Checking
481=SK_ERROR_CHECK= is a convenient macro used that will
482check 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
497A small abstraction for dealing with tables that are managed
498by patchwerk.
499* Registers
500** TODO regset/regget
501** TODO regmrk
502marks a register as being used
503** TODO regclr
504clears the register, making it free to be claimed.
505** TODO regnxt
506returns 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