~pbatch/patchwerk

patchwerk/patch.w -rw-r--r-- 14.8 KiB
9c265356 — paul plan9 additions from Sigrid 3 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
@** Patch.
\mkref{patch}
The patch interface provides an interface for controlling
and connecting multiple nodes and cables.

@<Top@>+=
@<Patch Data@>@/
@<Patch Top@>

@* Data. The struct for patch data is contained in a struct called
|pw_patch|.
@<Patch Data@>+=
struct pw_patch {
@<Variables in Patch Data@>@/
};

@ The |pw_patch| struct is forward declared as an opaque pointer.
@<Type Declarations@>+=
typedef struct pw_patch pw_patch;

@
% Nodes are stored in an array. The nodes are allocated at runtime, so the
% number of nodes that will be used must be known ahead of time. The number
% of active nodes are stored in a variable called nnodes.


@<Variables in Patch Data@>+=
pw_node *nodes;
pw_node *last;
int nnodes;

@ A patch has a delegate output cable. By default, this is set to be a
constant of zero via an internal cable.
@<Variables in Patch Data@>+=
pw_cable *out;
pw_cable zero;

@ The global blocksize is stored in the patch.
@<Variables in Patch Data@>+=
int blksize;

@ When dealing with single-sample rendering environments like Sporth or
Soundpipe, a counter is need to keep track of when to render a new block.
This function is kep in a variable called |counter|.

@<Variables in Patch Data@>+=
int counter;

@ Node ID management can be abstracted away inside of a patch through
an internal variable called |nodepos|, which keeps track of the current
node position.
@<Variables in Patch Data@>+=
int nodepos;

@ Any user data is contained in a linked list-type called a
|pw_pointerlist|.
@<Variables in Patch Data@>+=
pw_pointerlist plist;

@ Contained inside the patch data is a buffer pool.
@<Variables in Patch Data@>+=
pw_bufferpool pool;

@ The buffer pool is controlled via a LIFO (last in, first out)
stack interface.
@<Variables in Patch Data@>+=
pw_stack stack;

@ A variable storing the internal sampling rate can be used by nodes
created via the patchwerk interface.

@<Variables in Patch Data@>+=
int sr;

@ A generic |void*| pointer is used to store any persistent user data needed
by nodes. In the Runt interface, this is the Soundpipe data struct.

@<Variables in Patch Data@>+=
void *ud;

@ A global error code |err| can be used to notify patchwerk
that something has gone wrong, even if the user hasn't
explicitly checked for it. For example, |pw_node_set_block|
returns an error code that is rarely (if ever) checked.

@<Variables in Patch Data@>+=
int err;

@* Functions.

@ The function |pw_patch_init| initializes and zeros out variables inside of
|pw_patch|. No memory allocation happens here.
@<Header@>+=
void pw_patch_init(pw_patch *patch, int blksize);

@ @<Patch Top@>+=
void pw_patch_init(pw_patch *patch, int blksize)
{
    patch->blksize = blksize;
    pw_bufferpool_init(&patch->pool);
    pw_patch_srate_set(patch, 44100); /* default sampling rate of 44.1kHz */
    pw_memory_defaults(patch);
    pw_print_init(patch);
    pw_patch_reinit(patch);
}

@ The funcion |pw_patch_alloc| does all the memory allocation needed for
the patch. This includes allocating the buffer pool
|@<Buffer Pool Top@>| and buffer stack |@<Stack Top@>|.

The variable |nbuffers| is the number of buffers to be allocated in the
buffer pool, and the variable |stack_size| is the number of stack slots
to be allocated for the buffer stack.

Reasonable numbers for |nbuffers| and |stack_size| are 8 and 10, respectively.


@<Header@> +=
void pw_patch_alloc(pw_patch *patch, int nbuffers, int stack_size);

@ @<Patch Top@>+=
void pw_patch_alloc(pw_patch *patch, int nbuffers, int stack_size)
{
    pw_bufferpool_create(patch, &patch->pool, nbuffers, patch->blksize);
    pw_bufferpool_reset(&patch->pool);

    pw_stack_init(&patch->stack, &patch->pool);
    pw_stack_alloc(patch, &patch->stack, stack_size);
}

@ The function |pw_patch_realloc| will re-allocate the
buffer pool and stack.

@<Header@> +=
void pw_patch_realloc(pw_patch *patch,
    int nbuffers,
    int stack_size,
    int blksize);

@ Because the internal memory interface has no bindings for
|realloc|, the patch will free the existing stack and pool,
and allocate it. For this reason, use caution when calling
this!

