1 /************************************************************************
2 ************************************************************************
3 FAUST Architecture File
4 Copyright (C) 2006-2011 Albert Graef <Dr.Graef@t-online.de>
5 ---------------------------------------------------------------------
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20 ************************************************************************
21 ************************************************************************/
22
23 /* Pd architecture file, written by Albert Graef <Dr.Graef@t-online.de>.
24 This was derived from minimal.cpp included in the Faust distribution.
25 Please note that this is to be compiled as a shared library, which is
26 then loaded dynamically by Pd as an external. */
27
28 #include <stdlib.h>
29 #include <math.h>
30 #include <string>
31
32 #include "faust/misc.h"
33 #include "faust/gui/UI.h"
34 #include "faust/gui/meta.h"
35 #include "faust/dsp/dsp.h"
36
37 /******************************************************************************
38 *******************************************************************************
39
40 VECTOR INTRINSICS
41
42 *******************************************************************************
43 *******************************************************************************/
44
45 <<includeIntrinsic>>
46
47 /***************************************************************************
48 Pd UI interface
49 ***************************************************************************/
50
51 enum ui_elem_type_t {
52 UI_BUTTON, UI_CHECK_BUTTON,
53 UI_V_SLIDER, UI_H_SLIDER, UI_NUM_ENTRY,
54 UI_V_BARGRAPH, UI_H_BARGRAPH,
55 UI_END_GROUP, UI_V_GROUP, UI_H_GROUP, UI_T_GROUP
56 };
57
58 struct ui_elem_t {
59 ui_elem_type_t type;
60 char *label;
61 float *zone;
62 float init, min, max, step;
63 };
64
65 class PdUI : public UI
66 {
67 public:
68 const char *name;
69 int nelems, level;
70 ui_elem_t *elems;
71
72 PdUI();
73 PdUI(const char *nm, const char *s);
74 virtual ~PdUI();
75
76 protected:
77 std::string path;
78 void add_elem(ui_elem_type_t type, const char *label = NULL);
79 void add_elem(ui_elem_type_t type, const char *label, float *zone);
80 void add_elem(ui_elem_type_t type, const char *label, float *zone,
81 float init, float min, float max, float step);
82 void add_elem(ui_elem_type_t type, const char *label, float *zone,
83 float min, float max);
84
85 public:
86 virtual void addButton(const char* label, float* zone);
87 virtual void addCheckButton(const char* label, float* zone);
88 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
89 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
90 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
91
92 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
93 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
94
95 virtual void openTabBox(const char* label);
96 virtual void openHorizontalBox(const char* label);
97 virtual void openVerticalBox(const char* label);
98 virtual void closeBox();
99
100 virtual void run();
101 };
102
mangle(const char * name,int level,const char * s)103 static std::string mangle(const char *name, int level, const char *s)
104 {
105 const char *s0 = s;
106 std::string t = "";
107 if (!s) return t;
108 // Get rid of bogus "0x00" labels in recent Faust revisions. Also, for
109 // backward compatibility with old Faust versions, make sure that default
110 // toplevel groups and explicit toplevel groups with an empty label are
111 // treated alike (these both return "0x00" labels in the latest Faust, but
112 // would be treated inconsistently in earlier versions).
113 if (!*s || strcmp(s, "0x00") == 0) {
114 if (level == 0)
115 // toplevel group with empty label, map to dsp name
116 s = name;
117 else
118 // empty label
119 s = "";
120 }
121 while (*s)
122 if (isalnum(*s))
123 t += *(s++);
124 else {
125 const char *s1 = s;
126 while (*s && !isalnum(*s)) ++s;
127 if (s1 != s0 && *s) t += "-";
128 }
129 return t;
130 }
131
normpath(std::string path)132 static std::string normpath(std::string path)
133 {
134 path = std::string("/")+path;
135 int pos = path.find("//");
136 while (pos >= 0) {
137 path.erase(pos, 1);
138 pos = path.find("//");
139 }
140 size_t len = path.length();
141 if (len > 1 && path[len-1] == '/')
142 path.erase(len-1, 1);
143 return path;
144 }
145
pathcat(std::string path,std::string label)146 static std::string pathcat(std::string path, std::string label)
147 {
148 if (path.empty())
149 return normpath(label);
150 else if (label.empty())
151 return normpath(path);
152 else
153 return normpath(path+"/"+label);
154 }
155
PdUI()156 PdUI::PdUI()
157 {
158 nelems = level = 0;
159 elems = NULL;
160 name = "";
161 path = "";
162 }
163
PdUI(const char * nm,const char * s)164 PdUI::PdUI(const char *nm, const char *s)
165 {
166 nelems = level = 0;
167 elems = NULL;
168 name = nm?nm:"";
169 path = s?s:"";
170 }
171
~PdUI()172 PdUI::~PdUI()
173 {
174 if (elems) {
175 for (int i = 0; i < nelems; i++)
176 if (elems[i].label)
177 free(elems[i].label);
178 free(elems);
179 }
180 }
181
add_elem(ui_elem_type_t type,const char * label)182 inline void PdUI::add_elem(ui_elem_type_t type, const char *label)
183 {
184 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
185 if (elems1)
186 elems = elems1;
187 else
188 return;
189 std::string s = pathcat(path, mangle(name, level, label));
190 elems[nelems].type = type;
191 elems[nelems].label = strdup(s.c_str());
192 elems[nelems].zone = NULL;
193 elems[nelems].init = 0.0;
194 elems[nelems].min = 0.0;
195 elems[nelems].max = 0.0;
196 elems[nelems].step = 0.0;
197 nelems++;
198 }
199
add_elem(ui_elem_type_t type,const char * label,float * zone)200 inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone)
201 {
202 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
203 if (elems1)
204 elems = elems1;
205 else
206 return;
207 std::string s = pathcat(path, mangle(name, level, label));
208 elems[nelems].type = type;
209 elems[nelems].label = strdup(s.c_str());
210 elems[nelems].zone = zone;
211 elems[nelems].init = 0.0;
212 elems[nelems].min = 0.0;
213 elems[nelems].max = 1.0;
214 elems[nelems].step = 1.0;
215 nelems++;
216 }
217
add_elem(ui_elem_type_t type,const char * label,float * zone,float init,float min,float max,float step)218 inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone,
219 float init, float min, float max, float step)
220 {
221 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
222 if (elems1)
223 elems = elems1;
224 else
225 return;
226 std::string s = pathcat(path, mangle(name, level, label));
227 elems[nelems].type = type;
228 elems[nelems].label = strdup(s.c_str());
229 elems[nelems].zone = zone;
230 elems[nelems].init = init;
231 elems[nelems].min = min;
232 elems[nelems].max = max;
233 elems[nelems].step = step;
234 nelems++;
235 }
236
add_elem(ui_elem_type_t type,const char * label,float * zone,float min,float max)237 inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone,
238 float min, float max)
239 {
240 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
241 if (elems1)
242 elems = elems1;
243 else
244 return;
245 std::string s = pathcat(path, mangle(name, level, label));
246 elems[nelems].type = type;
247 elems[nelems].label = strdup(s.c_str());
248 elems[nelems].zone = zone;
249 elems[nelems].init = 0.0;
250 elems[nelems].min = min;
251 elems[nelems].max = max;
252 elems[nelems].step = 0.0;
253 nelems++;
254 }
255
addButton(const char * label,float * zone)256 void PdUI::addButton(const char* label, float* zone)
257 { add_elem(UI_BUTTON, label, zone); }
addCheckButton(const char * label,float * zone)258 void PdUI::addCheckButton(const char* label, float* zone)
259 { add_elem(UI_CHECK_BUTTON, label, zone); }
addVerticalSlider(const char * label,float * zone,float init,float min,float max,float step)260 void PdUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
261 { add_elem(UI_V_SLIDER, label, zone, init, min, max, step); }
addHorizontalSlider(const char * label,float * zone,float init,float min,float max,float step)262 void PdUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
263 { add_elem(UI_H_SLIDER, label, zone, init, min, max, step); }
addNumEntry(const char * label,float * zone,float init,float min,float max,float step)264 void PdUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
265 { add_elem(UI_NUM_ENTRY, label, zone, init, min, max, step); }
266
addHorizontalBargraph(const char * label,float * zone,float min,float max)267 void PdUI::addHorizontalBargraph(const char* label, float* zone, float min, float max)
268 { add_elem(UI_H_BARGRAPH, label, zone, min, max); }
addVerticalBargraph(const char * label,float * zone,float min,float max)269 void PdUI::addVerticalBargraph(const char* label, float* zone, float min, float max)
270 { add_elem(UI_V_BARGRAPH, label, zone, min, max); }
271
openTabBox(const char * label)272 void PdUI::openTabBox(const char* label)
273 {
274 if (!path.empty()) path += "/";
275 path += mangle(name, level, label);
276 level++;
277 }
openHorizontalBox(const char * label)278 void PdUI::openHorizontalBox(const char* label)
279 {
280 if (!path.empty()) path += "/";
281 path += mangle(name, level, label);
282 level++;
283 }
openVerticalBox(const char * label)284 void PdUI::openVerticalBox(const char* label)
285 {
286 if (!path.empty()) path += "/";
287 path += mangle(name, level, label);
288 level++;
289 }
closeBox()290 void PdUI::closeBox()
291 {
292 int pos = path.rfind("/");
293 if (pos < 0) pos = 0;
294 path.erase(pos);
295 level--;
296 }
297
run()298 void PdUI::run() {}
299
300 /******************************************************************************
301 *******************************************************************************
302
303 FAUST DSP
304
305 *******************************************************************************
306 *******************************************************************************/
307
308 //----------------------------------------------------------------------------
309 // FAUST generated signal processor
310 //----------------------------------------------------------------------------
311
312 <<includeclass>>
313
314 #include <stdio.h>
315 #include <string>
316 #include "m_pd.h"
317
318 #define sym(name) xsym(name)
319 #define xsym(name) #name
320 #define faust_setup(name) xfaust_setup(name)
321 #define xfaust_setup(name) name ## _tilde_setup(void)
322
323 // time for "active" toggle xfades in secs
324 #define XFADE_TIME 0.1f
325
326 static t_class *faust_class;
327
328 struct t_faust {
329 t_object x_obj;
330 #ifdef __MINGW32__
331 /* This seems to be necessary as some as yet undetermined Pd routine seems
332 to write past the end of x_obj on Windows. */
333 int fence; /* dummy field (not used) */
334 #endif
335 mydsp *dsp;
336 PdUI *ui;
337 std::string *label;
338 int active, xfade, n_xfade, rate, n_in, n_out;
339 t_sample **inputs, **outputs, **buf;
340 t_outlet *out;
341 t_sample f;
342 };
343
344 static t_symbol *s_button, *s_checkbox, *s_vslider, *s_hslider, *s_nentry,
345 *s_vbargraph, *s_hbargraph;
346
zero_samples(int k,int n,t_sample ** out)347 static inline void zero_samples(int k, int n, t_sample **out)
348 {
349 for (int i = 0; i < k; i++)
350 #ifdef __STDC_IEC_559__
351 /* IEC 559 a.k.a. IEEE 754 floats can be initialized faster like this */
352 memset(out[i], 0, n*sizeof(t_sample));
353 #else
354 for (int j = 0; j < n; j++)
355 out[i][j] = 0.0f;
356 #endif
357 }
358
copy_samples(int k,int n,t_sample ** out,t_sample ** in)359 static inline void copy_samples(int k, int n, t_sample **out, t_sample **in)
360 {
361 for (int i = 0; i < k; i++)
362 memcpy(out[i], in[i], n*sizeof(t_sample));
363 }
364
faust_perform(t_int * w)365 static t_int *faust_perform(t_int *w)
366 {
367 t_faust *x = (t_faust *)(w[1]);
368 int n = (int)(w[2]);
369 if (!x->dsp || !x->buf) return (w+3);
370 AVOIDDENORMALS;
371 if (x->xfade > 0) {
372 float d = 1.0f/x->n_xfade, f = (x->xfade--)*d;
373 d = d/n;
374 x->dsp->compute(n, x->inputs, x->buf);
375 if (x->active)
376 if (x->n_in == x->n_out)
377 /* xfade inputs -> buf */
378 for (int j = 0; j < n; j++, f -= d)
379 for (int i = 0; i < x->n_out; i++)
380 x->outputs[i][j] = f*x->inputs[i][j]+(1.0f-f)*x->buf[i][j];
381 else
382 /* xfade 0 -> buf */
383 for (int j = 0; j < n; j++, f -= d)
384 for (int i = 0; i < x->n_out; i++)
385 x->outputs[i][j] = (1.0f-f)*x->buf[i][j];
386 else
387 if (x->n_in == x->n_out)
388 /* xfade buf -> inputs */
389 for (int j = 0; j < n; j++, f -= d)
390 for (int i = 0; i < x->n_out; i++)
391 x->outputs[i][j] = f*x->buf[i][j]+(1.0f-f)*x->inputs[i][j];
392 else
393 /* xfade buf -> 0 */
394 for (int j = 0; j < n; j++, f -= d)
395 for (int i = 0; i < x->n_out; i++)
396 x->outputs[i][j] = f*x->buf[i][j];
397 } else if (x->active) {
398 x->dsp->compute(n, x->inputs, x->buf);
399 copy_samples(x->n_out, n, x->outputs, x->buf);
400 } else if (x->n_in == x->n_out) {
401 copy_samples(x->n_out, n, x->buf, x->inputs);
402 copy_samples(x->n_out, n, x->outputs, x->buf);
403 } else
404 zero_samples(x->n_out, n, x->outputs);
405 return (w+3);
406 }
407
faust_dsp(t_faust * x,t_signal ** sp)408 static void faust_dsp(t_faust *x, t_signal **sp)
409 {
410 int n = sp[0]->s_n, sr = (int)sp[0]->s_sr;
411 if (x->rate <= 0) {
412 /* default sample rate is whatever Pd tells us */
413 PdUI *ui = x->ui;
414 float *z = NULL;
415 if (ui->nelems > 0 &&
416 (z = (float*)malloc(ui->nelems*sizeof(float)))) {
417 /* save the current control values */
418 for (int i = 0; i < ui->nelems; i++)
419 if (ui->elems[i].zone)
420 z[i] = *ui->elems[i].zone;
421 }
422 /* set the proper sample rate; this requires reinitializing the dsp */
423 x->rate = sr;
424 x->dsp->init(sr);
425 if (z) {
426 /* restore previous control values */
427 for (int i = 0; i < ui->nelems; i++)
428 if (ui->elems[i].zone)
429 *ui->elems[i].zone = z[i];
430 free(z);
431 }
432 }
433 if (n > 0)
434 x->n_xfade = (int)(x->rate*XFADE_TIME/n);
435 dsp_add(faust_perform, 2, x, n);
436 for (int i = 0; i < x->n_in; i++)
437 x->inputs[i] = sp[i+1]->s_vec;
438 for (int i = 0; i < x->n_out; i++)
439 x->outputs[i] = sp[x->n_in+i+1]->s_vec;
440 if (x->buf != NULL)
441 for (int i = 0; i < x->n_out; i++) {
442 x->buf[i] = (t_sample*)malloc(n*sizeof(t_sample));
443 if (x->buf[i] == NULL) {
444 for (int j = 0; j < i; j++)
445 free(x->buf[j]);
446 free(x->buf);
447 x->buf = NULL;
448 break;
449 }
450 }
451 }
452
pathcmp(const char * s,const char * t)453 static int pathcmp(const char *s, const char *t)
454 {
455 int n = strlen(s), m = strlen(t);
456 if (n == 0 || m == 0)
457 return 0;
458 else if (t[0] == '/')
459 return strcmp(s, t);
460 else if (n <= m || s[n-m-1] != '/')
461 return strcmp(s+1, t);
462 else
463 return strcmp(s+n-m, t);
464 }
465
faust_any(t_faust * x,t_symbol * s,int argc,t_atom * argv)466 static void faust_any(t_faust *x, t_symbol *s, int argc, t_atom *argv)
467 {
468 if (!x->dsp) return;
469 PdUI *ui = x->ui;
470 if (s == &s_bang) {
471 for (int i = 0; i < ui->nelems; i++)
472 if (ui->elems[i].label && ui->elems[i].zone) {
473 t_atom args[6];
474 t_symbol *_s;
475 switch (ui->elems[i].type) {
476 case UI_BUTTON:
477 _s = s_button;
478 break;
479 case UI_CHECK_BUTTON:
480 _s = s_checkbox;
481 break;
482 case UI_V_SLIDER:
483 _s = s_vslider;
484 break;
485 case UI_H_SLIDER:
486 _s = s_hslider;
487 break;
488 case UI_NUM_ENTRY:
489 _s = s_nentry;
490 break;
491 case UI_V_BARGRAPH:
492 _s = s_vbargraph;
493 break;
494 case UI_H_BARGRAPH:
495 _s = s_hbargraph;
496 break;
497 default:
498 continue;
499 }
500 SETSYMBOL(&args[0], gensym(ui->elems[i].label));
501 SETFLOAT(&args[1], *ui->elems[i].zone);
502 SETFLOAT(&args[2], ui->elems[i].init);
503 SETFLOAT(&args[3], ui->elems[i].min);
504 SETFLOAT(&args[4], ui->elems[i].max);
505 SETFLOAT(&args[5], ui->elems[i].step);
506 outlet_anything(x->out, _s, 6, args);
507 }
508 } else {
509 const char *label = s->s_name;
510 int count = 0;
511 for (int i = 0; i < ui->nelems; i++)
512 if (ui->elems[i].label &&
513 pathcmp(ui->elems[i].label, label) == 0) {
514 if (argc == 0) {
515 if (ui->elems[i].zone) {
516 t_atom arg;
517 SETFLOAT(&arg, *ui->elems[i].zone);
518 outlet_anything(x->out, gensym(ui->elems[i].label), 1, &arg);
519 }
520 ++count;
521 } else if (argc == 1 &&
522 (argv[0].a_type == A_FLOAT ||
523 argv[0].a_type == A_DEFFLOAT) &&
524 ui->elems[i].zone) {
525 float f = atom_getfloat(argv);
526 *ui->elems[i].zone = f;
527 ++count;
528 } else
529 pd_error(x, "[faust] %s: bad control argument: %s",
530 x->label->c_str(), label);
531 }
532 if (count == 0 && strcmp(label, "active") == 0) {
533 if (argc == 0) {
534 t_atom arg;
535 SETFLOAT(&arg, (float)x->active);
536 outlet_anything(x->out, gensym((char*)"active"), 1, &arg);
537 } else if (argc == 1 &&
538 (argv[0].a_type == A_FLOAT ||
539 argv[0].a_type == A_DEFFLOAT)) {
540 float f = atom_getfloat(argv);
541 x->active = (int)f;
542 x->xfade = x->n_xfade;
543 }
544 }
545 }
546 }
547
faust_free(t_faust * x)548 static void faust_free(t_faust *x)
549 {
550 if (x->label) delete x->label;
551 if (x->dsp) delete x->dsp;
552 if (x->ui) delete x->ui;
553 if (x->inputs) free(x->inputs);
554 if (x->outputs) free(x->outputs);
555 if (x->buf) {
556 for (int i = 0; i < x->n_out; i++)
557 if (x->buf[i]) free(x->buf[i]);
558 free(x->buf);
559 }
560 }
561
faust_new(t_symbol * s,int argc,t_atom * argv)562 static void *faust_new(t_symbol *s, int argc, t_atom *argv)
563 {
564 t_faust *x = (t_faust*)pd_new(faust_class);
565 int sr = -1;
566 t_symbol *id = NULL;
567 x->active = 1;
568 for (int i = 0; i < argc; i++)
569 if (argv[i].a_type == A_FLOAT || argv[i].a_type == A_DEFFLOAT)
570 sr = (int)argv[i].a_w.w_float;
571 else if (argv[i].a_type == A_SYMBOL || argv[i].a_type == A_DEFSYMBOL)
572 id = argv[i].a_w.w_symbol;
573 x->rate = sr;
574 if (sr <= 0) sr = 44100;
575 x->xfade = 0; x->n_xfade = (int)(sr*XFADE_TIME/64);
576 x->inputs = x->outputs = x->buf = NULL;
577 x->label = new std::string(sym(mydsp) "~");
578 x->dsp = new mydsp();
579 x->ui = new PdUI(sym(mydsp), id?id->s_name:NULL);
580 if (!x->dsp || !x->ui || !x->label) goto error;
581 if (id) {
582 *x->label += " ";
583 *x->label += id->s_name;
584 }
585 x->n_in = x->dsp->getNumInputs();
586 x->n_out = x->dsp->getNumOutputs();
587 if (x->n_in > 0)
588 x->inputs = (t_sample**)malloc(x->n_in*sizeof(t_sample*));
589 if (x->n_out > 0) {
590 x->outputs = (t_sample**)malloc(x->n_out*sizeof(t_sample*));
591 x->buf = (t_sample**)malloc(x->n_out*sizeof(t_sample*));
592 }
593 if ((x->n_in > 0 && x->inputs == NULL) ||
594 (x->n_out > 0 && (x->outputs == NULL || x->buf == NULL)))
595 goto error;
596 for (int i = 0; i < x->n_out; i++)
597 x->buf[i] = NULL;
598 x->dsp->init(sr);
599 x->dsp->buildUserInterface(x->ui);
600 for (int i = 0; i < x->n_in; i++)
601 inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
602 x->out = outlet_new(&x->x_obj, 0);
603 for (int i = 0; i < x->n_out; i++)
604 outlet_new(&x->x_obj, &s_signal);
605 return (void *)x;
606 error:
607 faust_free(x);
608 x->dsp = NULL; x->ui = NULL;
609 x->inputs = x->outputs = x->buf = NULL;
610 return (void *)x;
611 }
612
faust_setup(mydsp)613 extern "C" void faust_setup(mydsp)
614 {
615 t_symbol *s = gensym(sym(mydsp) "~");
616 faust_class =
617 class_new(s, (t_newmethod)faust_new, (t_method)faust_free,
618 sizeof(t_faust), CLASS_DEFAULT,
619 A_GIMME, A_NULL);
620 class_addmethod(faust_class, (t_method)faust_dsp, gensym((char*)"dsp"), A_NULL);
621 class_addanything(faust_class, faust_any);
622 class_addmethod(faust_class, nullfn, &s_signal, A_NULL);
623 s_button = gensym((char*)"button");
624 s_checkbox = gensym((char*)"checkbox");
625 s_vslider = gensym((char*)"vslider");
626 s_hslider = gensym((char*)"hslider");
627 s_nentry = gensym((char*)"nentry");
628 s_vbargraph = gensym((char*)"vbargraph");
629 s_hbargraph = gensym((char*)"hbargraph");
630 /* give some indication that we're loaded and ready to go */
631 mydsp dsp = mydsp();
632 post("[faust] %s: %d inputs, %d outputs", sym(mydsp) "~",
633 dsp.getNumInputs(), dsp.getNumOutputs());
634 }
635