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