@<Patch Top@>+=
void pw_patch_realloc(pw_patch *patch,
    int nbuffers,
    int stack_size,
    int blksize)
{
    pw_bufferpool_destroy(patch, &patch->pool);
    pw_stack_free(patch, &patch->stack);
    patch->blksize = blksize;
    pw_patch_alloc(patch, nbuffers, stack_size);
}

@ The function |pw_patch_reinit| can reinitialize the |pw_patch| interface.
This function is particularly useful for for implementing live coding
environments.
@<Header@>+=
void pw_patch_reinit(pw_patch *patch);

@ @<Patch Top@>+=
void pw_patch_reinit(pw_patch *patch)
{
    pw_cable_init(NULL, &patch->zero);
    patch->counter = 0;
    patch->nodepos = 0;

    pw_patch_clear(patch);

    pw_stack_reset(&patch->stack);
    pw_bufferpool_reset(&patch->pool);

    patch->err = PW_OK;
}

@ The function |pw_patch_clear| will partially reset a |pw_patch|. Clearing
is useful in some cases involving subpatches.

@<Header@>+=
void pw_patch_clear(pw_patch *patch);

@ Clearing a patch with |pw_patch_clear| is similar to reinitializing
a patch with |pw_patch_reinit|, except that it will not reset the
buffer stack or buffer pool. This function is useful can in some situations
involving subpatches. One such case of this would be using subpatches to build
a polyphonic synthesizer inside of a larger patch. If patchwerk is
reinitialied |pw_patch_reinit| instead of cleared with |pw_patch_clear|,
audio-rate signals existing on the buffer stack can be at risk of being
overwritten.

@<Patch Top@>+=
void pw_patch_clear(pw_patch *patch)
{
    patch->out = &patch->zero;
    patch->nodes = NULL;
    patch->last = NULL;
    patch->nnodes = 0;
    pw_pointerlist_init(&patch->plist);
}


@ Any nodes allocated with |pw_patch_new_node| must be freed with
|pw_patch_free_nodes|. Memory will only be freed if the number of nodes is
non-zero.
@<Header@>+=
void pw_patch_free_nodes(pw_patch *patch);

@ @<Patch Top@>+=
void pw_patch_free_nodes(pw_patch *patch)
{
    int i;
    pw_node *node;
    pw_node *next;
    node = patch->nodes;
    for(i = 0; i < patch->nnodes; i++) {
        next = pw_node_get_next(node);
        free(node);
        node = next;
    }

    patch->nnodes = 0;

}

@ The functions define below will call the setup, compute, and destroy
functions for each individual node.
@<Header@>+=
void pw_patch_setup(pw_patch *patch);
void pw_patch_destroy(pw_patch *patch);
void pw_patch_compute(pw_patch *patch);

@ @<Patch Top@>+=
void pw_patch_setup(pw_patch *patch)
{
    int n;
    pw_node *node;
    pw_node *next;
    node = patch->nodes;
    for(n = 0; n < patch->nnodes; n++) {
        next = pw_node_get_next(node);
        pw_node_setup(node);
        node = next;
    }
}

void pw_patch_destroy(pw_patch *patch)
{
    int n;
    pw_node *node;
    pw_node *next;
    node = patch->nodes;
    for(n = 0; n < patch->nnodes; n++) {
        next = pw_node_get_next(node);
        pw_node_destroy(node);
        node = next;
    }
    pw_pointerlist_free(&patch->plist);
    pw_bufferpool_destroy(patch, &patch->pool);
    pw_stack_free(patch, &patch->stack);
}

void pw_patch_compute(pw_patch *patch)
{
    int n;
    pw_node *node;
    pw_node *next;
    node = patch->nodes;
    for(n = 0; n < patch->nnodes; n++) {
        next = pw_node_get_next(node);
        pw_node_compute(node);
        node = next;
    }
}

@ The function |pw_patch_set_out| sets the output cable of the entire patch.
@<Header@>+=
void pw_patch_set_out(pw_patch *patch, pw_cable *cable);

@ @<Patch Top@>+=
void pw_patch_set_out(pw_patch *patch, pw_cable *cable)
{
    patch->out = cable;
}

@ The function |pw_patch_get_out| returns the output pointer of the patch.
@<Header@>+=
pw_cable* pw_patch_get_out(pw_patch *patch);

@ @<Patch Top@>+=
pw_cable* pw_patch_get_out(pw_patch *patch)
{
    return patch->out;
}

