aboutsummaryrefslogtreecommitdiff
path: root/pd/src/g_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'pd/src/g_io.c')
-rw-r--r--pd/src/g_io.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/pd/src/g_io.c b/pd/src/g_io.c
new file mode 100644
index 00000000..487be350
--- /dev/null
+++ b/pd/src/g_io.c
@@ -0,0 +1,612 @@
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* graphical inlets and outlets, both for control and signals. */
+
+/* This code is highly inefficient; messages actually have to be forwarded
+by inlets and outlets. The outlet is in even worse shape than the inlet;
+in order to avoid having a "signal" method in the class, the oulet actually
+sprouts an inlet, which forwards the message to the "outlet" object, which
+sends it on to the outlet proper. Another way to do it would be to have
+separate classes for "signal" and "control" outlets, but this would complicate
+life elsewhere. */
+
+
+/* hacked to run subpatches with different samplerates
+ *
+ * mfg.gfd.uil
+ * IOhannes
+ *
+ * edited lines are marked with "IOhannes"
+ *
+ */
+
+#include "m_pd.h"
+#include "g_canvas.h"
+#include <string.h>
+void signal_setborrowed(t_signal *sig, t_signal *sig2);
+void signal_makereusable(t_signal *sig);
+
+/* ------------------------- vinlet -------------------------- */
+t_class *vinlet_class;
+
+typedef struct _vinlet
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+ t_inlet *x_inlet;
+ int x_bufsize;
+ t_float *x_buf; /* signal buffer; zero if not a signal */
+ t_float *x_endbuf;
+ t_float *x_fill;
+ t_float *x_read;
+ int x_hop;
+ /* if not reblocking, the next slot communicates the parent's inlet
+ signal from the prolog to the DSP routine: */
+ t_signal *x_directsignal;
+
+ t_resample x_updown; /* IOhannes */
+} t_vinlet;
+
+static void *vinlet_new(t_symbol *s)
+{
+ t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0);
+ x->x_bufsize = 0;
+ x->x_buf = 0;
+ outlet_new(&x->x_obj, 0);
+ return (x);
+}
+
+static void vinlet_bang(t_vinlet *x)
+{
+ outlet_bang(x->x_obj.ob_outlet);
+}
+
+static void vinlet_pointer(t_vinlet *x, t_gpointer *gp)
+{
+ outlet_pointer(x->x_obj.ob_outlet, gp);
+}
+
+static void vinlet_float(t_vinlet *x, t_float f)
+{
+ outlet_float(x->x_obj.ob_outlet, f);
+}
+
+static void vinlet_symbol(t_vinlet *x, t_symbol *s)
+{
+ outlet_symbol(x->x_obj.ob_outlet, s);
+}
+
+static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->x_obj.ob_outlet, s, argc, argv);
+}
+
+static void vinlet_free(t_vinlet *x)
+{
+ canvas_rminlet(x->x_canvas, x->x_inlet);
+ resample_free(&x->x_updown);
+}
+
+t_inlet *vinlet_getit(t_pd *x)
+{
+ if (pd_class(x) != vinlet_class) bug("vinlet_getit");
+ return (((t_vinlet *)x)->x_inlet);
+}
+
+/* ------------------------- signal inlet -------------------------- */
+int vinlet_issignal(t_vinlet *x)
+{
+ return (x->x_buf != 0);
+}
+
+static int tot;
+
+t_int *vinlet_perform(t_int *w)
+{
+ t_vinlet *x = (t_vinlet *)(w[1]);
+ t_float *out = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *in = x->x_read;
+#if 0
+ if (tot < 5) post("-in %x out %x n %d", in, out, n);
+ if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
+ if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
+#endif
+ while (n--) *out++ = *in++;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_read = in;
+ return (w+4);
+}
+
+static void vinlet_dsp(t_vinlet *x, t_signal **sp)
+{
+ t_signal *outsig;
+ /* no buffer means we're not a signal inlet */
+ if (!x->x_buf)
+ return;
+ outsig = sp[0];
+ if (x->x_directsignal)
+ {
+ signal_setborrowed(sp[0], x->x_directsignal);
+ }
+ else
+ {
+ dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n);
+ x->x_read = x->x_buf;
+ }
+}
+
+ /* prolog code: loads buffer from parent patch */
+t_int *vinlet_doprolog(t_int *w)
+{
+ t_vinlet *x = (t_vinlet *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *out = x->x_fill;
+ if (out == x->x_endbuf)
+ {
+ t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop;
+ int nshift = x->x_bufsize - x->x_hop;
+ out -= x->x_hop;
+ while (nshift--) *f1++ = *f2++;
+ }
+#if 0
+ if (tot < 5) post("in %x out %x n %x", in, out, n), tot++;
+ if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]);
+#endif
+
+ while (n--) *out++ = *in++;
+ x->x_fill = out;
+ return (w+4);
+}
+
+int inlet_getsignalindex(t_inlet *x);
+
+ /* set up prolog DSP code */
+void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock,
+ int switched)
+{
+ t_signal *insig, *outsig;
+ x->x_updown.downsample = downsample;
+ x->x_updown.upsample = upsample;
+
+ /* if the "reblock" flag is set, arrange to copy data in from the
+ parent. */
+ if (reblock)
+ {
+ int parentvecsize, bufsize, oldbufsize, prologphase;
+ int re_parentvecsize; /* resampled parentvectorsize: IOhannes */
+ /* this should never happen: */
+ if (!x->x_buf) return;
+
+ /* the prolog code counts from 0 to period-1; the
+ phase is backed up by one so that AFTER the prolog code
+ runs, the "x_fill" phase is in sync with the "x_read" phase. */
+ prologphase = (phase - 1) & (period - 1);
+ if (parentsigs)
+ {
+ insig = parentsigs[inlet_getsignalindex(x->x_inlet)];
+ parentvecsize = insig->s_n;
+ re_parentvecsize = parentvecsize * upsample / downsample;
+ }
+ else
+ {
+ insig = 0;
+ parentvecsize = 1;
+ re_parentvecsize = 1;
+ }
+
+ bufsize = re_parentvecsize;
+ if (bufsize < myvecsize) bufsize = myvecsize;
+ if (bufsize != (oldbufsize = x->x_bufsize))
+ {
+ t_float *buf = x->x_buf;
+ t_freebytes(buf, oldbufsize * sizeof(*buf));
+ buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
+ memset((char *)buf, 0, bufsize * sizeof(*buf));
+ x->x_bufsize = bufsize;
+ x->x_endbuf = buf + bufsize;
+ x->x_buf = buf;
+ }
+ if (parentsigs)
+ {
+ /* IOhannes { */
+ x->x_hop = period * re_parentvecsize;
+
+ x->x_fill = x->x_endbuf -
+ (x->x_hop - prologphase * re_parentvecsize);
+
+ if (upsample * downsample == 1)
+ dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize);
+ else {
+ resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method);
+ dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize);
+ }
+
+ /* } IOhannes */
+ /* if the input signal's reference count is zero, we have
+ to free it here because we didn't in ugen_doit(). */
+ if (!insig->s_refcount)
+ signal_makereusable(insig);
+ }
+ else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf));
+ x->x_directsignal = 0;
+ }
+ else
+ {
+ /* no reblocking; in this case our output signal is "borrowed"
+ and merely needs to be pointed to the real one. */
+ x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)];
+ }
+}
+
+//static void *vinlet_newsig(void)
+static void *vinlet_newsig(t_symbol *s)
+{
+ t_vinlet *x = (t_vinlet *)pd_new(vinlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal);
+ x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
+ x->x_bufsize = 0;
+ x->x_directsignal = 0;
+ outlet_new(&x->x_obj, &s_signal);
+
+ resample_init(&x->x_updown);
+
+ /* this should be though over:
+ * it might prove hard to provide consistency between labeled up- & downsampling methods
+ * maybe indeces would be better...
+ *
+ * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
+ */
+ if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
+ else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
+ else x->x_updown.method=0; /* up: zero-padding */
+
+ return (x);
+}
+
+static void vinlet_setup(void)
+{
+ vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new,
+ (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0);
+ class_addbang(vinlet_class, vinlet_bang);
+ class_addpointer(vinlet_class, vinlet_pointer);
+ class_addfloat(vinlet_class, vinlet_float);
+ class_addsymbol(vinlet_class, vinlet_symbol);
+ class_addlist(vinlet_class, vinlet_list);
+ class_addanything(vinlet_class, vinlet_anything);
+ class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(vinlet_class, gensym("pd"));
+}
+
+/* ------------------------- voutlet -------------------------- */
+
+t_class *voutlet_class;
+
+typedef struct _voutlet
+{
+ t_object x_obj;
+ t_canvas *x_canvas;
+ t_outlet *x_parentoutlet;
+ int x_bufsize;
+ t_float *x_buf; /* signal buffer; zero if not a signal */
+ t_float *x_endbuf;
+ t_float *x_empty; /* next to read out of buffer in epilog code */
+ t_float *x_write; /* next to write in to buffer */
+ int x_hop; /* hopsize */
+ /* vice versa from the inlet, if we don't block, this holds the
+ parent's outlet signal, valid between the prolog and the dsp setup
+ routines. */
+ t_signal *x_directsignal;
+ /* and here's a flag indicating that we aren't blocked but have to
+ do a copy (because we're switched). */
+ char x_justcopyout;
+ t_resample x_updown; /* IOhannes */
+} t_voutlet;
+
+static void *voutlet_new(t_symbol *s)
+{
+ t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0);
+ x->x_bufsize = 0;
+ x->x_buf = 0;
+ return (x);
+}
+
+static void voutlet_bang(t_voutlet *x)
+{
+ outlet_bang(x->x_parentoutlet);
+}
+
+static void voutlet_pointer(t_voutlet *x, t_gpointer *gp)
+{
+ outlet_pointer(x->x_parentoutlet, gp);
+}
+
+static void voutlet_float(t_voutlet *x, t_float f)
+{
+ outlet_float(x->x_parentoutlet, f);
+}
+
+static void voutlet_symbol(t_voutlet *x, t_symbol *s)
+{
+ outlet_symbol(x->x_parentoutlet, s);
+}
+
+static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_list(x->x_parentoutlet, s, argc, argv);
+}
+
+static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv)
+{
+ outlet_anything(x->x_parentoutlet, s, argc, argv);
+}
+
+static void voutlet_free(t_voutlet *x)
+{
+ canvas_rmoutlet(x->x_canvas, x->x_parentoutlet);
+ resample_free(&x->x_updown);
+}
+
+t_outlet *voutlet_getit(t_pd *x)
+{
+ if (pd_class(x) != voutlet_class) bug("voutlet_getit");
+ return (((t_voutlet *)x)->x_parentoutlet);
+}
+
+/* ------------------------- signal outlet -------------------------- */
+
+int voutlet_issignal(t_voutlet *x)
+{
+ return (x->x_buf != 0);
+}
+
+ /* LATER optimize for non-overlapped case where the "+=" isn't needed */
+t_int *voutlet_perform(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ int n = (int)(w[3]);
+ t_float *out = x->x_write, *outwas = out;
+#if 0
+ if (tot < 5) post("-in %x out %x n %d", in, out, n);
+ if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf);
+#endif
+ while (n--)
+ {
+ *out++ += *in++;
+ if (out == x->x_endbuf) out = x->x_buf;
+ }
+ outwas += x->x_hop;
+ if (outwas >= x->x_endbuf) outwas = x->x_buf;
+ x->x_write = outwas;
+ return (w+4);
+}
+
+ /* epilog code for blocking: write buffer to parent patch */
+static t_int *voutlet_doepilog(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ t_float *out = (t_float *)(w[2]); /* IOhannes */
+
+ int n = (int)(w[3]);
+ t_float *in = x->x_empty;
+ if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */
+
+#if 0
+ if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
+#endif
+ for (; n--; in++) *out++ = *in, *in = 0;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_empty = in;
+ return (w+4);
+}
+
+/* IOhannes { */
+static t_int *voutlet_doepilog_resampling(t_int *w)
+{
+ t_voutlet *x = (t_voutlet *)(w[1]);
+ // t_float *dummy = (t_float *)(w[2]);
+ int n = (int)(w[2]);
+ t_float *in = x->x_empty;
+ t_float *out = x->x_updown.s_vec; /* IOhannes */
+
+#if 0
+ if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++;
+#endif
+ for (; n--; in++) *out++ = *in, *in = 0;
+ if (in == x->x_endbuf) in = x->x_buf;
+ x->x_empty = in;
+ return (w+3);
+}
+/* } IOhannes */
+int outlet_getsignalindex(t_outlet *x);
+
+ /* prolog for outlets -- store pointer to the outlet on the
+ parent, which, if "reblock" is false, will want to refer
+ back to whatever we see on our input during the "dsp" method
+ called later. */
+void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched)
+{
+ x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
+ x->x_justcopyout = (switched && !reblock);
+ if (reblock)
+ {
+ x->x_directsignal = 0;
+ }
+ else
+ {
+ if (!parentsigs) bug("voutlet_dspprolog");
+ x->x_directsignal =
+ parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ }
+}
+
+static void voutlet_dsp(t_voutlet *x, t_signal **sp)
+{
+ t_signal *insig;
+ if (!x->x_buf) return;
+ insig = sp[0];
+ if (x->x_justcopyout)
+ dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n);
+ else if (x->x_directsignal)
+ {
+ /* if we're just going to make the signal available on the
+ parent patch, hand it off to the parent signal. */
+ /* this is done elsewhere--> sp[0]->s_refcount++; */
+ signal_setborrowed(x->x_directsignal, sp[0]);
+ }
+ else
+ dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n);
+}
+
+ /* set up epilog DSP code. If we're reblocking, this is the
+ time to copy the samples out to the containing object's outlets.
+ If we aren't reblocking, there's nothing to do here. */
+void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs,
+ int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock,
+ int switched)
+{
+ if (!x->x_buf) return; /* this shouldn't be necesssary... */
+ x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */
+ if (reblock)
+ {
+ t_signal *insig, *outsig;
+ int parentvecsize, bufsize, oldbufsize;
+ int re_parentvecsize; /* IOhannes */
+ int bigperiod, epilogphase, blockphase;
+ if (parentsigs)
+ {
+ outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ parentvecsize = outsig->s_n;
+ re_parentvecsize = parentvecsize * upsample / downsample;
+ }
+ else
+ {
+ outsig = 0;
+ parentvecsize = 1;
+ re_parentvecsize = 1;
+ }
+ // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */
+ bigperiod = myvecsize/re_parentvecsize; /* IOhannes */
+ if (!bigperiod) bigperiod = 1;
+ epilogphase = phase & (bigperiod - 1);
+ blockphase = (phase + period - 1) & (bigperiod - 1) & (- period);
+ // bufsize = parentvecsize * upsample; /* IOhannes */
+ bufsize = re_parentvecsize; /* IOhannes */
+ if (bufsize < myvecsize) bufsize = myvecsize;
+ if (bufsize != (oldbufsize = x->x_bufsize))
+ {
+ t_float *buf = x->x_buf;
+ t_freebytes(buf, oldbufsize * sizeof(*buf));
+ buf = (t_float *)t_getbytes(bufsize * sizeof(*buf));
+ memset((char *)buf, 0, bufsize * sizeof(*buf));
+ x->x_bufsize = bufsize;
+ x->x_endbuf = buf + bufsize;
+ x->x_buf = buf;
+ }
+ /* IOhannes: { */
+ if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog");
+ x->x_write = x->x_buf + re_parentvecsize * blockphase;
+ if (x->x_write == x->x_endbuf) x->x_write = x->x_buf;
+ if (period == 1 && frequency > 1)
+ x->x_hop = re_parentvecsize / frequency;
+ else x->x_hop = period * re_parentvecsize;
+ /* } IOhannes */
+ /* post("phase %d, block %d, parent %d", phase & 63,
+ parentvecsize * blockphase, parentvecsize * epilogphase); */
+ if (parentsigs)
+ {
+ /* set epilog pointer and schedule it */
+ /* IOhannes { */
+ x->x_empty = x->x_buf + re_parentvecsize * epilogphase;
+ if (upsample * downsample == 1)
+ dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize);
+ else {
+ dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize);
+ resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method);
+ }
+ /* } IOhannes */
+ }
+ }
+ /* if we aren't blocked but we are switched, the epilog code just
+ copies zeros to the output. In this case the blocking code actually
+ jumps over the epilog if the block is running. */
+ else if (switched)
+ {
+ if (parentsigs)
+ {
+ t_signal *outsig =
+ parentsigs[outlet_getsignalindex(x->x_parentoutlet)];
+ dsp_add_zero(outsig->s_vec, outsig->s_n);
+ }
+ }
+}
+
+static void *voutlet_newsig(t_symbol *s)
+{
+ t_voutlet *x = (t_voutlet *)pd_new(voutlet_class);
+ x->x_canvas = canvas_getcurrent();
+ x->x_parentoutlet = canvas_addoutlet(x->x_canvas,
+ &x->x_obj.ob_pd, &s_signal);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+ x->x_endbuf = x->x_buf = (t_float *)getbytes(0);
+ x->x_bufsize = 0;
+
+ resample_init(&x->x_updown);
+
+ /* this should be though over:
+ * it might prove hard to provide consistency between labeled up- & downsampling methods
+ * maybe indeces would be better...
+ *
+ * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !)
+ */
+ if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */
+ else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */
+ else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */
+ else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */
+
+ return (x);
+}
+
+
+static void voutlet_setup(void)
+{
+ voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new,
+ (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0);
+ class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0);
+ class_addbang(voutlet_class, voutlet_bang);
+ class_addpointer(voutlet_class, voutlet_pointer);
+ class_addfloat(voutlet_class, (t_method)voutlet_float);
+ class_addsymbol(voutlet_class, voutlet_symbol);
+ class_addlist(voutlet_class, voutlet_list);
+ class_addanything(voutlet_class, voutlet_anything);
+ class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0);
+ class_sethelpsymbol(voutlet_class, gensym("pd"));
+}
+
+
+/* ---------------------------- overall setup ----------------------------- */
+
+void g_io_setup(void)
+{
+ vinlet_setup();
+ voutlet_setup();
+}