diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
commit | 513389b4c1bc8afe4b2dc9947c534bfeb105e3da (patch) | |
tree | 10e673b35651ac567fed2eda0c679c7ade64cbc6 /apps/plugins/pdbox/PDa/src/d_ugen.c | |
parent | 95fa7f6a2ef466444fbe3fe87efc6d5db6b77b36 (diff) | |
download | rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.tar.gz rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.zip |
Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/d_ugen.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/d_ugen.c | 2252 |
1 files changed, 2252 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/src/d_ugen.c b/apps/plugins/pdbox/PDa/src/d_ugen.c new file mode 100644 index 0000000000..9820f190cc --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_ugen.c | |||
@@ -0,0 +1,2252 @@ | |||
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 | /* These routines build a copy of the DSP portion of a graph, which is | ||
6 | then sorted into a linear list of DSP operations which are added to | ||
7 | the DSP duty cycle called by the scheduler. Once that's been done, | ||
8 | we delete the copy. The DSP objects are represented by "ugenbox" | ||
9 | structures which are parallel to the DSP objects in the graph and | ||
10 | have vectors of siginlets and sigoutlets which record their | ||
11 | interconnections. | ||
12 | */ | ||
13 | |||
14 | /* hacked to run subpatches with different samplerates | ||
15 | * only samplerates that are a power_of_2-multiple of the | ||
16 | * | ||
17 | * mfg.gfd.uil | ||
18 | * IOhannes | ||
19 | * | ||
20 | * edited lines are marked with "IOhannes" | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include "m_pd.h" | ||
26 | #include "m_imp.h" | ||
27 | #include <stdlib.h> | ||
28 | #include <stdarg.h> | ||
29 | |||
30 | extern t_class *vinlet_class, *voutlet_class, *canvas_class; | ||
31 | t_sample *obj_findsignalscalar(t_object *x, int m); | ||
32 | static int ugen_loud; | ||
33 | EXTERN_STRUCT _vinlet; | ||
34 | EXTERN_STRUCT _voutlet; | ||
35 | |||
36 | void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, | ||
37 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
38 | int switched); | ||
39 | void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, | ||
40 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
41 | int switched); | ||
42 | void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, | ||
43 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
44 | int switched); | ||
45 | |||
46 | t_int *zero_perform(t_int *w) /* zero out a vector */ | ||
47 | { | ||
48 | t_float *out = (t_float *)(w[1]); | ||
49 | int n = (int)(w[2]); | ||
50 | while (n--) *out++ = 0; | ||
51 | return (w+3); | ||
52 | } | ||
53 | |||
54 | t_int *zero_perf8(t_int *w) | ||
55 | { | ||
56 | t_float *out = (t_float *)(w[1]); | ||
57 | int n = (int)(w[2]); | ||
58 | |||
59 | for (; n; n -= 8, out += 8) | ||
60 | { | ||
61 | out[0] = 0; | ||
62 | out[1] = 0; | ||
63 | out[2] = 0; | ||
64 | out[3] = 0; | ||
65 | out[4] = 0; | ||
66 | out[5] = 0; | ||
67 | out[6] = 0; | ||
68 | out[7] = 0; | ||
69 | } | ||
70 | return (w+3); | ||
71 | } | ||
72 | |||
73 | void dsp_add_zero(t_sample *out, int n) | ||
74 | { | ||
75 | if (n&7) | ||
76 | dsp_add(zero_perform, 2, out, n); | ||
77 | else | ||
78 | dsp_add(zero_perf8, 2, out, n); | ||
79 | } | ||
80 | |||
81 | /* ---------------------------- block~ ----------------------------- */ | ||
82 | |||
83 | /* The "block~ object maintains the containing canvas's DSP computation, | ||
84 | calling it at a super- or sub-multiple of the containing canvas's | ||
85 | calling frequency. The block~'s creation arguments specify block size | ||
86 | and overlap. Block~ does no "dsp" computation in its own right, but it | ||
87 | adds prolog and epilog code before and after the canvas's unit generators. | ||
88 | |||
89 | A subcanvas need not have a block~ at all; if there's none, its | ||
90 | ugens are simply put on the list without any prolog or epilog code. | ||
91 | |||
92 | Block~ may be invoked as switch~, in which case it also acts to switch the | ||
93 | subcanvas on and off. The overall order of scheduling for a subcanvas | ||
94 | is thus, | ||
95 | |||
96 | inlet and outlet prologue code (1) | ||
97 | block prologue (2) | ||
98 | the objects in the subcanvas, including inlets and outlets | ||
99 | block epilogue (2) | ||
100 | outlet epilogue code (2) | ||
101 | |||
102 | where (1) means, "if reblocked" and (2) means, "if reblocked or switched". | ||
103 | |||
104 | If we're reblocked, the inlet prolog and outlet epilog code takes care of | ||
105 | overlapping and buffering to deal with vector size changes. If we're switched | ||
106 | but not reblocked, the inlet prolog is not needed, and the output epilog is | ||
107 | ONLY run when the block is switched off; in this case the epilog code simply | ||
108 | copies zeros to all signal outlets. | ||
109 | */ | ||
110 | |||
111 | static int dsp_phase; | ||
112 | static t_class *block_class; | ||
113 | |||
114 | typedef struct _block | ||
115 | { | ||
116 | t_object x_obj; | ||
117 | int x_vecsize; | ||
118 | int x_overlap; | ||
119 | int x_phase; /* from 0 to period-1; when zero we run the block */ | ||
120 | int x_period; /* submultiple of containing canvas */ | ||
121 | int x_frequency; /* supermultiple of comtaining canvas */ | ||
122 | int x_count; | ||
123 | int x_blocklength; /* length of dspchain for this block */ | ||
124 | int x_epiloglength; /* length of epilog */ | ||
125 | char x_switched; /* true if we're acting as a a switch */ | ||
126 | char x_switchon; /* true if we're switched on */ | ||
127 | char x_reblock; /* true if inlets and outlets are reblocking */ | ||
128 | int x_upsample; /* IOhannes: upsampling-factor */ | ||
129 | int x_downsample; /* IOhannes: downsampling-factor */ | ||
130 | |||
131 | } t_block; | ||
132 | |||
133 | static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, | ||
134 | t_floatarg fupsample); | ||
135 | |||
136 | static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, | ||
137 | t_floatarg fupsample) /* IOhannes */ | ||
138 | { | ||
139 | t_block *x = (t_block *)pd_new(block_class); | ||
140 | x->x_phase = 0; | ||
141 | x->x_period = 1; | ||
142 | x->x_frequency = 1; | ||
143 | x->x_switched = 0; | ||
144 | x->x_switchon = 1; | ||
145 | block_set(x, fvecsize, foverlap, fupsample); | ||
146 | return (x); | ||
147 | } | ||
148 | |||
149 | static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, | ||
150 | t_floatarg fupsample) | ||
151 | { | ||
152 | int upsample, downsample; /* IOhannes */ | ||
153 | int vecsize = fvecsize; | ||
154 | int overlap = foverlap; | ||
155 | int dspstate = canvas_suspend_dsp(); | ||
156 | if (overlap < 1) | ||
157 | overlap = 1; | ||
158 | if (vecsize < 0) | ||
159 | vecsize = 0; /* this means we'll get it from parent later. */ | ||
160 | |||
161 | /* IOhannes { */ | ||
162 | if (fupsample <= 0) upsample = downsample = 1; | ||
163 | else if (fupsample >= 1) { | ||
164 | upsample = fupsample; | ||
165 | downsample = 1; | ||
166 | } else { | ||
167 | downsample = 1.0 / fupsample; | ||
168 | upsample = 1; | ||
169 | } | ||
170 | /* } IOhannes */ | ||
171 | |||
172 | if (vecsize && (vecsize != (1 << ilog2(vecsize)))) | ||
173 | { | ||
174 | pd_error(x, "block~: vector size not a power of 2"); | ||
175 | vecsize = 64; | ||
176 | } | ||
177 | if (overlap != (1 << ilog2(overlap))) | ||
178 | { | ||
179 | pd_error(x, "block~: overlap not a power of 2"); | ||
180 | overlap = 1; | ||
181 | } | ||
182 | /* IOhannes { */ | ||
183 | if (downsample != (1 << ilog2(downsample))) | ||
184 | { | ||
185 | pd_error(x, "block~: downsampling not a power of 2"); | ||
186 | downsample = 1; | ||
187 | } | ||
188 | if (upsample != (1 << ilog2(upsample))) | ||
189 | { | ||
190 | pd_error(x, "block~: upsampling not a power of 2"); | ||
191 | upsample = 1; | ||
192 | } | ||
193 | /* } IOhannes */ | ||
194 | |||
195 | |||
196 | x->x_vecsize = vecsize; | ||
197 | x->x_overlap = overlap; | ||
198 | /* IOhannes { */ | ||
199 | x->x_upsample = upsample; | ||
200 | x->x_downsample = downsample; | ||
201 | /* } IOhannes */ | ||
202 | canvas_resume_dsp(dspstate); | ||
203 | } | ||
204 | |||
205 | static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, | ||
206 | t_floatarg fupsample) /* IOhannes */ | ||
207 | { | ||
208 | t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ | ||
209 | x->x_switched = 1; | ||
210 | x->x_switchon = 0; | ||
211 | return (x); | ||
212 | } | ||
213 | |||
214 | static void block_float(t_block *x, t_floatarg f) | ||
215 | { | ||
216 | if (x->x_switched) | ||
217 | x->x_switchon = (f != 0); | ||
218 | } | ||
219 | #define PROLOGCALL 2 | ||
220 | #define EPILOGCALL 2 | ||
221 | |||
222 | static t_int *block_prolog(t_int *w) | ||
223 | { | ||
224 | t_block *x = (t_block *)w[1]; | ||
225 | int phase = x->x_phase; | ||
226 | /* if we're switched off, jump past the epilog code */ | ||
227 | if (!x->x_switchon) | ||
228 | return (w + x->x_blocklength); | ||
229 | if (phase) | ||
230 | { | ||
231 | phase++; | ||
232 | if (phase == x->x_period) phase = 0; | ||
233 | x->x_phase = phase; | ||
234 | return (w + x->x_blocklength); /* skip block; jump past epilog */ | ||
235 | } | ||
236 | else | ||
237 | { | ||
238 | x->x_count = x->x_frequency; | ||
239 | x->x_phase = (x->x_period > 1 ? 1 : 0); | ||
240 | return (w + PROLOGCALL); /* beginning of block is next ugen */ | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static t_int *block_epilog(t_int *w) | ||
245 | { | ||
246 | t_block *x = (t_block *)w[1]; | ||
247 | int count = x->x_count - 1; | ||
248 | if (!x->x_reblock) | ||
249 | return (w + x->x_epiloglength + EPILOGCALL); | ||
250 | if (count) | ||
251 | { | ||
252 | x->x_count = count; | ||
253 | return (w - (x->x_blocklength - | ||
254 | (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ | ||
255 | } | ||
256 | else return (w + EPILOGCALL); | ||
257 | } | ||
258 | |||
259 | static void block_dsp(t_block *x, t_signal **sp) | ||
260 | { | ||
261 | /* do nothing here */ | ||
262 | } | ||
263 | |||
264 | /* ------------------ DSP call list ----------------------- */ | ||
265 | |||
266 | static t_int *dsp_chain; | ||
267 | static int dsp_chainsize; | ||
268 | |||
269 | void dsp_add(t_perfroutine f, int n, ...) | ||
270 | { | ||
271 | int newsize = dsp_chainsize + n+1, i; | ||
272 | va_list ap; | ||
273 | |||
274 | dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), | ||
275 | newsize * sizeof (t_int)); | ||
276 | dsp_chain[dsp_chainsize-1] = (t_int)f; | ||
277 | va_start(ap, n); | ||
278 | for (i = 0; i < n; i++) | ||
279 | dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); | ||
280 | va_end(ap); | ||
281 | dsp_chain[newsize-1] = 0; | ||
282 | dsp_chainsize = newsize; | ||
283 | } | ||
284 | |||
285 | /* at Guenter's suggestion, here's a vectorized version */ | ||
286 | void dsp_addv(t_perfroutine f, int n, t_int *vec) | ||
287 | { | ||
288 | int newsize = dsp_chainsize + n+1, i; | ||
289 | |||
290 | dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), | ||
291 | newsize * sizeof (t_int)); | ||
292 | dsp_chain[dsp_chainsize-1] = (t_int)f; | ||
293 | for (i = 0; i < n; i++) | ||
294 | dsp_chain[dsp_chainsize + i] = vec[i]; | ||
295 | dsp_chain[newsize-1] = 0; | ||
296 | dsp_chainsize = newsize; | ||
297 | } | ||
298 | |||
299 | void dsp_tick(void) | ||
300 | { | ||
301 | if (dsp_chain) | ||
302 | { | ||
303 | t_int *ip; | ||
304 | for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip); | ||
305 | dsp_phase++; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | /* ---------------- signals ---------------------------- */ | ||
310 | |||
311 | int ilog2(int n) | ||
312 | { | ||
313 | int r = -1; | ||
314 | if (n <= 0) return(0); | ||
315 | while (n) | ||
316 | { | ||
317 | r++; | ||
318 | n >>= 1; | ||
319 | } | ||
320 | return (r); | ||
321 | } | ||
322 | |||
323 | /* list of signals which can be reused, sorted by buffer size */ | ||
324 | static t_signal *signal_freelist[MAXLOGSIG+1]; | ||
325 | /* list of reusable "borrowed" signals (which don't own sample buffers) */ | ||
326 | static t_signal *signal_freeborrowed; | ||
327 | /* list of all signals allocated (not including "borrowed" ones) */ | ||
328 | static t_signal *signal_usedlist; | ||
329 | |||
330 | /* call this when DSP is stopped to free all the signals */ | ||
331 | void signal_cleanup(void) | ||
332 | { | ||
333 | t_signal **svec, *sig, *sig2; | ||
334 | int i; | ||
335 | while (sig = signal_usedlist) | ||
336 | { | ||
337 | signal_usedlist = sig->s_nextused; | ||
338 | if (!sig->s_isborrowed) | ||
339 | t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec)); | ||
340 | t_freebytes(sig, sizeof *sig); | ||
341 | } | ||
342 | for (i = 0; i <= MAXLOGSIG; i++) | ||
343 | signal_freelist[i] = 0; | ||
344 | signal_freeborrowed = 0; | ||
345 | } | ||
346 | |||
347 | /* mark the signal "reusable." */ | ||
348 | void signal_makereusable(t_signal *sig) | ||
349 | { | ||
350 | int logn = ilog2(sig->s_n); | ||
351 | #if 1 | ||
352 | t_signal *s5; | ||
353 | for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree) | ||
354 | { | ||
355 | if (s5 == sig) | ||
356 | { | ||
357 | bug("signal_free 3"); | ||
358 | return; | ||
359 | } | ||
360 | } | ||
361 | for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree) | ||
362 | { | ||
363 | if (s5 == sig) | ||
364 | { | ||
365 | bug("signal_free 4"); | ||
366 | return; | ||
367 | } | ||
368 | } | ||
369 | #endif | ||
370 | if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed); | ||
371 | if (sig->s_isborrowed) | ||
372 | { | ||
373 | /* if the signal is borrowed, decrement the borrowed-from signal's | ||
374 | reference count, possibly marking it reusable too */ | ||
375 | t_signal *s2 = sig->s_borrowedfrom; | ||
376 | if ((s2 == sig) || !s2) | ||
377 | bug("signal_free"); | ||
378 | s2->s_refcount--; | ||
379 | if (!s2->s_refcount) | ||
380 | signal_makereusable(s2); | ||
381 | sig->s_nextfree = signal_freeborrowed; | ||
382 | signal_freeborrowed = sig; | ||
383 | } | ||
384 | else | ||
385 | { | ||
386 | /* if it's a real signal (not borrowed), put it on the free list | ||
387 | so we can reuse it. */ | ||
388 | if (signal_freelist[logn] == sig) bug("signal_free 2"); | ||
389 | sig->s_nextfree = signal_freelist[logn]; | ||
390 | signal_freelist[logn] = sig; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | /* reclaim or make an audio signal. If n is zero, return a "borrowed" | ||
395 | signal whose buffer and size will be obtained later via | ||
396 | signal_setborrowed(). */ | ||
397 | |||
398 | t_signal *signal_new(int n, float sr) | ||
399 | { | ||
400 | int logn, n2; | ||
401 | t_signal *ret, **whichlist; | ||
402 | t_sample *fp; | ||
403 | logn = ilog2(n); | ||
404 | if (n) | ||
405 | { | ||
406 | if (n != (1 << logn)) | ||
407 | bug("signal buffer not a power of 2"); | ||
408 | if (logn > MAXLOGSIG) | ||
409 | bug("signal buffer too large"); | ||
410 | whichlist = signal_freelist + logn; | ||
411 | } | ||
412 | else | ||
413 | whichlist = &signal_freeborrowed; | ||
414 | |||
415 | /* first try to reclaim one from the free list */ | ||
416 | if (ret = *whichlist) | ||
417 | *whichlist = ret->s_nextfree; | ||
418 | else | ||
419 | { | ||
420 | /* LATER figure out what to do for out-of-space here! */ | ||
421 | ret = (t_signal *)t_getbytes(sizeof *ret); | ||
422 | if (n) | ||
423 | { | ||
424 | ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec)); | ||
425 | ret->s_isborrowed = 0; | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | ret->s_vec = 0; | ||
430 | ret->s_isborrowed = 1; | ||
431 | } | ||
432 | ret->s_nextused = signal_usedlist; | ||
433 | signal_usedlist = ret; | ||
434 | } | ||
435 | ret->s_n = n; | ||
436 | ret->s_sr = sr; | ||
437 | ret->s_refcount = 0; | ||
438 | ret->s_borrowedfrom = 0; | ||
439 | if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed); | ||
440 | return (ret); | ||
441 | } | ||
442 | |||
443 | static t_signal *signal_newlike(const t_signal *sig) | ||
444 | { | ||
445 | return (signal_new(sig->s_n, sig->s_sr)); | ||
446 | } | ||
447 | |||
448 | void signal_setborrowed(t_signal *sig, t_signal *sig2) | ||
449 | { | ||
450 | if (!sig->s_isborrowed || sig->s_borrowedfrom) | ||
451 | bug("signal_setborrowed"); | ||
452 | if (sig == sig2) | ||
453 | bug("signal_setborrowed 2"); | ||
454 | sig->s_borrowedfrom = sig2; | ||
455 | sig->s_vec = sig2->s_vec; | ||
456 | sig->s_n = sig2->s_n; | ||
457 | } | ||
458 | |||
459 | int signal_compatible(t_signal *s1, t_signal *s2) | ||
460 | { | ||
461 | return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr); | ||
462 | } | ||
463 | |||
464 | /* ------------------ ugen ("unit generator") sorting ----------------- */ | ||
465 | |||
466 | typedef struct _ugenbox | ||
467 | { | ||
468 | struct _siginlet *u_in; | ||
469 | int u_nin; | ||
470 | struct _sigoutlet *u_out; | ||
471 | int u_nout; | ||
472 | int u_phase; | ||
473 | struct _ugenbox *u_next; | ||
474 | t_object *u_obj; | ||
475 | int u_done; | ||
476 | } t_ugenbox; | ||
477 | |||
478 | typedef struct _siginlet | ||
479 | { | ||
480 | int i_nconnect; | ||
481 | int i_ngot; | ||
482 | t_signal *i_signal; | ||
483 | } t_siginlet; | ||
484 | |||
485 | typedef struct _sigoutconnect | ||
486 | { | ||
487 | t_ugenbox *oc_who; | ||
488 | int oc_inno; | ||
489 | struct _sigoutconnect *oc_next; | ||
490 | } t_sigoutconnect; | ||
491 | |||
492 | typedef struct _sigoutlet | ||
493 | { | ||
494 | int o_nconnect; | ||
495 | int o_nsent; | ||
496 | t_signal *o_signal; | ||
497 | t_sigoutconnect *o_connections; | ||
498 | } t_sigoutlet; | ||
499 | |||
500 | |||
501 | struct _dspcontext | ||
502 | { | ||
503 | struct _ugenbox *dc_ugenlist; | ||
504 | struct _dspcontext *dc_parentcontext; | ||
505 | int dc_ninlets; | ||
506 | int dc_noutlets; | ||
507 | t_signal **dc_iosigs; | ||
508 | float dc_srate; | ||
509 | int dc_vecsize; | ||
510 | char dc_toplevel; /* true if "iosigs" is invalid. */ | ||
511 | char dc_reblock; /* true if we have to reblock inlets/outlets */ | ||
512 | char dc_switched; /* true if we're switched */ | ||
513 | |||
514 | }; | ||
515 | |||
516 | #define t_dspcontext struct _dspcontext | ||
517 | |||
518 | static int ugen_sortno = 0; | ||
519 | static t_dspcontext *ugen_currentcontext; | ||
520 | |||
521 | void ugen_stop(void) | ||
522 | { | ||
523 | t_signal *s; | ||
524 | int i; | ||
525 | if (dsp_chain) | ||
526 | { | ||
527 | freebytes(dsp_chain, dsp_chainsize * sizeof (t_int)); | ||
528 | dsp_chain = 0; | ||
529 | } | ||
530 | signal_cleanup(); | ||
531 | |||
532 | } | ||
533 | |||
534 | void ugen_start(void) | ||
535 | { | ||
536 | ugen_stop(); | ||
537 | ugen_sortno++; | ||
538 | dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); | ||
539 | dsp_chain[0] = 0; | ||
540 | dsp_chainsize = 1; | ||
541 | if (ugen_currentcontext) bug("ugen_start"); | ||
542 | } | ||
543 | |||
544 | int ugen_getsortno(void) | ||
545 | { | ||
546 | return (ugen_sortno); | ||
547 | } | ||
548 | |||
549 | #if 0 | ||
550 | void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) | ||
551 | { | ||
552 | int i, count; | ||
553 | t_signal *sig; | ||
554 | for (count = 0, sig = signal_usedlist; sig; | ||
555 | count++, sig = sig->s_nextused) | ||
556 | ; | ||
557 | post("used signals %d", count); | ||
558 | for (i = 0; i < MAXLOGSIG; i++) | ||
559 | { | ||
560 | for (count = 0, sig = signal_freelist[i]; sig; | ||
561 | count++, sig = sig->s_nextfree) | ||
562 | ; | ||
563 | if (count) | ||
564 | post("size %d: free %d", (1 << i), count); | ||
565 | } | ||
566 | for (count = 0, sig = signal_freeborrowed; sig; | ||
567 | count++, sig = sig->s_nextfree) | ||
568 | ; | ||
569 | post("free borrowed %d", count); | ||
570 | |||
571 | ugen_loud = argc; | ||
572 | } | ||
573 | #endif | ||
574 | |||
575 | /* start building the graph for a canvas */ | ||
576 | t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, | ||
577 | int ninlets, int noutlets) | ||
578 | { | ||
579 | t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); | ||
580 | float parent_srate, srate; | ||
581 | int parent_vecsize, vecsize; | ||
582 | |||
583 | if (ugen_loud) post("ugen_start_graph..."); | ||
584 | |||
585 | dc->dc_ugenlist = 0; | ||
586 | dc->dc_toplevel = toplevel; | ||
587 | dc->dc_iosigs = sp; | ||
588 | dc->dc_ninlets = ninlets; | ||
589 | dc->dc_noutlets = noutlets; | ||
590 | dc->dc_parentcontext = ugen_currentcontext; | ||
591 | ugen_currentcontext = dc; | ||
592 | return (dc); | ||
593 | } | ||
594 | |||
595 | /* first the canvas calls this to create all the boxes... */ | ||
596 | void ugen_add(t_dspcontext *dc, t_object *obj) | ||
597 | { | ||
598 | t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); | ||
599 | int i; | ||
600 | t_sigoutlet *uout; | ||
601 | t_siginlet *uin; | ||
602 | |||
603 | x->u_next = dc->dc_ugenlist; | ||
604 | dc->dc_ugenlist = x; | ||
605 | x->u_obj = obj; | ||
606 | x->u_nin = obj_nsiginlets(obj); | ||
607 | x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); | ||
608 | for (uin = x->u_in, i = x->u_nin; i--; uin++) | ||
609 | uin->i_nconnect = 0; | ||
610 | x->u_nout = obj_nsigoutlets(obj); | ||
611 | x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); | ||
612 | for (uout = x->u_out, i = x->u_nout; i--; uout++) | ||
613 | uout->o_connections = 0, uout->o_nconnect = 0; | ||
614 | } | ||
615 | |||
616 | /* and then this to make all the connections. */ | ||
617 | void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, | ||
618 | int inno) | ||
619 | { | ||
620 | t_ugenbox *u1, *u2; | ||
621 | t_sigoutlet *uout; | ||
622 | t_siginlet *uin; | ||
623 | t_sigoutconnect *oc; | ||
624 | int sigoutno = obj_sigoutletindex(x1, outno); | ||
625 | int siginno = obj_siginletindex(x2, inno); | ||
626 | if (ugen_loud) | ||
627 | post("%s -> %s: %d->%d", | ||
628 | class_getname(x1->ob_pd), | ||
629 | class_getname(x2->ob_pd), outno, inno); | ||
630 | for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); | ||
631 | for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); | ||
632 | if (!u1 || !u2 || siginno < 0) | ||
633 | { | ||
634 | pd_error(u1->u_obj, | ||
635 | "signal outlet connect to nonsignal inlet (ignored)"); | ||
636 | return; | ||
637 | } | ||
638 | if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) | ||
639 | { | ||
640 | bug("ugen_connect %s %s %d %d (%d %d)", | ||
641 | class_getname(x1->ob_pd), | ||
642 | class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, | ||
643 | u2->u_nin); | ||
644 | } | ||
645 | uout = u1->u_out + sigoutno; | ||
646 | uin = u2->u_in + siginno; | ||
647 | |||
648 | /* add a new connection to the outlet's list */ | ||
649 | oc = (t_sigoutconnect *)getbytes(sizeof *oc); | ||
650 | oc->oc_next = uout->o_connections; | ||
651 | uout->o_connections = oc; | ||
652 | oc->oc_who = u2; | ||
653 | oc->oc_inno = siginno; | ||
654 | /* update inlet and outlet counts */ | ||
655 | uout->o_nconnect++; | ||
656 | uin->i_nconnect++; | ||
657 | } | ||
658 | |||
659 | /* get the index of a ugenbox or -1 if it's not on the list */ | ||
660 | static int ugen_index(t_dspcontext *dc, t_ugenbox *x) | ||
661 | { | ||
662 | int ret; | ||
663 | t_ugenbox *u; | ||
664 | for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) | ||
665 | if (u == x) return (ret); | ||
666 | return (-1); | ||
667 | } | ||
668 | |||
669 | /* put a ugenbox on the chain, recursively putting any others on that | ||
670 | this one might uncover. */ | ||
671 | static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) | ||
672 | { | ||
673 | t_sigoutlet *uout; | ||
674 | t_siginlet *uin; | ||
675 | t_sigoutconnect *oc, *oc2; | ||
676 | t_class *class = pd_class(&u->u_obj->ob_pd); | ||
677 | int i, n; | ||
678 | /* suppress creating new signals for the outputs of signal | ||
679 | inlets and subpatchs; except in the case we're an inlet and "blocking" | ||
680 | is set. We don't yet know if a subcanvas will be "blocking" so there | ||
681 | we delay new signal creation, which will be handled by calling | ||
682 | signal_setborrowed in the ugen_done_graph routine below. */ | ||
683 | int nonewsigs = (class == canvas_class || | ||
684 | (class == vinlet_class) && !(dc->dc_reblock)); | ||
685 | /* when we encounter a subcanvas or a signal outlet, suppress freeing | ||
686 | the input signals as they may be "borrowed" for the super or sub | ||
687 | patch; same exception as above, but also if we're "switched" we | ||
688 | have to do a copy rather than a borrow. */ | ||
689 | int nofreesigs = (class == canvas_class || | ||
690 | (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched)); | ||
691 | t_signal **insig, **outsig, **sig, *s1, *s2, *s3; | ||
692 | t_ugenbox *u2; | ||
693 | |||
694 | if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs, | ||
695 | nonewsigs); | ||
696 | for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) | ||
697 | { | ||
698 | if (!uin->i_nconnect) | ||
699 | { | ||
700 | t_sample *scalar; | ||
701 | s3 = signal_new(dc->dc_vecsize, dc->dc_srate); | ||
702 | /* post("%s: unconnected signal inlet set to zero", | ||
703 | class_getname(u->u_obj->ob_pd)); */ | ||
704 | if (scalar = obj_findsignalscalar(u->u_obj, i)) | ||
705 | dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n); | ||
706 | else | ||
707 | dsp_add_zero(s3->s_vec, s3->s_n); | ||
708 | uin->i_signal = s3; | ||
709 | s3->s_refcount = 1; | ||
710 | } | ||
711 | } | ||
712 | insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); | ||
713 | outsig = insig + u->u_nin; | ||
714 | for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) | ||
715 | { | ||
716 | int newrefcount; | ||
717 | *sig = uin->i_signal; | ||
718 | newrefcount = --(*sig)->s_refcount; | ||
719 | /* if the reference count went to zero, we free the signal now, | ||
720 | unless it's a subcanvas or outlet; these might keep the | ||
721 | signal around to send to objects connected to them. In this | ||
722 | case we increment the reference count; the corresponding decrement | ||
723 | is in sig_makereusable(). */ | ||
724 | if (nofreesigs) | ||
725 | (*sig)->s_refcount++; | ||
726 | else if (!newrefcount) | ||
727 | signal_makereusable(*sig); | ||
728 | } | ||
729 | for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) | ||
730 | { | ||
731 | /* similarly, for outlets of subcanvases we delay creating | ||
732 | them; instead we create "borrowed" ones so that the refcount | ||
733 | is known. The subcanvas replaces the fake signal with one showing | ||
734 | where the output data actually is, to avoid having to copy it. | ||
735 | For any other object, we just allocate a new output vector; | ||
736 | since we've already freed the inputs the objects might get called | ||
737 | "in place." */ | ||
738 | if (nonewsigs) | ||
739 | { | ||
740 | *sig = uout->o_signal = | ||
741 | signal_new(0, dc->dc_srate); | ||
742 | } | ||
743 | else | ||
744 | *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate); | ||
745 | (*sig)->s_refcount = uout->o_nconnect; | ||
746 | } | ||
747 | /* now call the DSP scheduling routine for the ugen. This | ||
748 | routine must fill in "borrowed" signal outputs in case it's either | ||
749 | a subcanvas or a signal inlet. */ | ||
750 | mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); | ||
751 | |||
752 | /* if any output signals aren't connected to anyone, free them | ||
753 | now; otherwise they'll either get freed when the reference count | ||
754 | goes back to zero, or even later as explained above. */ | ||
755 | |||
756 | for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) | ||
757 | { | ||
758 | if (!(*sig)->s_refcount) | ||
759 | signal_makereusable(*sig); | ||
760 | } | ||
761 | if (ugen_loud) | ||
762 | { | ||
763 | if (u->u_nin + u->u_nout == 0) post("put %s %d", | ||
764 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); | ||
765 | else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)", | ||
766 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]); | ||
767 | else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)", | ||
768 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), | ||
769 | sig[0], sig[1]); | ||
770 | else post("put %s %d (%x %x %x ...)", | ||
771 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), | ||
772 | sig[0], sig[1], sig[2]); | ||
773 | } | ||
774 | |||
775 | /* pass it on and trip anyone whose last inlet was filled */ | ||
776 | for (uout = u->u_out, i = u->u_nout; i--; uout++) | ||
777 | { | ||
778 | s1 = uout->o_signal; | ||
779 | for (oc = uout->o_connections; oc; oc = oc->oc_next) | ||
780 | { | ||
781 | u2 = oc->oc_who; | ||
782 | uin = &u2->u_in[oc->oc_inno]; | ||
783 | /* if there's already someone here, sum the two */ | ||
784 | if (s2 = uin->i_signal) | ||
785 | { | ||
786 | s1->s_refcount--; | ||
787 | s2->s_refcount--; | ||
788 | if (!signal_compatible(s1, s2)) | ||
789 | { | ||
790 | pd_error(u->u_obj, "%s: incompatible signal inputs", | ||
791 | class_getname(u->u_obj->ob_pd)); | ||
792 | return; | ||
793 | } | ||
794 | s3 = signal_newlike(s1); | ||
795 | dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n); | ||
796 | uin->i_signal = s3; | ||
797 | s3->s_refcount = 1; | ||
798 | if (!s1->s_refcount) signal_makereusable(s1); | ||
799 | if (!s2->s_refcount) signal_makereusable(s2); | ||
800 | } | ||
801 | else uin->i_signal = s1; | ||
802 | uin->i_ngot++; | ||
803 | /* if we didn't fill this inlet don't bother yet */ | ||
804 | if (uin->i_ngot < uin->i_nconnect) | ||
805 | goto notyet; | ||
806 | /* if there's more than one, check them all */ | ||
807 | if (u2->u_nin > 1) | ||
808 | { | ||
809 | for (uin = u2->u_in, n = u2->u_nin; n--; uin++) | ||
810 | if (uin->i_ngot < uin->i_nconnect) goto notyet; | ||
811 | } | ||
812 | /* so now we can schedule the ugen. */ | ||
813 | ugen_doit(dc, u2); | ||
814 | notyet: ; | ||
815 | } | ||
816 | } | ||
817 | t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); | ||
818 | u->u_done = 1; | ||
819 | } | ||
820 | |||
821 | /* once the DSP graph is built, we call this routine to sort it. | ||
822 | This routine also deletes the graph; later we might want to leave the | ||
823 | graph around, in case the user is editing the DSP network, to save having | ||
824 | to recreate it all the time. But not today. */ | ||
825 | |||
826 | void ugen_done_graph(t_dspcontext *dc) | ||
827 | { | ||
828 | t_ugenbox *u, *u2; | ||
829 | t_sigoutlet *uout; | ||
830 | t_siginlet *uin; | ||
831 | t_sigoutconnect *oc, *oc2; | ||
832 | int i, n; | ||
833 | t_block *blk; | ||
834 | t_dspcontext *parent_context = dc->dc_parentcontext; | ||
835 | float parent_srate; | ||
836 | int parent_vecsize; | ||
837 | int period, frequency, phase, vecsize; | ||
838 | float srate; | ||
839 | int chainblockbegin; /* DSP chain onset before block prolog code */ | ||
840 | int chainblockend; /* and after block epilog code */ | ||
841 | int chainafterall; /* and after signal outlet epilog */ | ||
842 | int reblock = 0, switched; | ||
843 | int downsample = 1, upsample = 1; /* IOhannes */ | ||
844 | /* debugging printout */ | ||
845 | |||
846 | if (ugen_loud) | ||
847 | { | ||
848 | post("ugen_done_graph..."); | ||
849 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
850 | { | ||
851 | post("ugen: %s", class_getname(u->u_obj->ob_pd)); | ||
852 | for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) | ||
853 | for (oc = uout->o_connections; oc; oc = oc->oc_next) | ||
854 | { | ||
855 | post("... out %d to %s, index %d, inlet %d", i, | ||
856 | class_getname(oc->oc_who->u_obj->ob_pd), | ||
857 | ugen_index(dc, oc->oc_who), oc->oc_inno); | ||
858 | } | ||
859 | } | ||
860 | } | ||
861 | |||
862 | /* search for an object of class "block~" */ | ||
863 | for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) | ||
864 | { | ||
865 | t_pd *zz = &u->u_obj->ob_pd; | ||
866 | if (pd_class(zz) == block_class) | ||
867 | { | ||
868 | if (blk) | ||
869 | pd_error(blk, "conflicting block~ objects in same page"); | ||
870 | else blk = (t_block *)zz; | ||
871 | } | ||
872 | } | ||
873 | |||
874 | /* figure out block size, calling frequency, sample rate */ | ||
875 | if (parent_context) | ||
876 | { | ||
877 | parent_srate = parent_context->dc_srate; | ||
878 | parent_vecsize = parent_context->dc_vecsize; | ||
879 | } | ||
880 | else | ||
881 | { | ||
882 | parent_srate = sys_getsr(); | ||
883 | parent_vecsize = sys_getblksize(); | ||
884 | } | ||
885 | if (blk) | ||
886 | { | ||
887 | int realoverlap; | ||
888 | vecsize = blk->x_vecsize; | ||
889 | if (vecsize == 0) | ||
890 | vecsize = parent_vecsize; | ||
891 | realoverlap = blk->x_overlap; | ||
892 | if (realoverlap > vecsize) realoverlap = vecsize; | ||
893 | /* IOhannes { */ | ||
894 | downsample = blk->x_downsample; | ||
895 | upsample = blk->x_upsample; | ||
896 | if (downsample > parent_vecsize) downsample=parent_vecsize; | ||
897 | period = (vecsize * downsample)/ | ||
898 | (parent_vecsize * realoverlap * upsample); | ||
899 | frequency = (parent_vecsize * realoverlap * upsample)/ | ||
900 | (vecsize * downsample); | ||
901 | /* } IOhannes*/ | ||
902 | phase = blk->x_phase; | ||
903 | srate = parent_srate * realoverlap * upsample / downsample; | ||
904 | /* IOhannes */ | ||
905 | if (period < 1) period = 1; | ||
906 | if (frequency < 1) frequency = 1; | ||
907 | blk->x_frequency = frequency; | ||
908 | blk->x_period = period; | ||
909 | blk->x_phase = dsp_phase & (period - 1); | ||
910 | if (! parent_context || (realoverlap != 1) || | ||
911 | (vecsize != parent_vecsize) || | ||
912 | (downsample != 1) || (upsample != 1)) /* IOhannes */ | ||
913 | reblock = 1; | ||
914 | switched = blk->x_switched; | ||
915 | } | ||
916 | else | ||
917 | { | ||
918 | srate = parent_srate; | ||
919 | vecsize = parent_vecsize; | ||
920 | downsample = upsample = 1;/* IOhannes */ | ||
921 | period = frequency = 1; | ||
922 | phase = 0; | ||
923 | if (!parent_context) reblock = 1; | ||
924 | switched = 0; | ||
925 | } | ||
926 | dc->dc_reblock = reblock; | ||
927 | dc->dc_switched = switched; | ||
928 | dc->dc_srate = srate; | ||
929 | dc->dc_vecsize = vecsize; | ||
930 | |||
931 | /* if we're reblocking or switched, we now have to create output | ||
932 | signals to fill in for the "borrowed" ones we have now. This | ||
933 | is also possibly true even if we're not blocked/switched, in | ||
934 | the case that there was a signal loop. But we don't know this | ||
935 | yet. */ | ||
936 | |||
937 | if (dc->dc_iosigs && (switched || reblock)) | ||
938 | { | ||
939 | t_signal **sigp; | ||
940 | for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; | ||
941 | i++, sigp++) | ||
942 | { | ||
943 | if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) | ||
944 | { | ||
945 | signal_setborrowed(*sigp, | ||
946 | signal_new(parent_vecsize, parent_srate)); | ||
947 | (*sigp)->s_refcount++; | ||
948 | |||
949 | if (ugen_loud) post("set %x->%x", *sigp, | ||
950 | (*sigp)->s_borrowedfrom); | ||
951 | } | ||
952 | } | ||
953 | } | ||
954 | |||
955 | if (ugen_loud) | ||
956 | post("reblock %d, switched %d", reblock, switched); | ||
957 | |||
958 | /* schedule prologs for inlets and outlets. If the "reblock" flag | ||
959 | is set, an inlet will put code on the DSP chain to copy its input | ||
960 | into an internal buffer here, before any unit generators' DSP code | ||
961 | gets scheduled. If we don't "reblock", inlets will need to get | ||
962 | pointers to their corresponding inlets/outlets on the box we're inside, | ||
963 | if any. Outlets will also need pointers, unless we're switched, in | ||
964 | which case outlet epilog code will kick in. */ | ||
965 | |||
966 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
967 | { | ||
968 | t_pd *zz = &u->u_obj->ob_pd; | ||
969 | t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs; | ||
970 | if (outsigs) outsigs += dc->dc_ninlets; | ||
971 | |||
972 | if (pd_class(zz) == vinlet_class) | ||
973 | vinlet_dspprolog((struct _vinlet *)zz, | ||
974 | dc->dc_iosigs, vecsize, dsp_phase, period, frequency, | ||
975 | downsample, upsample, /* IOhannes */ | ||
976 | reblock, switched); | ||
977 | else if (pd_class(zz) == voutlet_class) | ||
978 | voutlet_dspprolog((struct _voutlet *)zz, | ||
979 | outsigs, vecsize, dsp_phase, period, frequency, | ||
980 | downsample, upsample, /* IOhannes */ | ||
981 | reblock, switched); | ||
982 | } | ||
983 | chainblockbegin = dsp_chainsize; | ||
984 | |||
985 | if (blk && (reblock || switched)) /* add the block DSP prolog */ | ||
986 | dsp_add(block_prolog, 1, blk); | ||
987 | |||
988 | /* Initialize for sorting */ | ||
989 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
990 | { | ||
991 | u->u_done = 0; | ||
992 | for (uout = u->u_out, i = u->u_nout; i--; uout++) | ||
993 | uout->o_nsent = 0; | ||
994 | for (uin = u->u_in, i = u->u_nin; i--; uin++) | ||
995 | uin->i_ngot = 0, uin->i_signal = 0; | ||
996 | } | ||
997 | |||
998 | /* Do the sort */ | ||
999 | |||
1000 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
1001 | { | ||
1002 | /* check that we have no connected signal inlets */ | ||
1003 | if (u->u_done) continue; | ||
1004 | for (uin = u->u_in, i = u->u_nin; i--; uin++) | ||
1005 | if (uin->i_nconnect) goto next; | ||
1006 | |||
1007 | ugen_doit(dc, u); | ||
1008 | next: ; | ||
1009 | } | ||
1010 | |||
1011 | /* check for a DSP loop, which is evidenced here by the presence | ||
1012 | of ugens not yet scheduled. */ | ||
1013 | |||
1014 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
1015 | if (!u->u_done) | ||
1016 | { | ||
1017 | t_signal **sigp; | ||
1018 | pd_error(u->u_obj, | ||
1019 | "DSP loop detected (some tilde objects not scheduled)"); | ||
1020 | /* this might imply that we have unfilled "borrowed" outputs | ||
1021 | which we'd better fill in now. */ | ||
1022 | for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; | ||
1023 | i++, sigp++) | ||
1024 | { | ||
1025 | if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) | ||
1026 | { | ||
1027 | t_signal *s3 = signal_new(parent_vecsize, parent_srate); | ||
1028 | signal_setborrowed(*sigp, s3); | ||
1029 | (*sigp)->s_refcount++; | ||
1030 | dsp_add_zero(s3->s_vec, s3->s_n); | ||
1031 | if (ugen_loud) | ||
1032 | post("oops, belatedly set %x->%x", *sigp, | ||
1033 | (*sigp)->s_borrowedfrom); | ||
1034 | } | ||
1035 | } | ||
1036 | break; /* don't need to keep looking. */ | ||
1037 | } | ||
1038 | |||
1039 | if (blk && (reblock || switched)) /* add block DSP epilog */ | ||
1040 | dsp_add(block_epilog, 1, blk); | ||
1041 | chainblockend = dsp_chainsize; | ||
1042 | |||
1043 | /* add epilogs for outlets. */ | ||
1044 | |||
1045 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
1046 | { | ||
1047 | t_pd *zz = &u->u_obj->ob_pd; | ||
1048 | if (pd_class(zz) == voutlet_class) | ||
1049 | { | ||
1050 | t_signal **iosigs = dc->dc_iosigs; | ||
1051 | if (iosigs) iosigs += dc->dc_ninlets; | ||
1052 | voutlet_dspepilog((struct _voutlet *)zz, | ||
1053 | iosigs, vecsize, dsp_phase, period, frequency, | ||
1054 | downsample, upsample, /* IOhannes */ | ||
1055 | reblock, switched); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | chainafterall = dsp_chainsize; | ||
1060 | if (blk) | ||
1061 | { | ||
1062 | blk->x_blocklength = chainblockend - chainblockbegin; | ||
1063 | blk->x_epiloglength = chainafterall - chainblockend; | ||
1064 | blk->x_reblock = reblock; | ||
1065 | } | ||
1066 | |||
1067 | if (ugen_loud) | ||
1068 | { | ||
1069 | t_int *ip; | ||
1070 | if (!dc->dc_parentcontext) | ||
1071 | for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) | ||
1072 | post("chain %x", *ip); | ||
1073 | post("... ugen_done_graph done."); | ||
1074 | } | ||
1075 | /* now delete everything. */ | ||
1076 | while (dc->dc_ugenlist) | ||
1077 | { | ||
1078 | for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; | ||
1079 | n--; uout++) | ||
1080 | { | ||
1081 | oc = uout->o_connections; | ||
1082 | while (oc) | ||
1083 | { | ||
1084 | oc2 = oc->oc_next; | ||
1085 | freebytes(oc, sizeof *oc); | ||
1086 | oc = oc2; | ||
1087 | } | ||
1088 | } | ||
1089 | freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * | ||
1090 | sizeof (*dc->dc_ugenlist->u_out)); | ||
1091 | freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * | ||
1092 | sizeof(*dc->dc_ugenlist->u_in)); | ||
1093 | u = dc->dc_ugenlist; | ||
1094 | dc->dc_ugenlist = u->u_next; | ||
1095 | freebytes(u, sizeof *u); | ||
1096 | } | ||
1097 | if (ugen_currentcontext == dc) | ||
1098 | ugen_currentcontext = dc->dc_parentcontext; | ||
1099 | else bug("ugen_currentcontext"); | ||
1100 | freebytes(dc, sizeof(*dc)); | ||
1101 | |||
1102 | } | ||
1103 | |||
1104 | t_signal *ugen_getiosig(int index, int inout) | ||
1105 | { | ||
1106 | if (!ugen_currentcontext) bug("ugen_getiosig"); | ||
1107 | if (ugen_currentcontext->dc_toplevel) return (0); | ||
1108 | if (inout) index += ugen_currentcontext->dc_ninlets; | ||
1109 | return (ugen_currentcontext->dc_iosigs[index]); | ||
1110 | } | ||
1111 | |||
1112 | |||
1113 | /* -------------------- setup routine -------------------------- */ | ||
1114 | |||
1115 | void d_ugen_setup(void) /* really just block_setup */ | ||
1116 | { | ||
1117 | block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, | ||
1118 | sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); | ||
1119 | class_addcreator((t_newmethod)switch_new, gensym("switch~"), | ||
1120 | A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); | ||
1121 | class_addmethod(block_class, (t_method)block_set, gensym("set"), | ||
1122 | A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); | ||
1123 | class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0); | ||
1124 | class_addfloat(block_class, block_float); | ||
1125 | } | ||
1126 | |||
1127 | /* Copyright (c) 1997-1999 Miller Puckette. | ||
1128 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
1129 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
1130 | |||
1131 | /* These routines build a copy of the DSP portion of a graph, which is | ||
1132 | then sorted into a linear list of DSP operations which are added to | ||
1133 | the DSP duty cycle called by the scheduler. Once that's been done, | ||
1134 | we delete the copy. The DSP objects are represented by "ugenbox" | ||
1135 | structures which are parallel to the DSP objects in the graph and | ||
1136 | have vectors of siginlets and sigoutlets which record their | ||
1137 | interconnections. | ||
1138 | */ | ||
1139 | |||
1140 | /* hacked to run subpatches with different samplerates | ||
1141 | * only samplerates that are a power_of_2-multiple of the | ||
1142 | * | ||
1143 | * mfg.gfd.uil | ||
1144 | * IOhannes | ||
1145 | * | ||
1146 | * edited lines are marked with "IOhannes" | ||
1147 | * | ||
1148 | */ | ||
1149 | |||
1150 | |||
1151 | #include "m_pd.h" | ||
1152 | #include "m_imp.h" | ||
1153 | #include <stdlib.h> | ||
1154 | #include <stdarg.h> | ||
1155 | |||
1156 | extern t_class *vinlet_class, *voutlet_class, *canvas_class; | ||
1157 | t_sample *obj_findsignalscalar(t_object *x, int m); | ||
1158 | static int ugen_loud; | ||
1159 | EXTERN_STRUCT _vinlet; | ||
1160 | EXTERN_STRUCT _voutlet; | ||
1161 | |||
1162 | void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, | ||
1163 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
1164 | int switched); | ||
1165 | void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, | ||
1166 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
1167 | int switched); | ||
1168 | void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, | ||
1169 | int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, | ||
1170 | int switched); | ||
1171 | |||
1172 | t_int *zero_perform(t_int *w) /* zero out a vector */ | ||
1173 | { | ||
1174 | t_float *out = (t_float *)(w[1]); | ||
1175 | int n = (int)(w[2]); | ||
1176 | while (n--) *out++ = 0; | ||
1177 | return (w+3); | ||
1178 | } | ||
1179 | |||
1180 | t_int *zero_perf8(t_int *w) | ||
1181 | { | ||
1182 | t_float *out = (t_float *)(w[1]); | ||
1183 | int n = (int)(w[2]); | ||
1184 | |||
1185 | for (; n; n -= 8, out += 8) | ||
1186 | { | ||
1187 | out[0] = 0; | ||
1188 | out[1] = 0; | ||
1189 | out[2] = 0; | ||
1190 | out[3] = 0; | ||
1191 | out[4] = 0; | ||
1192 | out[5] = 0; | ||
1193 | out[6] = 0; | ||
1194 | out[7] = 0; | ||
1195 | } | ||
1196 | return (w+3); | ||
1197 | } | ||
1198 | |||
1199 | void dsp_add_zero(t_sample *out, int n) | ||
1200 | { | ||
1201 | if (n&7) | ||
1202 | dsp_add(zero_perform, 2, out, n); | ||
1203 | else | ||
1204 | dsp_add(zero_perf8, 2, out, n); | ||
1205 | } | ||
1206 | |||
1207 | /* ---------------------------- block~ ----------------------------- */ | ||
1208 | |||
1209 | /* The "block~ object maintains the containing canvas's DSP computation, | ||
1210 | calling it at a super- or sub-multiple of the containing canvas's | ||
1211 | calling frequency. The block~'s creation arguments specify block size | ||
1212 | and overlap. Block~ does no "dsp" computation in its own right, but it | ||
1213 | adds prolog and epilog code before and after the canvas's unit generators. | ||
1214 | |||
1215 | A subcanvas need not have a block~ at all; if there's none, its | ||
1216 | ugens are simply put on the list without any prolog or epilog code. | ||
1217 | |||
1218 | Block~ may be invoked as switch~, in which case it also acts to switch the | ||
1219 | subcanvas on and off. The overall order of scheduling for a subcanvas | ||
1220 | is thus, | ||
1221 | |||
1222 | inlet and outlet prologue code (1) | ||
1223 | block prologue (2) | ||
1224 | the objects in the subcanvas, including inlets and outlets | ||
1225 | block epilogue (2) | ||
1226 | outlet epilogue code (2) | ||
1227 | |||
1228 | where (1) means, "if reblocked" and (2) means, "if reblocked or switched". | ||
1229 | |||
1230 | If we're reblocked, the inlet prolog and outlet epilog code takes care of | ||
1231 | overlapping and buffering to deal with vector size changes. If we're switched | ||
1232 | but not reblocked, the inlet prolog is not needed, and the output epilog is | ||
1233 | ONLY run when the block is switched off; in this case the epilog code simply | ||
1234 | copies zeros to all signal outlets. | ||
1235 | */ | ||
1236 | |||
1237 | static int dsp_phase; | ||
1238 | static t_class *block_class; | ||
1239 | |||
1240 | typedef struct _block | ||
1241 | { | ||
1242 | t_object x_obj; | ||
1243 | int x_vecsize; | ||
1244 | int x_overlap; | ||
1245 | int x_phase; /* from 0 to period-1; when zero we run the block */ | ||
1246 | int x_period; /* submultiple of containing canvas */ | ||
1247 | int x_frequency; /* supermultiple of comtaining canvas */ | ||
1248 | int x_count; | ||
1249 | int x_blocklength; /* length of dspchain for this block */ | ||
1250 | int x_epiloglength; /* length of epilog */ | ||
1251 | char x_switched; /* true if we're acting as a a switch */ | ||
1252 | char x_switchon; /* true if we're switched on */ | ||
1253 | char x_reblock; /* true if inlets and outlets are reblocking */ | ||
1254 | int x_upsample; /* IOhannes: upsampling-factor */ | ||
1255 | int x_downsample; /* IOhannes: downsampling-factor */ | ||
1256 | |||
1257 | } t_block; | ||
1258 | |||
1259 | static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, | ||
1260 | t_floatarg fupsample); | ||
1261 | |||
1262 | static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, | ||
1263 | t_floatarg fupsample) /* IOhannes */ | ||
1264 | { | ||
1265 | t_block *x = (t_block *)pd_new(block_class); | ||
1266 | x->x_phase = 0; | ||
1267 | x->x_period = 1; | ||
1268 | x->x_frequency = 1; | ||
1269 | x->x_switched = 0; | ||
1270 | x->x_switchon = 1; | ||
1271 | block_set(x, fvecsize, foverlap, fupsample); | ||
1272 | return (x); | ||
1273 | } | ||
1274 | |||
1275 | static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, | ||
1276 | t_floatarg fupsample) | ||
1277 | { | ||
1278 | int upsample, downsample; /* IOhannes */ | ||
1279 | int vecsize = fvecsize; | ||
1280 | int overlap = foverlap; | ||
1281 | int dspstate = canvas_suspend_dsp(); | ||
1282 | if (overlap < 1) | ||
1283 | overlap = 1; | ||
1284 | if (vecsize < 0) | ||
1285 | vecsize = 0; /* this means we'll get it from parent later. */ | ||
1286 | |||
1287 | /* IOhannes { */ | ||
1288 | if (fupsample <= 0) upsample = downsample = 1; | ||
1289 | else if (fupsample >= 1) { | ||
1290 | upsample = fupsample; | ||
1291 | downsample = 1; | ||
1292 | } else { | ||
1293 | downsample = 1.0 / fupsample; | ||
1294 | upsample = 1; | ||
1295 | } | ||
1296 | /* } IOhannes */ | ||
1297 | |||
1298 | if (vecsize && (vecsize != (1 << ilog2(vecsize)))) | ||
1299 | { | ||
1300 | pd_error(x, "block~: vector size not a power of 2"); | ||
1301 | vecsize = 64; | ||
1302 | } | ||
1303 | if (overlap != (1 << ilog2(overlap))) | ||
1304 | { | ||
1305 | pd_error(x, "block~: overlap not a power of 2"); | ||
1306 | overlap = 1; | ||
1307 | } | ||
1308 | /* IOhannes { */ | ||
1309 | if (downsample != (1 << ilog2(downsample))) | ||
1310 | { | ||
1311 | pd_error(x, "block~: downsampling not a power of 2"); | ||
1312 | downsample = 1; | ||
1313 | } | ||
1314 | if (upsample != (1 << ilog2(upsample))) | ||
1315 | { | ||
1316 | pd_error(x, "block~: upsampling not a power of 2"); | ||
1317 | upsample = 1; | ||
1318 | } | ||
1319 | /* } IOhannes */ | ||
1320 | |||
1321 | |||
1322 | x->x_vecsize = vecsize; | ||
1323 | x->x_overlap = overlap; | ||
1324 | /* IOhannes { */ | ||
1325 | x->x_upsample = upsample; | ||
1326 | x->x_downsample = downsample; | ||
1327 | /* } IOhannes */ | ||
1328 | canvas_resume_dsp(dspstate); | ||
1329 | } | ||
1330 | |||
1331 | static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, | ||
1332 | t_floatarg fupsample) /* IOhannes */ | ||
1333 | { | ||
1334 | t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ | ||
1335 | x->x_switched = 1; | ||
1336 | x->x_switchon = 0; | ||
1337 | return (x); | ||
1338 | } | ||
1339 | |||
1340 | static void block_float(t_block *x, t_floatarg f) | ||
1341 | { | ||
1342 | if (x->x_switched) | ||
1343 | x->x_switchon = (f != 0); | ||
1344 | } | ||
1345 | #define PROLOGCALL 2 | ||
1346 | #define EPILOGCALL 2 | ||
1347 | |||
1348 | static t_int *block_prolog(t_int *w) | ||
1349 | { | ||
1350 | t_block *x = (t_block *)w[1]; | ||
1351 | int phase = x->x_phase; | ||
1352 | /* if we're switched off, jump past the epilog code */ | ||
1353 | if (!x->x_switchon) | ||
1354 | return (w + x->x_blocklength); | ||
1355 | if (phase) | ||
1356 | { | ||
1357 | phase++; | ||
1358 | if (phase == x->x_period) phase = 0; | ||
1359 | x->x_phase = phase; | ||
1360 | return (w + x->x_blocklength); /* skip block; jump past epilog */ | ||
1361 | } | ||
1362 | else | ||
1363 | { | ||
1364 | x->x_count = x->x_frequency; | ||
1365 | x->x_phase = (x->x_period > 1 ? 1 : 0); | ||
1366 | return (w + PROLOGCALL); /* beginning of block is next ugen */ | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | static t_int *block_epilog(t_int *w) | ||
1371 | { | ||
1372 | t_block *x = (t_block *)w[1]; | ||
1373 | int count = x->x_count - 1; | ||
1374 | if (!x->x_reblock) | ||
1375 | return (w + x->x_epiloglength + EPILOGCALL); | ||
1376 | if (count) | ||
1377 | { | ||
1378 | x->x_count = count; | ||
1379 | return (w - (x->x_blocklength - | ||
1380 | (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ | ||
1381 | } | ||
1382 | else return (w + EPILOGCALL); | ||
1383 | } | ||
1384 | |||
1385 | static void block_dsp(t_block *x, t_signal **sp) | ||
1386 | { | ||
1387 | /* do nothing here */ | ||
1388 | } | ||
1389 | |||
1390 | /* ------------------ DSP call list ----------------------- */ | ||
1391 | |||
1392 | static t_int *dsp_chain; | ||
1393 | static int dsp_chainsize; | ||
1394 | |||
1395 | void dsp_add(t_perfroutine f, int n, ...) | ||
1396 | { | ||
1397 | int newsize = dsp_chainsize + n+1, i; | ||
1398 | va_list ap; | ||
1399 | |||
1400 | dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), | ||
1401 | newsize * sizeof (t_int)); | ||
1402 | dsp_chain[dsp_chainsize-1] = (t_int)f; | ||
1403 | va_start(ap, n); | ||
1404 | for (i = 0; i < n; i++) | ||
1405 | dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); | ||
1406 | va_end(ap); | ||
1407 | dsp_chain[newsize-1] = 0; | ||
1408 | dsp_chainsize = newsize; | ||
1409 | } | ||
1410 | |||
1411 | /* at Guenter's suggestion, here's a vectorized version */ | ||
1412 | void dsp_addv(t_perfroutine f, int n, t_int *vec) | ||
1413 | { | ||
1414 | int newsize = dsp_chainsize + n+1, i; | ||
1415 | |||
1416 | dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), | ||
1417 | newsize * sizeof (t_int)); | ||
1418 | dsp_chain[dsp_chainsize-1] = (t_int)f; | ||
1419 | for (i = 0; i < n; i++) | ||
1420 | dsp_chain[dsp_chainsize + i] = vec[i]; | ||
1421 | dsp_chain[newsize-1] = 0; | ||
1422 | dsp_chainsize = newsize; | ||
1423 | } | ||
1424 | |||
1425 | void dsp_tick(void) | ||
1426 | { | ||
1427 | if (dsp_chain) | ||
1428 | { | ||
1429 | t_int *ip; | ||
1430 | for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip); | ||
1431 | dsp_phase++; | ||
1432 | } | ||
1433 | } | ||
1434 | |||
1435 | /* ---------------- signals ---------------------------- */ | ||
1436 | |||
1437 | int ilog2(int n) | ||
1438 | { | ||
1439 | int r = -1; | ||
1440 | if (n <= 0) return(0); | ||
1441 | while (n) | ||
1442 | { | ||
1443 | r++; | ||
1444 | n >>= 1; | ||
1445 | } | ||
1446 | return (r); | ||
1447 | } | ||
1448 | |||
1449 | /* list of signals which can be reused, sorted by buffer size */ | ||
1450 | static t_signal *signal_freelist[MAXLOGSIG+1]; | ||
1451 | /* list of reusable "borrowed" signals (which don't own sample buffers) */ | ||
1452 | static t_signal *signal_freeborrowed; | ||
1453 | /* list of all signals allocated (not including "borrowed" ones) */ | ||
1454 | static t_signal *signal_usedlist; | ||
1455 | |||
1456 | /* call this when DSP is stopped to free all the signals */ | ||
1457 | void signal_cleanup(void) | ||
1458 | { | ||
1459 | t_signal **svec, *sig, *sig2; | ||
1460 | int i; | ||
1461 | while (sig = signal_usedlist) | ||
1462 | { | ||
1463 | signal_usedlist = sig->s_nextused; | ||
1464 | if (!sig->s_isborrowed) | ||
1465 | t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec)); | ||
1466 | t_freebytes(sig, sizeof *sig); | ||
1467 | } | ||
1468 | for (i = 0; i <= MAXLOGSIG; i++) | ||
1469 | signal_freelist[i] = 0; | ||
1470 | signal_freeborrowed = 0; | ||
1471 | } | ||
1472 | |||
1473 | /* mark the signal "reusable." */ | ||
1474 | void signal_makereusable(t_signal *sig) | ||
1475 | { | ||
1476 | int logn = ilog2(sig->s_n); | ||
1477 | #if 1 | ||
1478 | t_signal *s5; | ||
1479 | for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree) | ||
1480 | { | ||
1481 | if (s5 == sig) | ||
1482 | { | ||
1483 | bug("signal_free 3"); | ||
1484 | return; | ||
1485 | } | ||
1486 | } | ||
1487 | for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree) | ||
1488 | { | ||
1489 | if (s5 == sig) | ||
1490 | { | ||
1491 | bug("signal_free 4"); | ||
1492 | return; | ||
1493 | } | ||
1494 | } | ||
1495 | #endif | ||
1496 | if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed); | ||
1497 | if (sig->s_isborrowed) | ||
1498 | { | ||
1499 | /* if the signal is borrowed, decrement the borrowed-from signal's | ||
1500 | reference count, possibly marking it reusable too */ | ||
1501 | t_signal *s2 = sig->s_borrowedfrom; | ||
1502 | if ((s2 == sig) || !s2) | ||
1503 | bug("signal_free"); | ||
1504 | s2->s_refcount--; | ||
1505 | if (!s2->s_refcount) | ||
1506 | signal_makereusable(s2); | ||
1507 | sig->s_nextfree = signal_freeborrowed; | ||
1508 | signal_freeborrowed = sig; | ||
1509 | } | ||
1510 | else | ||
1511 | { | ||
1512 | /* if it's a real signal (not borrowed), put it on the free list | ||
1513 | so we can reuse it. */ | ||
1514 | if (signal_freelist[logn] == sig) bug("signal_free 2"); | ||
1515 | sig->s_nextfree = signal_freelist[logn]; | ||
1516 | signal_freelist[logn] = sig; | ||
1517 | } | ||
1518 | } | ||
1519 | |||
1520 | /* reclaim or make an audio signal. If n is zero, return a "borrowed" | ||
1521 | signal whose buffer and size will be obtained later via | ||
1522 | signal_setborrowed(). */ | ||
1523 | |||
1524 | t_signal *signal_new(int n, float sr) | ||
1525 | { | ||
1526 | int logn, n2; | ||
1527 | t_signal *ret, **whichlist; | ||
1528 | t_sample *fp; | ||
1529 | logn = ilog2(n); | ||
1530 | if (n) | ||
1531 | { | ||
1532 | if (n != (1 << logn)) | ||
1533 | bug("signal buffer not a power of 2"); | ||
1534 | if (logn > MAXLOGSIG) | ||
1535 | bug("signal buffer too large"); | ||
1536 | whichlist = signal_freelist + logn; | ||
1537 | } | ||
1538 | else | ||
1539 | whichlist = &signal_freeborrowed; | ||
1540 | |||
1541 | /* first try to reclaim one from the free list */ | ||
1542 | if (ret = *whichlist) | ||
1543 | *whichlist = ret->s_nextfree; | ||
1544 | else | ||
1545 | { | ||
1546 | /* LATER figure out what to do for out-of-space here! */ | ||
1547 | ret = (t_signal *)t_getbytes(sizeof *ret); | ||
1548 | if (n) | ||
1549 | { | ||
1550 | ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec)); | ||
1551 | ret->s_isborrowed = 0; | ||
1552 | } | ||
1553 | else | ||
1554 | { | ||
1555 | ret->s_vec = 0; | ||
1556 | ret->s_isborrowed = 1; | ||
1557 | } | ||
1558 | ret->s_nextused = signal_usedlist; | ||
1559 | signal_usedlist = ret; | ||
1560 | } | ||
1561 | ret->s_n = n; | ||
1562 | ret->s_sr = sr; | ||
1563 | ret->s_refcount = 0; | ||
1564 | ret->s_borrowedfrom = 0; | ||
1565 | if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed); | ||
1566 | return (ret); | ||
1567 | } | ||
1568 | |||
1569 | static t_signal *signal_newlike(const t_signal *sig) | ||
1570 | { | ||
1571 | return (signal_new(sig->s_n, sig->s_sr)); | ||
1572 | } | ||
1573 | |||
1574 | void signal_setborrowed(t_signal *sig, t_signal *sig2) | ||
1575 | { | ||
1576 | if (!sig->s_isborrowed || sig->s_borrowedfrom) | ||
1577 | bug("signal_setborrowed"); | ||
1578 | if (sig == sig2) | ||
1579 | bug("signal_setborrowed 2"); | ||
1580 | sig->s_borrowedfrom = sig2; | ||
1581 | sig->s_vec = sig2->s_vec; | ||
1582 | sig->s_n = sig2->s_n; | ||
1583 | } | ||
1584 | |||
1585 | int signal_compatible(t_signal *s1, t_signal *s2) | ||
1586 | { | ||
1587 | return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr); | ||
1588 | } | ||
1589 | |||
1590 | /* ------------------ ugen ("unit generator") sorting ----------------- */ | ||
1591 | |||
1592 | typedef struct _ugenbox | ||
1593 | { | ||
1594 | struct _siginlet *u_in; | ||
1595 | int u_nin; | ||
1596 | struct _sigoutlet *u_out; | ||
1597 | int u_nout; | ||
1598 | int u_phase; | ||
1599 | struct _ugenbox *u_next; | ||
1600 | t_object *u_obj; | ||
1601 | int u_done; | ||
1602 | } t_ugenbox; | ||
1603 | |||
1604 | typedef struct _siginlet | ||
1605 | { | ||
1606 | int i_nconnect; | ||
1607 | int i_ngot; | ||
1608 | t_signal *i_signal; | ||
1609 | } t_siginlet; | ||
1610 | |||
1611 | typedef struct _sigoutconnect | ||
1612 | { | ||
1613 | t_ugenbox *oc_who; | ||
1614 | int oc_inno; | ||
1615 | struct _sigoutconnect *oc_next; | ||
1616 | } t_sigoutconnect; | ||
1617 | |||
1618 | typedef struct _sigoutlet | ||
1619 | { | ||
1620 | int o_nconnect; | ||
1621 | int o_nsent; | ||
1622 | t_signal *o_signal; | ||
1623 | t_sigoutconnect *o_connections; | ||
1624 | } t_sigoutlet; | ||
1625 | |||
1626 | |||
1627 | struct _dspcontext | ||
1628 | { | ||
1629 | struct _ugenbox *dc_ugenlist; | ||
1630 | struct _dspcontext *dc_parentcontext; | ||
1631 | int dc_ninlets; | ||
1632 | int dc_noutlets; | ||
1633 | t_signal **dc_iosigs; | ||
1634 | float dc_srate; | ||
1635 | int dc_vecsize; | ||
1636 | char dc_toplevel; /* true if "iosigs" is invalid. */ | ||
1637 | char dc_reblock; /* true if we have to reblock inlets/outlets */ | ||
1638 | char dc_switched; /* true if we're switched */ | ||
1639 | |||
1640 | }; | ||
1641 | |||
1642 | #define t_dspcontext struct _dspcontext | ||
1643 | |||
1644 | static int ugen_sortno = 0; | ||
1645 | static t_dspcontext *ugen_currentcontext; | ||
1646 | |||
1647 | void ugen_stop(void) | ||
1648 | { | ||
1649 | t_signal *s; | ||
1650 | int i; | ||
1651 | if (dsp_chain) | ||
1652 | { | ||
1653 | freebytes(dsp_chain, dsp_chainsize * sizeof (t_int)); | ||
1654 | dsp_chain = 0; | ||
1655 | } | ||
1656 | signal_cleanup(); | ||
1657 | |||
1658 | } | ||
1659 | |||
1660 | void ugen_start(void) | ||
1661 | { | ||
1662 | ugen_stop(); | ||
1663 | ugen_sortno++; | ||
1664 | dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); | ||
1665 | dsp_chain[0] = 0; | ||
1666 | dsp_chainsize = 1; | ||
1667 | if (ugen_currentcontext) bug("ugen_start"); | ||
1668 | } | ||
1669 | |||
1670 | int ugen_getsortno(void) | ||
1671 | { | ||
1672 | return (ugen_sortno); | ||
1673 | } | ||
1674 | |||
1675 | #if 0 | ||
1676 | void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) | ||
1677 | { | ||
1678 | int i, count; | ||
1679 | t_signal *sig; | ||
1680 | for (count = 0, sig = signal_usedlist; sig; | ||
1681 | count++, sig = sig->s_nextused) | ||
1682 | ; | ||
1683 | post("used signals %d", count); | ||
1684 | for (i = 0; i < MAXLOGSIG; i++) | ||
1685 | { | ||
1686 | for (count = 0, sig = signal_freelist[i]; sig; | ||
1687 | count++, sig = sig->s_nextfree) | ||
1688 | ; | ||
1689 | if (count) | ||
1690 | post("size %d: free %d", (1 << i), count); | ||
1691 | } | ||
1692 | for (count = 0, sig = signal_freeborrowed; sig; | ||
1693 | count++, sig = sig->s_nextfree) | ||
1694 | ; | ||
1695 | post("free borrowed %d", count); | ||
1696 | |||
1697 | ugen_loud = argc; | ||
1698 | } | ||
1699 | #endif | ||
1700 | |||
1701 | /* start building the graph for a canvas */ | ||
1702 | t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, | ||
1703 | int ninlets, int noutlets) | ||
1704 | { | ||
1705 | t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); | ||
1706 | float parent_srate, srate; | ||
1707 | int parent_vecsize, vecsize; | ||
1708 | |||
1709 | if (ugen_loud) post("ugen_start_graph..."); | ||
1710 | |||
1711 | dc->dc_ugenlist = 0; | ||
1712 | dc->dc_toplevel = toplevel; | ||
1713 | dc->dc_iosigs = sp; | ||
1714 | dc->dc_ninlets = ninlets; | ||
1715 | dc->dc_noutlets = noutlets; | ||
1716 | dc->dc_parentcontext = ugen_currentcontext; | ||
1717 | ugen_currentcontext = dc; | ||
1718 | return (dc); | ||
1719 | } | ||
1720 | |||
1721 | /* first the canvas calls this to create all the boxes... */ | ||
1722 | void ugen_add(t_dspcontext *dc, t_object *obj) | ||
1723 | { | ||
1724 | t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); | ||
1725 | int i; | ||
1726 | t_sigoutlet *uout; | ||
1727 | t_siginlet *uin; | ||
1728 | |||
1729 | x->u_next = dc->dc_ugenlist; | ||
1730 | dc->dc_ugenlist = x; | ||
1731 | x->u_obj = obj; | ||
1732 | x->u_nin = obj_nsiginlets(obj); | ||
1733 | x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); | ||
1734 | for (uin = x->u_in, i = x->u_nin; i--; uin++) | ||
1735 | uin->i_nconnect = 0; | ||
1736 | x->u_nout = obj_nsigoutlets(obj); | ||
1737 | x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); | ||
1738 | for (uout = x->u_out, i = x->u_nout; i--; uout++) | ||
1739 | uout->o_connections = 0, uout->o_nconnect = 0; | ||
1740 | } | ||
1741 | |||
1742 | /* and then this to make all the connections. */ | ||
1743 | void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, | ||
1744 | int inno) | ||
1745 | { | ||
1746 | t_ugenbox *u1, *u2; | ||
1747 | t_sigoutlet *uout; | ||
1748 | t_siginlet *uin; | ||
1749 | t_sigoutconnect *oc; | ||
1750 | int sigoutno = obj_sigoutletindex(x1, outno); | ||
1751 | int siginno = obj_siginletindex(x2, inno); | ||
1752 | if (ugen_loud) | ||
1753 | post("%s -> %s: %d->%d", | ||
1754 | class_getname(x1->ob_pd), | ||
1755 | class_getname(x2->ob_pd), outno, inno); | ||
1756 | for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); | ||
1757 | for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); | ||
1758 | if (!u1 || !u2 || siginno < 0) | ||
1759 | { | ||
1760 | pd_error(u1->u_obj, | ||
1761 | "signal outlet connect to nonsignal inlet (ignored)"); | ||
1762 | return; | ||
1763 | } | ||
1764 | if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) | ||
1765 | { | ||
1766 | bug("ugen_connect %s %s %d %d (%d %d)", | ||
1767 | class_getname(x1->ob_pd), | ||
1768 | class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, | ||
1769 | u2->u_nin); | ||
1770 | } | ||
1771 | uout = u1->u_out + sigoutno; | ||
1772 | uin = u2->u_in + siginno; | ||
1773 | |||
1774 | /* add a new connection to the outlet's list */ | ||
1775 | oc = (t_sigoutconnect *)getbytes(sizeof *oc); | ||
1776 | oc->oc_next = uout->o_connections; | ||
1777 | uout->o_connections = oc; | ||
1778 | oc->oc_who = u2; | ||
1779 | oc->oc_inno = siginno; | ||
1780 | /* update inlet and outlet counts */ | ||
1781 | uout->o_nconnect++; | ||
1782 | uin->i_nconnect++; | ||
1783 | } | ||
1784 | |||
1785 | /* get the index of a ugenbox or -1 if it's not on the list */ | ||
1786 | static int ugen_index(t_dspcontext *dc, t_ugenbox *x) | ||
1787 | { | ||
1788 | int ret; | ||
1789 | t_ugenbox *u; | ||
1790 | for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) | ||
1791 | if (u == x) return (ret); | ||
1792 | return (-1); | ||
1793 | } | ||
1794 | |||
1795 | /* put a ugenbox on the chain, recursively putting any others on that | ||
1796 | this one might uncover. */ | ||
1797 | static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) | ||
1798 | { | ||
1799 | t_sigoutlet *uout; | ||
1800 | t_siginlet *uin; | ||
1801 | t_sigoutconnect *oc, *oc2; | ||
1802 | t_class *class = pd_class(&u->u_obj->ob_pd); | ||
1803 | int i, n; | ||
1804 | /* suppress creating new signals for the outputs of signal | ||
1805 | inlets and subpatchs; except in the case we're an inlet and "blocking" | ||
1806 | is set. We don't yet know if a subcanvas will be "blocking" so there | ||
1807 | we delay new signal creation, which will be handled by calling | ||
1808 | signal_setborrowed in the ugen_done_graph routine below. */ | ||
1809 | int nonewsigs = (class == canvas_class || | ||
1810 | (class == vinlet_class) && !(dc->dc_reblock)); | ||
1811 | /* when we encounter a subcanvas or a signal outlet, suppress freeing | ||
1812 | the input signals as they may be "borrowed" for the super or sub | ||
1813 | patch; same exception as above, but also if we're "switched" we | ||
1814 | have to do a copy rather than a borrow. */ | ||
1815 | int nofreesigs = (class == canvas_class || | ||
1816 | (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched)); | ||
1817 | t_signal **insig, **outsig, **sig, *s1, *s2, *s3; | ||
1818 | t_ugenbox *u2; | ||
1819 | |||
1820 | if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs, | ||
1821 | nonewsigs); | ||
1822 | for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) | ||
1823 | { | ||
1824 | if (!uin->i_nconnect) | ||
1825 | { | ||
1826 | t_sample *scalar; | ||
1827 | s3 = signal_new(dc->dc_vecsize, dc->dc_srate); | ||
1828 | /* post("%s: unconnected signal inlet set to zero", | ||
1829 | class_getname(u->u_obj->ob_pd)); */ | ||
1830 | if (scalar = obj_findsignalscalar(u->u_obj, i)) | ||
1831 | dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n); | ||
1832 | else | ||
1833 | dsp_add_zero(s3->s_vec, s3->s_n); | ||
1834 | uin->i_signal = s3; | ||
1835 | s3->s_refcount = 1; | ||
1836 | } | ||
1837 | } | ||
1838 | insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); | ||
1839 | outsig = insig + u->u_nin; | ||
1840 | for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) | ||
1841 | { | ||
1842 | int newrefcount; | ||
1843 | *sig = uin->i_signal; | ||
1844 | newrefcount = --(*sig)->s_refcount; | ||
1845 | /* if the reference count went to zero, we free the signal now, | ||
1846 | unless it's a subcanvas or outlet; these might keep the | ||
1847 | signal around to send to objects connected to them. In this | ||
1848 | case we increment the reference count; the corresponding decrement | ||
1849 | is in sig_makereusable(). */ | ||
1850 | if (nofreesigs) | ||
1851 | (*sig)->s_refcount++; | ||
1852 | else if (!newrefcount) | ||
1853 | signal_makereusable(*sig); | ||
1854 | } | ||
1855 | for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) | ||
1856 | { | ||
1857 | /* similarly, for outlets of subcanvases we delay creating | ||
1858 | them; instead we create "borrowed" ones so that the refcount | ||
1859 | is known. The subcanvas replaces the fake signal with one showing | ||
1860 | where the output data actually is, to avoid having to copy it. | ||
1861 | For any other object, we just allocate a new output vector; | ||
1862 | since we've already freed the inputs the objects might get called | ||
1863 | "in place." */ | ||
1864 | if (nonewsigs) | ||
1865 | { | ||
1866 | *sig = uout->o_signal = | ||
1867 | signal_new(0, dc->dc_srate); | ||
1868 | } | ||
1869 | else | ||
1870 | *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate); | ||
1871 | (*sig)->s_refcount = uout->o_nconnect; | ||
1872 | } | ||
1873 | /* now call the DSP scheduling routine for the ugen. This | ||
1874 | routine must fill in "borrowed" signal outputs in case it's either | ||
1875 | a subcanvas or a signal inlet. */ | ||
1876 | mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); | ||
1877 | |||
1878 | /* if any output signals aren't connected to anyone, free them | ||
1879 | now; otherwise they'll either get freed when the reference count | ||
1880 | goes back to zero, or even later as explained above. */ | ||
1881 | |||
1882 | for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) | ||
1883 | { | ||
1884 | if (!(*sig)->s_refcount) | ||
1885 | signal_makereusable(*sig); | ||
1886 | } | ||
1887 | if (ugen_loud) | ||
1888 | { | ||
1889 | if (u->u_nin + u->u_nout == 0) post("put %s %d", | ||
1890 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); | ||
1891 | else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)", | ||
1892 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]); | ||
1893 | else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)", | ||
1894 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), | ||
1895 | sig[0], sig[1]); | ||
1896 | else post("put %s %d (%x %x %x ...)", | ||
1897 | class_getname(u->u_obj->ob_pd), ugen_index(dc, u), | ||
1898 | sig[0], sig[1], sig[2]); | ||
1899 | } | ||
1900 | |||
1901 | /* pass it on and trip anyone whose last inlet was filled */ | ||
1902 | for (uout = u->u_out, i = u->u_nout; i--; uout++) | ||
1903 | { | ||
1904 | s1 = uout->o_signal; | ||
1905 | for (oc = uout->o_connections; oc; oc = oc->oc_next) | ||
1906 | { | ||
1907 | u2 = oc->oc_who; | ||
1908 | uin = &u2->u_in[oc->oc_inno]; | ||
1909 | /* if there's already someone here, sum the two */ | ||
1910 | if (s2 = uin->i_signal) | ||
1911 | { | ||
1912 | s1->s_refcount--; | ||
1913 | s2->s_refcount--; | ||
1914 | if (!signal_compatible(s1, s2)) | ||
1915 | { | ||
1916 | pd_error(u->u_obj, "%s: incompatible signal inputs", | ||
1917 | class_getname(u->u_obj->ob_pd)); | ||
1918 | return; | ||
1919 | } | ||
1920 | s3 = signal_newlike(s1); | ||
1921 | dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n); | ||
1922 | uin->i_signal = s3; | ||
1923 | s3->s_refcount = 1; | ||
1924 | if (!s1->s_refcount) signal_makereusable(s1); | ||
1925 | if (!s2->s_refcount) signal_makereusable(s2); | ||
1926 | } | ||
1927 | else uin->i_signal = s1; | ||
1928 | uin->i_ngot++; | ||
1929 | /* if we didn't fill this inlet don't bother yet */ | ||
1930 | if (uin->i_ngot < uin->i_nconnect) | ||
1931 | goto notyet; | ||
1932 | /* if there's more than one, check them all */ | ||
1933 | if (u2->u_nin > 1) | ||
1934 | { | ||
1935 | for (uin = u2->u_in, n = u2->u_nin; n--; uin++) | ||
1936 | if (uin->i_ngot < uin->i_nconnect) goto notyet; | ||
1937 | } | ||
1938 | /* so now we can schedule the ugen. */ | ||
1939 | ugen_doit(dc, u2); | ||
1940 | notyet: ; | ||
1941 | } | ||
1942 | } | ||
1943 | t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); | ||
1944 | u->u_done = 1; | ||
1945 | } | ||
1946 | |||
1947 | /* once the DSP graph is built, we call this routine to sort it. | ||
1948 | This routine also deletes the graph; later we might want to leave the | ||
1949 | graph around, in case the user is editing the DSP network, to save having | ||
1950 | to recreate it all the time. But not today. */ | ||
1951 | |||
1952 | void ugen_done_graph(t_dspcontext *dc) | ||
1953 | { | ||
1954 | t_ugenbox *u, *u2; | ||
1955 | t_sigoutlet *uout; | ||
1956 | t_siginlet *uin; | ||
1957 | t_sigoutconnect *oc, *oc2; | ||
1958 | int i, n; | ||
1959 | t_block *blk; | ||
1960 | t_dspcontext *parent_context = dc->dc_parentcontext; | ||
1961 | float parent_srate; | ||
1962 | int parent_vecsize; | ||
1963 | int period, frequency, phase, vecsize; | ||
1964 | float srate; | ||
1965 | int chainblockbegin; /* DSP chain onset before block prolog code */ | ||
1966 | int chainblockend; /* and after block epilog code */ | ||
1967 | int chainafterall; /* and after signal outlet epilog */ | ||
1968 | int reblock = 0, switched; | ||
1969 | int downsample = 1, upsample = 1; /* IOhannes */ | ||
1970 | /* debugging printout */ | ||
1971 | |||
1972 | if (ugen_loud) | ||
1973 | { | ||
1974 | post("ugen_done_graph..."); | ||
1975 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
1976 | { | ||
1977 | post("ugen: %s", class_getname(u->u_obj->ob_pd)); | ||
1978 | for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) | ||
1979 | for (oc = uout->o_connections; oc; oc = oc->oc_next) | ||
1980 | { | ||
1981 | post("... out %d to %s, index %d, inlet %d", i, | ||
1982 | class_getname(oc->oc_who->u_obj->ob_pd), | ||
1983 | ugen_index(dc, oc->oc_who), oc->oc_inno); | ||
1984 | } | ||
1985 | } | ||
1986 | } | ||
1987 | |||
1988 | /* search for an object of class "block~" */ | ||
1989 | for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) | ||
1990 | { | ||
1991 | t_pd *zz = &u->u_obj->ob_pd; | ||
1992 | if (pd_class(zz) == block_class) | ||
1993 | { | ||
1994 | if (blk) | ||
1995 | pd_error(blk, "conflicting block~ objects in same page"); | ||
1996 | else blk = (t_block *)zz; | ||
1997 | } | ||
1998 | } | ||
1999 | |||
2000 | /* figure out block size, calling frequency, sample rate */ | ||
2001 | if (parent_context) | ||
2002 | { | ||
2003 | parent_srate = parent_context->dc_srate; | ||
2004 | parent_vecsize = parent_context->dc_vecsize; | ||
2005 | } | ||
2006 | else | ||
2007 | { | ||
2008 | parent_srate = sys_getsr(); | ||
2009 | parent_vecsize = sys_getblksize(); | ||
2010 | } | ||
2011 | if (blk) | ||
2012 | { | ||
2013 | int realoverlap; | ||
2014 | vecsize = blk->x_vecsize; | ||
2015 | if (vecsize == 0) | ||
2016 | vecsize = parent_vecsize; | ||
2017 | realoverlap = blk->x_overlap; | ||
2018 | if (realoverlap > vecsize) realoverlap = vecsize; | ||
2019 | /* IOhannes { */ | ||
2020 | downsample = blk->x_downsample; | ||
2021 | upsample = blk->x_upsample; | ||
2022 | if (downsample > parent_vecsize) downsample=parent_vecsize; | ||
2023 | period = (vecsize * downsample)/ | ||
2024 | (parent_vecsize * realoverlap * upsample); | ||
2025 | frequency = (parent_vecsize * realoverlap * upsample)/ | ||
2026 | (vecsize * downsample); | ||
2027 | /* } IOhannes*/ | ||
2028 | phase = blk->x_phase; | ||
2029 | srate = parent_srate * realoverlap * upsample / downsample; | ||
2030 | /* IOhannes */ | ||
2031 | if (period < 1) period = 1; | ||
2032 | if (frequency < 1) frequency = 1; | ||
2033 | blk->x_frequency = frequency; | ||
2034 | blk->x_period = period; | ||
2035 | blk->x_phase = dsp_phase & (period - 1); | ||
2036 | if (! parent_context || (realoverlap != 1) || | ||
2037 | (vecsize != parent_vecsize) || | ||
2038 | (downsample != 1) || (upsample != 1)) /* IOhannes */ | ||
2039 | reblock = 1; | ||
2040 | switched = blk->x_switched; | ||
2041 | } | ||
2042 | else | ||
2043 | { | ||
2044 | srate = parent_srate; | ||
2045 | vecsize = parent_vecsize; | ||
2046 | downsample = upsample = 1;/* IOhannes */ | ||
2047 | period = frequency = 1; | ||
2048 | phase = 0; | ||
2049 | if (!parent_context) reblock = 1; | ||
2050 | switched = 0; | ||
2051 | } | ||
2052 | dc->dc_reblock = reblock; | ||
2053 | dc->dc_switched = switched; | ||
2054 | dc->dc_srate = srate; | ||
2055 | dc->dc_vecsize = vecsize; | ||
2056 | |||
2057 | /* if we're reblocking or switched, we now have to create output | ||
2058 | signals to fill in for the "borrowed" ones we have now. This | ||
2059 | is also possibly true even if we're not blocked/switched, in | ||
2060 | the case that there was a signal loop. But we don't know this | ||
2061 | yet. */ | ||
2062 | |||
2063 | if (dc->dc_iosigs && (switched || reblock)) | ||
2064 | { | ||
2065 | t_signal **sigp; | ||
2066 | for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; | ||
2067 | i++, sigp++) | ||
2068 | { | ||
2069 | if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) | ||
2070 | { | ||
2071 | signal_setborrowed(*sigp, | ||
2072 | signal_new(parent_vecsize, parent_srate)); | ||
2073 | (*sigp)->s_refcount++; | ||
2074 | |||
2075 | if (ugen_loud) post("set %x->%x", *sigp, | ||
2076 | (*sigp)->s_borrowedfrom); | ||
2077 | } | ||
2078 | } | ||
2079 | } | ||
2080 | |||
2081 | if (ugen_loud) | ||
2082 | post("reblock %d, switched %d", reblock, switched); | ||
2083 | |||
2084 | /* schedule prologs for inlets and outlets. If the "reblock" flag | ||
2085 | is set, an inlet will put code on the DSP chain to copy its input | ||
2086 | into an internal buffer here, before any unit generators' DSP code | ||
2087 | gets scheduled. If we don't "reblock", inlets will need to get | ||
2088 | pointers to their corresponding inlets/outlets on the box we're inside, | ||
2089 | if any. Outlets will also need pointers, unless we're switched, in | ||
2090 | which case outlet epilog code will kick in. */ | ||
2091 | |||
2092 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
2093 | { | ||
2094 | t_pd *zz = &u->u_obj->ob_pd; | ||
2095 | t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs; | ||
2096 | if (outsigs) outsigs += dc->dc_ninlets; | ||
2097 | |||
2098 | if (pd_class(zz) == vinlet_class) | ||
2099 | vinlet_dspprolog((struct _vinlet *)zz, | ||
2100 | dc->dc_iosigs, vecsize, dsp_phase, period, frequency, | ||
2101 | downsample, upsample, /* IOhannes */ | ||
2102 | reblock, switched); | ||
2103 | else if (pd_class(zz) == voutlet_class) | ||
2104 | voutlet_dspprolog((struct _voutlet *)zz, | ||
2105 | outsigs, vecsize, dsp_phase, period, frequency, | ||
2106 | downsample, upsample, /* IOhannes */ | ||
2107 | reblock, switched); | ||
2108 | } | ||
2109 | chainblockbegin = dsp_chainsize; | ||
2110 | |||
2111 | if (blk && (reblock || switched)) /* add the block DSP prolog */ | ||
2112 | dsp_add(block_prolog, 1, blk); | ||
2113 | |||
2114 | /* Initialize for sorting */ | ||
2115 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
2116 | { | ||
2117 | u->u_done = 0; | ||
2118 | for (uout = u->u_out, i = u->u_nout; i--; uout++) | ||
2119 | uout->o_nsent = 0; | ||
2120 | for (uin = u->u_in, i = u->u_nin; i--; uin++) | ||
2121 | uin->i_ngot = 0, uin->i_signal = 0; | ||
2122 | } | ||
2123 | |||
2124 | /* Do the sort */ | ||
2125 | |||
2126 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
2127 | { | ||
2128 | /* check that we have no connected signal inlets */ | ||
2129 | if (u->u_done) continue; | ||
2130 | for (uin = u->u_in, i = u->u_nin; i--; uin++) | ||
2131 | if (uin->i_nconnect) goto next; | ||
2132 | |||
2133 | ugen_doit(dc, u); | ||
2134 | next: ; | ||
2135 | } | ||
2136 | |||
2137 | /* check for a DSP loop, which is evidenced here by the presence | ||
2138 | of ugens not yet scheduled. */ | ||
2139 | |||
2140 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
2141 | if (!u->u_done) | ||
2142 | { | ||
2143 | t_signal **sigp; | ||
2144 | pd_error(u->u_obj, | ||
2145 | "DSP loop detected (some tilde objects not scheduled)"); | ||
2146 | /* this might imply that we have unfilled "borrowed" outputs | ||
2147 | which we'd better fill in now. */ | ||
2148 | for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; | ||
2149 | i++, sigp++) | ||
2150 | { | ||
2151 | if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) | ||
2152 | { | ||
2153 | t_signal *s3 = signal_new(parent_vecsize, parent_srate); | ||
2154 | signal_setborrowed(*sigp, s3); | ||
2155 | (*sigp)->s_refcount++; | ||
2156 | dsp_add_zero(s3->s_vec, s3->s_n); | ||
2157 | if (ugen_loud) | ||
2158 | post("oops, belatedly set %x->%x", *sigp, | ||
2159 | (*sigp)->s_borrowedfrom); | ||
2160 | } | ||
2161 | } | ||
2162 | break; /* don't need to keep looking. */ | ||
2163 | } | ||
2164 | |||
2165 | if (blk && (reblock || switched)) /* add block DSP epilog */ | ||
2166 | dsp_add(block_epilog, 1, blk); | ||
2167 | chainblockend = dsp_chainsize; | ||
2168 | |||
2169 | /* add epilogs for outlets. */ | ||
2170 | |||
2171 | for (u = dc->dc_ugenlist; u; u = u->u_next) | ||
2172 | { | ||
2173 | t_pd *zz = &u->u_obj->ob_pd; | ||
2174 | if (pd_class(zz) == voutlet_class) | ||
2175 | { | ||
2176 | t_signal **iosigs = dc->dc_iosigs; | ||
2177 | if (iosigs) iosigs += dc->dc_ninlets; | ||
2178 | voutlet_dspepilog((struct _voutlet *)zz, | ||
2179 | iosigs, vecsize, dsp_phase, period, frequency, | ||
2180 | downsample, upsample, /* IOhannes */ | ||
2181 | reblock, switched); | ||
2182 | } | ||
2183 | } | ||
2184 | |||
2185 | chainafterall = dsp_chainsize; | ||
2186 | if (blk) | ||
2187 | { | ||
2188 | blk->x_blocklength = chainblockend - chainblockbegin; | ||
2189 | blk->x_epiloglength = chainafterall - chainblockend; | ||
2190 | blk->x_reblock = reblock; | ||
2191 | } | ||
2192 | |||
2193 | if (ugen_loud) | ||
2194 | { | ||
2195 | t_int *ip; | ||
2196 | if (!dc->dc_parentcontext) | ||
2197 | for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) | ||
2198 | post("chain %x", *ip); | ||
2199 | post("... ugen_done_graph done."); | ||
2200 | } | ||
2201 | /* now delete everything. */ | ||
2202 | while (dc->dc_ugenlist) | ||
2203 | { | ||
2204 | for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; | ||
2205 | n--; uout++) | ||
2206 | { | ||
2207 | oc = uout->o_connections; | ||
2208 | while (oc) | ||
2209 | { | ||
2210 | oc2 = oc->oc_next; | ||
2211 | freebytes(oc, sizeof *oc); | ||
2212 | oc = oc2; | ||
2213 | } | ||
2214 | } | ||
2215 | freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * | ||
2216 | sizeof (*dc->dc_ugenlist->u_out)); | ||
2217 | freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * | ||
2218 | sizeof(*dc->dc_ugenlist->u_in)); | ||
2219 | u = dc->dc_ugenlist; | ||
2220 | dc->dc_ugenlist = u->u_next; | ||
2221 | freebytes(u, sizeof *u); | ||
2222 | } | ||
2223 | if (ugen_currentcontext == dc) | ||
2224 | ugen_currentcontext = dc->dc_parentcontext; | ||
2225 | else bug("ugen_currentcontext"); | ||
2226 | freebytes(dc, sizeof(*dc)); | ||
2227 | |||
2228 | } | ||
2229 | |||
2230 | t_signal *ugen_getiosig(int index, int inout) | ||
2231 | { | ||
2232 | if (!ugen_currentcontext) bug("ugen_getiosig"); | ||
2233 | if (ugen_currentcontext->dc_toplevel) return (0); | ||
2234 | if (inout) index += ugen_currentcontext->dc_ninlets; | ||
2235 | return (ugen_currentcontext->dc_iosigs[index]); | ||
2236 | } | ||
2237 | |||
2238 | |||
2239 | /* -------------------- setup routine -------------------------- */ | ||
2240 | |||
2241 | void d_ugen_setup(void) /* really just block_setup */ | ||
2242 | { | ||
2243 | block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, | ||
2244 | sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); | ||
2245 | class_addcreator((t_newmethod)switch_new, gensym("switch~"), | ||
2246 | A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); | ||
2247 | class_addmethod(block_class, (t_method)block_set, gensym("set"), | ||
2248 | A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); | ||
2249 | class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0); | ||
2250 | class_addfloat(block_class, block_float); | ||
2251 | } | ||
2252 | |||