@ The function |pw_patch_size| returns the size of the |pw_patch| data struct.
@<Header@>+=
size_t pw_patch_size(void);

@ @<Patch Top@>+=
size_t pw_patch_size(void)
{
    return sizeof(pw_patch);
}

@* High Level Functions. These are functions that provide small abstractions
around common tasks.

@ The function |pw_patch_tick| is a tick function that can be used inside of
single-sample DSP function environments such as Soundpipe or Sporth.
It uses an internal counter that keeps track of when to render a new block
of audio.
@<Header@>+=
PWFLT pw_patch_tick(pw_patch *patch);

@ @<Patch Top@>+=
PWFLT pw_patch_tick(pw_patch *patch)
{
    PWFLT smp;
    pw_cable *out;
    if(patch->counter == 0) {
        pw_patch_compute(patch);
    }

    out = pw_patch_get_out(patch);
    smp = pw_cable_get(out, patch->counter);
    patch->counter = (patch->counter + 1) % patch->blksize;

    return smp;
}

@ The function |pw_patch_new_node| allocates a node and stores in the pointer
|node|.

@<Header@>+=
int pw_patch_new_node(pw_patch *patch, pw_node **node);

@ @<Patch Top@>+=
int pw_patch_new_node(pw_patch *patch, pw_node **node)
{
    pw_node *tmp;
    int rc;

    if(patch == NULL) return PW_NULL_VALUE;

    if(patch->err != PW_OK) return patch->err;

    rc = pw_memory_alloc(patch, sizeof(pw_node), (void **)&tmp);

    if(rc != PW_OK) return rc;

    pw_node_init(tmp, patch->blksize);
    pw_node_set_id(tmp, patch->nnodes);
    pw_node_set_patch(tmp, patch);

    if(patch->nnodes == 0) {
        patch->nodes = tmp;
    } else {
        pw_node_set_next(patch->last, tmp);
    }


    patch->nnodes++;
    patch->last = tmp;

    *node = tmp;

    return PW_OK;
}

@ The function |pw_patch_new_cable| allocates and initializes a new cable
that is independent from a node.

@<Header@>+=
int pw_patch_new_cable(pw_patch *patch, pw_cable **cable);

@ @<Patch Top@>+=

static void delete_cable(pw_pointer *p)
{
    pw_cable *c;
    c = pw_pointer_data(p);
    pw_memory_free(p->patch, (void **)&c);
}

int pw_patch_new_cable(pw_patch *patch, pw_cable **cable)
{
    pw_cable *tmp;
    int rc;
    rc = pw_memory_alloc(patch, sizeof(pw_cable), (void **)&tmp);
    if(rc != PW_OK) return rc;
    pw_cable_init(NULL, tmp);
    pw_patch_append_userdata(patch, delete_cable, tmp);
    *cable = tmp;
    return PW_OK;
}

@ The function |pw_patch_append_userdata| creates and appends a |pw_pointer|
to the internal |pw_pointerlist| inside of the patch.
@ @<Header@>+=
int pw_patch_append_userdata(
    pw_patch *patch,
    pw_pointer_function dfun,
    void *ud
);

@ @<Patch Top@>+=
int pw_patch_append_userdata(
    pw_patch *patch,
    pw_pointer_function dfun,
    void *ud
)
{
    pw_pointer *ptr;
    int rc;

    rc = pw_pointer_create(patch, &ptr, dfun, ud);
    if(rc != PW_OK) return rc;

    pw_pointerlist_append(&patch->plist, ptr);
    return PW_OK;
}

@ @<Header@>+= pw_stack * pw_patch_stack(pw_patch *patch);

@ The function |pw_patch_stack| returns the |pw_stack| contained inside
of a |pw_patch|.
@<Patch Top@>+=
pw_stack * pw_patch_stack(pw_patch *patch)
{
    return &patch->stack;
}

@ The audio block size can be returned using |pw_patch_blksize|.
@<Header@>+=
int pw_patch_blksize(pw_patch *patch);

@
@<Patch Top@>+=

int pw_patch_blksize(pw_patch *patch)
{
    return patch->blksize;
}

@ The function |pw_patch_pool| returns the bufferpool inside of a |pw_patch|
@<Header@>+=
pw_bufferpool * pw_patch_pool(pw_patch *patch);
@
@<Patch Top@>+=

pw_bufferpool * pw_patch_pool(pw_patch *patch)
{
    return &patch->pool;
}

@ The sampling rate can be set and retrieved using |pw_patch_srate_set| and
|pw_patch_srate_get|, respectively.

