9aef8af02f22d0eb2a0380c2be0166340388529a
[Faustine.git] / interpretor / preprocessor / faust-0.9.47mr3 / architecture / dssi.cpp
1 /************************************************************************
2 IMPORTANT NOTE : this file contains two clearly delimited
3 sections : the ARCHITECTURE section (in two parts) and the
4 USER section. Each section is governed by its own copyright
5 and license. Please check individually each section for
6 license and copyright information.
7 *************************************************************************/
8
9 /*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/
10
11 /************************************************************************
12 FAUST Architecture File
13 Copyright (C) 2011 Michael J. Wilson
14
15 ---------------------------------------------------------------------
16 This Architecture section is free software; you can redistribute
17 it and/or modify it under the terms of the GNU General Public
18 License as published by the Free Software Foundation; either
19 version 3 of the License, or (at your option) any later version.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; If not, see <http://www.gnu.org/licenses/>.
28
29 EXCEPTION : As a special exception, you may create a larger work
30 that contains this FAUST architecture section and distribute that
31 work under terms of your choice, so long as this FAUST
32 architecture section is not modified.
33 ---------------------------------------------------------------------
34
35 ************************************************************************/
36
37 /********************************************************************
38 * dssi.cpp - Polyphonic dssi wrapper for the FAUST language.
39 *
40 * Usage: faust -a dssi.cpp myfaustprog.dsp
41 *
42 * By Michael J. Wilson (mwilson@alumni.caltech.edu)
43 *
44 * Made with reference to:
45 * - vsti-mono.cpp by Julius Smith (http://ccrma.stanford.edu/~jos/)
46 * - ladspa.cpp by GRAME, Centre National de Creation Musicale
47 * - karplong.cpp by Chris Cannam, Steve Harris, Sean Bolton
48 *
49 * Because of the inclusion of code from ladspa.cpp, this architecture
50 * file is also released under the GNU General Public Licenses version
51 * 3. Sections which were taken from ladspa.cpp are clearly marked
52 * below, in order to trace the GPL dependency.
53 * As with faust2pd and vsti-mono.cpp, to obtain MIDI control via
54 * NoteOn/Off, Velocity, and KeyNumber, there must be a button named
55 * "gate" and sliders (or numeric entries) named "gain" and "freq" in
56 * the Faust patch specified in myfaustprog.dsp.
57 *
58 * FAUST
59 * Copyright (C) 2003-2007 GRAME, Centre National de Creation Musicale
60 * http://www.grame.fr/
61 *
62 ********************************************************************/
63
64 #include "dssi.h"
65 #include "ladspa.h"
66
67 #include <math.h>
68 #include <stdlib.h>
69 #include <string.h>
70
71 #include <stack>
72 #include <string>
73 #include <map>
74 #include <vector>
75 #include <list>
76
77 // On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
78 // flags to avoid costly denormals
79 #ifdef __SSE__
80 #include <xmmintrin.h>
81 #ifdef __SSE2__
82 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
83 #else
84 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
85 #endif
86 #else
87 #warning *** dssi.cpp: NO SSE FLAG (denormals may slow things down) ***
88 #define AVOIDDENORMALS
89 #endif
90
91 struct Meta : std::map<const char*, const char*>
92 {
93 void declare (const char* key, const char* value) { (*this)[key]=value; }
94 };
95
96 #define max(x,y) (((x)>(y)) ? (x) : (y))
97 #define min(x,y) (((x)<(y)) ? (x) : (y))
98
99 #define sym(name) xsym(name)
100 #define xsym(name) #name
101
102 inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }
103 inline int int2pow2 (int x) { int r=0; while ((1<<r)<x) r++; return r; }
104
105 /******************************************************************************
106 *******************************************************************************
107
108 VECTOR INTRINSICS
109
110 *******************************************************************************
111 *******************************************************************************/
112
113 <<includeIntrinsic>>
114
115 //---------------------Abstract User Interface--------------------
116 //
117 // Abstract definition of a User Interface to be passed to the
118 // buildUserInterface method of a Faust Signal Processor
119 //
120 //----------------------------------------------------------------
121 class UI
122 {
123 bool fStopped;
124
125 public:
126 UI() : fStopped(false) {}
127 virtual ~UI() {}
128
129 virtual void addButton(const char* label, float* zone) = 0;
130 virtual void addToggleButton(const char* label, float* zone) = 0;
131 virtual void addCheckButton(const char* label, float* zone) = 0;
132 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
133 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0;
134 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step) = 0;
135
136 // -- passive widgets
137 virtual void addNumDisplay(const char* label, float* zone, int precision) = 0;
138 virtual void addTextDisplay(const char* label, float* zone, char* names[], float min, float max) = 0;
139 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max) = 0;
140 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max) = 0;
141
142 // -- frames and labels
143 virtual void openFrameBox(const char* label) = 0;
144 virtual void openTabBox(const char* label) = 0;
145 virtual void openHorizontalBox(const char* label) = 0;
146 virtual void openVerticalBox(const char* label) = 0;
147 virtual void closeBox() = 0;
148
149 virtual void show() = 0;
150 virtual void run() = 0;
151
152 void stop() { fStopped = true; }
153 bool stopped() { return fStopped; }
154
155 virtual void declare(float* zone, const char* key, const char* value) {}
156 };
157
158 //------------------Abstract Signal Processor---------------------
159 //
160 // Abstract definition of a Faust Signal Processor
161 //
162 //----------------------------------------------------------------
163 class dsp
164 {
165 protected:
166 int fSamplingFreq;
167 public:
168 dsp() {}
169 virtual ~dsp() {}
170 virtual int getNumInputs() = 0;
171 virtual int getNumOutputs() = 0;
172 virtual void buildUserInterface(UI* interface) = 0;
173 virtual void init(int samplingRate) = 0;
174 virtual void compute(int len, float** inputs, float** outputs)= 0;
175 };
176
177 /********************END ARCHITECTURE SECTION (part 1/2)****************/
178
179 /**************************BEGIN USER SECTION **************************/
180
181 <<includeclass>>
182
183 /***************************END USER SECTION ***************************/
184
185 /*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/
186
187 ////////////////////////////////////////////////////////////////////////////////
188 // Forward declarations
189 ////////////////////////////////////////////////////////////////////////////////
190 class Plugin;
191 class DescriptorUI;
192 class Voice;
193
194 ////////////////////////////////////////////////////////////////////////////////
195 // Global data
196 ////////////////////////////////////////////////////////////////////////////////
197 // Maximum polyphony, must be at least 1 (TODO make this configurable at compile / runtime?)
198 const int MAX_POLYPHONY = 64;
199 // Descriptor
200 DSSI_Descriptor* g_dssi_descriptor;
201 // Additional data for descriptor:
202 LADSPA_Descriptor* g_ladspa_descriptor;
203 // Program descriptor:
204 DSSI_Program_Descriptor g_program_descriptor;
205
206 // Global data for the descriptor:
207 std::vector<LADSPA_PortDescriptor> g_port_descriptors;
208 std::vector<LADSPA_PortRangeHint> g_port_range_hints;
209 std::vector<const char*> g_port_names;
210 std::string g_name;
211
212 ////////////////////////////////////////////////////////////////////////////////
213 // The enclosed code is from ladspa.cpp
214 // TODO groups of port names
215 static const char* inames[] =
216 {
217 "input00", "input01", "input02", "input03", "input04",
218 "input05", "input06", "input07", "input08", "input09",
219 "input10", "input11", "input12", "input13", "input14",
220 "input15", "input16", "input17", "input18", "input19",
221 "input20", "input21", "input22", "input23", "input24",
222 "input25", "input26", "input27", "input28", "input29",
223 "input30", "input31", "input32", "input33", "input34",
224 "input35", "input36", "input37", "input38", "input39"
225 };
226 static const char* onames[] =
227 {
228 "output00", "output01", "output02", "output03", "output04",
229 "output05", "output06", "output07", "output08", "output09",
230 "output10", "output11", "output12", "output13", "output14",
231 "output15", "output16", "output17", "output18", "output19",
232 "output20", "output21", "output22", "output23", "output24",
233 "output25", "output26", "output27", "output28", "output29",
234 "output30", "output31", "output32", "output33", "output34",
235 "output35", "output36", "output37", "output38", "output39"
236 };
237 // END code from ladspa.cpp
238 ////////////////////////////////////////////////////////////////////////////////
239
240 ////////////////////////////////////////////////////////////////////////////////
241 // Global helper functions
242 ////////////////////////////////////////////////////////////////////////////////
243 char* get_metadata_if_exists(const char* key, const char* default_string)
244 {
245 // TODO probably want to free these somehow. Currently only used for ladspa descriptor
246 Meta meta;
247 mydsp::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
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(int);
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
478 virtual void addButton(const char* label, float* zone) {
479 if (!ckAnyMatch(label, "gate"))
480 {
481 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
482 }
483 }
484 virtual void addToggleButton(const char* label, float* zone) {
485 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
486 }
487 virtual void addCheckButton(const char* label, float* zone) {
488 addPortDescr(ICONTROL, label, LADSPA_HINT_TOGGLED);
489 }
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 }
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 }
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 }
508 virtual void addNumDisplay(const char* label, float* zone, int precision) {
509 addPortDescr(OCONTROL, label, 0, -10000, +10000);
510 }
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 }
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 }
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
521 virtual void openFrameBox(const char* label) { openAnyBox(label); }
522 virtual void openTabBox(const char* label) { openAnyBox(label); }
523 virtual void openHorizontalBox(const char* label) { openAnyBox(label); }
524 virtual void openVerticalBox(const char* label) { openAnyBox(label); }
525
526 virtual void closeBox() { fPrefix.pop(); }
527
528 virtual void show() {}
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
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 }
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 ////////////////////////////////////////////////////////////////////////////////
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
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
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 ////////////////////////////////////////////////////////////////////////////////
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
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
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
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
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
719 void Plugin::run(LADSPA_Handle handle, unsigned long samples)
720 {
721 runSynth(handle, samples, 0, 0);
722 }
723
724 void Plugin::cleanup(LADSPA_Handle handle)
725 {
726 delete (Plugin *)handle;
727 }
728
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
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
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
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
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
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
845 void Plugin::addSamples(int samples)
846 {
847 size_t i;
848 int 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
897 void Plugin::setFreq(float val, int voice)
898 {
899 m_voices[voice]->setFreq(val);
900 }
901
902 void Plugin::setGate(float val, int voice)
903 {
904 m_voices[voice]->setGate(val);
905 }
906
907 void Plugin::setGain(float val, int voice)
908 {
909 m_voices[voice]->setGain(val);
910 }
911
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
920 void Plugin::addToggleButton(const char* label, float* zone)
921 {
922 add_control_with_default(0);
923 }
924
925 void Plugin::addCheckButton(const char* label, float* zone)
926 {
927 add_control_with_default(0);
928 }
929
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 }
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 }
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
952 void Plugin::addNumDisplay(const char* label, float* zone, int precision)
953 {
954 add_control_with_default(0);
955 }
956 void Plugin::addTextDisplay(const char* label, float* zone, char* names[], float min, float max)
957 {
958 add_control_with_default(0);
959 }
960 void Plugin::addHorizontalBargraph(const char* label, float* zone, float min, float max)
961 {
962 add_control_with_default(min);
963 }
964 void Plugin::addVerticalBargraph(const char* label, float* zone, float min, float max)
965 {
966 add_control_with_default(min);
967 }
968
969 void Plugin::openFrameBox(const char* label)
970 {
971 }
972 void Plugin::openTabBox(const char* label)
973 {
974 }
975 void Plugin::openHorizontalBox(const char* label)
976 {
977 }
978 void Plugin::openVerticalBox(const char* label)
979 {
980 }
981 void Plugin::closeBox()
982 {
983 }
984
985 void Plugin::show()
986 {
987 }
988 void Plugin::run()
989 {
990 }
991
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 }
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 }
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 ////////////////////////////////////////////////////////////////////////////////
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
1029 Voice::~Voice()
1030 {
1031 delete m_mydsp;
1032 }
1033
1034 void Voice::setFreq(float val)
1035 {
1036 if (m_freq_zone)
1037 {
1038 *m_freq_zone = val;
1039 }
1040 }
1041
1042 void Voice::setGate(float val)
1043 {
1044 if (m_gate_zone)
1045 {
1046 *m_gate_zone = val;
1047 }
1048 }
1049
1050 void Voice::setGain(float val)
1051 {
1052 if (m_gain_zone)
1053 {
1054 *m_gain_zone = val;
1055 }
1056 }
1057
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
1066 void Voice::addToggleButton(const char* label, float* zone)
1067 {
1068 addZone(zone);
1069 }
1070
1071 void Voice::addCheckButton(const char* label, float* zone)
1072 {
1073 addZone(zone);
1074 }
1075
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 }
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 }
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
1098 void Voice::addNumDisplay(const char* label, float* zone, int precision)
1099 {
1100 addZone(zone);
1101 }
1102 void Voice::addTextDisplay(const char* label, float* zone, char* names[], float min, float max)
1103 {
1104 addZone(zone);
1105 }
1106 void Voice::addHorizontalBargraph(const char* label, float* zone, float min, float max)
1107 {
1108 addZone(zone);
1109 }
1110 void Voice::addVerticalBargraph(const char* label, float* zone, float min, float max)
1111 {
1112 addZone(zone);
1113 }
1114
1115 void Voice::openFrameBox(const char* label)
1116 {
1117 }
1118 void Voice::openTabBox(const char* label)
1119 {
1120 }
1121 void Voice::openHorizontalBox(const char* label)
1122 {
1123 }
1124 void Voice::openVerticalBox(const char* label)
1125 {
1126 }
1127 void Voice::closeBox()
1128 {
1129 }
1130
1131 void Voice::show()
1132 {
1133 }
1134 void Voice::run()
1135 {
1136 }
1137
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 }
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 }
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__
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.c_str());
1189 g_ladspa_descriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
1190 g_ladspa_descriptor->Name = get_metadata_if_exists("name", g_name.c_str());
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.c_str());
1222
1223 delete temp_mydsp;
1224 delete temp_descriptor_ui;
1225 }
1226 }
1227
1228 #ifdef __GNUC__
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
1244 const LADSPA_Descriptor *ladspa_descriptor(unsigned long index)
1245 {
1246 return 0;
1247 }
1248
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 ARCHITECTURE SECTION (part 2/2)****************/