summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/src/g_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/g_io.c')
-rw-r--r--apps/plugins/pdbox/PDa/src/g_io.c1224
1 files changed, 1224 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/g_io.c b/apps/plugins/pdbox/PDa/src/g_io.c
new file mode 100644
index 0000000000..39788d2adb
--- /dev/null
+++ b/apps/plugins/pdbox/PDa/src/g_io.c
@@ -0,0 +1,1224 @@
1/* Copyright (c) 1997-1999 Miller Puckette.
2* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
4
5/* graphical inlets and outlets, both for control and signals. */
6
7/* This code is highly inefficient; messages actually have to be forwarded
8by inlets and outlets. The outlet is in even worse shape than the inlet;
9in order to avoid having a "signal" method in the class, the oulet actually
10sprouts an inlet, which forwards the message to the "outlet" object, which
11sends it on to the outlet proper. Another way to do it would be to have
12separate classes for "signal" and "control" outlets, but this would complicate
13life elsewhere. */
14
15
16/* hacked to run subpatches with different samplerates
17 *
18 * mfg.gfd.uil
19 * IOhannes
20 *
21 * edited lines are marked with "IOhannes"
22 *
23 */
24
25#include "m_pd.h"
26#include "g_canvas.h"
27#include <string.h>
28void signal_setborrowed(t_signal *sig, t_signal *sig2);
29void signal_makereusable(t_signal *sig);
30
31/* ------------------------- vinlet -------------------------- */
32t_class *vinlet_class;
33
34typedef struct _vinlet
35{
36 t_object x_obj;
37 t_canvas *x_canvas;
38 t_inlet *x_inlet;
39 int x_bufsize;
40 t_float *x_buf; /* signal buffer; zero if not a signal */
41 t_float *x_endbuf;
42 t_float *x_fill;
43 t_float *x_read;
44 int x_hop;
45 /* if not reblocking, the next slot communicates the parent's inlet
46 signal from the prolog to the DSP routine: */
47 t_signal *x_directsignal;
48
49 t_resample x_updown; /* IOhannes */
50} t_vinlet;
51
52static void *vinlet_new(t_symbol *s)
53{
54 t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
55 x->x_canvas = canvas_getcurrent();
56 x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0);
57 x->x_bufsize = 0;
58 x->x_buf = 0;
59 outlet_new(&x->x_obj, 0);
60 return (x);
61}
62
63static void vinlet_bang(t_vinlet *x)
64{
65 outlet_bang(x->x_obj.ob_outlet);
66}
67
68static void vinlet_pointer(t_vinlet *x, t_gpointer *gp)
69{
70 outlet_pointer(x->x_obj.ob_outlet, gp);
71}
72
73static void vinlet_float(t_vinlet *x, t_float f)
74{
75 outlet_float(x->x_obj.ob_outlet, f);
76}
77
78static void vinlet_symbol(t_vinlet *x, t_symbol *s)
79{
80 outlet_symbol(x->x_obj.ob_outlet, s);
81}
82
83static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
84{
85 outlet_list(x->x_obj.ob_outlet, s, argc, argv);
86}
87
88static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
89{
90 outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
91}
92
93static void vinlet_free(t_vinlet *x)
94{
95 canvas_rminlet(x->x_canvas, x->x_inlet);
96 resample_free(&x->x_updown);
97}
98
99t_inlet *vinlet_getit(t_pd *x)
100{
101 if (pd_class(x) != vinlet_class) bug("vinlet_getit");
102 return (((t_vinlet *)x)->x_inlet);
103}
104
105/* ------------------------- signal inlet -------------------------- */
106int vinlet_issignal(t_vinlet *x)
107{
108 return (x->x_buf != 0);
109}
110
111static int tot;
112
113t_int *vinlet_perform(t_int *w)
114{
115 t_vinlet *x = (t_vinlet *)(w[1]);
116 t_float *out = (t_float *)(w[2]);
117 int n = (int)(w[3]);
118 t_float *in = x->x_read;
119#if 0
120 if (tot < 5) post("-in %x out %x n %d", in, out, n);
121 if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
122 if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
123#endif
124 while (n--) *out++ = *in++;
125 if (in == x->x_endbuf) in = x->x_buf;
126 x->x_read = in;
127 return (w+4);
128}
129
130static void vinlet_dsp(t_vinlet *x, t_signal **sp)
131{
132 t_signal *outsig;
133 /* no buffer means we're not a signal inlet */
134 if (!x->x_buf)
135 return;
136 outsig = sp[0];
137 if (x->x_directsignal)
138 {
139 signal_setborrowed(sp[0], x->x_directsignal);
140 }
141 else
142 {
143 dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n);
144 x->x_read = x->x_buf;
145 }
146}
147
148 /* prolog code: loads buffer from parent patch */
149t_int *vinlet_doprolog(t_int *w)
150{
151 t_vinlet *x = (t_vinlet *)(w[1]);
152 t_float *in = (t_float *)(w[2]);
153 int n = (int)(w[3]);
154 t_float *out = x->x_fill;
155 if (out == x->x_endbuf)
156 {
157 t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop;
158 int nshift = x->x_bufsize - x->x_hop;
159 out -= x->x_hop;
160 while (nshift--) *f1++ = *f2++;
161 }
162#if 0
163 if (tot < 5) post("in %x out %x n %x", in, out, n), tot++;
164 if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
165#endif
166
167 while (n--) *out++ = *in++;
168 x->x_fill = out;
169 return (w+4);
170}
171
172int inlet_getsignalindex(t_inlet *x);
173
174 /* set up prolog DSP code */
175void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs,
176 int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock,
177 int switched)
178{
179 t_signal *insig, *outsig;
180 x->x_updown.downsample = downsample;
181 x->x_updown.upsample = upsample;
182
183 /* if the "reblock" flag is set, arrange to copy data in from the
184 parent. */
185 if (reblock)
186 {
187 int parentvecsize, bufsize, oldbufsize, prologphase;
188 int re_parentvecsize; /* resampled parentvectorsize: IOhannes */
189 /* this should never happen: */
190 if (!x->x_buf) return;
191
192 /* the prolog code counts from 0 to period-1; the
193 phase is backed up by one so that AFTER the prolog code
194 runs, the "x_fill" phase is in sync with the "x_read" phase. */
195 prologphase = (phase - 1) & (period - 1);
196 if (parentsigs)
197 {
198 insig = parentsigs[inlet_getsignalindex(x->x_inlet)];
199 parentvecsize = insig->s_n;
200 re_parentvecsize = parentvecsize * upsample / downsample;
201 }
202 else
203 {
204 insig = 0;
205 parentvecsize = 1;
206 re_parentvecsize = 1;
207 }
208
209 bufsize = re_parentvecsize;
210 if (bufsize < myvecsize) bufsize = myvecsize;
211 if (bufsize != (oldbufsize = x->x_bufsize))
212 {
213 t_float *buf = x->x_buf;
214 t_freebytes(buf, oldbufsize * sizeof(*buf));
215 buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
216 memset((char *)buf, 0, bufsize * sizeof(*buf));
217 x->x_bufsize = bufsize;
218 x->x_endbuf = buf + bufsize;
219 x->x_buf = buf;
220 }
221 if (parentsigs)
222 {
223 /* IOhannes { */
224 x->x_hop = period * re_parentvecsize;
225
226 x->x_fill = x->x_endbuf -
227 (x->x_hop - prologphase * re_parentvecsize);
228
229 if (upsample * downsample == 1)
230 dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize);
231 else {
232 resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method);
233 dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize);
234 }
235
236 /* } IOhannes */
237 /* if the input signal's reference count is zero, we have
238 to free it here because we didn't in ugen_doit(). */
239 if (!insig->s_refcount)
240 signal_makereusable(insig);
241 }
242 else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf));
243 x->x_directsignal = 0;
244 }
245 else
246 {
247 /* no reblocking; in this case our output signal is "borrowed"
248 and merely needs to be pointed to the real one. */
249 x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)];
250 }
251}
252
253//static void *vinlet_newsig(void)
254static void *vinlet_newsig(t_symbol *s)
255{
256 t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
257 x->x_canvas = canvas_getcurrent();
258 x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal);
259 x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
260 x->x_bufsize = 0;
261 x->x_directsignal = 0;
262 outlet_new(&x->x_obj, &s_signal);
263
264 resample_init(&x->x_updown);
265
266 /* this should be though over:
267 * it might prove hard to provide consistency between labeled up- & downsampling methods
268 * maybe indeces would be better...
269 *
270 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
271 */
272 if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
273 else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
274 else x->x_updown.method=0; /* up: zero-padding */
275
276 return (x);
277}
278
279static void vinlet_setup(void)
280{
281 vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new,
282 (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0);
283 class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0);
284 class_addbang(vinlet_class, vinlet_bang);
285 class_addpointer(vinlet_class, vinlet_pointer);
286 class_addfloat(vinlet_class, vinlet_float);
287 class_addsymbol(vinlet_class, vinlet_symbol);
288 class_addlist(vinlet_class, vinlet_list);
289 class_addanything(vinlet_class, vinlet_anything);
290 class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0);
291 class_sethelpsymbol(vinlet_class, gensym("pd"));
292}
293
294/* ------------------------- voutlet -------------------------- */
295
296t_class *voutlet_class;
297
298typedef struct _voutlet
299{
300 t_object x_obj;
301 t_canvas *x_canvas;
302 t_outlet *x_parentoutlet;
303 int x_bufsize;
304 t_sample *x_buf; /* signal buffer; zero if not a signal */
305 t_sample *x_endbuf;
306 t_sample *x_empty; /* next to read out of buffer in epilog code */
307 t_sample *x_write; /* next to write in to buffer */
308 int x_hop; /* hopsize */
309 /* vice versa from the inlet, if we don't block, this holds the
310 parent's outlet signal, valid between the prolog and the dsp setup
311 routines. */
312 t_signal *x_directsignal;
313 /* and here's a flag indicating that we aren't blocked but have to
314 do a copy (because we're switched). */
315 char x_justcopyout;
316 t_resample x_updown; /* IOhannes */
317} t_voutlet;
318
319static void *voutlet_new(t_symbol *s)
320{
321 t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
322 x->x_canvas = canvas_getcurrent();
323 x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0);
324 inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0);
325 x->x_bufsize = 0;
326 x->x_buf = 0;
327 return (x);
328}
329
330static void voutlet_bang(t_voutlet *x)
331{
332 outlet_bang(x->x_parentoutlet);
333}
334
335static void voutlet_pointer(t_voutlet *x, t_gpointer *gp)
336{
337 outlet_pointer(x->x_parentoutlet, gp);
338}
339
340static void voutlet_float(t_voutlet *x, t_float f)
341{
342 outlet_float(x->x_parentoutlet, f);
343}
344
345static void voutlet_symbol(t_voutlet *x, t_symbol *s)
346{
347 outlet_symbol(x->x_parentoutlet, s);
348}
349
350static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
351{
352 outlet_list(x->x_parentoutlet, s, argc, argv);
353}
354
355static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
356{
357 outlet_anything(x->x_parentoutlet, s, argc, argv);
358}
359
360static void voutlet_free(t_voutlet *x)
361{
362 canvas_rmoutlet(x->x_canvas, x->x_parentoutlet);
363 resample_free(&x->x_updown);
364}
365
366t_outlet *voutlet_getit(t_pd *x)
367{
368 if (pd_class(x) != voutlet_class) bug("voutlet_getit");
369 return (((t_voutlet *)x)->x_parentoutlet);
370}
371
372/* ------------------------- signal outlet -------------------------- */
373
374int voutlet_issignal(t_voutlet *x)
375{
376 return (x->x_buf != 0);
377}
378
379 /* LATER optimize for non-overlapped case where the "+=" isn't needed */
380t_int *voutlet_perform(t_int *w)
381{
382 t_voutlet *x = (t_voutlet *)(w[1]);
383 t_sample *in = (t_sample *)(w[2]);
384 int n = (int)(w[3]);
385 t_sample *out = x->x_write, *outwas = out;
386#if 0
387 if (tot < 5) post("-in %x out %x n %d", in, out, n);
388 if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
389#endif
390 while (n--)
391 {
392 *out++ += *in++;
393 if (out == x->x_endbuf) out = x->x_buf;
394 }
395 outwas += x->x_hop;
396 if (outwas >= x->x_endbuf) outwas = x->x_buf;
397 x->x_write = outwas;
398 return (w+4);
399}
400
401 /* epilog code for blocking: write buffer to parent patch */
402static t_int *voutlet_doepilog(t_int *w)
403{
404 t_voutlet *x = (t_voutlet *)(w[1]);
405 t_sample *out = (t_sample *)(w[2]); /* IOhannes */
406
407 int n = (int)(w[3]);
408 t_sample *in = x->x_empty;
409 if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */
410
411#if 0
412 if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
413#endif
414 for (; n--; in++) *out++ = *in, *in = 0;
415 if (in == x->x_endbuf) in = x->x_buf;
416 x->x_empty = in;
417 return (w+4);
418}
419
420/* IOhannes { */
421static t_int *voutlet_doepilog_resampling(t_int *w)
422{
423 t_voutlet *x = (t_voutlet *)(w[1]);
424 // t_float *dummy = (t_float *)(w[2]);
425 int n = (int)(w[2]);
426 t_sample *in = x->x_empty;
427 t_sample *out = x->x_updown.s_vec; /* IOhannes */
428
429#if 0
430 if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
431#endif
432 for (; n--; in++) *out++ = *in, *in = 0;
433 if (in == x->x_endbuf) in = x->x_buf;
434 x->x_empty = in;
435 return (w+3);
436}
437/* } IOhannes */
438int outlet_getsignalindex(t_outlet *x);
439
440 /* prolog for outlets -- store pointer to the outlet on the
441 parent, which, if "reblock" is false, will want to refer
442 back to whatever we see on our input during the "dsp" method
443 called later. */
444void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs,
445 int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
446 int switched)
447{
448 x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
449 x->x_justcopyout = (switched && !reblock);
450 if (reblock)
451 {
452 x->x_directsignal = 0;
453 }
454 else
455 {
456 if (!parentsigs) bug("voutlet_dspprolog");
457 x->x_directsignal =
458 parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
459 }
460}
461
462static void voutlet_dsp(t_voutlet *x, t_signal **sp)
463{
464 t_signal *insig;
465 if (!x->x_buf) return;
466 insig = sp[0];
467 if (x->x_justcopyout)
468 dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n);
469 else if (x->x_directsignal)
470 {
471 /* if we're just going to make the signal available on the
472 parent patch, hand it off to the parent signal. */
473 /* this is done elsewhere--> sp[0]->s_refcount++; */
474 signal_setborrowed(x->x_directsignal, sp[0]);
475 }
476 else
477 dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n);
478}
479
480 /* set up epilog DSP code. If we're reblocking, this is the
481 time to copy the samples out to the containing object's outlets.
482 If we aren't reblocking, there's nothing to do here. */
483void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs,
484 int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
485 int switched)
486{
487 if (!x->x_buf) return; /* this shouldn't be necesssary... */
488 x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
489 if (reblock)
490 {
491 t_signal *insig, *outsig;
492 int parentvecsize, bufsize, oldbufsize;
493 int re_parentvecsize; /* IOhannes */
494 int bigperiod, epilogphase, blockphase;
495 if (parentsigs)
496 {
497 outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
498 parentvecsize = outsig->s_n;
499 re_parentvecsize = parentvecsize * upsample / downsample;
500 }
501 else
502 {
503 outsig = 0;
504 parentvecsize = 1;
505 re_parentvecsize = 1;
506 }
507 // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */
508 bigperiod = myvecsize/re_parentvecsize; /* IOhannes */
509 if (!bigperiod) bigperiod = 1;
510 epilogphase = phase & (bigperiod - 1);
511 blockphase = (phase + period - 1) & (bigperiod - 1) & (- period);
512 // bufsize = parentvecsize * upsample; /* IOhannes */
513 bufsize = re_parentvecsize; /* IOhannes */
514 if (bufsize < myvecsize) bufsize = myvecsize;
515 if (bufsize != (oldbufsize = x->x_bufsize))
516 {
517 t_sample *buf = x->x_buf;
518 t_freebytes(buf, oldbufsize * sizeof(*buf));
519 buf = (t_sample *)t_getbytes(bufsize * sizeof(*buf));
520 memset((char *)buf, 0, bufsize * sizeof(*buf));
521 x->x_bufsize = bufsize;
522 x->x_endbuf = buf + bufsize;
523 x->x_buf = buf;
524 }
525 /* IOhannes: { */
526 if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog");
527 x->x_write = x->x_buf + re_parentvecsize * blockphase;
528 if (x->x_write == x->x_endbuf) x->x_write = x->x_buf;
529 if (period == 1 && frequency > 1)
530 x->x_hop = re_parentvecsize / frequency;
531 else x->x_hop = period * re_parentvecsize;
532 /* } IOhannes */
533 /* post("phase %d, block %d, parent %d", phase & 63,
534 parentvecsize * blockphase, parentvecsize * epilogphase); */
535 if (parentsigs)
536 {
537 /* set epilog pointer and schedule it */
538 /* IOhannes { */
539 x->x_empty = x->x_buf + re_parentvecsize * epilogphase;
540 if (upsample * downsample == 1)
541 dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize);
542 else {
543 dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize);
544 resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method);
545 }
546 /* } IOhannes */
547 }
548 }
549 /* if we aren't blocked but we are switched, the epilog code just
550 copies zeros to the output. In this case the blocking code actually
551 jumps over the epilog if the block is running. */
552 else if (switched)
553 {
554 if (parentsigs)
555 {
556 t_signal *outsig =
557 parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
558 dsp_add_zero(outsig->s_vec, outsig->s_n);
559 }
560 }
561}
562
563static void *voutlet_newsig(t_symbol *s)
564{
565 t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
566 x->x_canvas = canvas_getcurrent();
567 x->x_parentoutlet = canvas_addoutlet(x->x_canvas,
568 &x->x_obj.ob_pd, &s_signal);
569 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
570 x->x_endbuf = x->x_buf = (t_sample *)getbytes(0);
571 x->x_bufsize = 0;
572
573 resample_init(&x->x_updown);
574
575 /* this should be though over:
576 * it might prove hard to provide consistency between labeled up- & downsampling methods
577 * maybe indeces would be better...
578 *
579 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
580 */
581 if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
582 else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
583 else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */
584 else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */
585
586 return (x);
587}
588
589
590static void voutlet_setup(void)
591{
592 voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new,
593 (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0);
594 class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0);
595 class_addbang(voutlet_class, voutlet_bang);
596 class_addpointer(voutlet_class, voutlet_pointer);
597 class_addfloat(voutlet_class, (t_method)voutlet_float);
598 class_addsymbol(voutlet_class, voutlet_symbol);
599 class_addlist(voutlet_class, voutlet_list);
600 class_addanything(voutlet_class, voutlet_anything);
601 class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0);
602 class_sethelpsymbol(voutlet_class, gensym("pd"));
603}
604
605
606/* ---------------------------- overall setup ----------------------------- */
607
608void g_io_setup(void)
609{
610 vinlet_setup();
611 voutlet_setup();
612}
613/* Copyright (c) 1997-1999 Miller Puckette.
614* For information on usage and redistribution, and for a DISCLAIMER OF ALL
615* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
616
617/* graphical inlets and outlets, both for control and signals. */
618
619/* This code is highly inefficient; messages actually have to be forwarded
620by inlets and outlets. The outlet is in even worse shape than the inlet;
621in order to avoid having a "signal" method in the class, the oulet actually
622sprouts an inlet, which forwards the message to the "outlet" object, which
623sends it on to the outlet proper. Another way to do it would be to have
624separate classes for "signal" and "control" outlets, but this would complicate
625life elsewhere. */
626
627
628/* hacked to run subpatches with different samplerates
629 *
630 * mfg.gfd.uil
631 * IOhannes
632 *
633 * edited lines are marked with "IOhannes"
634 *
635 */
636
637#include "m_pd.h"
638#include "g_canvas.h"
639#include <string.h>
640void signal_setborrowed(t_signal *sig, t_signal *sig2);
641void signal_makereusable(t_signal *sig);
642
643/* ------------------------- vinlet -------------------------- */
644t_class *vinlet_class;
645
646typedef struct _vinlet
647{
648 t_object x_obj;
649 t_canvas *x_canvas;
650 t_inlet *x_inlet;
651 int x_bufsize;
652 t_float *x_buf; /* signal buffer; zero if not a signal */
653 t_float *x_endbuf;
654 t_float *x_fill;
655 t_float *x_read;
656 int x_hop;
657 /* if not reblocking, the next slot communicates the parent's inlet
658 signal from the prolog to the DSP routine: */
659 t_signal *x_directsignal;
660
661 t_resample x_updown; /* IOhannes */
662} t_vinlet;
663
664static void *vinlet_new(t_symbol *s)
665{
666 t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
667 x->x_canvas = canvas_getcurrent();
668 x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0);
669 x->x_bufsize = 0;
670 x->x_buf = 0;
671 outlet_new(&x->x_obj, 0);
672 return (x);
673}
674
675static void vinlet_bang(t_vinlet *x)
676{
677 outlet_bang(x->x_obj.ob_outlet);
678}
679
680static void vinlet_pointer(t_vinlet *x, t_gpointer *gp)
681{
682 outlet_pointer(x->x_obj.ob_outlet, gp);
683}
684
685static void vinlet_float(t_vinlet *x, t_float f)
686{
687 outlet_float(x->x_obj.ob_outlet, f);
688}
689
690static void vinlet_symbol(t_vinlet *x, t_symbol *s)
691{
692 outlet_symbol(x->x_obj.ob_outlet, s);
693}
694
695static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
696{
697 outlet_list(x->x_obj.ob_outlet, s, argc, argv);
698}
699
700static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
701{
702 outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
703}
704
705static void vinlet_free(t_vinlet *x)
706{
707 canvas_rminlet(x->x_canvas, x->x_inlet);
708 resample_free(&x->x_updown);
709}
710
711t_inlet *vinlet_getit(t_pd *x)
712{
713 if (pd_class(x) != vinlet_class) bug("vinlet_getit");
714 return (((t_vinlet *)x)->x_inlet);
715}
716
717/* ------------------------- signal inlet -------------------------- */
718int vinlet_issignal(t_vinlet *x)
719{
720 return (x->x_buf != 0);
721}
722
723static int tot;
724
725t_int *vinlet_perform(t_int *w)
726{
727 t_vinlet *x = (t_vinlet *)(w[1]);
728 t_float *out = (t_float *)(w[2]);
729 int n = (int)(w[3]);
730 t_float *in = x->x_read;
731#if 0
732 if (tot < 5) post("-in %x out %x n %d", in, out, n);
733 if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
734 if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
735#endif
736 while (n--) *out++ = *in++;
737 if (in == x->x_endbuf) in = x->x_buf;
738 x->x_read = in;
739 return (w+4);
740}
741
742static void vinlet_dsp(t_vinlet *x, t_signal **sp)
743{
744 t_signal *outsig;
745 /* no buffer means we're not a signal inlet */
746 if (!x->x_buf)
747 return;
748 outsig = sp[0];
749 if (x->x_directsignal)
750 {
751 signal_setborrowed(sp[0], x->x_directsignal);
752 }
753 else
754 {
755 dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n);
756 x->x_read = x->x_buf;
757 }
758}
759
760 /* prolog code: loads buffer from parent patch */
761t_int *vinlet_doprolog(t_int *w)
762{
763 t_vinlet *x = (t_vinlet *)(w[1]);
764 t_float *in = (t_float *)(w[2]);
765 int n = (int)(w[3]);
766 t_float *out = x->x_fill;
767 if (out == x->x_endbuf)
768 {
769 t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop;
770 int nshift = x->x_bufsize - x->x_hop;
771 out -= x->x_hop;
772 while (nshift--) *f1++ = *f2++;
773 }
774#if 0
775 if (tot < 5) post("in %x out %x n %x", in, out, n), tot++;
776 if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
777#endif
778
779 while (n--) *out++ = *in++;
780 x->x_fill = out;
781 return (w+4);
782}
783
784int inlet_getsignalindex(t_inlet *x);
785
786 /* set up prolog DSP code */
787void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs,
788 int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock,
789 int switched)
790{
791 t_signal *insig, *outsig;
792 x->x_updown.downsample = downsample;
793 x->x_updown.upsample = upsample;
794
795 /* if the "reblock" flag is set, arrange to copy data in from the
796 parent. */
797 if (reblock)
798 {
799 int parentvecsize, bufsize, oldbufsize, prologphase;
800 int re_parentvecsize; /* resampled parentvectorsize: IOhannes */
801 /* this should never happen: */
802 if (!x->x_buf) return;
803
804 /* the prolog code counts from 0 to period-1; the
805 phase is backed up by one so that AFTER the prolog code
806 runs, the "x_fill" phase is in sync with the "x_read" phase. */
807 prologphase = (phase - 1) & (period - 1);
808 if (parentsigs)
809 {
810 insig = parentsigs[inlet_getsignalindex(x->x_inlet)];
811 parentvecsize = insig->s_n;
812 re_parentvecsize = parentvecsize * upsample / downsample;
813 }
814 else
815 {
816 insig = 0;
817 parentvecsize = 1;
818 re_parentvecsize = 1;
819 }
820
821 bufsize = re_parentvecsize;
822 if (bufsize < myvecsize) bufsize = myvecsize;
823 if (bufsize != (oldbufsize = x->x_bufsize))
824 {
825 t_float *buf = x->x_buf;
826 t_freebytes(buf, oldbufsize * sizeof(*buf));
827 buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
828 memset((char *)buf, 0, bufsize * sizeof(*buf));
829 x->x_bufsize = bufsize;
830 x->x_endbuf = buf + bufsize;
831 x->x_buf = buf;
832 }
833 if (parentsigs)
834 {
835 /* IOhannes { */
836 x->x_hop = period * re_parentvecsize;
837
838 x->x_fill = x->x_endbuf -
839 (x->x_hop - prologphase * re_parentvecsize);
840
841 if (upsample * downsample == 1)
842 dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize);
843 else {
844 resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method);
845 dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize);
846 }
847
848 /* } IOhannes */
849 /* if the input signal's reference count is zero, we have
850 to free it here because we didn't in ugen_doit(). */
851 if (!insig->s_refcount)
852 signal_makereusable(insig);
853 }
854 else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf));
855 x->x_directsignal = 0;
856 }
857 else
858 {
859 /* no reblocking; in this case our output signal is "borrowed"
860 and merely needs to be pointed to the real one. */
861 x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)];
862 }
863}
864
865//static void *vinlet_newsig(void)
866static void *vinlet_newsig(t_symbol *s)
867{
868 t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
869 x->x_canvas = canvas_getcurrent();
870 x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal);
871 x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
872 x->x_bufsize = 0;
873 x->x_directsignal = 0;
874 outlet_new(&x->x_obj, &s_signal);
875
876 resample_init(&x->x_updown);
877
878 /* this should be though over:
879 * it might prove hard to provide consistency between labeled up- & downsampling methods
880 * maybe indeces would be better...
881 *
882 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
883 */
884 if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
885 else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
886 else x->x_updown.method=0; /* up: zero-padding */
887
888 return (x);
889}
890
891static void vinlet_setup(void)
892{
893 vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new,
894 (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0);
895 class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0);
896 class_addbang(vinlet_class, vinlet_bang);
897 class_addpointer(vinlet_class, vinlet_pointer);
898 class_addfloat(vinlet_class, vinlet_float);
899 class_addsymbol(vinlet_class, vinlet_symbol);
900 class_addlist(vinlet_class, vinlet_list);
901 class_addanything(vinlet_class, vinlet_anything);
902 class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0);
903 class_sethelpsymbol(vinlet_class, gensym("pd"));
904}
905
906/* ------------------------- voutlet -------------------------- */
907
908t_class *voutlet_class;
909
910typedef struct _voutlet
911{
912 t_object x_obj;
913 t_canvas *x_canvas;
914 t_outlet *x_parentoutlet;
915 int x_bufsize;
916 t_sample *x_buf; /* signal buffer; zero if not a signal */
917 t_sample *x_endbuf;
918 t_sample *x_empty; /* next to read out of buffer in epilog code */
919 t_sample *x_write; /* next to write in to buffer */
920 int x_hop; /* hopsize */
921 /* vice versa from the inlet, if we don't block, this holds the
922 parent's outlet signal, valid between the prolog and the dsp setup
923 routines. */
924 t_signal *x_directsignal;
925 /* and here's a flag indicating that we aren't blocked but have to
926 do a copy (because we're switched). */
927 char x_justcopyout;
928 t_resample x_updown; /* IOhannes */
929} t_voutlet;
930
931static void *voutlet_new(t_symbol *s)
932{
933 t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
934 x->x_canvas = canvas_getcurrent();
935 x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0);
936 inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0);
937 x->x_bufsize = 0;
938 x->x_buf = 0;
939 return (x);
940}
941
942static void voutlet_bang(t_voutlet *x)
943{
944 outlet_bang(x->x_parentoutlet);
945}
946
947static void voutlet_pointer(t_voutlet *x, t_gpointer *gp)
948{
949 outlet_pointer(x->x_parentoutlet, gp);
950}
951
952static void voutlet_float(t_voutlet *x, t_float f)
953{
954 outlet_float(x->x_parentoutlet, f);
955}
956
957static void voutlet_symbol(t_voutlet *x, t_symbol *s)
958{
959 outlet_symbol(x->x_parentoutlet, s);
960}
961
962static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
963{
964 outlet_list(x->x_parentoutlet, s, argc, argv);
965}
966
967static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
968{
969 outlet_anything(x->x_parentoutlet, s, argc, argv);
970}
971
972static void voutlet_free(t_voutlet *x)
973{
974 canvas_rmoutlet(x->x_canvas, x->x_parentoutlet);
975 resample_free(&x->x_updown);
976}
977
978t_outlet *voutlet_getit(t_pd *x)
979{
980 if (pd_class(x) != voutlet_class) bug("voutlet_getit");
981 return (((t_voutlet *)x)->x_parentoutlet);
982}
983
984/* ------------------------- signal outlet -------------------------- */
985
986int voutlet_issignal(t_voutlet *x)
987{
988 return (x->x_buf != 0);
989}
990
991 /* LATER optimize for non-overlapped case where the "+=" isn't needed */
992t_int *voutlet_perform(t_int *w)
993{
994 t_voutlet *x = (t_voutlet *)(w[1]);
995 t_sample *in = (t_sample *)(w[2]);
996 int n = (int)(w[3]);
997 t_sample *out = x->x_write, *outwas = out;
998#if 0
999 if (tot < 5) post("-in %x out %x n %d", in, out, n);
1000 if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
1001#endif
1002 while (n--)
1003 {
1004 *out++ += *in++;
1005 if (out == x->x_endbuf) out = x->x_buf;
1006 }
1007 outwas += x->x_hop;
1008 if (outwas >= x->x_endbuf) outwas = x->x_buf;
1009 x->x_write = outwas;
1010 return (w+4);
1011}
1012
1013 /* epilog code for blocking: write buffer to parent patch */
1014static t_int *voutlet_doepilog(t_int *w)
1015{
1016 t_voutlet *x = (t_voutlet *)(w[1]);
1017 t_sample *out = (t_sample *)(w[2]); /* IOhannes */
1018
1019 int n = (int)(w[3]);
1020 t_sample *in = x->x_empty;
1021 if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */
1022
1023#if 0
1024 if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
1025#endif
1026 for (; n--; in++) *out++ = *in, *in = 0;
1027 if (in == x->x_endbuf) in = x->x_buf;
1028 x->x_empty = in;
1029 return (w+4);
1030}
1031
1032/* IOhannes { */
1033static t_int *voutlet_doepilog_resampling(t_int *w)
1034{
1035 t_voutlet *x = (t_voutlet *)(w[1]);
1036 // t_float *dummy = (t_float *)(w[2]);
1037 int n = (int)(w[2]);
1038 t_sample *in = x->x_empty;
1039 t_sample *out = x->x_updown.s_vec; /* IOhannes */
1040
1041#if 0
1042 if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
1043#endif
1044 for (; n--; in++) *out++ = *in, *in = 0;
1045 if (in == x->x_endbuf) in = x->x_buf;
1046 x->x_empty = in;
1047 return (w+3);
1048}
1049/* } IOhannes */
1050int outlet_getsignalindex(t_outlet *x);
1051
1052 /* prolog for outlets -- store pointer to the outlet on the
1053 parent, which, if "reblock" is false, will want to refer
1054 back to whatever we see on our input during the "dsp" method
1055 called later. */
1056void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs,
1057 int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
1058 int switched)
1059{
1060 x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
1061 x->x_justcopyout = (switched && !reblock);
1062 if (reblock)
1063 {
1064 x->x_directsignal = 0;
1065 }
1066 else
1067 {
1068 if (!parentsigs) bug("voutlet_dspprolog");
1069 x->x_directsignal =
1070 parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
1071 }
1072}
1073
1074static void voutlet_dsp(t_voutlet *x, t_signal **sp)
1075{
1076 t_signal *insig;
1077 if (!x->x_buf) return;
1078 insig = sp[0];
1079 if (x->x_justcopyout)
1080 dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n);
1081 else if (x->x_directsignal)
1082 {
1083 /* if we're just going to make the signal available on the
1084 parent patch, hand it off to the parent signal. */
1085 /* this is done elsewhere--> sp[0]->s_refcount++; */
1086 signal_setborrowed(x->x_directsignal, sp[0]);
1087 }
1088 else
1089 dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n);
1090}
1091
1092 /* set up epilog DSP code. If we're reblocking, this is the
1093 time to copy the samples out to the containing object's outlets.
1094 If we aren't reblocking, there's nothing to do here. */
1095void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs,
1096 int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
1097 int switched)
1098{
1099 if (!x->x_buf) return; /* this shouldn't be necesssary... */
1100 x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
1101 if (reblock)
1102 {
1103 t_signal *insig, *outsig;
1104 int parentvecsize, bufsize, oldbufsize;
1105 int re_parentvecsize; /* IOhannes */
1106 int bigperiod, epilogphase, blockphase;
1107 if (parentsigs)
1108 {
1109 outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
1110 parentvecsize = outsig->s_n;
1111 re_parentvecsize = parentvecsize * upsample / downsample;
1112 }
1113 else
1114 {
1115 outsig = 0;
1116 parentvecsize = 1;
1117 re_parentvecsize = 1;
1118 }
1119 // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */
1120 bigperiod = myvecsize/re_parentvecsize; /* IOhannes */
1121 if (!bigperiod) bigperiod = 1;
1122 epilogphase = phase & (bigperiod - 1);
1123 blockphase = (phase + period - 1) & (bigperiod - 1) & (- period);
1124 // bufsize = parentvecsize * upsample; /* IOhannes */
1125 bufsize = re_parentvecsize; /* IOhannes */
1126 if (bufsize < myvecsize) bufsize = myvecsize;
1127 if (bufsize != (oldbufsize = x->x_bufsize))
1128 {
1129 t_sample *buf = x->x_buf;
1130 t_freebytes(buf, oldbufsize * sizeof(*buf));
1131 buf = (t_sample *)t_getbytes(bufsize * sizeof(*buf));
1132 memset((char *)buf, 0, bufsize * sizeof(*buf));
1133 x->x_bufsize = bufsize;
1134 x->x_endbuf = buf + bufsize;
1135 x->x_buf = buf;
1136 }
1137 /* IOhannes: { */
1138 if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog");
1139 x->x_write = x->x_buf + re_parentvecsize * blockphase;
1140 if (x->x_write == x->x_endbuf) x->x_write = x->x_buf;
1141 if (period == 1 && frequency > 1)
1142 x->x_hop = re_parentvecsize / frequency;
1143 else x->x_hop = period * re_parentvecsize;
1144 /* } IOhannes */
1145 /* post("phase %d, block %d, parent %d", phase & 63,
1146 parentvecsize * blockphase, parentvecsize * epilogphase); */
1147 if (parentsigs)
1148 {
1149 /* set epilog pointer and schedule it */
1150 /* IOhannes { */
1151 x->x_empty = x->x_buf + re_parentvecsize * epilogphase;
1152 if (upsample * downsample == 1)
1153 dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize);
1154 else {
1155 dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize);
1156 resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method);
1157 }
1158 /* } IOhannes */
1159 }
1160 }
1161 /* if we aren't blocked but we are switched, the epilog code just
1162 copies zeros to the output. In this case the blocking code actually
1163 jumps over the epilog if the block is running. */
1164 else if (switched)
1165 {
1166 if (parentsigs)
1167 {
1168 t_signal *outsig =
1169 parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
1170 dsp_add_zero(outsig->s_vec, outsig->s_n);
1171 }
1172 }
1173}
1174
1175static void *voutlet_newsig(t_symbol *s)
1176{
1177 t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
1178 x->x_canvas = canvas_getcurrent();
1179 x->x_parentoutlet = canvas_addoutlet(x->x_canvas,
1180 &x->x_obj.ob_pd, &s_signal);
1181 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
1182 x->x_endbuf = x->x_buf = (t_sample *)getbytes(0);
1183 x->x_bufsize = 0;
1184
1185 resample_init(&x->x_updown);
1186
1187 /* this should be though over:
1188 * it might prove hard to provide consistency between labeled up- & downsampling methods
1189 * maybe indeces would be better...
1190 *
1191 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
1192 */
1193 if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
1194 else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
1195 else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */
1196 else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */
1197
1198 return (x);
1199}
1200
1201
1202static void voutlet_setup(void)
1203{
1204 voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new,
1205 (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0);
1206 class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0);
1207 class_addbang(voutlet_class, voutlet_bang);
1208 class_addpointer(voutlet_class, voutlet_pointer);
1209 class_addfloat(voutlet_class, (t_method)voutlet_float);
1210 class_addsymbol(voutlet_class, voutlet_symbol);
1211 class_addlist(voutlet_class, voutlet_list);
1212 class_addanything(voutlet_class, voutlet_anything);
1213 class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0);
1214 class_sethelpsymbol(voutlet_class, gensym("pd"));
1215}
1216
1217
1218/* ---------------------------- overall setup ----------------------------- */
1219
1220void g_io_setup(void)
1221{
1222 vinlet_setup();
1223 voutlet_setup();
1224}