@<Header@>+=
void pw_patch_srate_set(pw_patch *patch, int sr);
int pw_patch_srate_get(pw_patch *patch);

@ @<Patch Top@>+=
void pw_patch_srate_set(pw_patch *patch, int sr)
{
    patch->sr = sr;
}

int pw_patch_srate_get(pw_patch *patch)
{
    return patch->sr;
}

@ The user data can be set ant get using |pw_patch_data_set| and
|pw_patch_data_get|, respectively.

@<Header@>+=
void pw_patch_data_set(pw_patch *patch, void *ud);
void *pw_patch_data_get(pw_patch *patch);

@ @<Patch Top@>+=
void pw_patch_data_set(pw_patch *patch, void *ud)
{
    patch->ud = ud;
}

void *pw_patch_data_get(pw_patch *patch)
{
    return patch->ud;
}

@ For holding buffers via cables, the functions |pw_patch_holdbuf| and
|pw_patch_unholdbuf| can be used. These helper functions will access and
hold the buffer inside of a patchwerk cable.

@<Header@>+=
void pw_patch_holdbuf(pw_patch *patch, pw_cable *c);
void pw_patch_unholdbuf(pw_patch *patch, pw_cable *c);

@

After the buffer is held, the |last_free| variable in the bufferpool is
reset with |pw_bufferpool_clear_last_free|, which forces the bufferpool
to query for a free buffer instead of using the cached value. This ensures
that the buffer being held doesn't accidently get reclaimed.

% TODO: do we need these functions anymore? bunhold and bhold are better.

@<Patch Top@>+=
void pw_patch_holdbuf(pw_patch *patch, pw_cable *c)
{
    pw_bufferpool *pool;
    pw_buffer *buf;

    if(pw_cable_is_constant(c)) return;

    pool = pw_patch_pool(patch);
    buf = pw_cable_get_buffer(c->pcable);
    pw_bufferpool_holdu(pool, buf);
    pw_bufferpool_clear_last_free(pool);
}

void pw_patch_unholdbuf(pw_patch *patch, pw_cable *c)
{
    pw_bufferpool *pool;
    pw_buffer *buf;

    if(pw_cable_is_constant(c)) return;
    pool = pw_patch_pool(patch);
    buf = pw_cable_get_buffer(c->pcable);
    pw_bufferpool_unholdu(pool, buf);
}

@ The functions |pw_patch_bhold| and |pw_patch_bunhold| patchwerk C functions
built to somewhat mimic the behaviors of the runt words bhold and bunhold.

The bhold word will pop the last buffer off the buffer stack and hold it. The
output buffer will be stored in the variable |b|, assuming it is not a null
value.

The bunhold word will take in a held buffer |b| to be unheld.

Both
functions will return |PW_OK| on success, and an error code on failure
(|PW_INVALID_BUFFER| will be used on null buffers, and |PW_NOT_OK|
for everything else).

@<Header@>+=
int pw_patch_bhold(pw_patch *patch, pw_buffer **b);
int pw_patch_bunhold(pw_patch *patch, pw_buffer *b);

@
It should be noted that in |pw_patch_bhold|,
|pw_stack_pop| makes the buffer available to be reassigned. This is cleared
with |pw_bufferpool_clear_last_free|.

@<Patch Top@>+=
int pw_patch_bhold(pw_patch *patch, pw_buffer **b)
{
    int rc;
    pw_stack *stack;
    pw_buffer *buf;
    pw_bufferpool *pool;

    stack = pw_patch_stack(patch);
    pool = pw_patch_pool(patch);
    rc = pw_stack_pop(stack, &buf);

    if(rc != PW_OK) return rc;

    pw_bufferpool_clear_last_free(pool);
    pw_bufferpool_holdu(pool, buf);

    if(b != NULL) *b = buf;

    return PW_OK;
}

int pw_patch_bunhold(pw_patch *patch, pw_buffer *b)
{
    pw_bufferpool *pool;
    int rc;

    pool = pw_patch_pool(patch);
    rc = pw_bufferpool_unholdu(pool, b);
    if(rc != PW_OK) return rc;

    return PW_OK;
}

@ The function |pw_patch_err| sets the global error code.
By setting this error code, this will force patchwerk to
shutdown next time a new node is created.

@<Header@>+=
void pw_patch_err(pw_patch *patch, int rc);

@

@<Patch Top@>+=
void pw_patch_err(pw_patch *patch, int rc)
{
    patch->err = rc;
}