1 /************************************************************************
2 IMPORTANT NOTE : this file contains two clearly delimited sections :
3 the ARCHITECTURE section (in two parts) and the USER section. Each section
4 is governed by its own copyright and license. Please check individually
5 each section for license and copyright information.
6 *************************************************************************/
7
8 /******************* BEGIN dssi.cpp ****************/
9
10 /************************************************************************
11 FAUST Architecture File
12 Copyright (C) 2003-2019 GRAME, Centre National de Creation Musicale
13 ---------------------------------------------------------------------
14 This Architecture section is free software; you can redistribute it
15 and/or modify it under the terms of the GNU General Public License
16 as published by the Free Software Foundation; either version 3 of
17 the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; If not, see <http://www.gnu.org/licenses/>.
26
27 EXCEPTION : As a special exception, you may create a larger work
28 that contains this FAUST architecture section and distribute
29 that work under terms of your choice, so long as this FAUST
30 architecture section is not modified.
31
32 ************************************************************************
33 ************************************************************************/
34
35 /********************************************************************
36 * dssi.cpp - Polyphonic dssi wrapper for the FAUST language.
37 *
38 * Usage: faust -a dssi.cpp myfaustprog.dsp
39 *
40 * By Michael J. Wilson (mwilson@alumni.caltech.edu)
41 *
42 * Made with reference to:
43 * - vsti-mono.cpp by Julius Smith (http://ccrma.stanford.edu/~jos/)
44 * - ladspa.cpp by GRAME, Centre National de Creation Musicale
45 * - karplong.cpp by Chris Cannam, Steve Harris, Sean Bolton
46 *
47 * Because of the inclusion of code from ladspa.cpp, this architecture
48 * file is also released under the GNU General Public Licenses version
49 * 3. Sections which were taken from ladspa.cpp are clearly marked
50 * below, in order to trace the GPL dependency.
51 * As with faust2pd and vsti-mono.cpp, to obtain MIDI control via
52 * NoteOn/Off, Velocity, and KeyNumber, there must be a button named
53 * "gate" and sliders (or numeric entries) named "gain" and "freq" in
54 * the Faust patch specified in myfaustprog.dsp.
55 *
56 * FAUST
57 * Copyright (C) 2003-2007 GRAME, Centre National de Creation Musicale
58 * http://www.grame.fr/
59 *
60 ********************************************************************/
61
62 #include "dssi.h"
63 #include "ladspa.h"
64
65 #include <math.h>
66 #include <stdlib.h>
67 #include <string.h>
68
69 #include <iostream>
70 #include <stack>
71 #include <string>
72 #include <map>
73 #include <vector>
74 #include <list>
75
76 using namespace std;
77
78 // On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
79 // flags to avoid costly denormals
80 #ifdef __SSE__
81 #include <xmmintrin.h>
82 #ifdef __SSE2__
83 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
84 #else
85 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
86 #endif
87 #else
88 #warning *** dssi.cpp: NO SSE FLAG (denormals may slow things down) ***
89 #define AVOIDDENORMALS
90 #endif
91
92 struct Meta : std::map<const char*, const char*>
93 {
declareMeta94 void declare (const char* key, const char* value) { (*this)[key]=value; }
95 };
96
97
98 #define sym(name) xsym(name)
99 #define xsym(name) #name
100
lsr(int x,int n)101 inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }
int2pow2(int x)102 inline int int2pow2 (int x) { int r=0; while ((1<<r)<x) r++; return r; }
103
104 /******************************************************************************
105 *******************************************************************************
106
107 VECTOR INTRINSICS
108
109 *******************************************************************************
110 *******************************************************************************/
111
112 <<includeIntrinsic>>
113
114 //---------------------Abstract User Interface--------------------
115 //
116 // Abstract definition of a User Interface to be passed to the
117 // buildUserInterface method of a Faust Signal Processor
118 //
119 //----------------------------------------------------------------
120 class UI
121 {
122 bool fStopped;
123
124 public:
UI()125 UI() : fStopped(false) {}
~UI()126 virtual ~UI() {}
127
128 virtual void addButton(const char* label, float* zone) = 0;
129 virtual void addToggleButton(const char* label, float* zone) = 0;
130 virtual void addCheckButton(const char* label, float* zone) = 0;
131 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
132 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
133 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step) = 0;
134
135 // -- passive widgets
136 virtual void addNumDisplay(const char* label, float* zone, int precision) = 0;
137 virtual void addTextDisplay(const char* label, float* zone, char* names[], float min, float max) = 0;
138 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max) = 0;
139 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max) = 0;
140
141 // -- frames and labels
142 virtual void openFrameBox(const char* label) = 0;
143 virtual void openTabBox(const char* label) = 0;
144 virtual void openHorizontalBox(const char* label) = 0;
145 virtual void openVerticalBox(const char* label) = 0;
146 virtual void closeBox() = 0;
147
148 virtual void show() = 0;
149 virtual void run() = 0;
150
stop()151 void stop() { fStopped = true; }
stopped()152 bool stopped() { return fStopped; }
153
declare(float * zone,const char * key,const char * value)154 virtual void declare(float* zone, const char* key, const char* value) {}
155 };
156
157 //------------------Abstract Signal Processor---------------------
158 //
159 // Abstract definition of a Faust Signal Processor
160 //
161 //----------------------------------------------------------------
162 class dsp
163 {
164 protected:
165 int fSampleRate;
166 public:
dsp()167 dsp() {}
~dsp()168 virtual ~dsp() {}
169 virtual int getNumInputs() = 0;
170 virtual int getNumOutputs() = 0;
171 virtual void buildUserInterface(UI* interface) = 0;
172 virtual void init(int sample_rate) = 0;
173 virtual void compute(int len, float** inputs, float** outputs)= 0;
174 };
175
176 /********************END ARCHITECTURE SECTION (part 1/2)****************/
177
178 /**************************BEGIN USER SECTION **************************/
179
180 <<includeclass>>
181
182 /***************************END USER SECTION ***************************/
183
184 /*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/
185
186 ////////////////////////////////////////////////////////////////////////////////
187 // Forward declarations
188 ////////////////////////////////////////////////////////////////////////////////
189 class Plugin;
190 class DescriptorUI;
191 class Voice;
192
193 ////////////////////////////////////////////////////////////////////////////////
194 // Global data
195 ////////////////////////////////////////////////////////////////////////////////
196 // Maximum polyphony, must be at least 1 (TODO make this configurable at compile / runtime?)
197 const int MAX_POLYPHONY = 64;
198 // Descriptor
199 DSSI_Descriptor* g_dssi_descriptor;
200 // Additional data for descriptor:
201 LADSPA_Descriptor* g_ladspa_descriptor;
202 // Program descriptor:
203 DSSI_Program_Descriptor g_program_descriptor;
204
205 // Global data for the descriptor:
206 std::vector<LADSPA_PortDescriptor> g_port_descriptors;
207 std::vector<LADSPA_PortRangeHint> g_port_range_hints;
208 std::vector<const char*> g_port_names;
209 const char* g_name;
210
211 ////////////////////////////////////////////////////////////////////////////////
212 // The enclosed code is from ladspa.cpp
213 // TODO groups of port names
214 static const char* inames[] =
215 {
216 "input00", "input01", "input02", "input03", "input04",
217 "input05", "input06", "input07", "input08", "input09",
218 "input10", "input11", "input12", "input13", "input14",
219 "input15", "input16", "input17", "input18", "input19",
220 "input20", "input21", "input22", "input23", "input24",
221 "input25", "input26", "input27", "input28", "input29",
222 "input30", "input31", "input32", "input33", "input34",
223 "input35", "input36", "input37", "input38", "input39"
224 };
225 static const char* onames[] =
226 {
227 "output00", "output01", "output02", "output03", "output04",
228 "output05", "output06", "output07", "output08", "output09",
229 "output10", "output11", "output12", "output13", "output14",
230 "output15", "output16", "output17", "output18", "output19",
231 "output20", "output21", "output22", "output23", "output24",
232 "output25", "output26", "output27", "output28", "output29",
233 "output30", "output31", "output32", "output33", "output34",
234 "output35", "output36", "output37", "output38", "output39"
235 };
236 // END code from ladspa.cpp
237 ////////////////////////////////////////////////////////////////////////////////
238
239 ////////////////////////////////////////////////////////////////////////////////
240 // Global helper functions
241 ////////////////////////////////////////////////////////////////////////////////
get_metadata_if_exists(const char * key,const char * default_string)242 char* get_metadata_if_exists(const char* key, const char* default_string)
243 {
244 // TODO probably want to free these somehow. Currently only used for ladspa descriptor
245 Meta meta;
246 static mydsp dsp;
247 dsp.metadata(&meta);
248 if(meta.find(key) != meta.end())
249 {
250 return strdup(meta[key]);
251 }
252 else
253 {
254 return strdup(default_string);
255 }
256 }
257
258 ////////////////////////////////////////////////////////////////////////////////
259 // The enclosed code is from ladspa.cpp
simplify(const std::string & src)260 std::string simplify(const std::string& src)
261 {
262 int i=0;
263 int level=2;
264 std::string dst;
265
266 while (src[i] ) {
267
268 switch (level) {
269
270 case 0 :
271 case 1 :
272 case 2 :
273 // Skip the begin of the label "--foo-"
274 // until 3 '-' have been read
275 if (src[i]=='-') { level++; }
276 break;
277
278 case 3 :
279 // copy the content, but skip non alphnum
280 // and content in parenthesis
281 switch (src[i]) {
282 case '(' :
283 case '[' :
284 level++;
285 break;
286
287 case '-' :
288 dst += '-';
289 break;
290
291 default :
292 if (isalnum(src[i])) {
293 dst+= tolower(src[i]);
294 }
295
296 }
297 break;
298
299 default :
300 // here we are inside parenthesis and
301 // we skip the content until we are back to
302 // level 3
303 switch (src[i]) {
304
305 case '(' :
306 case '[' :
307 level++;
308 break;
309
310 case ')' :
311 case ']' :
312 level--;
313 break;
314
315 default :
316 break;
317 }
318 }
319 i++;
320 }
321 return (dst.size() > 0) ? dst :src;
322 }
323 // END code from ladspa.cpp
324 ////////////////////////////////////////////////////////////////////////////////
325
326 ////////////////////////////////////////////////////////////////////////////////
327 // Single voice; use multiple for polyphony
328 ////////////////////////////////////////////////////////////////////////////////
329 class Voice : public UI
330 {
331 public:
332 Voice(int sampleRate);
333 ~Voice();
334
335 // UI methods:
336 // TODO don't hardcode these so hard maybe
337 void setFreq(float val);
338 void setGate(float val);
339 void setGain(float val);
340
341 virtual void addButton(const char* label, float* zone);
342 virtual void addToggleButton(const char* label, float* zone);
343 virtual void addCheckButton(const char* label, float* zone);
344 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
345 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
346 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
347
348 virtual void addNumDisplay(const char* label, float* zone, int precision);
349 virtual void addTextDisplay(const char* label, float* zone, char* names[], float min, float max);
350 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
351 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
352
353 virtual void openFrameBox(const char* label);
354 virtual void openTabBox(const char* label);
355 virtual void openHorizontalBox(const char* label);
356 virtual void openVerticalBox(const char* label);
357 virtual void closeBox();
358
359 virtual void show();
360 virtual void run();
361
362 // Internal control (to Faust DSP)
363 std::vector<float*> m_controls;
364
365 // TODO maybe don't make this public eventually
366 mydsp* m_mydsp;
367
368 private:
369 // Helpers for UI building:
370 // TODO organize this a bit better later...
371 bool ckAnyMatch(const char* label, const char* indexName, float **zone, float* newZone);
372 bool ckAllMatches(const char* label, float* zone);
373 void addZone(float* zone);
374
375 int m_samplerate;
376
377 float* m_freq_zone;
378 float* m_gate_zone;
379 float* m_gain_zone;
380 };
381
382 ////////////////////////////////////////////////////////////////////////////////
383 // Plugin class to handle DSSI methods:
384 ////////////////////////////////////////////////////////////////////////////////
385 class Plugin : public UI
386 {
387 public:
388 // LADSPA methods:
389 static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
390 static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
391 static void activate(LADSPA_Handle);
392 static void run(LADSPA_Handle, unsigned long);
393 static void cleanup(LADSPA_Handle);
394
395 // DSSI methods:
396 static const DSSI_Program_Descriptor* getProgram(LADSPA_Handle, unsigned long);
397 static void selectProgram(LADSPA_Handle, unsigned long, unsigned long);
398 static int getMidiController(LADSPA_Handle, unsigned long);
399 static void runSynth(LADSPA_Handle, unsigned long, snd_seq_event_t *, unsigned long);
400
401 // UI methods:
402 // TODO don't hardcode these so hard maybe
403 void setFreq(float val, int voice = 0);
404 void setGate(float val, int voice = 0);
405 void setGain(float val, int voice = 0);
406
407 virtual void addButton(const char* label, float* zone);
408 virtual void addToggleButton(const char* label, float* zone);
409 virtual void addCheckButton(const char* label, float* zone);
410 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
411 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
412 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
413
414 virtual void addNumDisplay(const char* label, float* zone, int precision);
415 virtual void addTextDisplay(const char* label, float* zone, char* names[], float min, float max);
416 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
417 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
418
419 virtual void openFrameBox(const char* label);
420 virtual void openTabBox(const char* label);
421 virtual void openHorizontalBox(const char* label);
422 virtual void openVerticalBox(const char* label);
423 virtual void closeBox();
424
425 virtual void show();
426 virtual void run();
427
428 private:
429 Plugin(int sampleRate);
430 ~Plugin();
431
432 // Helper methods:
433 void updateControlZones();
434 void runImpl(unsigned long, snd_seq_event_t *, unsigned long);
435 void addSamples(size_t);
436
437 // Helpers for UI building:
438 // TODO organize this a bit better later...
439 bool ckAnyMatch(const char* label, const char* indexName);
440 bool ckAllMatches(const char* label);
441 void add_control_with_default(float default_value);
442
443 int m_samplerate;
444
445 // Voice allocation memebers (TODO maybe break voice allocator into separate class later)
446 // Note each voice is playing
447 std::vector<int> voice_notes;
448 // Queue of free voices
449 std::list<size_t> voice_free;
450
451 // Voices for polyphony
452 std::vector<Voice*> m_voices;
453
454 // Top-level inputs (from DSSI host)
455 std::vector<float*> m_inputs;
456 // Top-level outputs (to DSSI host)
457 std::vector<float*> m_outputs;
458 // Temp vector for collecting outputs TODO this is an ugly way to do it...
459 std::vector<std::vector<float> > m_temp_outputs;
460 // External control (from DSSI host)
461 std::vector<float*> m_controls;
462
463 // Control default values
464 std::vector<float> m_control_defaults;
465 };
466
467 ////////////////////////////////////////////////////////////////////////////////
468 // UI class to build descriptor, analogous to ladspa.cpp's portCollector
469 ////////////////////////////////////////////////////////////////////////////////
470 const int ICONTROL = LADSPA_PORT_INPUT|LADSPA_PORT_CONTROL;
471 const int OCONTROL = LADSPA_PORT_OUTPUT|LADSPA_PORT_CONTROL;
472
473 class DescriptorUI : public UI
474 {
475 public:
476 DescriptorUI(int ins, int outs);
477
addButton(const char * label,float * zone)478 virtual void addButton(const char* label, float* zone) {
479 if (!ckAnyMatch(label, "gate"))
480 {
481 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
482 }
483 }
addToggleButton(const char * label,float * zone)484 virtual void addToggleButton(const char* label, float* zone) {
485 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
486 }
addCheckButton(const char * label,float * zone)487 virtual void addCheckButton(const char* label, float* zone) {
488 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
489 }
addVerticalSlider(const char * label,float * zone,float init,float min,float max,float step)490 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) {
491 if (!ckAllMatches(label))
492 {
493 addPortDescr(ICONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
494 }
495 }
addHorizontalSlider(const char * label,float * zone,float init,float min,float max,float step)496 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) {
497 if (!ckAllMatches(label))
498 {
499 addPortDescr(ICONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
500 }
501 }
addNumEntry(const char * label,float * zone,float init,float min,float max,float step)502 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step) {
503 if (!ckAllMatches(label))
504 {
505 addPortDescr(ICONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
506 }
507 }
addNumDisplay(const char * label,float * zone,int precision)508 virtual void addNumDisplay(const char* label, float* zone, int precision) {
509 addPortDescr(OCONTROL, label, 0, -10000, +10000);
510 }
addTextDisplay(const char * label,float * zone,char * names[],float min,float max)511 virtual void addTextDisplay(const char* label, float* zone, char* names[], float min, float max) {
512 addPortDescr(OCONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
513 }
addHorizontalBargraph(const char * label,float * zone,float min,float max)514 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max) {
515 addPortDescr(OCONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
516 }
addVerticalBargraph(const char * label,float * zone,float min,float max)517 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max){
518 addPortDescr(OCONTROL, label, LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, min, max);
519 }
520
openFrameBox(const char * label)521 virtual void openFrameBox(const char* label) { openAnyBox(label); }
openTabBox(const char * label)522 virtual void openTabBox(const char* label) { openAnyBox(label); }
openHorizontalBox(const char * label)523 virtual void openHorizontalBox(const char* label) { openAnyBox(label); }
openVerticalBox(const char * label)524 virtual void openVerticalBox(const char* label) { openAnyBox(label); }
525
closeBox()526 virtual void closeBox() { fPrefix.pop(); }
527
show()528 virtual void show() {}
run()529 virtual void run() {}
530
531 private:
532 std::stack<std::string> fPrefix;
533
534 void addPortDescr(int type, const char* label, int hint, float min=0.0, float max=0.0);
535
536 void openAnyBox(const char* label);
537
ckAnyMatch(const char * label,const char * indexName)538 bool ckAnyMatch(const char* label, const char* indexName)
539 {
540 // TODO do case-insensitive here...
541 if (strcmp(label,indexName)==0)
542 {
543 // Don't set values in the DescriptorUI
544 // TODO consolidate this later
545 return true;
546 }
547 return false;
548 }
ckAllMatches(const char * label)549 bool ckAllMatches(const char* label)
550 {
551 bool result = false;
552 result = result || ckAnyMatch(label,"gain");
553 result = result || ckAnyMatch(label,"gate");
554 result = result || ckAnyMatch(label,"freq");
555 return result;
556 }
557 };
558
559 ////////////////////////////////////////////////////////////////////////////////
560 // DescriptorUI methods
561 ////////////////////////////////////////////////////////////////////////////////
DescriptorUI(int ins,int outs)562 DescriptorUI::DescriptorUI(int ins, int outs)
563 {
564 // Note inputs and outputs
565 for (int i = 0; i < ins; i++)
566 {
567 g_port_descriptors.push_back(LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO);
568 LADSPA_PortRangeHint temp;
569 temp.HintDescriptor = 0;
570 temp.LowerBound = 0;
571 temp.UpperBound = 0;
572 g_port_range_hints.push_back(temp);
573 g_port_names.push_back(inames[i]);
574 }
575 for (int j = 0; j < outs; j++)
576 {
577 g_port_descriptors.push_back(LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO);
578 LADSPA_PortRangeHint temp;
579 temp.HintDescriptor = 0;
580 temp.LowerBound = 0;
581 temp.UpperBound = 0;
582 g_port_range_hints.push_back(temp);
583 g_port_names.push_back(onames[j]);
584 }
585 }
586
addPortDescr(int type,const char * label,int hint,float min,float max)587 void DescriptorUI::addPortDescr(int type, const char* label, int hint, float min, float max)
588 {
589 ////////////////////////////////////////////////////////////////////////////////
590 // The enclosed code is derived from ladspa.cpp
591 std::string fullname = simplify(fPrefix.top() + "-" + label);
592 // TODO for debugging, I'm just using the label for now instead of the full name (since fullname can get long)
593 // char * str = strdup(fullname.c_str());
594 char * str = strdup(label);
595 // END code from ladspa.cpp
596 ////////////////////////////////////////////////////////////////////////////////
597
598 g_port_descriptors.push_back(type);
599 LADSPA_PortRangeHint temp;
600 temp.HintDescriptor = hint;
601 temp.LowerBound = min;
602 temp.UpperBound = max;
603 g_port_range_hints.push_back(temp);
604 g_port_names.push_back(str); // TODO memory leak; need to free
605 }
606
openAnyBox(const char * label)607 void DescriptorUI::openAnyBox(const char* label)
608 {
609 ////////////////////////////////////////////////////////////////////////////////
610 // The enclosed code is from ladspa.cpp
611 if (fPrefix.size() == 0)
612 {
613 // top level label is used as plugin name
614 g_name = label;
615 fPrefix.push(label);
616 }
617 else
618 {
619 std::string s;
620 if (label && label[0])
621 {
622 s = fPrefix.top() + "-" + label;
623 }
624 else
625 {
626 s = fPrefix.top();
627 }
628 fPrefix.push(s);
629 }
630 // END code from ladspa.cpp
631 ////////////////////////////////////////////////////////////////////////////////
632 }
633
634 ////////////////////////////////////////////////////////////////////////////////
635 // Plugin methods
636 ////////////////////////////////////////////////////////////////////////////////
Plugin(int sampleRate)637 Plugin::Plugin(int sampleRate) :
638 m_samplerate(sampleRate)
639 {
640 int i;
641
642 mydsp temp_mydsp;
643
644 for (i = 0; i < MAX_POLYPHONY; i++)
645 {
646 m_voices.push_back(new Voice(m_samplerate));
647
648 // Voice parameters:
649 voice_free.push_back(i);
650 // TODO using -1 to represent nothing; think of a more clear way to do this
651 voice_notes.push_back(-1);
652 }
653 for (i = 0; i < temp_mydsp.getNumInputs(); i++)
654 {
655 m_inputs.push_back(0);
656 }
657 for (i = 0; i < temp_mydsp.getNumOutputs(); i++)
658 {
659 m_outputs.push_back(0);
660 std::vector<float> temp;
661 temp.push_back(0.0);
662 m_temp_outputs.push_back(temp);
663 }
664 }
665
~Plugin()666 Plugin::~Plugin()
667 {
668 size_t i;
669 for (i = 0; i < m_voices.size(); i++)
670 {
671 delete m_voices[i];
672 }
673 }
674
instantiate(const LADSPA_Descriptor *,unsigned long rate)675 LADSPA_Handle Plugin::instantiate(const LADSPA_Descriptor *, unsigned long rate)
676 {
677 Plugin *plugin = new Plugin(rate);
678 size_t i;
679
680 mydsp* temp_mydsp = new mydsp();
681 temp_mydsp->buildUserInterface(plugin);
682 delete temp_mydsp;
683
684 for (i = 0; i < plugin->m_voices.size(); i++)
685 {
686 plugin->m_voices[i]->m_mydsp->buildUserInterface(plugin->m_voices[i]);
687 }
688 return plugin;
689 }
690
connectPort(LADSPA_Handle handle,unsigned long port,LADSPA_Data * location)691 void Plugin::connectPort(LADSPA_Handle handle, unsigned long port, LADSPA_Data *location)
692 {
693 Plugin *plugin = (Plugin *)handle;
694
695 // Map inputs, then outputs, then controls:
696 if (port < plugin->m_inputs.size())
697 {
698 *(&plugin->m_inputs[port]) = (float *)location;
699 }
700 else if (port < plugin->m_inputs.size() + plugin->m_outputs.size())
701 {
702 *(&plugin->m_outputs[port - plugin->m_inputs.size()]) = (float *)location;
703 }
704 else
705 {
706 plugin->m_controls[port - plugin->m_inputs.size() - plugin->m_outputs.size()] = (float *)location;
707 }
708 }
709
activate(LADSPA_Handle handle)710 void Plugin::activate(LADSPA_Handle handle)
711 {
712 Plugin *plugin = (Plugin *)handle;
713 for (size_t i = 0; i < plugin->m_voices.size(); i++)
714 {
715 plugin->m_voices[i]->m_mydsp->init(plugin->m_samplerate);
716 }
717 }
718
run(LADSPA_Handle handle,unsigned long samples)719 void Plugin::run(LADSPA_Handle handle, unsigned long samples)
720 {
721 runSynth(handle, samples, 0, 0);
722 }
723
cleanup(LADSPA_Handle handle)724 void Plugin::cleanup(LADSPA_Handle handle)
725 {
726 delete (Plugin *)handle;
727 }
728
getProgram(LADSPA_Handle handle,unsigned long index)729 const DSSI_Program_Descriptor* Plugin::getProgram(LADSPA_Handle handle, unsigned long index)
730 {
731 if (index == 0)
732 {
733 return &g_program_descriptor;
734 }
735 else
736 {
737 return NULL;
738 }
739 }
740
selectProgram(LADSPA_Handle handle,unsigned long bank,unsigned long program)741 void Plugin::selectProgram(LADSPA_Handle handle, unsigned long bank, unsigned long program)
742 {
743 Plugin *plugin = (Plugin *)handle;
744 for (size_t i = 0; i < plugin->m_controls.size(); i++)
745 {
746 *(plugin->m_controls[i]) = plugin->m_control_defaults[i];
747 }
748 }
749
getMidiController(LADSPA_Handle,unsigned long port)750 int Plugin::getMidiController(LADSPA_Handle, unsigned long port)
751 {
752 // TODO this is where we need to map MIDI controllers to ports
753 return DSSI_NONE;
754 }
755
updateControlZones()756 void Plugin::updateControlZones()
757 {
758 for (size_t i = 0; i < m_controls.size(); i++)
759 {
760 for (size_t j = 0; j < m_voices.size(); j++)
761 {
762 *(m_voices[j]->m_controls[i]) = *(m_controls[i]);
763 }
764 }
765 }
766
runSynth(LADSPA_Handle handle,unsigned long samples,snd_seq_event_t * events,unsigned long eventCount)767 void Plugin::runSynth(LADSPA_Handle handle, unsigned long samples, snd_seq_event_t *events, unsigned long eventCount)
768 {
769 Plugin *plugin = (Plugin *)handle;
770 plugin->updateControlZones();
771 plugin->runImpl(samples, events, eventCount);
772 }
773
runImpl(unsigned long sampleCount,snd_seq_event_t * events,unsigned long eventCount)774 void Plugin::runImpl(unsigned long sampleCount, snd_seq_event_t *events, unsigned long eventCount)
775 {
776 unsigned long pos;
777 unsigned long count;
778 unsigned long eventPos;
779 snd_seq_ev_note_t n;
780
781 size_t voice_index;
782
783 pos = 0;
784 eventPos = 0;
785 while (pos < sampleCount)
786 {
787 while ((eventPos < eventCount) &&
788 (pos >= events[eventPos].time.tick))
789 {
790 switch (events[eventPos].type)
791 {
792 case SND_SEQ_EVENT_NOTEON:
793 n = events[eventPos].data.note;
794 if (n.velocity > 0)
795 {
796 // Look for the next free voice:
797 if (!voice_free.empty())
798 {
799 // Get the index of the first free string and remove it from the list
800 voice_index = voice_free.front();
801 voice_free.pop_front();
802
803 // Play the note on that voice
804 voice_notes[voice_index] = n.note;
805 float freq = 440.0f * powf(2.0f,(((float)n.note)-69.0f)/12.0f);
806 float gain = n.velocity/127.0f;
807 setFreq(freq, voice_index); // Hz - requires Faust control-signal "freq"
808 setGain(gain, voice_index); // 0-1 - requires Faust control-signal "gain"
809 setGate(1.0f, voice_index); // 0 or 1 - requires Faust button-signal "gate"
810 }
811 }
812 break;
813 case SND_SEQ_EVENT_NOTEOFF:
814 for (voice_index = 0; voice_index < voice_notes.size(); voice_index++)
815 {
816 if (voice_notes[voice_index] == events[eventPos].data.note.note)
817 {
818 setGate(0, voice_index);
819 // TODO using -1 to represent nothing; think of a more clear way to do this
820 voice_notes[voice_index] = -1;
821 voice_free.push_back(voice_index);
822 }
823 }
824 break;
825 default:
826 break;
827 }
828 ++eventPos;
829 }
830
831 if ((eventPos < eventCount) && (events[eventPos].time.tick < sampleCount))
832 {
833 count = events[eventPos].time.tick - pos;
834 }
835 else
836 {
837 count = sampleCount - pos;
838 }
839
840 addSamples(count);
841 pos += count;
842 }
843 }
844
addSamples(size_t samples)845 void Plugin::addSamples(size_t samples)
846 {
847 size_t i;
848 size_t j;
849 size_t v;
850
851 // TODO this isn't very efficient right now...
852
853 // Grow temp buffers to appropriate size / zero them:
854 for (i = 0; i < m_temp_outputs.size(); i++)
855 {
856 for (j = 0; j < samples; j++)
857 {
858 if (m_temp_outputs[i].size() < (j+1))
859 {
860 m_temp_outputs[i].push_back(0.0);
861 }
862 else
863 {
864 m_temp_outputs[i][j] = 0.0;
865 }
866 }
867 }
868
869 AVOIDDENORMALS;
870
871 // Add all voices together
872 for (v = 0; v < m_voices.size(); v++)
873 {
874 m_voices[v]->m_mydsp->compute(samples, &m_inputs[0], &m_outputs[0]);
875
876 // Accumulate in temp buffer
877 for (i = 0; i < m_outputs.size(); i++)
878 {
879 for (j = 0; j < samples; j++)
880 {
881 m_temp_outputs[i][j] += m_outputs[i][j];
882 }
883 }
884 }
885
886 // Transfer accumulator to actual outputs
887 for (i = 0; i < m_outputs.size(); i++)
888 {
889 for (j = 0; j < samples; j++)
890 {
891 // TODO find a better way to protect against clipping.
892 m_outputs[i][j] = m_temp_outputs[i][j] / (float)MAX_POLYPHONY;
893 }
894 }
895 }
896
setFreq(float val,int voice)897 void Plugin::setFreq(float val, int voice)
898 {
899 m_voices[voice]->setFreq(val);
900 }
901
setGate(float val,int voice)902 void Plugin::setGate(float val, int voice)
903 {
904 m_voices[voice]->setGate(val);
905 }
906
setGain(float val,int voice)907 void Plugin::setGain(float val, int voice)
908 {
909 m_voices[voice]->setGain(val);
910 }
911
addButton(const char * label,float * zone)912 void Plugin::addButton(const char* label, float* zone)
913 {
914 if (!ckAnyMatch(label, "gate"))
915 {
916 add_control_with_default(0);
917 }
918 }
919
addToggleButton(const char * label,float * zone)920 void Plugin::addToggleButton(const char* label, float* zone)
921 {
922 add_control_with_default(0);
923 }
924
addCheckButton(const char * label,float * zone)925 void Plugin::addCheckButton(const char* label, float* zone)
926 {
927 add_control_with_default(0);
928 }
929
addVerticalSlider(const char * label,float * zone,float init,float min,float max,float step)930 void Plugin::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
931 {
932 if (!ckAllMatches(label))
933 {
934 add_control_with_default(init);
935 }
936 }
addHorizontalSlider(const char * label,float * zone,float init,float min,float max,float step)937 void Plugin::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
938 {
939 if (!ckAllMatches(label))
940 {
941 add_control_with_default(init);
942 }
943 }
addNumEntry(const char * label,float * zone,float init,float min,float max,float step)944 void Plugin::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
945 {
946 if (!ckAllMatches(label))
947 {
948 add_control_with_default(init);
949 }
950 }
951
addNumDisplay(const char * label,float * zone,int precision)952 void Plugin::addNumDisplay(const char* label, float* zone, int precision)
953 {
954 add_control_with_default(0);
955 }
addTextDisplay(const char * label,float * zone,char * names[],float min,float max)956 void Plugin::addTextDisplay(const char* label, float* zone, char* names[], float min, float max)
957 {
958 add_control_with_default(0);
959 }
addHorizontalBargraph(const char * label,float * zone,float min,float max)960 void Plugin::addHorizontalBargraph(const char* label, float* zone, float min, float max)
961 {
962 add_control_with_default(min);
963 }
addVerticalBargraph(const char * label,float * zone,float min,float max)964 void Plugin::addVerticalBargraph(const char* label, float* zone, float min, float max)
965 {
966 add_control_with_default(min);
967 }
968
openFrameBox(const char * label)969 void Plugin::openFrameBox(const char* label)
970 {
971 }
openTabBox(const char * label)972 void Plugin::openTabBox(const char* label)
973 {
974 }
openHorizontalBox(const char * label)975 void Plugin::openHorizontalBox(const char* label)
976 {
977 }
openVerticalBox(const char * label)978 void Plugin::openVerticalBox(const char* label)
979 {
980 }
closeBox()981 void Plugin::closeBox()
982 {
983 }
984
show()985 void Plugin::show()
986 {
987 }
run()988 void Plugin::run()
989 {
990 }
991
ckAnyMatch(const char * label,const char * indexName)992 bool Plugin::ckAnyMatch(const char* label, const char* indexName)
993 {
994 // TODO do case-insensitive here...
995 if (strcmp(label,indexName)==0)
996 {
997 return true;
998 }
999 return false;
1000 }
ckAllMatches(const char * label)1001 bool Plugin::ckAllMatches(const char* label)
1002 {
1003 bool result = false;
1004 result = result || ckAnyMatch(label,"gain");
1005 result = result || ckAnyMatch(label,"gate");
1006 result = result || ckAnyMatch(label,"freq");
1007 return result;
1008 }
add_control_with_default(float default_value)1009 void Plugin::add_control_with_default(float default_value)
1010 {
1011 // TODO may need to consider different things for input and output controls...
1012 m_controls.push_back(0);
1013 m_control_defaults.push_back(default_value);
1014 }
1015
1016
1017 ////////////////////////////////////////////////////////////////////////////////
1018 // Voice methods
1019 ////////////////////////////////////////////////////////////////////////////////
Voice(int sampleRate)1020 Voice::Voice(int sampleRate) :
1021 m_samplerate(sampleRate),
1022 m_freq_zone(0),
1023 m_gate_zone(0),
1024 m_gain_zone(0)
1025 {
1026 m_mydsp = new mydsp();
1027 }
1028
~Voice()1029 Voice::~Voice()
1030 {
1031 delete m_mydsp;
1032 }
1033
setFreq(float val)1034 void Voice::setFreq(float val)
1035 {
1036 if (m_freq_zone)
1037 {
1038 *m_freq_zone = val;
1039 }
1040 }
1041
setGate(float val)1042 void Voice::setGate(float val)
1043 {
1044 if (m_gate_zone)
1045 {
1046 *m_gate_zone = val;
1047 }
1048 }
1049
setGain(float val)1050 void Voice::setGain(float val)
1051 {
1052 if (m_gain_zone)
1053 {
1054 *m_gain_zone = val;
1055 }
1056 }
1057
addButton(const char * label,float * zone)1058 void Voice::addButton(const char* label, float* zone)
1059 {
1060 if (!ckAnyMatch(label, "gate", &m_gate_zone, zone))
1061 {
1062 addZone(zone);
1063 }
1064 }
1065
addToggleButton(const char * label,float * zone)1066 void Voice::addToggleButton(const char* label, float* zone)
1067 {
1068 addZone(zone);
1069 }
1070
addCheckButton(const char * label,float * zone)1071 void Voice::addCheckButton(const char* label, float* zone)
1072 {
1073 addZone(zone);
1074 }
1075
addVerticalSlider(const char * label,float * zone,float init,float min,float max,float step)1076 void Voice::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
1077 {
1078 if (!ckAllMatches(label, zone))
1079 {
1080 addZone(zone);
1081 }
1082 }
addHorizontalSlider(const char * label,float * zone,float init,float min,float max,float step)1083 void Voice::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
1084 {
1085 if (!ckAllMatches(label, zone))
1086 {
1087 addZone(zone);
1088 }
1089 }
addNumEntry(const char * label,float * zone,float init,float min,float max,float step)1090 void Voice::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
1091 {
1092 if (!ckAllMatches(label, zone))
1093 {
1094 addZone(zone);
1095 }
1096 }
1097
addNumDisplay(const char * label,float * zone,int precision)1098 void Voice::addNumDisplay(const char* label, float* zone, int precision)
1099 {
1100 addZone(zone);
1101 }
addTextDisplay(const char * label,float * zone,char * names[],float min,float max)1102 void Voice::addTextDisplay(const char* label, float* zone, char* names[], float min, float max)
1103 {
1104 addZone(zone);
1105 }
addHorizontalBargraph(const char * label,float * zone,float min,float max)1106 void Voice::addHorizontalBargraph(const char* label, float* zone, float min, float max)
1107 {
1108 addZone(zone);
1109 }
addVerticalBargraph(const char * label,float * zone,float min,float max)1110 void Voice::addVerticalBargraph(const char* label, float* zone, float min, float max)
1111 {
1112 addZone(zone);
1113 }
1114
openFrameBox(const char * label)1115 void Voice::openFrameBox(const char* label)
1116 {
1117 }
openTabBox(const char * label)1118 void Voice::openTabBox(const char* label)
1119 {
1120 }
openHorizontalBox(const char * label)1121 void Voice::openHorizontalBox(const char* label)
1122 {
1123 }
openVerticalBox(const char * label)1124 void Voice::openVerticalBox(const char* label)
1125 {
1126 }
closeBox()1127 void Voice::closeBox()
1128 {
1129 }
1130
show()1131 void Voice::show()
1132 {
1133 }
run()1134 void Voice::run()
1135 {
1136 }
1137
ckAnyMatch(const char * label,const char * indexName,float ** zone,float * newZone)1138 bool Voice::ckAnyMatch(const char* label, const char* indexName, float **zone, float* newZone)
1139 {
1140 // TODO do case-insensitive here...
1141 if (strcmp(label,indexName)==0)
1142 {
1143 *zone = newZone;
1144 return true;
1145 }
1146 return false;
1147 }
ckAllMatches(const char * label,float * zone)1148 bool Voice::ckAllMatches(const char* label, float* zone)
1149 {
1150 bool result = false;
1151 result = result || ckAnyMatch(label,"gain", &m_gain_zone, zone);
1152 result = result || ckAnyMatch(label,"gate", &m_gate_zone, zone);
1153 result = result || ckAnyMatch(label,"freq", &m_freq_zone, zone);
1154 return result;
1155 }
addZone(float * zone)1156 void Voice::addZone(float* zone)
1157 {
1158 m_controls.push_back(zone);
1159 }
1160
1161
1162 ////////////////////////////////////////////////////////////////////////////////
1163 // Shared object hooks
1164 ////////////////////////////////////////////////////////////////////////////////
1165 extern "C"
1166 {
1167
1168 #ifdef __GNUC__
init(void)1169 __attribute__((constructor)) void init(void)
1170 #else
1171 void _init(void)
1172 #endif
1173 {
1174 AVOIDDENORMALS;
1175
1176 if (g_dssi_descriptor == 0)
1177 {
1178 g_ladspa_descriptor = new LADSPA_Descriptor();
1179 g_dssi_descriptor = new DSSI_Descriptor();
1180
1181 mydsp* temp_mydsp = new mydsp();
1182 DescriptorUI* temp_descriptor_ui = new DescriptorUI(temp_mydsp->getNumInputs(), temp_mydsp->getNumOutputs());
1183 temp_mydsp->buildUserInterface(temp_descriptor_ui);
1184
1185 // Fill the descriptors:
1186 // TODO figure out strdup with const strings
1187 g_ladspa_descriptor->UniqueID = 0;
1188 g_ladspa_descriptor->Label = get_metadata_if_exists("name", g_name);
1189 g_ladspa_descriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
1190 g_ladspa_descriptor->Name = get_metadata_if_exists("name", g_name);
1191 g_ladspa_descriptor->Maker = get_metadata_if_exists("author", "Maker");
1192 g_ladspa_descriptor->Copyright = get_metadata_if_exists("copyright", "Copyright");
1193 g_ladspa_descriptor->PortCount = g_port_descriptors.size();
1194 g_ladspa_descriptor->PortDescriptors = &g_port_descriptors[0];
1195 g_ladspa_descriptor->PortNames = &g_port_names[0];
1196 g_ladspa_descriptor->PortRangeHints = &g_port_range_hints[0];
1197 g_ladspa_descriptor->ImplementationData = 0;
1198 g_ladspa_descriptor->instantiate = Plugin::instantiate;
1199 g_ladspa_descriptor->connect_port = Plugin::connectPort;
1200 g_ladspa_descriptor->activate = Plugin::activate;
1201 g_ladspa_descriptor->run = Plugin::run;
1202 g_ladspa_descriptor->run_adding = 0;
1203 g_ladspa_descriptor->set_run_adding_gain = 0;
1204 g_ladspa_descriptor->deactivate = 0;
1205 g_ladspa_descriptor->cleanup = Plugin::cleanup;
1206
1207 g_dssi_descriptor->DSSI_API_Version = 1;
1208 g_dssi_descriptor->LADSPA_Plugin = g_ladspa_descriptor;
1209 g_dssi_descriptor->configure = 0;
1210 g_dssi_descriptor->get_program = Plugin::getProgram;
1211 g_dssi_descriptor->select_program = Plugin::selectProgram;
1212 g_dssi_descriptor->get_midi_controller_for_port = Plugin::getMidiController;
1213 g_dssi_descriptor->run_synth = Plugin::runSynth;
1214 g_dssi_descriptor->run_synth_adding = 0;
1215 g_dssi_descriptor->run_multiple_synths = 0;
1216 g_dssi_descriptor->run_multiple_synths_adding = 0;
1217
1218 // Program description (TODO if we eventually support multiple programs we will need to handle this differently)
1219 g_program_descriptor.Bank = 0;
1220 g_program_descriptor.Program = 0;
1221 g_program_descriptor.Name = get_metadata_if_exists("name", g_name);
1222
1223 delete temp_mydsp;
1224 delete temp_descriptor_ui;
1225 }
1226 }
1227
1228 #ifdef __GNUC__
fini(void)1229 __attribute__((destructor)) void fini(void)
1230 #else
1231 void _fini()
1232 #endif
1233 {
1234 if (g_ladspa_descriptor)
1235 {
1236 delete g_ladspa_descriptor;
1237 }
1238 if (g_dssi_descriptor)
1239 {
1240 delete g_dssi_descriptor;
1241 }
1242 }
1243
ladspa_descriptor(unsigned long index)1244 const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
1245 {
1246 return 0;
1247 }
1248
dssi_descriptor(unsigned long index)1249 const DSSI_Descriptor *dssi_descriptor(unsigned long index)
1250 {
1251 if (index == 0)
1252 {
1253 return g_dssi_descriptor;
1254 }
1255 else
1256 {
1257 return NULL;
1258 }
1259 }
1260 }
1261
1262 /******************** END dssi.cpp ****************/
1263