From 57045df5fe3ec557e57dc7434ac1a07b5521bffc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 29 Jul 2002 17:06:19 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r58, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=59 --- pd/src/d_ugen.c | 1078 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1078 insertions(+) create mode 100644 pd/src/d_ugen.c (limited to 'pd/src/d_ugen.c') diff --git a/pd/src/d_ugen.c b/pd/src/d_ugen.c new file mode 100644 index 00000000..68e931e6 --- /dev/null +++ b/pd/src/d_ugen.c @@ -0,0 +1,1078 @@ +/* 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. */ + +/* These routines build a copy of the DSP portion of a graph, which is + then sorted into a linear list of DSP operations which are added to + the DSP duty cycle called by the scheduler. Once that's been done, + we delete the copy. The DSP objects are represented by "ugenbox" + structures which are parallel to the DSP objects in the graph and + have vectors of siginlets and sigoutlets which record their + interconnections. +*/ + +/* hacked to run subpatches with different samplerates + * only samplerates that are a power_of_2-multiple of the + * + * mfg.gfd.uil + * IOhannes + * + * edited lines are marked with "IOhannes" + * + */ + + +#include "m_pd.h" +#include +#include + +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +int obj_nsiginlets(t_object *x); +int obj_siginletindex(t_object *x, int m); +int obj_nsigoutlets(t_object *x); +int obj_sigoutletindex(t_object *x, int m); +t_sample *obj_findsignalscalar(t_object *x, int m); +static int ugen_loud; +EXTERN_STRUCT _vinlet; +EXTERN_STRUCT _voutlet; + +void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); + +t_int *zero_perform(t_int *w) /* zero out a vector */ +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + while (n--) *out++ = 0; + return (w+3); +} + +t_int *zero_perf8(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + + for (; n; n -= 8, out += 8) + { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + return (w+3); +} + +void dsp_add_zero(t_sample *out, int n) +{ + if (n&7) + dsp_add(zero_perform, 2, out, n); + else + dsp_add(zero_perf8, 2, out, n); +} + +/* ---------------------------- block~ ----------------------------- */ + +/* The "block~ object maintains the containing canvas's DSP computation, +calling it at a super- or sub-multiple of the containing canvas's +calling frequency. The block~'s creation arguments specify block size +and overlap. Block~ does no "dsp" computation in its own right, but it +adds prolog and epilog code before and after the canvas's unit generators. + +A subcanvas need not have a block~ at all; if there's none, its +ugens are simply put on the list without any prolog or epilog code. + +Block~ may be invoked as switch~, in which case it also acts to switch the +subcanvas on and off. The overall order of scheduling for a subcanvas +is thus, + + inlet and outlet prologue code (1) + block prologue (2) + the objects in the subcanvas, including inlets and outlets + block epilogue (2) + outlet epilogue code (2) + +where (1) means, "if reblocked" and (2) means, "if reblocked or switched". + +If we're reblocked, the inlet prolog and outlet epilog code takes care of +overlapping and buffering to deal with vector size changes. If we're switched +but not reblocked, the inlet prolog is not needed, and the output epilog is +ONLY run when the block is switched off; in this case the epilog code simply +copies zeros to all signal outlets. +*/ + +static int dsp_phase; +static t_class *block_class; + +typedef struct _block +{ + t_object x_obj; + int x_vecsize; + int x_overlap; + int x_phase; /* from 0 to period-1; when zero we run the block */ + int x_period; /* submultiple of containing canvas */ + int x_frequency; /* supermultiple of comtaining canvas */ + int x_count; + int x_blocklength; /* length of dspchain for this block */ + int x_epiloglength; /* length of epilog */ + char x_switched; /* true if we're acting as a a switch */ + char x_switchon; /* true if we're switched on */ + char x_reblock; /* true if inlets and outlets are reblocking */ + + int x_upsample; /* IOhannes: upsampling-factor */ + int x_downsample; /* IOhannes: downsampling-factor */ + +} t_block; + +static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + int vecsize = fvecsize; + int overlap = foverlap; + int upsample, downsample; /* IOhannes */ + t_block *x = (t_block *)pd_new(block_class); + if (overlap < 1) + overlap = 1; + if (vecsize < 0) + vecsize = 0; /* this means we'll get it from parent later. */ + + /* IOhannes { */ + if (fupsample <= 0) upsample = downsample = 1; + else if (fupsample >= 1) { + upsample = fupsample; + downsample = 1; + } else { + downsample = 1.0 / fupsample; + upsample = 1; + } + /* } IOhannes */ + + if (vecsize && (vecsize != (1 << ilog2(vecsize)))) + { + pd_error(x, "block~: vector size not a power of 2"); + vecsize = 64; + } + if (overlap != (1 << ilog2(overlap))) + { + pd_error(x, "block~: overlap not a power of 2"); + overlap = 1; + } + /* IOhannes { */ + if (downsample != (1 << ilog2(downsample))) + { + pd_error(x, "block~: downsampling not a power of 2"); + downsample = 1; + } + if (upsample != (1 << ilog2(upsample))) + { + pd_error(x, "block~: upsampling not a power of 2"); + upsample = 1; + } + /* } IOhannes */ + + + x->x_vecsize = vecsize; + x->x_overlap = overlap; + x->x_phase = 0; + x->x_period = 1; + x->x_frequency = 1; + x->x_switched = 0; + x->x_switchon = 1; + /* IOhannes { */ + x->x_upsample = upsample; + x->x_downsample = downsample; + /* } IOhannes */ + return (x); +} + +static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ + x->x_switched = 1; + x->x_switchon = 0; + return (x); +} + +static void block_float(t_block *x, t_floatarg f) +{ + if (x->x_switched) + x->x_switchon = (f != 0); +} +#define PROLOGCALL 2 +#define EPILOGCALL 2 + +static t_int *block_prolog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int phase = x->x_phase; + /* if we're switched off, jump past the epilog code */ + if (!x->x_switchon) + return (w + x->x_blocklength); + if (phase) + { + phase++; + if (phase == x->x_period) phase = 0; + x->x_phase = phase; + return (w + x->x_blocklength); /* skip block; jump past epilog */ + } + else + { + x->x_count = x->x_frequency; + x->x_phase = (x->x_period > 1 ? 1 : 0); + return (w + PROLOGCALL); /* beginning of block is next ugen */ + } +} + +static t_int *block_epilog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int count = x->x_count - 1; + if (!x->x_reblock) + return (w + x->x_epiloglength + EPILOGCALL); + if (count) + { + x->x_count = count; + return (w - (x->x_blocklength - + (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ + } + else return (w + EPILOGCALL); +} + +static void block_dsp(t_block *x, t_signal **sp) +{ + /* do nothing here */ +} + +/* ------------------ DSP call list ----------------------- */ + +static t_int *dsp_chain; +static int dsp_chainsize; + +void dsp_add(t_perfroutine f, int n, ...) +{ + int newsize = dsp_chainsize + n+1, i; + va_list ap; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + va_start(ap, n); + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); + va_end(ap); + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + + /* at Guenter's suggestion, here's a vectorized version */ +void dsp_addv(t_perfroutine f, int n, t_int *vec) +{ + int newsize = dsp_chainsize + n+1, i; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = vec[i]; + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + +void dsp_tick(void) +{ + if (dsp_chain) + { + t_int *ip; + for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip); + dsp_phase++; + } +} + +/* ---------------- signals ---------------------------- */ + +int ilog2(int n) +{ + int r = -1; + if (n <= 0) return(0); + while (n) + { + r++; + n >>= 1; + } + return (r); +} + + /* list of signals which can be reused, sorted by buffer size */ +static t_signal *signal_freelist[MAXLOGSIG+1]; + /* list of reusable "borrowed" signals */ +static t_signal *signal_freeborrowed; + /* list of all signals allocated (not including "borrowed" ones) */ +static t_signal *signal_usedlist; + + /* call this when DSP is stopped to free all the signals */ +void signal_cleanup(void) +{ + t_signal **svec, *sig, *sig2; + int i; + while (sig = signal_usedlist) + { + signal_usedlist = sig->s_nextused; + if (!sig->s_isborrowed) + t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec)); + t_freebytes(sig, sizeof *sig); + } + for (i = 0; i <= MAXLOGSIG; i++) + signal_freelist[i] = 0; + signal_freeborrowed = 0; +} + + /* mark the signal "reusable." */ +void signal_makereusable(t_signal *sig) +{ + int logn = ilog2(sig->s_n); +#if 1 + t_signal *s5; + for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 3"); + return; + } + } + for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 4"); + return; + } + } +#endif + if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed); + if (sig->s_isborrowed) + { + /* if the signal is borrowed, decrement the borowee's reference + count, possibly marking it reusable too */ + t_signal *s2 = sig->s_borrowedfrom; + if ((s2 == sig) || !s2) + bug("signal_free"); + s2->s_refcount--; + if (!s2->s_refcount) + signal_makereusable(s2); + sig->s_nextfree = signal_freeborrowed; + signal_freeborrowed = sig; + } + else + { + /* if it's a real signal, put it on the free list so we can + reuse it. */ + if (signal_freelist[logn] == sig) bug("signal_free 2"); + sig->s_nextfree = signal_freelist[logn]; + signal_freelist[logn] = sig; + } +} + + /* reclaim or make an audio signal. If n is zero, return a "borrowed" + signal whose buffer and size will be obtained later via + signal_setborrowed(). */ + +t_signal *signal_new(int n, float sr) +{ + int logn, n2; + t_signal *ret, **whichlist; + t_sample *fp; + logn = ilog2(n); + if (n) + { + if (n != (1 << logn)) + bug("signal buffer not a power of 2"); + if (logn > MAXLOGSIG) + bug("signal buffer too large"); + whichlist = signal_freelist + logn; + } + else + whichlist = &signal_freeborrowed; + + /* first try to reclaim one from the free list */ + if (ret = *whichlist) + *whichlist = ret->s_nextfree; + else + { + /* LATER figure out what to do for out-of-space here! */ + ret = (t_signal *)t_getbytes(sizeof *ret); + if (n) + { + ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec)); + ret->s_isborrowed = 0; + } + else + { + ret->s_vec = 0; + ret->s_isborrowed = 1; + } + ret->s_nextused = signal_usedlist; + signal_usedlist = ret; + } + ret->s_n = n; + ret->s_sr = sr; + ret->s_refcount = 0; + ret->s_borrowedfrom = 0; + if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed); + return (ret); +} + +static t_signal *signal_newlike(const t_signal *sig) +{ + return (signal_new(sig->s_n, sig->s_sr)); +} + +void signal_setborrowed(t_signal *sig, t_signal *sig2) +{ + if (!sig->s_isborrowed || sig->s_borrowedfrom) + bug("signal_setborrowed"); + sig->s_borrowedfrom = sig2; + sig->s_vec = sig2->s_vec; + sig->s_n = sig2->s_n; +} + +int signal_compatible(t_signal *s1, t_signal *s2) +{ + return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr); +} + +/* ------------------ ugen ("unit generator") sorting ----------------- */ + +typedef struct _ugenbox +{ + struct _siginlet *u_in; + int u_nin; + struct _sigoutlet *u_out; + int u_nout; + int u_phase; + struct _ugenbox *u_next; + t_object *u_obj; + int u_done; +} t_ugenbox; + +typedef struct _siginlet +{ + int i_nconnect; + int i_ngot; + t_signal *i_signal; +} t_siginlet; + +typedef struct _sigoutconnect +{ + t_ugenbox *oc_who; + int oc_inno; + struct _sigoutconnect *oc_next; +} t_sigoutconnect; + +typedef struct _sigoutlet +{ + int o_nconnect; + int o_nsent; + t_signal *o_signal; + t_sigoutconnect *o_connections; +} t_sigoutlet; + + +struct _dspcontext +{ + struct _ugenbox *dc_ugenlist; + struct _dspcontext *dc_parentcontext; + int dc_ninlets; + int dc_noutlets; + t_signal **dc_iosigs; + float dc_srate; + int dc_vecsize; + char dc_toplevel; /* true if "iosigs" is invalid. */ + char dc_reblock; /* true if we have to reblock inlets/outlets */ + char dc_switched; /* true if we're switched */ + +}; + +#define t_dspcontext struct _dspcontext + +static int ugen_sortno = 0; +static t_dspcontext *ugen_currentcontext; + +void ugen_stop(void) +{ + t_signal *s; + int i; + if (dsp_chain) + { + freebytes(dsp_chain, dsp_chainsize * sizeof (t_int)); + dsp_chain = 0; + } + signal_cleanup(); + +} + +void ugen_start(void) +{ + ugen_stop(); + ugen_sortno++; + dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); + dsp_chain[0] = 0; + dsp_chainsize = 1; + if (ugen_currentcontext) bug("ugen_start"); +} + +int ugen_getsortno(void) +{ + return (ugen_sortno); +} + +#if 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + int i, count; + t_signal *sig; + for (count = 0, sig = signal_usedlist; sig; + count++, sig = sig->s_nextused) + ; + post("used signals %d", count); + for (i = 0; i < MAXLOGSIG; i++) + { + for (count = 0, sig = signal_freelist[i]; sig; + count++, sig = sig->s_nextfree) + ; + if (count) + post("size %d: free %d", (1 << i), count); + } + for (count = 0, sig = signal_freeborrowed; sig; + count++, sig = sig->s_nextfree) + ; + post("free borrowed %d", count); + + ugen_loud = argc; +} +#endif + + /* start building the graph for a canvas */ +t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, + int ninlets, int noutlets) +{ + t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); + float parent_srate, srate; + int parent_vecsize, vecsize; + + if (ugen_loud) post("ugen_start_graph..."); + + dc->dc_ugenlist = 0; + dc->dc_toplevel = toplevel; + dc->dc_iosigs = sp; + dc->dc_ninlets = ninlets; + dc->dc_noutlets = noutlets; + dc->dc_parentcontext = ugen_currentcontext; + ugen_currentcontext = dc; + return (dc); +} + + /* first the canvas calls this to create all the boxes... */ +void ugen_add(t_dspcontext *dc, t_object *obj, int nextjump) +{ + t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); + int i; + t_sigoutlet *uout; + t_siginlet *uin; + + x->u_next = dc->dc_ugenlist; + dc->dc_ugenlist = x; + x->u_obj = obj; + x->u_nin = obj_nsiginlets(obj); + x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); + for (uin = x->u_in, i = x->u_nin; i--; uin++) + uin->i_nconnect = 0; + x->u_nout = obj_nsigoutlets(obj); + x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); + for (uout = x->u_out, i = x->u_nout; i--; uout++) + uout->o_connections = 0, uout->o_nconnect = 0; +} + + /* and then this to make all the connections. */ +void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, + int inno) +{ + t_ugenbox *u1, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc; + int sigoutno = obj_sigoutletindex(x1, outno); + int siginno = obj_siginletindex(x2, inno); + if (ugen_loud) + post("%s -> %s: %d->%d", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), outno, inno); + for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); + for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); + if (!u1 || !u2 || siginno < 0) + { + pd_error(u1->u_obj, "signal outlet connect to nonsignal inlet (ignored)"); + return; + } + if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) + { + bug("ugen_connect %s %s %d %d (%d %d)", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, + u2->u_nin); + } + uout = u1->u_out + sigoutno; + uin = u2->u_in + siginno; + + /* add a new connection to the outlet's list */ + oc = (t_sigoutconnect *)getbytes(sizeof *oc); + oc->oc_next = uout->o_connections; + uout->o_connections = oc; + oc->oc_who = u2; + oc->oc_inno = siginno; + /* update inlet and outlet counts */ + uout->o_nconnect++; + uin->i_nconnect++; +} + + /* get the index of a ugenbox or -1 if it's not on the list */ +static int ugen_index(t_dspcontext *dc, t_ugenbox *x) +{ + int ret; + t_ugenbox *u; + for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) + if (u == x) return (ret); + return (-1); +} + + /* put a ugenbox on the chain, recursively putting any others on that + this one might uncover. */ +static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) +{ + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + t_class *class = pd_class(&u->u_obj->ob_pd); + int i, n; + /* suppress creating new signals for the outputs of signal + inlets and subpatchs; except in the case we're an inlet and "blocking" + is set. We don't yet know if a subcanvas will be "blocking" so there + we delay new signal creation, which will be handled by calling + signal_setborrowed in the ugen_done_graph routine below. */ + int nonewsigs = (class == canvas_class || + (class == vinlet_class) && !(dc->dc_reblock)); + /* when we encounter a subcanvas or a signal outlet, suppress freeing + the input signals as they may be "borrowed" for the super or sub + patch; same exception as above, but also if we're "switched" we + have to do a copy rather than a borrow. */ + int nofreesigs = (class == canvas_class || + (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched)); + t_signal **insig, **outsig, **sig, *s1, *s2, *s3; + t_ugenbox *u2; + + if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs, + nonewsigs); + for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) + { + if (!uin->i_nconnect) + { + t_sample *scalar; + s3 = signal_new(dc->dc_vecsize, dc->dc_srate); + /* post("%s: unconnected signal inlet set to zero", + class_getname(u->u_obj->ob_pd)); */ + if (scalar = obj_findsignalscalar(u->u_obj, i)) + dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n); + else + dsp_add_zero(s3->s_vec, s3->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + } + } + insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); + outsig = insig + u->u_nin; + for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) + { + int newrefcount; + *sig = uin->i_signal; + newrefcount = --(*sig)->s_refcount; + /* if the reference count went to zero, we free the signal now, + unless it's a subcanvas or outlet; these might keep the + signal around to send to objects connected to them. In this + case we increment the reference count; the corresponding decrement + is in sig_makereusable(). */ + if (nofreesigs) + (*sig)->s_refcount++; + else if (!newrefcount) + signal_makereusable(*sig); + } + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + /* similarly, for outlets of subcanvases we delay creating + them; instead we create "borrowed" ones so that the refcount + is known. The subcanvas replaces the fake signal with one showing + where the output data actually is, to avoid having to copy it. + For any other object, we just allocate a new output vector; + since we've already freed the inputs the objects might get called + "in place." */ + if (nonewsigs) + { + *sig = uout->o_signal = + signal_new(0, dc->dc_srate); + } + else + *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate); + (*sig)->s_refcount = uout->o_nconnect; + } + /* now call the DSP scheduling routine for the ugen. This + routine must fill in "borrowed" signal outputs in case it's either + a subcanvas or a signal inlet. */ + mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); + + /* if any output signals aren't connected to anyone, free them + now; otherwise they'll either get freed when the reference count + goes back to zero, or even later as explained above. */ + + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + if (!(*sig)->s_refcount) + signal_makereusable(*sig); + } + if (ugen_loud) + { + if (u->u_nin + u->u_nout == 0) post("put %s %d", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); + else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]); + else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1]); + else post("put %s %d (%x %x %x ...)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1], sig[2]); + } + + /* pass it on and trip anyone whose last inlet was filled */ + for (uout = u->u_out, i = u->u_nout; i--; uout++) + { + s1 = uout->o_signal; + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + u2 = oc->oc_who; + uin = &u2->u_in[oc->oc_inno]; + /* if there's already someone here, sum the two */ + if (s2 = uin->i_signal) + { + s1->s_refcount--; + s2->s_refcount--; + if (!signal_compatible(s1, s2)) + { + pd_error(u->u_obj, "%s: incompatible signal inputs", + class_getname(u->u_obj->ob_pd)); + return; + } + s3 = signal_newlike(s1); + dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + if (!s1->s_refcount) signal_makereusable(s1); + if (!s2->s_refcount) signal_makereusable(s2); + } + else uin->i_signal = s1; + uin->i_ngot++; + /* if we didn't fill this inlet don't bother yet */ + if (uin->i_ngot < uin->i_nconnect) + goto notyet; + /* if there's more than one, check them all */ + if (u2->u_nin > 1) + { + for (uin = u2->u_in, n = u2->u_nin; n--; uin++) + if (uin->i_ngot < uin->i_nconnect) goto notyet; + } + /* so now we can schedule the ugen. */ + ugen_doit(dc, u2); + notyet: ; + } + } + t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); +} + + /* once the DSP graph is built, we call this routine to sort it. + This routine also deletes the graph; later we might want to leave the + graph around, in case the user is editing the DSP network, to save having + to recreate it all the time. But not today. */ + +void ugen_done_graph(t_dspcontext *dc) +{ + t_ugenbox *u, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + int i, n; + t_block *blk; + t_dspcontext *parent_context = dc->dc_parentcontext; + float parent_srate; + int parent_vecsize; + int period, frequency, phase, vecsize; + float srate; + int chainblockbegin; /* DSP chain onset before block prolog code */ + int chainblockend; /* and after block epilog code */ + int chainafterall; /* and after signal outlet epilog */ + int reblock = 0, switched; + int downsample = 1, upsample = 1; /* IOhannes */ + /* debugging printout */ + + if (ugen_loud) + { + post("ugen_done_graph..."); + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + post("ugen: %s", class_getname(u->u_obj->ob_pd)); + for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + post("... out %d to %s, index %d, inlet %d", i, + class_getname(oc->oc_who->u_obj->ob_pd), + ugen_index(dc, oc->oc_who), oc->oc_inno); + } + } + } + + /* search for an object of class "block~" */ + for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == block_class) + { + if (blk) pd_error(blk, "conflicting block~ objects in same page"); + else blk = (t_block *)zz; + } + } + + /* figure out block size, calling frequency, sample rate */ + if (parent_context) + { + parent_srate = parent_context->dc_srate; + parent_vecsize = parent_context->dc_vecsize; + } + else + { + parent_srate = sys_getsr(); + parent_vecsize = sys_getblksize(); + } + if (blk) + { + int realoverlap; + vecsize = blk->x_vecsize; + if (vecsize == 0) + vecsize = parent_vecsize; + realoverlap = blk->x_overlap; + if (realoverlap > vecsize) realoverlap = vecsize; + /* IOhannes { */ + downsample = blk->x_downsample; + upsample = blk->x_upsample; + if (downsample > parent_vecsize) downsample=parent_vecsize; + period = (vecsize * downsample)/(parent_vecsize * realoverlap * upsample); + frequency = (parent_vecsize * realoverlap * upsample)/(vecsize * downsample); + /* } IOhannes*/ + phase = blk->x_phase; + srate = parent_srate * realoverlap * upsample / downsample; + /* IOhannes */ + if (period < 1) period = 1; + if (frequency < 1) frequency = 1; + blk->x_frequency = frequency; + blk->x_period = period; + blk->x_phase = dsp_phase & (period - 1); + if (! parent_context || (realoverlap != 1) || (vecsize != parent_vecsize) || + (downsample != 1) || (upsample != 1)) /* IOhannes */ + reblock = 1; + switched = blk->x_switched; + } + else + { + srate = parent_srate; + vecsize = parent_vecsize; + downsample = upsample = 1;/* IOhannes */ + period = frequency = 1; + phase = 0; + if (!parent_context) reblock = 1; + switched = 0; + } + dc->dc_reblock = reblock; + dc->dc_switched = switched; + dc->dc_srate = srate; + dc->dc_vecsize = vecsize; + + /* if we're reblocking or switched, we now have to create output + signals to fill in for the "borrowed" ones we have now. The + output signals will be filled by the outlet epilog code. */ + + if (reblock || switched) + { + t_signal **iosigs = dc->dc_iosigs; + if (iosigs) + { + t_signal **sigp; + int noutlets = dc->dc_noutlets; + for (i = 0, sigp = iosigs + dc->dc_ninlets; i < noutlets; + i++, sigp++) + { + signal_setborrowed(*sigp, + signal_new(parent_vecsize, parent_srate)); + (*sigp)->s_refcount++; + if (ugen_loud) post("set %x->%x", *sigp, (*sigp)->s_borrowedfrom); + } + } + } + if (ugen_loud) + post("reblock %d, switched %d", reblock, switched); + + /* schedule prologs for inlets and outlets. If the "reblock" flag + is set, an inlet will put code on the DSP chain to copy its input + into an internal buffer here, before any unit generators' DSP code + gets scheduled. If we don't "reblock", inlets will need to get + pointers to their corresponding inlets/outlets on the box we're inside, + if any. Outlets will also need pointers, unless we're switched, in + which case outlet epilog code will kick in. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs; + if (outsigs) outsigs += dc->dc_ninlets; + + if (pd_class(zz) == vinlet_class) + vinlet_dspprolog((struct _vinlet *)zz, + dc->dc_iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + else if (pd_class(zz) == voutlet_class) + voutlet_dspprolog((struct _voutlet *)zz, + outsigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + chainblockbegin = dsp_chainsize; + + if (blk && (reblock || switched)) /* add the block DSP prolog */ + dsp_add(block_prolog, 1, blk); + + /* Initialize for sorting */ + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + u->u_done = 0; + for (uout = u->u_out, i = u->u_nout; i--; uout++) + uout->o_nsent = 0; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + uin->i_ngot = 0, uin->i_signal = 0; + } + + /* Do the sort */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + /* check that we have no connected signal inlets */ + if (u->u_done) continue; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + if (uin->i_nconnect) goto next; + + ugen_doit(dc, u); + next: ; + } + + if (blk && (reblock || switched)) /* add block DSP epilog */ + dsp_add(block_epilog, 1, blk); + chainblockend = dsp_chainsize; + + /* add epilogs for outlets. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == voutlet_class) + { + t_signal **iosigs = dc->dc_iosigs; + if (iosigs) iosigs += dc->dc_ninlets; + voutlet_dspepilog((struct _voutlet *)zz, + iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + } + + chainafterall = dsp_chainsize; + if (blk) + { + blk->x_blocklength = chainblockend - chainblockbegin; + blk->x_epiloglength = chainafterall - chainblockend; + blk->x_reblock = reblock; + } + + if (ugen_loud) + { + t_int *ip; + if (!dc->dc_parentcontext) + for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) + post("chain %x", *ip); + post("... ugen_done_graph done."); + } + /* now delete everything. */ + while (dc->dc_ugenlist) + { + for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; + n--; uout++) + { + oc = uout->o_connections; + while (oc) + { + oc2 = oc->oc_next; + freebytes(oc, sizeof *oc); + oc = oc2; + } + } + freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * + sizeof (*dc->dc_ugenlist->u_out)); + freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * + sizeof(*dc->dc_ugenlist->u_in)); + u = dc->dc_ugenlist; + dc->dc_ugenlist = u->u_next; + freebytes(u, sizeof *u); + } + if (ugen_currentcontext == dc) + ugen_currentcontext = dc->dc_parentcontext; + else bug("ugen_currentcontext"); + freebytes(dc, sizeof(*dc)); + +} + +t_signal *ugen_getiosig(int index, int inout) +{ + if (!ugen_currentcontext) bug("ugen_getiosig"); + if (ugen_currentcontext->dc_toplevel) return (0); + if (inout) index += ugen_currentcontext->dc_ninlets; + return (ugen_currentcontext->dc_iosigs[index]); +} + + +/* -------------------- setup routine -------------------------- */ + +void d_ugen_setup(void) /* really just block_setup */ +{ + block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, + sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addcreator((t_newmethod)switch_new, gensym("switch~"), + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0); + class_addfloat(block_class, block_float); +} + -- cgit v1.2.1