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. */
5 /* graphical inlets and outlets, both for control and signals. */
7 /* This code is highly inefficient; messages actually have to be forwarded
8 by inlets and outlets. The outlet is in even worse shape than the inlet;
9 in order to avoid having a "signal" method in the class, the oulet actually
10 sprouts an inlet, which forwards the message to the "outlet" object, which
11 sends it on to the outlet proper. Another way to do it would be to have
12 separate classes for "signal" and "control" outlets, but this would complicate
16 /* hacked to run subpatches with different samplerates
21 * edited lines are marked with "IOhannes"
27 #include "../../pdbox.h"
36 void signal_setborrowed(t_signal
*sig
, t_signal
*sig2
);
37 void signal_makereusable(t_signal
*sig
);
39 /* ------------------------- vinlet -------------------------- */
40 t_class
*vinlet_class
;
42 typedef struct _vinlet
48 t_float
*x_buf
; /* signal buffer; zero if not a signal */
53 /* if not reblocking, the next slot communicates the parent's inlet
54 signal from the prolog to the DSP routine: */
55 t_signal
*x_directsignal
;
57 t_resample x_updown
; /* IOhannes */
60 static void *vinlet_new(t_symbol
*s
)
65 t_vinlet
*x
= (t_vinlet
*)pd_new(vinlet_class
);
66 x
->x_canvas
= canvas_getcurrent();
67 x
->x_inlet
= canvas_addinlet(x
->x_canvas
, &x
->x_obj
.ob_pd
, 0);
70 outlet_new(&x
->x_obj
, 0);
74 static void vinlet_bang(t_vinlet
*x
)
76 outlet_bang(x
->x_obj
.ob_outlet
);
79 static void vinlet_pointer(t_vinlet
*x
, t_gpointer
*gp
)
81 outlet_pointer(x
->x_obj
.ob_outlet
, gp
);
84 static void vinlet_float(t_vinlet
*x
, t_float f
)
86 outlet_float(x
->x_obj
.ob_outlet
, f
);
89 static void vinlet_symbol(t_vinlet
*x
, t_symbol
*s
)
91 outlet_symbol(x
->x_obj
.ob_outlet
, s
);
94 static void vinlet_list(t_vinlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
96 outlet_list(x
->x_obj
.ob_outlet
, s
, argc
, argv
);
99 static void vinlet_anything(t_vinlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
101 outlet_anything(x
->x_obj
.ob_outlet
, s
, argc
, argv
);
104 static void vinlet_free(t_vinlet
*x
)
106 canvas_rminlet(x
->x_canvas
, x
->x_inlet
);
107 resample_free(&x
->x_updown
);
110 t_inlet
*vinlet_getit(t_pd
*x
)
112 if (pd_class(x
) != vinlet_class
) bug("vinlet_getit");
113 return (((t_vinlet
*)x
)->x_inlet
);
116 /* ------------------------- signal inlet -------------------------- */
117 int vinlet_issignal(t_vinlet
*x
)
119 return (x
->x_buf
!= 0);
126 t_int
*vinlet_perform(t_int
*w
)
128 t_vinlet
*x
= (t_vinlet
*)(w
[1]);
129 t_float
*out
= (t_float
*)(w
[2]);
131 t_float
*in
= x
->x_read
;
133 if (tot
< 5) post("-in %x out %x n %d", in
, out
, n
);
134 if (tot
< 5) post("-buf %x endbuf %x", x
->x_buf
, x
->x_endbuf
);
135 if (tot
< 5) post("in[0] %f in[1] %f in[2] %f", in
[0], in
[1], in
[2]);
137 while (n
--) *out
++ = *in
++;
138 if (in
== x
->x_endbuf
) in
= x
->x_buf
;
143 static void vinlet_dsp(t_vinlet
*x
, t_signal
**sp
)
146 /* no buffer means we're not a signal inlet */
150 if (x
->x_directsignal
)
152 signal_setborrowed(sp
[0], x
->x_directsignal
);
156 dsp_add(vinlet_perform
, 3, x
, outsig
->s_vec
, outsig
->s_n
);
157 x
->x_read
= x
->x_buf
;
161 /* prolog code: loads buffer from parent patch */
162 t_int
*vinlet_doprolog(t_int
*w
)
164 t_vinlet
*x
= (t_vinlet
*)(w
[1]);
165 t_float
*in
= (t_float
*)(w
[2]);
167 t_float
*out
= x
->x_fill
;
168 if (out
== x
->x_endbuf
)
170 t_float
*f1
= x
->x_buf
, *f2
= x
->x_buf
+ x
->x_hop
;
171 int nshift
= x
->x_bufsize
- x
->x_hop
;
173 while (nshift
--) *f1
++ = *f2
++;
176 if (tot
< 5) post("in %x out %x n %x", in
, out
, n
), tot
++;
177 if (tot
< 5) post("in[0] %f in[1] %f in[2] %f", in
[0], in
[1], in
[2]);
180 while (n
--) *out
++ = *in
++;
185 int inlet_getsignalindex(t_inlet
*x
);
187 /* set up prolog DSP code */
188 void vinlet_dspprolog(t_vinlet
*x
, t_signal
**parentsigs
,
189 int myvecsize
, int phase
, int period
, int frequency
, int downsample
, int upsample
/* IOhannes */, int reblock
,
197 t_signal
*insig
, *outsig
;
199 x
->x_updown
.downsample
= downsample
;
200 x
->x_updown
.upsample
= upsample
;
202 /* if the "reblock" flag is set, arrange to copy data in from the
206 int parentvecsize
, bufsize
, oldbufsize
, prologphase
;
207 int re_parentvecsize
; /* resampled parentvectorsize: IOhannes */
208 /* this should never happen: */
209 if (!x
->x_buf
) return;
211 /* the prolog code counts from 0 to period-1; the
212 phase is backed up by one so that AFTER the prolog code
213 runs, the "x_fill" phase is in sync with the "x_read" phase. */
214 prologphase
= (phase
- 1) & (period
- 1);
217 insig
= parentsigs
[inlet_getsignalindex(x
->x_inlet
)];
218 parentvecsize
= insig
->s_n
;
219 re_parentvecsize
= parentvecsize
* upsample
/ downsample
;
225 re_parentvecsize
= 1;
228 bufsize
= re_parentvecsize
;
229 if (bufsize
< myvecsize
) bufsize
= myvecsize
;
230 if (bufsize
!= (oldbufsize
= x
->x_bufsize
))
232 t_float
*buf
= x
->x_buf
;
233 t_freebytes(buf
, oldbufsize
* sizeof(*buf
));
234 buf
= (t_float
*)t_getbytes(bufsize
* sizeof(*buf
));
235 memset((char *)buf
, 0, bufsize
* sizeof(*buf
));
236 x
->x_bufsize
= bufsize
;
237 x
->x_endbuf
= buf
+ bufsize
;
243 x
->x_hop
= period
* re_parentvecsize
;
245 x
->x_fill
= x
->x_endbuf
-
246 (x
->x_hop
- prologphase
* re_parentvecsize
);
248 if (upsample
* downsample
== 1)
249 dsp_add(vinlet_doprolog
, 3, x
, insig
->s_vec
, re_parentvecsize
);
251 resamplefrom_dsp(&x
->x_updown
, insig
->s_vec
, parentvecsize
, re_parentvecsize
, x
->x_updown
.method
);
252 dsp_add(vinlet_doprolog
, 3, x
, x
->x_updown
.s_vec
, re_parentvecsize
);
256 /* if the input signal's reference count is zero, we have
257 to free it here because we didn't in ugen_doit(). */
258 if (!insig
->s_refcount
)
259 signal_makereusable(insig
);
261 else memset((char *)(x
->x_buf
), 0, bufsize
* sizeof(*x
->x_buf
));
262 x
->x_directsignal
= 0;
266 /* no reblocking; in this case our output signal is "borrowed"
267 and merely needs to be pointed to the real one. */
268 x
->x_directsignal
= parentsigs
[inlet_getsignalindex(x
->x_inlet
)];
272 //static void *vinlet_newsig(void)
273 static void *vinlet_newsig(t_symbol
*s
)
275 t_vinlet
*x
= (t_vinlet
*)pd_new(vinlet_class
);
276 x
->x_canvas
= canvas_getcurrent();
277 x
->x_inlet
= canvas_addinlet(x
->x_canvas
, &x
->x_obj
.ob_pd
, &s_signal
);
278 x
->x_endbuf
= x
->x_buf
= (t_float
*)getbytes(0);
280 x
->x_directsignal
= 0;
281 outlet_new(&x
->x_obj
, &s_signal
);
283 resample_init(&x
->x_updown
);
285 /* this should be though over:
286 * it might prove hard to provide consistency between labeled up- & downsampling methods
287 * maybe indeces would be better...
289 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
291 if (s
== gensym("hold"))x
->x_updown
.method
=1; /* up: sample and hold */
292 else if (s
== gensym("lin"))x
->x_updown
.method
=2; /* up: linear interpolation */
293 else x
->x_updown
.method
=0; /* up: zero-padding */
298 static void vinlet_setup(void)
300 vinlet_class
= class_new(gensym("inlet"), (t_newmethod
)vinlet_new
,
301 (t_method
)vinlet_free
, sizeof(t_vinlet
), CLASS_NOINLET
, A_DEFSYM
, 0);
302 class_addcreator((t_newmethod
)vinlet_newsig
, gensym("inlet~"), A_DEFSYM
, 0);
303 class_addbang(vinlet_class
, vinlet_bang
);
304 class_addpointer(vinlet_class
, vinlet_pointer
);
305 class_addfloat(vinlet_class
, vinlet_float
);
306 class_addsymbol(vinlet_class
, vinlet_symbol
);
307 class_addlist(vinlet_class
, vinlet_list
);
308 class_addanything(vinlet_class
, vinlet_anything
);
309 class_addmethod(vinlet_class
, (t_method
)vinlet_dsp
, gensym("dsp"), 0);
310 class_sethelpsymbol(vinlet_class
, gensym("pd"));
313 /* ------------------------- voutlet -------------------------- */
315 t_class
*voutlet_class
;
317 typedef struct _voutlet
321 t_outlet
*x_parentoutlet
;
323 t_sample
*x_buf
; /* signal buffer; zero if not a signal */
325 t_sample
*x_empty
; /* next to read out of buffer in epilog code */
326 t_sample
*x_write
; /* next to write in to buffer */
327 int x_hop
; /* hopsize */
328 /* vice versa from the inlet, if we don't block, this holds the
329 parent's outlet signal, valid between the prolog and the dsp setup
331 t_signal
*x_directsignal
;
332 /* and here's a flag indicating that we aren't blocked but have to
333 do a copy (because we're switched). */
335 t_resample x_updown
; /* IOhannes */
338 static void *voutlet_new(t_symbol
*s
)
343 t_voutlet
*x
= (t_voutlet
*)pd_new(voutlet_class
);
344 x
->x_canvas
= canvas_getcurrent();
345 x
->x_parentoutlet
= canvas_addoutlet(x
->x_canvas
, &x
->x_obj
.ob_pd
, 0);
346 inlet_new(&x
->x_obj
, &x
->x_obj
.ob_pd
, 0, 0);
352 static void voutlet_bang(t_voutlet
*x
)
354 outlet_bang(x
->x_parentoutlet
);
357 static void voutlet_pointer(t_voutlet
*x
, t_gpointer
*gp
)
359 outlet_pointer(x
->x_parentoutlet
, gp
);
362 static void voutlet_float(t_voutlet
*x
, t_float f
)
364 outlet_float(x
->x_parentoutlet
, f
);
367 static void voutlet_symbol(t_voutlet
*x
, t_symbol
*s
)
369 outlet_symbol(x
->x_parentoutlet
, s
);
372 static void voutlet_list(t_voutlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
374 outlet_list(x
->x_parentoutlet
, s
, argc
, argv
);
377 static void voutlet_anything(t_voutlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
379 outlet_anything(x
->x_parentoutlet
, s
, argc
, argv
);
382 static void voutlet_free(t_voutlet
*x
)
384 canvas_rmoutlet(x
->x_canvas
, x
->x_parentoutlet
);
385 resample_free(&x
->x_updown
);
388 t_outlet
*voutlet_getit(t_pd
*x
)
390 if (pd_class(x
) != voutlet_class
) bug("voutlet_getit");
391 return (((t_voutlet
*)x
)->x_parentoutlet
);
394 /* ------------------------- signal outlet -------------------------- */
396 int voutlet_issignal(t_voutlet
*x
)
398 return (x
->x_buf
!= 0);
401 /* LATER optimize for non-overlapped case where the "+=" isn't needed */
402 t_int
*voutlet_perform(t_int
*w
)
404 t_voutlet
*x
= (t_voutlet
*)(w
[1]);
405 t_sample
*in
= (t_sample
*)(w
[2]);
407 t_sample
*out
= x
->x_write
, *outwas
= out
;
409 if (tot
< 5) post("-in %x out %x n %d", in
, out
, n
);
410 if (tot
< 5) post("-buf %x endbuf %x", x
->x_buf
, x
->x_endbuf
);
415 if (out
== x
->x_endbuf
) out
= x
->x_buf
;
418 if (outwas
>= x
->x_endbuf
) outwas
= x
->x_buf
;
423 /* epilog code for blocking: write buffer to parent patch */
424 static t_int
*voutlet_doepilog(t_int
*w
)
426 t_voutlet
*x
= (t_voutlet
*)(w
[1]);
427 t_sample
*out
= (t_sample
*)(w
[2]); /* IOhannes */
430 t_sample
*in
= x
->x_empty
;
431 if (x
->x_updown
.downsample
!= x
->x_updown
.upsample
) out
= x
->x_updown
.s_vec
; /* IOhannes */
434 if (tot
< 5) post("outlet in %x out %x n %x", in
, out
, n
), tot
++;
436 for (; n
--; in
++) *out
++ = *in
, *in
= 0;
437 if (in
== x
->x_endbuf
) in
= x
->x_buf
;
443 static t_int
*voutlet_doepilog_resampling(t_int
*w
)
445 t_voutlet
*x
= (t_voutlet
*)(w
[1]);
446 // t_float *dummy = (t_float *)(w[2]);
448 t_sample
*in
= x
->x_empty
;
449 t_sample
*out
= x
->x_updown
.s_vec
; /* IOhannes */
452 if (tot
< 5) post("outlet in %x out %x n %x", in
, out
, n
), tot
++;
454 for (; n
--; in
++) *out
++ = *in
, *in
= 0;
455 if (in
== x
->x_endbuf
) in
= x
->x_buf
;
460 int outlet_getsignalindex(t_outlet
*x
);
462 /* prolog for outlets -- store pointer to the outlet on the
463 parent, which, if "reblock" is false, will want to refer
464 back to whatever we see on our input during the "dsp" method
466 void voutlet_dspprolog(t_voutlet
*x
, t_signal
**parentsigs
,
467 int myvecsize
, int phase
, int period
, int frequency
, int downsample
, int upsample
/* IOhannes */, int reblock
,
476 x
->x_updown
.downsample
=downsample
; x
->x_updown
.upsample
=upsample
; /* IOhannes */
477 x
->x_justcopyout
= (switched
&& !reblock
);
480 x
->x_directsignal
= 0;
484 if (!parentsigs
) bug("voutlet_dspprolog");
486 parentsigs
[outlet_getsignalindex(x
->x_parentoutlet
)];
490 static void voutlet_dsp(t_voutlet
*x
, t_signal
**sp
)
493 if (!x
->x_buf
) return;
495 if (x
->x_justcopyout
)
496 dsp_add_copy(insig
->s_vec
, x
->x_directsignal
->s_vec
, insig
->s_n
);
497 else if (x
->x_directsignal
)
499 /* if we're just going to make the signal available on the
500 parent patch, hand it off to the parent signal. */
501 /* this is done elsewhere--> sp[0]->s_refcount++; */
502 signal_setborrowed(x
->x_directsignal
, sp
[0]);
505 dsp_add(voutlet_perform
, 3, x
, insig
->s_vec
, insig
->s_n
);
508 /* set up epilog DSP code. If we're reblocking, this is the
509 time to copy the samples out to the containing object's outlets.
510 If we aren't reblocking, there's nothing to do here. */
511 void voutlet_dspepilog(t_voutlet
*x
, t_signal
**parentsigs
,
512 int myvecsize
, int phase
, int period
, int frequency
, int downsample
, int upsample
/* IOhannes */, int reblock
,
515 if (!x
->x_buf
) return; /* this shouldn't be necesssary... */
516 x
->x_updown
.downsample
=downsample
; x
->x_updown
.upsample
=upsample
; /* IOhannes */
522 t_signal
*insig
, *outsig
;
524 int parentvecsize
, bufsize
, oldbufsize
;
525 int re_parentvecsize
; /* IOhannes */
526 int bigperiod
, epilogphase
, blockphase
;
529 outsig
= parentsigs
[outlet_getsignalindex(x
->x_parentoutlet
)];
530 parentvecsize
= outsig
->s_n
;
531 re_parentvecsize
= parentvecsize
* upsample
/ downsample
;
537 re_parentvecsize
= 1;
539 // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */
540 bigperiod
= myvecsize
/re_parentvecsize
; /* IOhannes */
541 if (!bigperiod
) bigperiod
= 1;
542 epilogphase
= phase
& (bigperiod
- 1);
543 blockphase
= (phase
+ period
- 1) & (bigperiod
- 1) & (- period
);
544 // bufsize = parentvecsize * upsample; /* IOhannes */
545 bufsize
= re_parentvecsize
; /* IOhannes */
546 if (bufsize
< myvecsize
) bufsize
= myvecsize
;
547 if (bufsize
!= (oldbufsize
= x
->x_bufsize
))
549 t_sample
*buf
= x
->x_buf
;
550 t_freebytes(buf
, oldbufsize
* sizeof(*buf
));
551 buf
= (t_sample
*)t_getbytes(bufsize
* sizeof(*buf
));
552 memset((char *)buf
, 0, bufsize
* sizeof(*buf
));
553 x
->x_bufsize
= bufsize
;
554 x
->x_endbuf
= buf
+ bufsize
;
558 if (re_parentvecsize
* period
> bufsize
) bug("voutlet_dspepilog");
559 x
->x_write
= x
->x_buf
+ re_parentvecsize
* blockphase
;
560 if (x
->x_write
== x
->x_endbuf
) x
->x_write
= x
->x_buf
;
561 if (period
== 1 && frequency
> 1)
562 x
->x_hop
= re_parentvecsize
/ frequency
;
563 else x
->x_hop
= period
* re_parentvecsize
;
565 /* post("phase %d, block %d, parent %d", phase & 63,
566 parentvecsize * blockphase, parentvecsize * epilogphase); */
569 /* set epilog pointer and schedule it */
571 x
->x_empty
= x
->x_buf
+ re_parentvecsize
* epilogphase
;
572 if (upsample
* downsample
== 1)
573 dsp_add(voutlet_doepilog
, 3, x
, outsig
->s_vec
, re_parentvecsize
);
575 dsp_add(voutlet_doepilog_resampling
, 2, x
, re_parentvecsize
);
576 resampleto_dsp(&x
->x_updown
, outsig
->s_vec
, re_parentvecsize
, parentvecsize
, x
->x_updown
.method
);
581 /* if we aren't blocked but we are switched, the epilog code just
582 copies zeros to the output. In this case the blocking code actually
583 jumps over the epilog if the block is running. */
589 parentsigs
[outlet_getsignalindex(x
->x_parentoutlet
)];
590 dsp_add_zero(outsig
->s_vec
, outsig
->s_n
);
595 static void *voutlet_newsig(t_symbol
*s
)
597 t_voutlet
*x
= (t_voutlet
*)pd_new(voutlet_class
);
598 x
->x_canvas
= canvas_getcurrent();
599 x
->x_parentoutlet
= canvas_addoutlet(x
->x_canvas
,
600 &x
->x_obj
.ob_pd
, &s_signal
);
601 inlet_new(&x
->x_obj
, &x
->x_obj
.ob_pd
, &s_signal
, &s_signal
);
602 x
->x_endbuf
= x
->x_buf
= (t_sample
*)getbytes(0);
605 resample_init(&x
->x_updown
);
607 /* this should be though over:
608 * it might prove hard to provide consistency between labeled up- & downsampling methods
609 * maybe indeces would be better...
611 * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
613 if (s
== gensym("hold"))x
->x_updown
.method
=1; /* up: sample and hold */
614 else if (s
== gensym("lin"))x
->x_updown
.method
=2; /* up: linear interpolation */
615 else if (s
== gensym("linear"))x
->x_updown
.method
=2; /* up: linear interpolation */
616 else x
->x_updown
.method
=0; /* up: zero-padding; down: ignore samples inbetween */
622 static void voutlet_setup(void)
624 voutlet_class
= class_new(gensym("outlet"), (t_newmethod
)voutlet_new
,
625 (t_method
)voutlet_free
, sizeof(t_voutlet
), CLASS_NOINLET
, A_DEFSYM
, 0);
626 class_addcreator((t_newmethod
)voutlet_newsig
, gensym("outlet~"), A_DEFSYM
, 0);
627 class_addbang(voutlet_class
, voutlet_bang
);
628 class_addpointer(voutlet_class
, voutlet_pointer
);
629 class_addfloat(voutlet_class
, (t_method
)voutlet_float
);
630 class_addsymbol(voutlet_class
, voutlet_symbol
);
631 class_addlist(voutlet_class
, voutlet_list
);
632 class_addanything(voutlet_class
, voutlet_anything
);
633 class_addmethod(voutlet_class
, (t_method
)voutlet_dsp
, gensym("dsp"), 0);
634 class_sethelpsymbol(voutlet_class
, gensym("pd"));
638 /* ---------------------------- overall setup ----------------------------- */
640 void g_io_setup(void)