1 /* ------------------------------------------------------------
2 name: "harm_trem_ext"
3 Code generated with Faust 2.20.2 (https://faust.grame.fr)
4 Compilation options: -lang cpp -scal -ftz 0
5 ------------------------------------------------------------ */
6
7 #ifndef __harm_trem_ext_H__
8 #define __harm_trem_ext_H__
9
10 /************************************************************************
11 ************************************************************************
12 FAUST Architecture File
13 Copyright (C) 2009-2016 Albert Graef <aggraef@gmail.com>
14 ---------------------------------------------------------------------
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU Lesser General Public License as
17 published by the Free Software Foundation; either version 3 of the
18 License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Lesser General Public License for more details.
24
25 You should have received a copy of the GNU Lesser General Public
26 License along with the GNU C Library; if not, write to the Free
27 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 02111-1307 USA.
29 ************************************************************************
30 ************************************************************************/
31
32 /* LV2 architecture for Faust synths. */
33
34 /* NOTE: This requires one of the Boost headers (boost/circular_buffer.hpp),
35 so to compile Faust programs created with this architecture you need to
36 have at least the Boost headers installed somewhere on your include path
37 (the Boost libraries aren't needed). */
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 #include <list>
43 #include <map>
44 #include <set>
45
46 // generic Faust dsp and UI classes
47 /************************** BEGIN dsp.h **************************/
48 /************************************************************************
49 FAUST Architecture File
50 Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
51 ---------------------------------------------------------------------
52 This Architecture section is free software; you can redistribute it
53 and/or modify it under the terms of the GNU General Public License
54 as published by the Free Software Foundation; either version 3 of
55 the License, or (at your option) any later version.
56
57 This program is distributed in the hope that it will be useful,
58 but WITHOUT ANY WARRANTY; without even the implied warranty of
59 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60 GNU General Public License for more details.
61
62 You should have received a copy of the GNU General Public License
63 along with this program; If not, see <http://www.gnu.org/licenses/>.
64
65 EXCEPTION : As a special exception, you may create a larger work
66 that contains this FAUST architecture section and distribute
67 that work under terms of your choice, so long as this FAUST
68 architecture section is not modified.
69 ************************************************************************/
70
71 #ifndef __dsp__
72 #define __dsp__
73
74 #include <string>
75 #include <vector>
76
77 #ifndef FAUSTFLOAT
78 #define FAUSTFLOAT float
79 #endif
80
81 class UI;
82 struct Meta;
83
84 /**
85 * DSP memory manager.
86 */
87
88 struct dsp_memory_manager {
89
~dsp_memory_managerdsp_memory_manager90 virtual ~dsp_memory_manager() {}
91
92 virtual void* allocate(size_t size) = 0;
93 virtual void destroy(void* ptr) = 0;
94
95 };
96
97 /**
98 * Signal processor definition.
99 */
100
101 class dsp {
102
103 public:
104
dsp()105 dsp() {}
~dsp()106 virtual ~dsp() {}
107
108 /* Return instance number of audio inputs */
109 virtual int getNumInputs() = 0;
110
111 /* Return instance number of audio outputs */
112 virtual int getNumOutputs() = 0;
113
114 /**
115 * Trigger the ui_interface parameter with instance specific calls
116 * to 'addBtton', 'addVerticalSlider'... in order to build the UI.
117 *
118 * @param ui_interface - the user interface builder
119 */
120 virtual void buildUserInterface(UI* ui_interface) = 0;
121
122 /* Returns the sample rate currently used by the instance */
123 virtual int getSampleRate() = 0;
124
125 /**
126 * Global init, calls the following methods:
127 * - static class 'classInit': static tables initialization
128 * - 'instanceInit': constants and instance state initialization
129 *
130 * @param sample_rate - the sampling rate in Hertz
131 */
132 virtual void init(int sample_rate) = 0;
133
134 /**
135 * Init instance state
136 *
137 * @param sample_rate - the sampling rate in Hertz
138 */
139 virtual void instanceInit(int sample_rate) = 0;
140
141 /**
142 * Init instance constant state
143 *
144 * @param sample_rate - the sampling rate in Hertz
145 */
146 virtual void instanceConstants(int sample_rate) = 0;
147
148 /* Init default control parameters values */
149 virtual void instanceResetUserInterface() = 0;
150
151 /* Init instance state (delay lines...) */
152 virtual void instanceClear() = 0;
153
154 /**
155 * Return a clone of the instance.
156 *
157 * @return a copy of the instance on success, otherwise a null pointer.
158 */
159 virtual dsp* clone() = 0;
160
161 /**
162 * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata.
163 *
164 * @param m - the Meta* meta user
165 */
166 virtual void metadata(Meta* m) = 0;
167
168 /**
169 * DSP instance computation, to be called with successive in/out audio buffers.
170 *
171 * @param count - the number of frames to compute
172 * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
173 * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad)
174 *
175 */
176 virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0;
177
178 /**
179 * DSP instance computation: alternative method to be used by subclasses.
180 *
181 * @param date_usec - the timestamp in microsec given by audio driver.
182 * @param count - the number of frames to compute
183 * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
184 * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad)
185 *
186 */
compute(double,int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)187 virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); }
188
189 };
190
191 /**
192 * Generic DSP decorator.
193 */
194
195 class decorator_dsp : public dsp {
196
197 protected:
198
199 dsp* fDSP;
200
201 public:
202
decorator_dsp(dsp * dsp=nullptr)203 decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {}
~decorator_dsp()204 virtual ~decorator_dsp() { delete fDSP; }
205
getNumInputs()206 virtual int getNumInputs() { return fDSP->getNumInputs(); }
getNumOutputs()207 virtual int getNumOutputs() { return fDSP->getNumOutputs(); }
buildUserInterface(UI * ui_interface)208 virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); }
getSampleRate()209 virtual int getSampleRate() { return fDSP->getSampleRate(); }
init(int sample_rate)210 virtual void init(int sample_rate) { fDSP->init(sample_rate); }
instanceInit(int sample_rate)211 virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); }
instanceConstants(int sample_rate)212 virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); }
instanceResetUserInterface()213 virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); }
instanceClear()214 virtual void instanceClear() { fDSP->instanceClear(); }
clone()215 virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); }
metadata(Meta * m)216 virtual void metadata(Meta* m) { fDSP->metadata(m); }
217 // Beware: subclasses usually have to overload the two 'compute' methods
compute(int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)218 virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); }
compute(double date_usec,int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)219 virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); }
220
221 };
222
223 /**
224 * DSP factory class.
225 */
226
227 class dsp_factory {
228
229 protected:
230
231 // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory);
~dsp_factory()232 virtual ~dsp_factory() {}
233
234 public:
235
236 virtual std::string getName() = 0;
237 virtual std::string getSHAKey() = 0;
238 virtual std::string getDSPCode() = 0;
239 virtual std::string getCompileOptions() = 0;
240 virtual std::vector<std::string> getLibraryList() = 0;
241 virtual std::vector<std::string> getIncludePathnames() = 0;
242
243 virtual dsp* createDSPInstance() = 0;
244
245 virtual void setMemoryManager(dsp_memory_manager* manager) = 0;
246 virtual dsp_memory_manager* getMemoryManager() = 0;
247
248 };
249
250 /**
251 * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
252 * flags to avoid costly denormals.
253 */
254
255 #ifdef __SSE__
256 #include <xmmintrin.h>
257 #ifdef __SSE2__
258 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
259 #else
260 #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
261 #endif
262 #else
263 #define AVOIDDENORMALS
264 #endif
265
266 #endif
267 /************************** END dsp.h **************************/
268 /************************** BEGIN UI.h **************************/
269 /************************************************************************
270 FAUST Architecture File
271 Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale
272 ---------------------------------------------------------------------
273 This Architecture section is free software; you can redistribute it
274 and/or modify it under the terms of the GNU General Public License
275 as published by the Free Software Foundation; either version 3 of
276 the License, or (at your option) any later version.
277
278 This program is distributed in the hope that it will be useful,
279 but WITHOUT ANY WARRANTY; without even the implied warranty of
280 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
281 GNU General Public License for more details.
282
283 You should have received a copy of the GNU General Public License
284 along with this program; If not, see <http://www.gnu.org/licenses/>.
285
286 EXCEPTION : As a special exception, you may create a larger work
287 that contains this FAUST architecture section and distribute
288 that work under terms of your choice, so long as this FAUST
289 architecture section is not modified.
290 ************************************************************************/
291
292 #ifndef __UI_H__
293 #define __UI_H__
294
295 #ifndef FAUSTFLOAT
296 #define FAUSTFLOAT float
297 #endif
298
299 /*******************************************************************************
300 * UI : Faust DSP User Interface
301 * User Interface as expected by the buildUserInterface() method of a DSP.
302 * This abstract class contains only the method that the Faust compiler can
303 * generate to describe a DSP user interface.
304 ******************************************************************************/
305
306 struct Soundfile;
307
308 template <typename REAL>
309 class UIReal
310 {
311
312 public:
313
UIReal()314 UIReal() {}
~UIReal()315 virtual ~UIReal() {}
316
317 // -- widget's layouts
318
319 virtual void openTabBox(const char* label) = 0;
320 virtual void openHorizontalBox(const char* label) = 0;
321 virtual void openVerticalBox(const char* label) = 0;
322 virtual void closeBox() = 0;
323
324 // -- active widgets
325
326 virtual void addButton(const char* label, REAL* zone) = 0;
327 virtual void addCheckButton(const char* label, REAL* zone) = 0;
328 virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
329 virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
330 virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0;
331
332 // -- passive widgets
333
334 virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
335 virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0;
336
337 // -- soundfiles
338
339 virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0;
340
341 // -- metadata declarations
342
declare(REAL * zone,const char * key,const char * val)343 virtual void declare(REAL* zone, const char* key, const char* val) {}
344 };
345
346 class UI : public UIReal<FAUSTFLOAT>
347 {
348
349 public:
350
UI()351 UI() {}
~UI()352 virtual ~UI() {}
353 };
354
355 #endif
356 /************************** END UI.h **************************/
357
358 using namespace std;
359
360 typedef pair<const char*,const char*> strpair;
361
362 struct Meta : std::map<const char*, const char*>
363 {
declareMeta364 void declare(const char *key, const char *value)
365 {
366 (*this)[key] = value;
367 }
getMeta368 const char* get(const char *key, const char *def)
369 {
370 if (this->find(key) != this->end())
371 return (*this)[key];
372 else
373 return def;
374 }
375 };
376
377 /******************************************************************************
378 *******************************************************************************
379
380 VECTOR INTRINSICS
381
382 *******************************************************************************
383 *******************************************************************************/
384
385
386 /***************************************************************************
387 LV2 UI interface
388 ***************************************************************************/
389
390 enum ui_elem_type_t {
391 UI_BUTTON, UI_CHECK_BUTTON,
392 UI_V_SLIDER, UI_H_SLIDER, UI_NUM_ENTRY,
393 UI_V_BARGRAPH, UI_H_BARGRAPH,
394 UI_END_GROUP, UI_V_GROUP, UI_H_GROUP, UI_T_GROUP
395 };
396
397 struct ui_elem_t {
398 ui_elem_type_t type;
399 const char *label;
400 int port;
401 float *zone;
402 void *ref;
403 float init, min, max, step;
404 };
405
406 class LV2UI : public UI
407 {
408 public:
409 bool is_instr;
410 int nelems, nports;
411 ui_elem_t *elems;
412 map< int, list<strpair> > metadata;
413
414 LV2UI(int maxvoices = 0);
415 virtual ~LV2UI();
416
417 protected:
418 void add_elem(ui_elem_type_t type, const char *label = NULL);
419 void add_elem(ui_elem_type_t type, const char *label, float *zone);
420 void add_elem(ui_elem_type_t type, const char *label, float *zone,
421 float init, float min, float max, float step);
422 void add_elem(ui_elem_type_t type, const char *label, float *zone,
423 float min, float max);
424
425 bool have_freq, have_gain, have_gate;
426 bool is_voice_ctrl(const char *label);
427
428 public:
429 virtual void addButton(const char* label, float* zone);
430 virtual void addCheckButton(const char* label, float* zone);
431 virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step);
432 virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step);
433 virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step);
434
435 virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max);
436 virtual void addVerticalBargraph(const char* label, float* zone, float min, float max);
437
addSoundfile(const char * label,const char * filename,Soundfile ** sf_zone)438 virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {}
439
440 virtual void openTabBox(const char* label);
441 virtual void openHorizontalBox(const char* label);
442 virtual void openVerticalBox(const char* label);
443 virtual void closeBox();
444
445 virtual void run();
446
447 virtual void declare(float* zone, const char* key, const char* value);
448 };
449
LV2UI(int maxvoices)450 LV2UI::LV2UI(int maxvoices)
451 {
452 is_instr = maxvoices>0;
453 have_freq = have_gain = have_gate = false;
454 nelems = nports = 0;
455 elems = NULL;
456 }
457
~LV2UI()458 LV2UI::~LV2UI()
459 {
460 if (elems) free(elems);
461 }
462
declare(float * zone,const char * key,const char * value)463 void LV2UI::declare(float* zone, const char* key, const char* value)
464 {
465 map< int, list<strpair> >::iterator it = metadata.find(nelems);
466 if (it != metadata.end())
467 it->second.push_back(strpair(key, value));
468 else
469 metadata[nelems] = list<strpair>(1, strpair(key, value));
470 }
471
add_elem(ui_elem_type_t type,const char * label)472 inline void LV2UI::add_elem(ui_elem_type_t type, const char *label)
473 {
474 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
475 if (elems1)
476 elems = elems1;
477 else
478 return;
479 elems[nelems].type = type;
480 elems[nelems].label = label;
481 elems[nelems].port = -1;
482 elems[nelems].zone = NULL;
483 elems[nelems].ref = NULL;
484 elems[nelems].init = 0.0;
485 elems[nelems].min = 0.0;
486 elems[nelems].max = 0.0;
487 elems[nelems].step = 0.0;
488 nelems++;
489 }
490
491 #define portno(label) (is_voice_ctrl(label)?-1:nports++)
492
add_elem(ui_elem_type_t type,const char * label,float * zone)493 inline void LV2UI::add_elem(ui_elem_type_t type, const char *label, float *zone)
494 {
495 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
496 if (elems1)
497 elems = elems1;
498 else
499 return;
500 elems[nelems].type = type;
501 elems[nelems].label = label;
502 elems[nelems].port = portno(label);
503 elems[nelems].zone = zone;
504 elems[nelems].ref = NULL;
505 elems[nelems].init = 0.0;
506 elems[nelems].min = 0.0;
507 elems[nelems].max = 0.0;
508 elems[nelems].step = 0.0;
509 nelems++;
510 }
511
add_elem(ui_elem_type_t type,const char * label,float * zone,float init,float min,float max,float step)512 inline void LV2UI::add_elem(ui_elem_type_t type, const char *label, float *zone,
513 float init, float min, float max, float step)
514 {
515 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
516 if (elems1)
517 elems = elems1;
518 else
519 return;
520 elems[nelems].type = type;
521 elems[nelems].label = label;
522 elems[nelems].port = portno(label);
523 elems[nelems].zone = zone;
524 elems[nelems].ref = NULL;
525 elems[nelems].init = init;
526 elems[nelems].min = min;
527 elems[nelems].max = max;
528 elems[nelems].step = step;
529 nelems++;
530 }
531
add_elem(ui_elem_type_t type,const char * label,float * zone,float min,float max)532 inline void LV2UI::add_elem(ui_elem_type_t type, const char *label, float *zone,
533 float min, float max)
534 {
535 ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t));
536 if (elems1)
537 elems = elems1;
538 else
539 return;
540 elems[nelems].type = type;
541 elems[nelems].label = label;
542 elems[nelems].port = portno(label);
543 elems[nelems].zone = zone;
544 elems[nelems].ref = NULL;
545 elems[nelems].init = 0.0;
546 elems[nelems].min = min;
547 elems[nelems].max = max;
548 elems[nelems].step = 0.0;
549 nelems++;
550 }
551
is_voice_ctrl(const char * label)552 inline bool LV2UI::is_voice_ctrl(const char *label)
553 {
554 if (!is_instr)
555 return false;
556 else if (!have_freq && !strcmp(label, "freq"))
557 return (have_freq = true);
558 else if (!have_gain && !strcmp(label, "gain"))
559 return (have_gain = true);
560 else if (!have_gate && !strcmp(label, "gate"))
561 return (have_gate = true);
562 else
563 return false;
564 }
565
addButton(const char * label,float * zone)566 void LV2UI::addButton(const char* label, float* zone)
567 { add_elem(UI_BUTTON, label, zone); }
addCheckButton(const char * label,float * zone)568 void LV2UI::addCheckButton(const char* label, float* zone)
569 { add_elem(UI_CHECK_BUTTON, label, zone); }
addVerticalSlider(const char * label,float * zone,float init,float min,float max,float step)570 void LV2UI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step)
571 { add_elem(UI_V_SLIDER, label, zone, init, min, max, step); }
addHorizontalSlider(const char * label,float * zone,float init,float min,float max,float step)572 void LV2UI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step)
573 { add_elem(UI_H_SLIDER, label, zone, init, min, max, step); }
addNumEntry(const char * label,float * zone,float init,float min,float max,float step)574 void LV2UI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step)
575 { add_elem(UI_NUM_ENTRY, label, zone, init, min, max, step); }
576
addHorizontalBargraph(const char * label,float * zone,float min,float max)577 void LV2UI::addHorizontalBargraph(const char* label, float* zone, float min, float max)
578 { add_elem(UI_H_BARGRAPH, label, zone, min, max); }
addVerticalBargraph(const char * label,float * zone,float min,float max)579 void LV2UI::addVerticalBargraph(const char* label, float* zone, float min, float max)
580 { add_elem(UI_V_BARGRAPH, label, zone, min, max); }
581
openTabBox(const char * label)582 void LV2UI::openTabBox(const char* label)
583 { add_elem(UI_T_GROUP, label); }
openHorizontalBox(const char * label)584 void LV2UI::openHorizontalBox(const char* label)
585 { add_elem(UI_H_GROUP, label); }
openVerticalBox(const char * label)586 void LV2UI::openVerticalBox(const char* label)
587 { add_elem(UI_V_GROUP, label); }
closeBox()588 void LV2UI::closeBox()
589 { add_elem(UI_END_GROUP); }
590
run()591 void LV2UI::run() {}
592
593 //----------------------------------------------------------------------------
594 // FAUST generated signal processor
595 //----------------------------------------------------------------------------
596
597 #ifndef FAUSTFLOAT
598 #define FAUSTFLOAT float
599 #endif
600
601 #include <algorithm>
602 #include <cmath>
603 #include <math.h>
604
harm_trem_ext_faustpower2_f(float value)605 static float harm_trem_ext_faustpower2_f(float value) {
606 return (value * value);
607 }
608
609 #ifndef FAUSTCLASS
610 #define FAUSTCLASS harm_trem_ext
611 #endif
612
613 #ifdef __APPLE__
614 #define exp10f __exp10f
615 #define exp10 __exp10
616 #endif
617
618 class harm_trem_ext : public dsp {
619
620 private:
621
622 float fVec0[2];
623 int fSampleRate;
624 float fConst0;
625 FAUSTFLOAT fVslider0;
626 float fRec2[2];
627 float fRec1[2];
628 float fRec0[3];
629 FAUSTFLOAT fHslider0;
630 float fRec3[2];
631 float fRec5[2];
632 float fRec4[3];
633
634 public:
635
metadata(Meta * m)636 void metadata(Meta* m) {
637 m->declare("analyzers.lib/name", "Faust Analyzer Library");
638 m->declare("analyzers.lib/version", "0.0");
639 m->declare("basics.lib/name", "Faust Basic Element Library");
640 m->declare("basics.lib/version", "0.1");
641 m->declare("filename", "harm_trem_ext.dsp");
642 m->declare("filters.lib/filterbank:author", "Julius O. Smith III");
643 m->declare("filters.lib/filterbank:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
644 m->declare("filters.lib/filterbank:license", "MIT-style STK-4.3 license");
645 m->declare("filters.lib/fir:author", "Julius O. Smith III");
646 m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
647 m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license");
648 m->declare("filters.lib/highpass:author", "Julius O. Smith III");
649 m->declare("filters.lib/highpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
650 m->declare("filters.lib/iir:author", "Julius O. Smith III");
651 m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
652 m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license");
653 m->declare("filters.lib/lowpass0_highpass1", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
654 m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III");
655 m->declare("filters.lib/lowpass:author", "Julius O. Smith III");
656 m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
657 m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license");
658 m->declare("filters.lib/name", "Faust Filters Library");
659 m->declare("filters.lib/tf1:author", "Julius O. Smith III");
660 m->declare("filters.lib/tf1:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
661 m->declare("filters.lib/tf1:license", "MIT-style STK-4.3 license");
662 m->declare("filters.lib/tf1s:author", "Julius O. Smith III");
663 m->declare("filters.lib/tf1s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
664 m->declare("filters.lib/tf1s:license", "MIT-style STK-4.3 license");
665 m->declare("filters.lib/tf2:author", "Julius O. Smith III");
666 m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
667 m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license");
668 m->declare("filters.lib/tf2s:author", "Julius O. Smith III");
669 m->declare("filters.lib/tf2s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>");
670 m->declare("filters.lib/tf2s:license", "MIT-style STK-4.3 license");
671 m->declare("maths.lib/author", "GRAME");
672 m->declare("maths.lib/copyright", "GRAME");
673 m->declare("maths.lib/license", "LGPL with exception");
674 m->declare("maths.lib/name", "Faust Math Library");
675 m->declare("maths.lib/version", "2.1");
676 m->declare("name", "harm_trem_ext");
677 m->declare("signals.lib/name", "Faust Signal Routing Library");
678 m->declare("signals.lib/version", "0.0");
679 }
680
getNumInputs()681 virtual int getNumInputs() {
682 return 2;
683 }
getNumOutputs()684 virtual int getNumOutputs() {
685 return 1;
686 }
getInputRate(int channel)687 virtual int getInputRate(int channel) {
688 int rate;
689 switch ((channel)) {
690 case 0: {
691 rate = 1;
692 break;
693 }
694 case 1: {
695 rate = 1;
696 break;
697 }
698 default: {
699 rate = -1;
700 break;
701 }
702 }
703 return rate;
704 }
getOutputRate(int channel)705 virtual int getOutputRate(int channel) {
706 int rate;
707 switch ((channel)) {
708 case 0: {
709 rate = 1;
710 break;
711 }
712 default: {
713 rate = -1;
714 break;
715 }
716 }
717 return rate;
718 }
719
classInit(int sample_rate)720 static void classInit(int sample_rate) {
721 }
722
instanceConstants(int sample_rate)723 virtual void instanceConstants(int sample_rate) {
724 fSampleRate = sample_rate;
725 fConst0 = (3.14159274f / std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))));
726 }
727
instanceResetUserInterface()728 virtual void instanceResetUserInterface() {
729 fVslider0 = FAUSTFLOAT(800.0f);
730 fHslider0 = FAUSTFLOAT(0.0f);
731 }
732
instanceClear()733 virtual void instanceClear() {
734 for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) {
735 fVec0[l0] = 0.0f;
736 }
737 for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) {
738 fRec2[l1] = 0.0f;
739 }
740 for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) {
741 fRec1[l2] = 0.0f;
742 }
743 for (int l3 = 0; (l3 < 3); l3 = (l3 + 1)) {
744 fRec0[l3] = 0.0f;
745 }
746 for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) {
747 fRec3[l4] = 0.0f;
748 }
749 for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) {
750 fRec5[l5] = 0.0f;
751 }
752 for (int l6 = 0; (l6 < 3); l6 = (l6 + 1)) {
753 fRec4[l6] = 0.0f;
754 }
755 }
756
init(int sample_rate)757 virtual void init(int sample_rate) {
758 classInit(sample_rate);
759 instanceInit(sample_rate);
760 }
instanceInit(int sample_rate)761 virtual void instanceInit(int sample_rate) {
762 instanceConstants(sample_rate);
763 instanceResetUserInterface();
764 instanceClear();
765 }
766
clone()767 virtual harm_trem_ext* clone() {
768 return new harm_trem_ext();
769 }
770
getSampleRate()771 virtual int getSampleRate() {
772 return fSampleRate;
773 }
774
buildUserInterface(UI * ui_interface)775 virtual void buildUserInterface(UI* ui_interface) {
776 ui_interface->openVerticalBox("harm_trem_ext");
777 ui_interface->declare(&fVslider0, "1", "");
778 ui_interface->declare(&fVslider0, "midi", "ctrl 64");
779 ui_interface->declare(&fVslider0, "style", "knob");
780 ui_interface->addVerticalSlider("CrossoverFreq", &fVslider0, 800.0f, 20.0f, 10000.0f, 1.0f);
781 ui_interface->declare(&fHslider0, "2", "");
782 ui_interface->addHorizontalSlider("depth", &fHslider0, 0.0f, 0.0f, 1.0f, 0.00999999978f);
783 ui_interface->closeBox();
784 }
785
compute(int count,FAUSTFLOAT ** inputs,FAUSTFLOAT ** outputs)786 virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
787 FAUSTFLOAT* input0 = inputs[0];
788 FAUSTFLOAT* input1 = inputs[1];
789 FAUSTFLOAT* output0 = outputs[0];
790 float fSlow0 = (0.00100000005f * float(fVslider0));
791 float fSlow1 = (0.00100000005f * float(fHslider0));
792 for (int i = 0; (i < count); i = (i + 1)) {
793 float fTemp0 = float(input0[i]);
794 fVec0[0] = fTemp0;
795 fRec2[0] = (fSlow0 + (0.999000013f * fRec2[1]));
796 float fTemp1 = std::tan((fConst0 * fRec2[0]));
797 float fTemp2 = (1.0f / fTemp1);
798 float fTemp3 = (fTemp2 + 1.0f);
799 float fTemp4 = (1.0f - fTemp2);
800 fRec1[0] = ((fVec0[1] * (0.0f - (1.0f / (fTemp1 * fTemp3)))) - (((fRec1[1] * fTemp4) - (fTemp0 / fTemp1)) / fTemp3));
801 float fTemp5 = (((fTemp2 + -1.0f) / fTemp1) + 1.0f);
802 float fTemp6 = harm_trem_ext_faustpower2_f(fTemp1);
803 float fTemp7 = (1.0f - (1.0f / fTemp6));
804 float fTemp8 = (((fTemp2 + 1.0f) / fTemp1) + 1.0f);
805 fRec0[0] = (fRec1[0] - (((fRec0[2] * fTemp5) + (2.0f * (fRec0[1] * fTemp7))) / fTemp8));
806 fRec3[0] = (fSlow1 + (0.999000013f * fRec3[1]));
807 float fTemp9 = float(input1[i]);
808 fRec5[0] = (0.0f - (((fTemp4 * fRec5[1]) - (fTemp0 + fVec0[1])) / fTemp3));
809 fRec4[0] = (fRec5[0] - (((fTemp5 * fRec4[2]) + (2.0f * (fTemp7 * fRec4[1]))) / fTemp8));
810 output0[i] = FAUSTFLOAT(((((((fRec0[1] * (0.0f - (2.0f / fTemp6))) + (fRec0[0] / fTemp6)) + (fRec0[2] / fTemp6)) * (1.0f - (0.5f * (fRec3[0] * (fTemp9 + 1.0f))))) + ((fRec4[2] + (fRec4[0] + (2.0f * fRec4[1]))) * (1.0f - (0.5f * (fRec3[0] * ((1.0f - fTemp9) + 1.0f)))))) / fTemp8));
811 fVec0[1] = fVec0[0];
812 fRec2[1] = fRec2[0];
813 fRec1[1] = fRec1[0];
814 fRec0[2] = fRec0[1];
815 fRec0[1] = fRec0[0];
816 fRec3[1] = fRec3[0];
817 fRec5[1] = fRec5[0];
818 fRec4[2] = fRec4[1];
819 fRec4[1] = fRec4[0];
820 }
821 }
822
823 };
824
825 //----------------------------------------------------------------------------
826 // LV2 interface
827 //----------------------------------------------------------------------------
828
829 #line 286 "lv2.cpp"
830
831 #include <assert.h>
832 #include <stdio.h>
833 #include <stdlib.h>
834
835 #include <bitset>
836 #include <boost/circular_buffer.hpp>
837
838 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
839 #include <lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h>
840 #include <lv2/lv2plug.in/ns/ext/atom/util.h>
841 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
842
843 // Set this to the proper shared library extension for your system
844 #ifndef DLLEXT
845 #define DLLEXT ".so"
846 #endif
847
848 #ifndef URI_PREFIX
849 #define URI_PREFIX "https://faustlv2.bitbucket.io"
850 #endif
851
852 #ifndef PLUGIN_URI
853 #define PLUGIN_URI URI_PREFIX "/harm_trem_ext"
854 #endif
855
856 #define MIDI_EVENT_URI "http://lv2plug.in/ns/ext/midi#MidiEvent"
857
858 /* Setting NVOICES at compile time overrides meta data in the Faust source. If
859 set, this must be an integer value >= 0. A nonzero value indicates an
860 instrument (VSTi) plugin with the given maximum number of voices. Use 1 for
861 a monophonic synthesizer, and 0 for a simple effect plugin. If NVOICES
862 isn't defined at compile time then the number of voices of an instrument
863 plugin can also be set with the global "nvoices" meta data key in the Faust
864 source. This setting also adds a special "polyphony" control to the plugin
865 which can be used to dynamically adjust the actual number of voices in the
866 range 1..NVOICES. */
867 //#define NVOICES 16
868
869 /* This enables a special "tuning" control for instruments which lets you
870 select the MTS tuning to be used for the synth. In order to use this, you
871 just drop some sysex (.syx) files with MTS octave-based tunings in 1- or
872 2-byte format into the ~/.fautvst/tuning directory (these can be generated
873 with the author's sclsyx program, https://bitbucket.org/agraef/sclsyx).
874 The control will only be shown if any .syx files were found at startup. 0
875 selects the default tuning (standard 12-tone equal temperament), i>0 the
876 tuning in the ith sysex file (in alphabetic order). */
877 #ifndef FAUST_MTS
878 #define FAUST_MTS 1
879 #endif
880
881 /* This allows various manifest data to be generated from the corresponding
882 metadata (author, name, description, license) in the Faust source. */
883 #ifndef FAUST_META
884 #define FAUST_META 1
885 #endif
886
887 /* This enables automatic MIDI controller mapping based on the midi:ctrl
888 attributes in the Faust source. We have this enabled by default, but you
889 may have to disable it if the custom controller mapping gets in the way of
890 the automation facilities that the host provides. (But then again if the
891 host wants to do its own controller mapping then it probably won't, or at
892 least shouldn't, send us the MIDI controllers in the first place.) */
893 #ifndef FAUST_MIDICC
894 #define FAUST_MIDICC 1
895 #endif
896
897 /* This enables or disables the plugin's custom Qt GUI (cf. lv2ui.cpp). Note
898 that this only affects the plugin manifest, the GUI code itself is in a
899 separate module created with the lv2ui.cpp architecture. Also, you'll have
900 to use the alternative lv2ui manifest templates to tell the LV2 host about
901 the GUI. */
902 #ifndef FAUST_UI
903 #define FAUST_UI 0
904 #endif
905
906 // You can define these for various debugging output items.
907 //#define DEBUG_META 1 // recognized MIDI controller metadata
908 //#define DEBUG_VOICES 1 // triggering of synth voices
909 //#define DEBUG_VOICE_ALLOC 1 // voice allocation
910 //#define DEBUG_MIDI 1 // incoming MIDI messages
911 //#define DEBUG_NOTES 1 // note messages
912 //#define DEBUG_MIDICC 1 // controller messages
913 //#define DEBUG_RPN 1 // RPN messages (pitch bend range, master tuning)
914 //#define DEBUG_MTS 1 // MTS messages (octave/scale tuning)
915
916 // Note and voice data structures.
917
918 struct NoteInfo {
919 uint8_t ch;
920 int8_t note;
921 };
922
923 struct VoiceData {
924 // Octave tunings (offsets in semitones) per MIDI channel.
925 float tuning[16][12];
926 // Allocated voices per MIDI channel and note.
927 int8_t notes[16][128];
928 // Free and used voices.
929 int n_free, n_used;
930 boost::circular_buffer<int> free_voices;
931 boost::circular_buffer<int> used_voices;
932 NoteInfo *note_info;
933 // Voices queued for note-offs (zero-length notes).
934 set<int> queued;
935 // Last gate value during run() for each voice. We need to keep track of
936 // these so that we can force the Faust synth to retrigger a note when
937 // needed.
938 float *lastgate;
939 // Current pitch bend and pitch bend range on each MIDI channel, in semitones.
940 float bend[16], range[16];
941 // Current coarse, fine and total master tuning on each MIDI channel (tuning
942 // offset relative to A4 = 440 Hz, in semitones).
943 float coarse[16], fine[16], tune[16];
VoiceDataVoiceData944 VoiceData(int n) : free_voices(n), used_voices(n) { }
945 };
946
947 #if FAUST_MTS
948
949 // Helper classes to read and store MTS tunings.
950
951 #include <sys/types.h>
952 #include <sys/stat.h>
953 #include <dirent.h>
954
955 #include <string>
956 #include <vector>
957
958 struct MTSTuning {
959 char *name; // name of the tuning
960 int len; // length of sysex data in bytes
961 unsigned char *data; // sysex data
MTSTuningMTSTuning962 MTSTuning() : name(0), len(0), data(0) {}
operator =MTSTuning963 MTSTuning& operator=(const MTSTuning &t)
964 {
965 if (this == &t) return *this;
966 if (name) free(name); if (data) free(data);
967 name = 0; data = 0; len = t.len;
968 if (t.name) {
969 name = strdup(t.name); assert(name);
970 }
971 if (t.data) {
972 data = (unsigned char*)malloc(len); assert(data);
973 memcpy(data, t.data, len);
974 }
975 return *this;
976 }
MTSTuningMTSTuning977 MTSTuning(const MTSTuning& t) : name(0), len(0), data(0)
978 { *this = t; }
979 MTSTuning(const char *filename);
~MTSTuningMTSTuning980 ~MTSTuning()
981 { if (name) free(name); if (data) free(data); }
982 };
983
MTSTuning(const char * filename)984 MTSTuning::MTSTuning(const char *filename)
985 {
986 FILE *fp = fopen(filename, "rb");
987 name = 0; len = 0; data = 0;
988 if (!fp) return;
989 struct stat st;
990 if (fstat(fileno(fp), &st)) return;
991 len = st.st_size;
992 data = (unsigned char*)calloc(len, 1);
993 if (!data) {
994 len = 0; fclose(fp);
995 return;
996 }
997 assert(len > 0);
998 if (fread(data, 1, len, fp) < len) {
999 free(data); len = 0; data = 0; fclose(fp);
1000 return;
1001 }
1002 fclose(fp);
1003 // Do some basic sanity checks.
1004 if (data[0] != 0xf0 || data[len-1] != 0xf7 || // not a sysex message
1005 (data[1] != 0x7e && data[1] != 0x7f) || data[3] != 8 || // not MTS
1006 !((len == 21 && data[4] == 8) ||
1007 (len == 33 && data[4] == 9))) { // no 1- or 2-byte tuning
1008 free(data); len = 0; data = 0;
1009 return;
1010 }
1011 // Name of the tuning is the basename of the file, without the trailing .syx
1012 // suffix.
1013 string nm = filename;
1014 size_t p = nm.rfind(".syx");
1015 if (p != string::npos) nm.erase(p);
1016 p = nm.rfind('/');
1017 if (p != string::npos) nm.erase(0, p+1);
1018 name = strdup(nm.c_str());
1019 assert(name);
1020 }
1021
1022 struct MTSTunings {
1023 vector<MTSTuning> tuning;
MTSTuningsMTSTunings1024 MTSTunings() {}
1025 MTSTunings(const char *path);
1026 };
1027
compareByName(const MTSTuning & a,const MTSTuning & b)1028 static bool compareByName(const MTSTuning &a, const MTSTuning &b)
1029 {
1030 return strcmp(a.name, b.name) < 0;
1031 }
1032
MTSTunings(const char * path)1033 MTSTunings::MTSTunings(const char *path)
1034 {
1035 DIR *dp = opendir(path);
1036 if (!dp) return;
1037 struct dirent *d;
1038 while ((d = readdir(dp))) {
1039 string nm = d->d_name;
1040 if (nm.length() > 4 && nm.substr(nm.length()-4) == ".syx") {
1041 string pathname = path;
1042 pathname += "/";
1043 pathname += nm;
1044 MTSTuning t(pathname.c_str());
1045 if (t.data) tuning.push_back(t);
1046 }
1047 }
1048 closedir(dp);
1049 // sort found tunings by name
1050 sort(tuning.begin(), tuning.end(), compareByName);
1051 }
1052
1053 #endif
1054
1055 #if FAUST_MIDICC
ctrlval(const ui_elem_t & el,uint8_t v)1056 static float ctrlval(const ui_elem_t &el, uint8_t v)
1057 {
1058 // Translate the given MIDI controller value to the range and stepsize
1059 // indicated by the Faust control.
1060 switch (el.type) {
1061 case UI_BUTTON: case UI_CHECK_BUTTON:
1062 return (float)(v>=64);
1063 default:
1064 /* Continuous controllers. The problem here is that the range 0..127 is
1065 not symmetric. We'd like to map 64 to the center of the range
1066 (max-min)/2 and at the same time retain the full control range
1067 min..max. So let's just pretend that there are 128 controller values
1068 and map value 127 to the max value anyway. */
1069 if (v==127)
1070 return el.max;
1071 else
1072 // XXXFIXME: We might want to add proper quantization according to
1073 // el.step here.
1074 return el.min+(el.max-el.min)*v/128;
1075 }
1076 }
1077 #endif
1078
1079 /***************************************************************************/
1080
1081 /* Polyphonic Faust plugin data structure. XXXTODO: At present this is just a
1082 big struct which exposes all requisite data. Some more work is needed to
1083 make the interface a bit more abstract and properly encapsulate the
1084 internal data structures, so that implementation details can be changed
1085 more easily. */
1086
1087 struct LV2Plugin {
1088 const int maxvoices; // maximum number of voices (zero if not an instrument)
1089 const int ndsps; // number of dsp instances (1 if maxvoices==0)
1090 bool active; // activation status
1091 int rate; // sampling rate
1092 int nvoices; // current number of voices (<= maxvoices)
1093 int tuning_no; // current tuning number (<= n_tunings)
1094 harm_trem_ext **dsp; // the dsps
1095 LV2UI **ui; // their Faust interface descriptions
1096 int n_in, n_out; // number of input and output control ports
1097 int *ctrls; // Faust ui elements (indices into ui->elems)
1098 float **ports; // corresponding LV2 data
1099 float *portvals; // cached port data from the last run
1100 float *midivals[16]; // per-midi channel data
1101 int *inctrls, *outctrls; // indices for active and passive controls
1102 float **inputs, **outputs; // audio buffers
1103 int freq, gain, gate; // indices of voice controls
1104 unsigned n_samples; // current block size
1105 float **outbuf; // audio buffers for mixing down the voices
1106 float **inbuf; // dummy input buffer
1107 LV2_Atom_Sequence* event_port; // midi input
1108 float *poly, *tuning; // polyphony and tuning ports
1109 std::map<uint8_t,int> ctrlmap; // MIDI controller map
1110 // Needed host features.
1111 LV2_URID_Map* map; // the urid extension
1112 LV2_URID midi_event; // midi event uri
1113 // Current RPN MSB and LSB numbers, as set with controllers 101 and 100.
1114 uint8_t rpn_msb[16], rpn_lsb[16];
1115 // Current data entry MSB and LSB numbers, as set with controllers 6 and 38.
1116 uint8_t data_msb[16], data_lsb[16];
1117 // Synth voice data (instruments only).
1118 VoiceData *vd;
1119
1120 // Static methods. These all use static data so they can be invoked before
1121 // instantiating a plugin.
1122
1123 // Global meta data (dsp name, author, etc.).
1124 static Meta *meta;
init_metaLV2Plugin1125 static void init_meta()
1126 {
1127 if (!meta && (meta = new Meta)) {
1128 // We allocate the temporary dsp object on the heap here, to prevent
1129 // large dsp objects from running out of stack in environments where
1130 // stack space is precious (e.g., Reaper). Note that if any of these
1131 // allocations fail then no meta data will be available, but at least we
1132 // won't make the host crash and burn.
1133 harm_trem_ext* tmp_dsp = new harm_trem_ext();
1134 if (tmp_dsp) {
1135 tmp_dsp->metadata(meta);
1136 delete tmp_dsp;
1137 }
1138 }
1139 }
meta_getLV2Plugin1140 static const char *meta_get(const char *key, const char *deflt)
1141 {
1142 init_meta();
1143 return meta?meta->get(key, deflt):deflt;
1144 }
1145
pluginNameLV2Plugin1146 static const char *pluginName()
1147 {
1148 return meta_get("name", "harm_trem_ext");
1149 }
1150
pluginAuthorLV2Plugin1151 static const char *pluginAuthor()
1152 {
1153 return meta_get("author", "");
1154 }
1155
pluginDescriptionLV2Plugin1156 static const char *pluginDescription()
1157 {
1158 return meta_get("description", "");
1159 }
1160
pluginLicenseLV2Plugin1161 static const char *pluginLicense()
1162 {
1163 return meta_get("license", "");
1164 }
1165
pluginVersionLV2Plugin1166 static const char *pluginVersion()
1167 {
1168 return meta_get("version", "");
1169 }
1170
1171 // Load a collection of sysex files with MTS tunings in ~/.faust/tuning.
1172 static int n_tunings;
1173 #if FAUST_MTS
1174 static MTSTunings *mts;
1175
load_sysex_dataLV2Plugin1176 static MTSTunings *load_sysex_data()
1177 {
1178 if (!mts) {
1179 string mts_path;
1180 // Look for FAUST_HOME. If that isn't set, try $HOME/.faust. If HOME
1181 // isn't set either, just assume a .faust subdir of the cwd.
1182 const char *home = getenv("FAUST_HOME");
1183 if (home)
1184 mts_path = home;
1185 else {
1186 home = getenv("HOME");
1187 if (home) {
1188 mts_path = home;
1189 mts_path += "/.faust";
1190 } else
1191 mts_path = ".faust";
1192 }
1193 // MTS tunings are looked for in this subdir.
1194 mts_path += "/tuning";
1195 mts = new MTSTunings(mts_path.c_str());
1196 #ifdef __APPLE__
1197 if (!mts || mts->tuning.size() == 0) {
1198 // Also check ~/Library/Faust/Tuning on the Mac.
1199 home = getenv("HOME");
1200 if (home) {
1201 if (mts) delete mts;
1202 mts_path = home;
1203 mts_path += "/Library/Faust/Tuning";
1204 mts = new MTSTunings(mts_path.c_str());
1205 }
1206 }
1207 #endif
1208 n_tunings = mts->tuning.size();
1209 }
1210 return mts;
1211 }
1212 #endif
1213
1214 // The number of voices of an instrument plugin. We get this information
1215 // from the global meta data (nvoices key) of the dsp module if present, and
1216 // you can also override this setting at compile time by defining the
1217 // NVOICES macro. If neither is defined or the value is zero then the plugin
1218 // becomes a simple audio effect instead.
numVoicesLV2Plugin1219 static int numVoices()
1220 {
1221 #ifdef NVOICES
1222 return NVOICES;
1223 #else
1224 const char *numVoices = meta_get("nvoices", "0");
1225 int nvoices = atoi(numVoices);
1226 if (nvoices < 0 ) nvoices = 0;
1227 return nvoices;
1228 #endif
1229 }
1230
1231 // Instance methods.
1232
LV2PluginLV2Plugin1233 LV2Plugin(const int num_voices, const int sr)
1234 : maxvoices(num_voices), ndsps(num_voices<=0?1:num_voices),
1235 vd(num_voices>0?new VoiceData(num_voices):0)
1236 {
1237 // Initialize static data.
1238 init_meta();
1239 #if FAUST_MTS
1240 // Synth: load tuning sysex data if present.
1241 if (num_voices>0) load_sysex_data();
1242 #endif
1243 // Allocate data structures and set some reasonable defaults.
1244 dsp = (harm_trem_ext**)calloc(ndsps, sizeof(harm_trem_ext*));
1245 ui = (LV2UI**)calloc(ndsps, sizeof(LV2UI*));
1246 assert(dsp && ui);
1247 if (vd) {
1248 vd->note_info = (NoteInfo*)calloc(ndsps, sizeof(NoteInfo));
1249 vd->lastgate = (float*)calloc(ndsps, sizeof(float));
1250 assert(vd->note_info && vd->lastgate);
1251 }
1252 active = false;
1253 rate = sr;
1254 nvoices = maxvoices;
1255 tuning_no = 0;
1256 n_in = n_out = 0;
1257 map = NULL;
1258 midi_event = -1;
1259 event_port = NULL;
1260 poly = tuning = NULL;
1261 freq = gain = gate = -1;
1262 if (vd) {
1263 vd->n_free = maxvoices;
1264 for (int i = 0; i < maxvoices; i++) {
1265 vd->free_voices.push_back(i);
1266 vd->lastgate[i] = 0.0f;
1267 }
1268 for (int i = 0; i < 16; i++) {
1269 vd->bend[i] = 0.0f;
1270 vd->range[i] = 2.0f;
1271 vd->coarse[i] = vd->fine[i] = vd->tune[i] = 0.0f;
1272 for (int j = 0; j < 12; j++)
1273 vd->tuning[i][j] = 0.0f;
1274 }
1275 vd->n_used = 0;
1276 memset(vd->notes, 0xff, sizeof(vd->notes));
1277 }
1278 n_samples = 0;
1279 ctrls = inctrls = outctrls = NULL;
1280 ports = inputs = outputs = inbuf = outbuf = NULL;
1281 portvals = NULL;
1282 memset(midivals, 0, sizeof(midivals));
1283 // Initialize the Faust DSPs.
1284 for (int i = 0; i < ndsps; i++) {
1285 dsp[i] = new harm_trem_ext();
1286 ui[i] = new LV2UI(num_voices);
1287 dsp[i]->init(rate);
1288 dsp[i]->buildUserInterface(ui[i]);
1289 }
1290 // The ports are numbered as follows: 0..k-1 are the control ports, then
1291 // come the n audio input ports, then the m audio output ports, and
1292 // finally the midi input port and the polyphony and tuning controls.
1293 int k = ui[0]->nports, p = 0, q = 0;
1294 int n = dsp[0]->getNumInputs(), m = dsp[0]->getNumOutputs();
1295 // Allocate tables for the built-in control elements and their ports.
1296 ctrls = (int*)calloc(k, sizeof(int));
1297 inctrls = (int*)calloc(k, sizeof(int));
1298 outctrls = (int*)calloc(k, sizeof(int));
1299 ports = (float**)calloc(k, sizeof(float*));
1300 portvals = (float*)calloc(k, sizeof(float));
1301 assert(k == 0 || (ctrls && inctrls && outctrls && ports && portvals));
1302 for (int ch = 0; ch < 16; ch++) {
1303 midivals[ch] = (float*)calloc(k, sizeof(float));
1304 assert(k == 0 || midivals[ch]);
1305 }
1306 // Scan the Faust UI for active and passive controls which become the
1307 // input and output control ports of the plugin, respectively.
1308 for (int i = 0, j = 0; i < ui[0]->nelems; i++) {
1309 switch (ui[0]->elems[i].type) {
1310 case UI_T_GROUP: case UI_H_GROUP: case UI_V_GROUP: case UI_END_GROUP:
1311 // control groups (ignored right now)
1312 break;
1313 case UI_H_BARGRAPH: case UI_V_BARGRAPH:
1314 // passive controls (output ports)
1315 ctrls[j++] = i;
1316 outctrls[q++] = i;
1317 break;
1318 default:
1319 // active controls (input ports)
1320 if (maxvoices == 0)
1321 goto noinstr;
1322 else if (freq == -1 &&
1323 !strcmp(ui[0]->elems[i].label, "freq"))
1324 freq = i;
1325 else if (gain == -1 &&
1326 !strcmp(ui[0]->elems[i].label, "gain"))
1327 gain = i;
1328 else if (gate == -1 &&
1329 !strcmp(ui[0]->elems[i].label, "gate"))
1330 gate = i;
1331 else {
1332 noinstr:
1333 #if FAUST_MIDICC
1334 std::map< int, list<strpair> >::iterator it =
1335 ui[0]->metadata.find(i);
1336 if (it != ui[0]->metadata.end()) {
1337 // Scan for controller mappings.
1338 for (std::list<strpair>::iterator jt = it->second.begin();
1339 jt != it->second.end(); jt++) {
1340 const char *key = jt->first, *val = jt->second;
1341 #if DEBUG_META
1342 fprintf(stderr, "ctrl '%s' meta: '%s' -> '%s'\n",
1343 ui[0]->elems[i].label, key, val);
1344 #endif
1345 if (strcmp(key, "midi") == 0) {
1346 unsigned num;
1347 if (sscanf(val, "ctrl %u", &num) < 1) continue;
1348 #if 0 // enable this to get feedback about controller assignments
1349 fprintf(stderr, "%s: cc %d -> %s\n", PLUGIN_URI, num,
1350 ui[0]->elems[i].label);
1351 #endif
1352 ctrlmap.insert(std::pair<uint8_t,int>(num, p));
1353 }
1354 }
1355 }
1356 #endif
1357 ctrls[j++] = i;
1358 inctrls[p++] = i;
1359 int p = ui[0]->elems[i].port;
1360 float val = ui[0]->elems[i].init;
1361 assert(p>=0);
1362 portvals[p] = val;
1363 for (int ch = 0; ch < 16; ch++)
1364 midivals[ch][p] = val;
1365 }
1366 break;
1367 }
1368 }
1369 // Realloc the inctrls and outctrls vectors to their appropriate sizes.
1370 inctrls = (int*)realloc(inctrls, p*sizeof(int));
1371 assert(p == 0 || inctrls);
1372 outctrls = (int*)realloc(outctrls, q*sizeof(int));
1373 assert(q == 0 || outctrls);
1374 n_in = p; n_out = q;
1375 // Allocate vectors for the audio input and output ports. Like
1376 // ports, these will be initialized in the connect_port callback.
1377 inputs = (float**)calloc(n, sizeof(float*));
1378 assert(n == 0 || inputs);
1379 outputs = (float**)calloc(m, sizeof(float*));
1380 assert(m == 0 || outputs);
1381 if (maxvoices > 0) {
1382 // Initialize the mixdown buffer.
1383 outbuf = (float**)calloc(m, sizeof(float*));
1384 assert(m == 0 || outbuf);
1385 // We start out with a blocksize of 512 samples here. Hopefully this is
1386 // enough for most realtime hosts so that we can avoid reallocations
1387 // later when we know what the actual blocksize is.
1388 n_samples = 512;
1389 for (int i = 0; i < m; i++) {
1390 outbuf[i] = (float*)malloc(n_samples*sizeof(float));
1391 assert(outbuf[i]);
1392 }
1393 // Initialize a 1-sample dummy input buffer used for retriggering notes.
1394 inbuf = (float**)calloc(n, sizeof(float*));
1395 assert(n == 0 || inbuf);
1396 for (int i = 0; i < m; i++) {
1397 inbuf[i] = (float*)malloc(sizeof(float));
1398 assert(inbuf[i]);
1399 *inbuf[i] = 0.0f;
1400 }
1401 }
1402 }
1403
~LV2PluginLV2Plugin1404 ~LV2Plugin()
1405 {
1406 const int n = dsp[0]->getNumInputs();
1407 const int m = dsp[0]->getNumOutputs();
1408 for (int i = 0; i < ndsps; i++) {
1409 delete dsp[i];
1410 delete ui[i];
1411 }
1412 free(ctrls); free(inctrls); free(outctrls);
1413 free(ports); free(portvals);
1414 free(inputs); free(outputs);
1415 for (int ch = 0; ch < 16; ch++)
1416 free(midivals[ch]);
1417 if (inbuf) {
1418 for (int i = 0; i < n; i++)
1419 free(inbuf[i]);
1420 free(inbuf);
1421 }
1422 if (outbuf) {
1423 for (int i = 0; i < m; i++)
1424 free(outbuf[i]);
1425 free(outbuf);
1426 }
1427 free(dsp); free(ui);
1428 if (vd) {
1429 free(vd->note_info);
1430 free(vd->lastgate);
1431 delete vd;
1432 }
1433 }
1434 // Voice allocation.
1435
1436 #if DEBUG_VOICE_ALLOC
print_voicesLV2Plugin1437 void print_voices(const char *msg)
1438 {
1439 fprintf(stderr, "%s: notes =", msg);
1440 for (uint8_t ch = 0; ch < 16; ch++)
1441 for (int note = 0; note < 128; note++)
1442 if (vd->notes[ch][note] >= 0)
1443 fprintf(stderr, " [%d] %d(#%d)", ch, note, vd->notes[ch][note]);
1444 fprintf(stderr, "\nqueued (%d):", vd->queued.size());
1445 for (int i = 0; i < nvoices; i++)
1446 if (vd->queued.find(i) != vd->queued.end()) fprintf(stderr, " #%d", i);
1447 fprintf(stderr, "\nused (%d):", vd->n_used);
1448 for (boost::circular_buffer<int>::iterator it = vd->used_voices.begin();
1449 it != vd->used_voices.end(); it++)
1450 fprintf(stderr, " #%d->%d", *it, vd->note_info[*it].note);
1451 fprintf(stderr, "\nfree (%d):", vd->n_free);
1452 for (boost::circular_buffer<int>::iterator it = vd->free_voices.begin();
1453 it != vd->free_voices.end(); it++)
1454 fprintf(stderr, " #%d", *it);
1455 fprintf(stderr, "\n");
1456 }
1457 #endif
1458
alloc_voiceLV2Plugin1459 int alloc_voice(uint8_t ch, int8_t note, int8_t vel)
1460 {
1461 int i = vd->notes[ch][note];
1462 if (i >= 0) {
1463 // note already playing on same channel, retrigger it
1464 voice_off(i);
1465 voice_on(i, note, vel, ch);
1466 // move this voice to the end of the used list
1467 for (boost::circular_buffer<int>::iterator it =
1468 vd->used_voices.begin();
1469 it != vd->used_voices.end(); it++) {
1470 if (*it == i) {
1471 vd->used_voices.erase(it);
1472 vd->used_voices.push_back(i);
1473 break;
1474 }
1475 }
1476 #if DEBUG_VOICE_ALLOC
1477 print_voices("retrigger");
1478 #endif
1479 return i;
1480 } else if (vd->n_free > 0) {
1481 // take voice from free list
1482 int i = vd->free_voices.front();
1483 vd->free_voices.pop_front();
1484 vd->n_free--;
1485 vd->used_voices.push_back(i);
1486 vd->note_info[i].ch = ch;
1487 vd->note_info[i].note = note;
1488 vd->n_used++;
1489 voice_on(i, note, vel, ch);
1490 vd->notes[ch][note] = i;
1491 #if DEBUG_VOICE_ALLOC
1492 print_voices("alloc");
1493 #endif
1494 return i;
1495 } else {
1496 // steal a voice
1497 assert(vd->n_used > 0);
1498 // FIXME: Maybe we should look for the oldest note on the *current*
1499 // channel here, but this is faster.
1500 int i = vd->used_voices.front();
1501 int oldch = vd->note_info[i].ch;
1502 int oldnote = vd->note_info[i].note;
1503 voice_off(i);
1504 vd->notes[oldch][oldnote] = -1;
1505 vd->queued.erase(i);
1506 vd->used_voices.pop_front();
1507 vd->used_voices.push_back(i);
1508 vd->note_info[i].ch = ch;
1509 vd->note_info[i].note = note;
1510 voice_on(i, note, vel, ch);
1511 vd->notes[ch][note] = i;
1512 #if DEBUG_VOICE_ALLOC
1513 print_voices("steal");
1514 #endif
1515 return i;
1516 }
1517 }
1518
dealloc_voiceLV2Plugin1519 int dealloc_voice(uint8_t ch, int8_t note, int8_t vel)
1520 {
1521 int i = vd->notes[ch][note];
1522 if (i >= 0) {
1523 if (vd->lastgate[i] == 0.0f && gate >= 0) {
1524 // zero-length note, queued for later
1525 vd->queued.insert(i);
1526 vd->notes[ch][note] = -1;
1527 #if DEBUG_VOICE_ALLOC
1528 print_voices("dealloc (queued)");
1529 #endif
1530 return i;
1531 }
1532 assert(vd->n_free < nvoices);
1533 vd->free_voices.push_back(i);
1534 vd->n_free++;
1535 voice_off(i);
1536 vd->notes[ch][note] = -1;
1537 // erase this voice from the used list
1538 for (boost::circular_buffer<int>::iterator it =
1539 vd->used_voices.begin();
1540 it != vd->used_voices.end(); it++) {
1541 if (*it == i) {
1542 vd->used_voices.erase(it);
1543 vd->n_used--;
1544 break;
1545 }
1546 }
1547 #if DEBUG_VOICE_ALLOC
1548 print_voices("dealloc");
1549 #endif
1550 return i;
1551 }
1552 return -1;
1553 }
1554
1555
midicpsLV2Plugin1556 float midicps(int8_t note, uint8_t chan)
1557 {
1558 float pitch = note + vd->tune[chan] +
1559 vd->tuning[chan][note%12] + vd->bend[chan];
1560 return 440.0*pow(2, (pitch-69.0)/12.0);
1561 }
1562
voice_onLV2Plugin1563 void voice_on(int i, int8_t note, int8_t vel, uint8_t ch)
1564 {
1565 if (vd->lastgate[i] == 1.0f && gate >= 0) {
1566 // Make sure that the synth sees the 0.0f gate so that the voice is
1567 // properly retriggered.
1568 *ui[i]->elems[gate].zone = 0.0f;
1569 dsp[i]->compute(1, inbuf, outbuf);
1570 }
1571 #if DEBUG_VOICES
1572 fprintf(stderr, "voice on: %d %d (%g Hz) %d (%g)\n", i,
1573 note, midicps(note, ch), vel, vel/127.0);
1574 #endif
1575 if (freq >= 0)
1576 *ui[i]->elems[freq].zone = midicps(note, ch);
1577 if (gate >= 0)
1578 *ui[i]->elems[gate].zone = 1.0f;
1579 if (gain >= 0)
1580 *ui[i]->elems[gain].zone = vel/127.0;
1581 // reinitialize the per-channel control data for this voice
1582 for (int idx = 0; idx < n_in; idx++) {
1583 int j = inctrls[idx], k = ui[0]->elems[j].port;
1584 *ui[i]->elems[j].zone = midivals[ch][k];
1585 }
1586 }
1587
voice_offLV2Plugin1588 void voice_off(int i)
1589 {
1590 #if DEBUG_VOICES
1591 fprintf(stderr, "voice off: %d\n", i);
1592 #endif
1593 if (gate >= 0)
1594 *ui[i]->elems[gate].zone = 0.0f;
1595 }
1596
update_voicesLV2Plugin1597 void update_voices(uint8_t chan)
1598 {
1599 // update running voices on the given channel after tuning or pitch bend
1600 // changes
1601 for (boost::circular_buffer<int>::iterator it =
1602 vd->used_voices.begin();
1603 it != vd->used_voices.end(); it++) {
1604 int i = *it;
1605 if (vd->note_info[i].ch == chan && freq >= 0) {
1606 int note = vd->note_info[i].note;
1607 *ui[i]->elems[freq].zone = midicps(note, chan);
1608 }
1609 }
1610 }
1611
all_notes_offLV2Plugin1612 void all_notes_off()
1613 {
1614 for (int i = 0; i < nvoices; i++)
1615 voice_off(i);
1616 for (int i = 0; i < 16; i++)
1617 vd->bend[i] = 0.0f;
1618 memset(vd->notes, 0xff, sizeof(vd->notes));
1619 vd->free_voices.clear();
1620 vd->n_free = nvoices;
1621 for (int i = 0; i < nvoices; i++)
1622 vd->free_voices.push_back(i);
1623 vd->queued.clear();
1624 vd->used_voices.clear();
1625 vd->n_used = 0;
1626 }
1627
all_notes_offLV2Plugin1628 void all_notes_off(uint8_t chan)
1629 {
1630 for (boost::circular_buffer<int>::iterator it =
1631 vd->used_voices.begin();
1632 it != vd->used_voices.end(); ) {
1633 int i = *it;
1634 if (vd->note_info[i].ch == chan) {
1635 assert(vd->n_free < nvoices);
1636 vd->free_voices.push_back(i);
1637 vd->n_free++;
1638 voice_off(i);
1639 vd->notes[vd->note_info[i].ch][vd->note_info[i].note] = -1;
1640 vd->queued.erase(i);
1641 // erase this voice from the used list
1642 it = vd->used_voices.erase(it);
1643 vd->n_used--;
1644 #if DEBUG_VOICE_ALLOC
1645 print_voices("dealloc (all-notes-off)");
1646 #endif
1647 } else
1648 it++;
1649 }
1650 vd->bend[chan] = 0.0f;
1651 }
1652
queued_notes_offLV2Plugin1653 void queued_notes_off()
1654 {
1655 if (vd->queued.empty()) return;
1656 for (int i = 0; i < nvoices; i++)
1657 if (vd->queued.find(i) != vd->queued.end()) {
1658 assert(vd->n_free < nvoices);
1659 vd->free_voices.push_back(i);
1660 vd->n_free++;
1661 voice_off(i);
1662 vd->notes[vd->note_info[i].ch][vd->note_info[i].note] = -1;
1663 vd->queued.erase(i);
1664 // erase this voice from the used list
1665 for (boost::circular_buffer<int>::iterator it =
1666 vd->used_voices.begin();
1667 it != vd->used_voices.end(); it++) {
1668 if (*it == i) {
1669 vd->used_voices.erase(it);
1670 vd->n_used--;
1671 break;
1672 }
1673 }
1674 #if DEBUG_VOICE_ALLOC
1675 print_voices("dealloc (unqueued)");
1676 #endif
1677 }
1678 }
1679
1680 // Plugin activation status. suspend() deactivates a plugin (disables audio
1681 // processing), resume() reactivates it. Also, set_rate() changes the sample
1682 // rate. Note that the audio and MIDI process functions (see below) can
1683 // still be called in deactivated state, but this is optional. The plugin
1684 // tries to do some reasonable processing in either case, no matter whether
1685 // the host plugin architecture actually executes callbacks in suspended
1686 // state or not.
1687
suspendLV2Plugin1688 void suspend()
1689 {
1690 active = false;
1691 if (maxvoices > 0) all_notes_off();
1692 }
1693
resumeLV2Plugin1694 void resume()
1695 {
1696 for (int i = 0; i < ndsps; i++)
1697 dsp[i]->init(rate);
1698 for (int i = 0, j = 0; i < ui[0]->nelems; i++) {
1699 int p = ui[0]->elems[i].port;
1700 if (p >= 0) {
1701 float val = ui[0]->elems[i].init;
1702 portvals[p] = val;
1703 }
1704 }
1705 active = true;
1706 }
1707
set_rateLV2Plugin1708 void set_rate(int sr)
1709 {
1710 rate = sr;
1711 for (int i = 0; i < ndsps; i++)
1712 dsp[i]->init(rate);
1713 }
1714
1715 // Audio and MIDI process functions. The plugin should run these in the
1716 // appropriate real-time callbacks.
1717
process_audioLV2Plugin1718 void process_audio(int blocksz, float **inputs, float **outputs)
1719 {
1720 int n = dsp[0]->getNumInputs(), m = dsp[0]->getNumOutputs();
1721 AVOIDDENORMALS;
1722 if (maxvoices > 0) queued_notes_off();
1723 if (!active) {
1724 // Depending on the plugin architecture and host, this code might never
1725 // be invoked, since the plugin is deactivitated at this point. But
1726 // let's do something reasonable here anyway.
1727 if (n == m) {
1728 // copy inputs to outputs
1729 for (int i = 0; i < m; i++)
1730 for (unsigned j = 0; j < blocksz; j++)
1731 outputs[i][j] = inputs[i][j];
1732 } else {
1733 // silence
1734 for (int i = 0; i < m; i++)
1735 for (unsigned j = 0; j < blocksz; j++)
1736 outputs[i][j] = 0.0f;
1737 }
1738 return;
1739 }
1740 // Handle changes in the polyphony and tuning controls.
1741 bool is_instr = maxvoices > 0;
1742 if (is_instr) {
1743 if (!poly)
1744 ; // this shouldn't happen but...
1745 else if (nvoices != (int)*poly &&
1746 (int)*poly > 0 && (int)*poly <= maxvoices) {
1747 for (int i = 0; i < nvoices; i++)
1748 voice_off(i);
1749 nvoices = (int)*poly;
1750 // Reset the voice allocation.
1751 memset(vd->notes, 0xff, sizeof(vd->notes));
1752 vd->free_voices.clear();
1753 vd->n_free = nvoices;
1754 for (int i = 0; i < nvoices; i++)
1755 vd->free_voices.push_back(i);
1756 vd->used_voices.clear();
1757 vd->n_used = 0;
1758 } else
1759 *poly = nvoices;
1760 #if FAUST_MTS
1761 if (tuning && tuning_no != (int)*tuning) change_tuning((int)*tuning);
1762 #endif
1763 }
1764 // Only update the controls (of all voices simultaneously) if a port value
1765 // actually changed. This is necessary to allow MIDI controllers to modify
1766 // the values for individual MIDI channels (see processEvents below). Also
1767 // note that this will be done *after* processing the MIDI controller data
1768 // for the current audio block, so manual inputs can still override these.
1769 for (int i = 0; i < n_in; i++) {
1770 int j = inctrls[i], k = ui[0]->elems[j].port;
1771 float &oldval = portvals[k], newval = *ports[k];
1772 if (newval != oldval) {
1773 if (is_instr) {
1774 // instrument: update running voices
1775 for (boost::circular_buffer<int>::iterator it =
1776 vd->used_voices.begin();
1777 it != vd->used_voices.end(); it++) {
1778 int i = *it;
1779 *ui[i]->elems[j].zone = newval;
1780 }
1781 } else {
1782 // simple effect: here we only have a single dsp instance
1783 *ui[0]->elems[j].zone = newval;
1784 }
1785 // also update the MIDI controller data for all channels (manual
1786 // control input is always omni)
1787 for (int ch = 0; ch < 16; ch++)
1788 midivals[ch][k] = newval;
1789 // record the new value
1790 oldval = newval;
1791 }
1792 }
1793 // Initialize the output buffers.
1794 if (n_samples < blocksz) {
1795 // We need to enlarge the buffers. We're not officially allowed to do
1796 // this here (presumably in the realtime thread), but since we usually
1797 // don't know the hosts's block size beforehand, there's really nothing
1798 // else that we can do. Let's just hope that doing this once suffices,
1799 // then hopefully noone will notice.
1800 if (outbuf) {
1801 for (int i = 0; i < m; i++) {
1802 outbuf[i] = (float*)realloc(outbuf[i],
1803 blocksz*sizeof(float));
1804 assert(outbuf[i]);
1805 }
1806 }
1807 n_samples = blocksz;
1808 }
1809 if (outbuf) {
1810 // Polyphonic instrument: Mix the voices down to one signal.
1811 for (int i = 0; i < m; i++)
1812 for (unsigned j = 0; j < blocksz; j++)
1813 outputs[i][j] = 0.0f;
1814 for (int l = 0; l < nvoices; l++) {
1815 // Let Faust do all the hard work.
1816 dsp[l]->compute(blocksz, inputs, outbuf);
1817 for (int i = 0; i < m; i++)
1818 for (unsigned j = 0; j < blocksz; j++)
1819 outputs[i][j] += outbuf[i][j];
1820 }
1821 } else {
1822 // Simple effect: We can write directly to the output buffer.
1823 dsp[0]->compute(blocksz, inputs, outputs);
1824 }
1825 // Finally grab the passive controls and write them back to the
1826 // corresponding control ports. NOTE: Depending on the plugin
1827 // architecture, this might require a host call to get the control GUI
1828 // updated in real-time (if the host supports this at all).
1829 // FIXME: It's not clear how to aggregate the data of the different
1830 // voices. We compute the maximum of each control for now.
1831 for (int i = 0; i < n_out; i++) {
1832 int j = outctrls[i], k = ui[0]->elems[j].port;
1833 float *z = ui[0]->elems[j].zone;
1834 *ports[k] = *z;
1835 for (int l = 1; l < nvoices; l++) {
1836 float *z = ui[l]->elems[j].zone;
1837 if (*ports[k] < *z)
1838 *ports[k] = *z;
1839 }
1840 }
1841 // Keep track of the last gates set for each voice, so that voices can be
1842 // forcibly retriggered if needed.
1843 if (gate >= 0)
1844 for (int i = 0; i < nvoices; i++)
1845 vd->lastgate[i] =
1846 *ui[i]->elems[gate].zone;
1847 }
1848
1849 // This processes just a single MIDI message, so to process an entire series
1850 // of MIDI events you'll have to loop over the event data in the plugin's
1851 // MIDI callback. XXXTODO: Sample-accurate processing of MIDI events.
1852
process_midiLV2Plugin1853 void process_midi(unsigned char *data, int sz)
1854 {
1855 #if DEBUG_MIDI
1856 fprintf(stderr, "midi ev (%d bytes):", sz);
1857 for (int i = 0; i < sz; i++)
1858 fprintf(stderr, " 0x%0x", data[i]);
1859 fprintf(stderr, "\n");
1860 #endif
1861 uint8_t status = data[0] & 0xf0, chan = data[0] & 0x0f;
1862 bool is_instr = maxvoices > 0;
1863 switch (status) {
1864 case 0x90: {
1865 if (!is_instr) break;
1866 // note on
1867 #if DEBUG_NOTES
1868 fprintf(stderr, "note-on chan %d, note %d, vel %d\n", chan+1,
1869 data[1], data[2]);
1870 #endif
1871 if (data[2] == 0) goto note_off;
1872 alloc_voice(chan, data[1], data[2]);
1873 break;
1874 }
1875 case 0x80: {
1876 if (!is_instr) break;
1877 // note off
1878 #if DEBUG_NOTES
1879 fprintf(stderr, "note-off chan %d, note %d, vel %d\n", chan+1,
1880 data[1], data[2]);
1881 #endif
1882 note_off:
1883 dealloc_voice(chan, data[1], data[2]);
1884 break;
1885 }
1886 case 0xe0: {
1887 if (!is_instr) break;
1888 // pitch bend
1889 // data[1] is LSB, data[2] MSB, range is 0..0x3fff (which maps to
1890 // -2..+2 semitones by default), center point is 0x2000 = 8192
1891 int val = data[1] | (data[2]<<7);
1892 vd->bend[chan] =
1893 (val-0x2000)/8192.0f*vd->range[chan];
1894 #if DEBUG_MIDICC
1895 fprintf(stderr, "pitch-bend (chan %d): %g cent\n", chan+1,
1896 vd->bend[chan]*100.0);
1897 #endif
1898 update_voices(chan);
1899 break;
1900 }
1901 case 0xb0: {
1902 // controller change
1903 switch (data[1]) {
1904 case 120: case 123:
1905 if (!is_instr) break;
1906 // all-sound-off and all-notes-off controllers (these are treated
1907 // the same in the current implementation)
1908 all_notes_off(chan);
1909 #if DEBUG_MIDICC
1910 fprintf(stderr, "all-notes-off (chan %d)\n", chan+1);
1911 #endif
1912 break;
1913 case 121:
1914 // all-controllers-off (in the current implementation, this just
1915 // resets the RPN-related controllers)
1916 data_msb[chan] = data_lsb[chan] = 0;
1917 rpn_msb[chan] = rpn_lsb[chan] = 0x7f;
1918 #if DEBUG_MIDICC
1919 fprintf(stderr, "all-controllers-off (chan %d)\n", chan+1);
1920 #endif
1921 break;
1922 case 101: case 100:
1923 // RPN MSB/LSB
1924 if (data[1] == 101)
1925 rpn_msb[chan] = data[2];
1926 else
1927 rpn_lsb[chan] = data[2];
1928 break;
1929 case 6: case 38:
1930 // data entry coarse/fine
1931 if (data[1] == 6)
1932 data_msb[chan] = data[2];
1933 else
1934 data_lsb[chan] = data[2];
1935 goto rpn;
1936 case 96: case 97:
1937 // data increment/decrement
1938 /* NOTE: The specification of these controllers is a complete
1939 mess. Originally, the MIDI specification didn't have anything
1940 to say about their exact behaviour at all. Nowadays, the
1941 behaviour depends on which RPN or NRPN is being modified, which
1942 is also rather confusing. Fortunately, as we only handle RPNs
1943 0..2 here anyway, it's sufficient to assume the MSB for RPN #2
1944 (channel coarse tuning) and the LSB otherwise. */
1945 if (rpn_msb[chan] == 0 && rpn_lsb[chan] == 2) {
1946 // modify the MSB
1947 if (data[1] == 96 && data_msb[chan] < 0x7f)
1948 data_msb[chan]++;
1949 else if (data[1] == 97 && data_msb[chan] > 0)
1950 data_msb[chan]--;
1951 } else {
1952 // modify the LSB
1953 if (data[1] == 96 && data_lsb[chan] < 0x7f)
1954 data_lsb[chan]++;
1955 else if (data[1] == 97 && data_lsb[chan] > 0)
1956 data_lsb[chan]--;
1957 }
1958 rpn:
1959 if (!is_instr) break;
1960 if (rpn_msb[chan] == 0) {
1961 switch (rpn_lsb[chan]) {
1962 case 0:
1963 // pitch bend range, coarse value is in semitones, fine value
1964 // in cents
1965 vd->range[chan] = data_msb[chan]+
1966 data_lsb[chan]/100.0;
1967 #if DEBUG_RPN
1968 fprintf(stderr, "pitch-bend-range (chan %d): %g cent\n", chan+1,
1969 vd->range[chan]*100.0);
1970 #endif
1971 break;
1972 case 1:
1973 {
1974 // channel fine tuning (14 bit value, range -100..+100 cents)
1975 int value = (data_msb[chan]<<7) |
1976 data_lsb[chan];
1977 vd->fine[chan] = (value-8192)/8192.0f;
1978 }
1979 goto master_tune;
1980 case 2:
1981 // channel coarse tuning (only msb is used, range -64..+63
1982 // semitones)
1983 vd->coarse[chan] = data_msb[chan]-64;
1984 master_tune:
1985 vd->tune[chan] = vd->coarse[chan]+
1986 vd->fine[chan];
1987 #if DEBUG_RPN
1988 fprintf(stderr, "master-tuning (chan %d): %g cent\n", chan+1,
1989 vd->tune[chan]*100.0);
1990 #endif
1991 update_voices(chan);
1992 break;
1993 default:
1994 break;
1995 }
1996 }
1997 break;
1998 default: {
1999 #if FAUST_MIDICC
2000 // interpret all other controller changes according to the MIDI
2001 // controller map defined in the Faust plugin itself
2002 std::map<uint8_t,int>::iterator it = ctrlmap.find(data[1]);
2003 if (it != ctrlmap.end()) {
2004 // defined MIDI controller
2005 int j = inctrls[it->second],
2006 k = ui[0]->elems[j].port;
2007 float val = ctrlval(ui[0]->elems[j], data[2]);
2008 midivals[chan][k] = val;
2009 if (is_instr) {
2010 // instrument: update running voices on this channel
2011 for (boost::circular_buffer<int>::iterator it =
2012 vd->used_voices.begin();
2013 it != vd->used_voices.end(); it++) {
2014 int i = *it;
2015 if (vd->note_info[i].ch == chan)
2016 *ui[i]->elems[j].zone = val;
2017 }
2018 } else {
2019 // simple effect: here we only have a single dsp instance and
2020 // we're operating in omni mode, so we just update the control no
2021 // matter what the midi channel is
2022 *ui[0]->elems[j].zone = val;
2023 }
2024 #if DEBUG_MIDICC
2025 fprintf(stderr, "ctrl-change chan %d, ctrl %d, val %d\n", chan+1,
2026 data[1], data[2]);
2027 #endif
2028 }
2029 #endif
2030 break;
2031 }
2032 }
2033 break;
2034 }
2035 default:
2036 break;
2037 }
2038 }
2039
2040 // Process an MTS sysex message and update the control values accordingly.
2041
process_sysexLV2Plugin2042 void process_sysex(uint8_t *data, int sz)
2043 {
2044 if (!data || sz < 2) return;
2045 #if DEBUG_MIDI
2046 fprintf(stderr, "midi sysex (%d bytes):", sz);
2047 for (int i = 0; i < sz; i++)
2048 fprintf(stderr, " 0x%0x", data[i]);
2049 fprintf(stderr, "\n");
2050 #endif
2051 if (data[0] == 0xf0) {
2052 // Skip over the f0 and f7 status bytes in case they are included in the
2053 // dump.
2054 data++; sz--;
2055 if (data[sz-1] == 0xf7) sz--;
2056 }
2057 if ((data[0] == 0x7e || data[0] == 0x7f) && data[2] == 8) {
2058 // MIDI tuning standard
2059 bool realtime = data[0] == 0x7f;
2060 if ((sz == 19 && data[3] == 8) ||
2061 (sz == 31 && data[3] == 9)) {
2062 // MTS scale/octave tuning 1- or 2-byte form
2063 bool onebyte = data[3] == 8;
2064 unsigned chanmsk = (data[4]<<14) | (data[5]<<7) | data[6];
2065 for (int i = 0; i < 12; i++) {
2066 float t;
2067 if (onebyte)
2068 t = (data[i+7]-64)/100.0;
2069 else
2070 t = (((data[2*i+7]<<7)|data[2*i+8])-8192)/8192.0;
2071 for (uint8_t ch = 0; ch < 16; ch++)
2072 if (chanmsk & (1<<ch))
2073 vd->tuning[ch][i] = t;
2074 }
2075 if (realtime) {
2076 for (uint8_t ch = 0; ch < 16; ch++)
2077 if (chanmsk & (1<<ch)) {
2078 // update running voices on this channel
2079 update_voices(ch);
2080 }
2081 }
2082 #if DEBUG_MTS
2083 fprintf(stderr, "octave-tuning-%s (chan ",
2084 realtime?"realtime":"non-realtime");
2085 bool first = true;
2086 for (uint8_t i = 0; i < 16; )
2087 if (chanmsk & (1<<i)) {
2088 uint8_t j;
2089 for (j = i+1; j < 16 && (chanmsk&(1<<j)); )
2090 j++;
2091 if (first)
2092 first = false;
2093 else
2094 fprintf(stderr, ",");
2095 if (j > i+1)
2096 fprintf(stderr, "%u-%u", i+1, j);
2097 else
2098 fprintf(stderr, "%u", i+1);
2099 i = j;
2100 } else
2101 i++;
2102 fprintf(stderr, "):");
2103 if (onebyte) {
2104 for (int i = 7; i < 19; i++) {
2105 int val = data[i];
2106 fprintf(stderr, " %d", val-64);
2107 }
2108 } else {
2109 for (int i = 7; i < 31; i++) {
2110 int val = data[i++] << 7;
2111 val |= data[i];
2112 fprintf(stderr, " %g", ((double)val-8192.0)/8192.0*100.0);
2113 }
2114 }
2115 fprintf(stderr, "\n");
2116 #endif
2117 }
2118 }
2119 }
2120
2121 // Change to a given preloaded tuning. The given tuning number may be in the
2122 // range 1..PFaustPlugin::n_tunings, zero denotes the default tuning (equal
2123 // temperament). This is only supported if FAUST_MTS is defined at compile
2124 // time.
2125
change_tuningLV2Plugin2126 void change_tuning(int num)
2127 {
2128 #if FAUST_MTS
2129 if (!mts || num == tuning_no) return;
2130 if (num < 0) num = 0;
2131 if (num > mts->tuning.size())
2132 num = mts->tuning.size();
2133 tuning_no = num;
2134 if (tuning_no > 0) {
2135 process_sysex(mts->tuning[tuning_no-1].data,
2136 mts->tuning[tuning_no-1].len);
2137 } else {
2138 memset(vd->tuning, 0, sizeof(vd->tuning));
2139 #if DEBUG_MTS
2140 fprintf(stderr,
2141 "octave-tuning-default (chan 1-16): equal temperament\n");
2142 #endif
2143 }
2144 #endif
2145 }
2146
2147 };
2148
2149 Meta *LV2Plugin::meta = 0;
2150 int LV2Plugin::n_tunings = 0;
2151 #if FAUST_MTS
2152 MTSTunings *LV2Plugin::mts = 0;
2153 #endif
2154
2155 /* LV2-specific part starts here. ********************************************/
2156
2157 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)2158 instantiate(const LV2_Descriptor* descriptor,
2159 double rate,
2160 const char* bundle_path,
2161 const LV2_Feature* const* features)
2162 {
2163 LV2Plugin* plugin =
2164 new LV2Plugin(LV2Plugin::numVoices(), (int)rate);
2165 // Scan host features for URID map.
2166 for (int i = 0; features[i]; i++) {
2167 if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) {
2168 plugin->map = (LV2_URID_Map*)features[i]->data;
2169 plugin->midi_event =
2170 plugin->map->map(plugin->map->handle, MIDI_EVENT_URI);
2171 }
2172 }
2173 if (!plugin->map) {
2174 fprintf
2175 (stderr, "%s: host doesn't support urid:map, giving up\n",
2176 PLUGIN_URI);
2177 delete plugin;
2178 return 0;
2179 }
2180 return (LV2_Handle)plugin;
2181 }
2182
2183 static void
cleanup(LV2_Handle instance)2184 cleanup(LV2_Handle instance)
2185 {
2186 LV2Plugin* plugin = (LV2Plugin*)instance;
2187 delete plugin;
2188 }
2189
2190 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)2191 connect_port(LV2_Handle instance,
2192 uint32_t port,
2193 void* data)
2194 {
2195 LV2Plugin* plugin = (LV2Plugin*)instance;
2196 int i = port, k = plugin->ui[0]->nports;
2197 int n = plugin->dsp[0]->getNumInputs(), m = plugin->dsp[0]->getNumOutputs();
2198 if (i < k)
2199 plugin->ports[i] = (float*)data;
2200 else {
2201 i -= k;
2202 if (i < n)
2203 plugin->inputs[i] = (float*)data;
2204 else {
2205 i -= n;
2206 if (i < m)
2207 plugin->outputs[i] = (float*)data;
2208 else if (i == m)
2209 plugin->event_port = (LV2_Atom_Sequence*)data;
2210 else if (i == m+1)
2211 plugin->poly = (float*)data;
2212 else if (i == m+2)
2213 plugin->tuning = (float*)data;
2214 else
2215 fprintf(stderr, "%s: bad port number %u\n", PLUGIN_URI, port);
2216 }
2217 }
2218 }
2219
2220 static void
run(LV2_Handle instance,uint32_t n_samples)2221 run(LV2_Handle instance, uint32_t n_samples)
2222 {
2223 LV2Plugin* plugin = (LV2Plugin*)instance;
2224 // Process incoming MIDI events.
2225 if (plugin->event_port) {
2226 LV2_ATOM_SEQUENCE_FOREACH(plugin->event_port, ev) {
2227 if (ev->body.type == plugin->midi_event) {
2228 uint8_t *data = (uint8_t*)(ev+1);
2229 #if 0
2230 // FIXME: Consider doing sample-accurate note onsets here. LV2 keeps
2231 // track of the exact onset in the frames and subframes fields
2232 // (http://lv2plug.in/ns/doc/html/structLV2__Atom__Event.html), but we
2233 // can't use that information at present, since our gate parameter is
2234 // a control variable which can only change at block boundaries. In
2235 // the future, the gate could be implemented as an audio signal to get
2236 // sample-accurate note onsets.
2237 uint32_t frames = ev->body.frames;
2238 #endif
2239 if (data[0] == 0xf0)
2240 plugin->process_sysex(data, ev->body.size);
2241 else
2242 plugin->process_midi(data, ev->body.size);
2243 }
2244 }
2245 }
2246 // Process audio.
2247 plugin->process_audio(n_samples, plugin->inputs, plugin->outputs);
2248 }
2249
2250 static void
activate(LV2_Handle instance)2251 activate(LV2_Handle instance)
2252 {
2253 LV2Plugin* plugin = (LV2Plugin*)instance;
2254 plugin->resume();
2255 }
2256
2257 static void
deactivate(LV2_Handle instance)2258 deactivate(LV2_Handle instance)
2259 {
2260 LV2Plugin* plugin = (LV2Plugin*)instance;
2261 plugin->suspend();
2262 }
2263
2264 const void*
extension_data(const char * uri)2265 extension_data(const char* uri)
2266 {
2267 return NULL;
2268 }
2269
2270 static const LV2_Descriptor descriptor = {
2271 PLUGIN_URI,
2272 instantiate,
2273 connect_port,
2274 activate,
2275 run,
2276 deactivate,
2277 cleanup,
2278 extension_data
2279 };
2280
2281 extern "C"
2282 LV2_SYMBOL_EXPORT
2283 const LV2_Descriptor*
lv2_descriptor(uint32_t index)2284 lv2_descriptor(uint32_t index)
2285 {
2286 switch (index) {
2287 case 0:
2288 return &descriptor;
2289 default:
2290 return NULL;
2291 }
2292 }
2293
2294 //----------------------------------------------------------------------------
2295 // Dynamic manifest
2296 //----------------------------------------------------------------------------
2297
2298 // NOTE: If your LV2 host doesn't offer this extension then you'll have to
2299 // create a static ttl file with the descriptions of the ports. You can do
2300 // this by compiling this code to a standalone executable. Running the
2301 // executable then prints the manifest on stdout.
2302
2303 extern "C"
2304 LV2_SYMBOL_EXPORT
lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle * handle,const LV2_Feature * const * features)2305 int lv2_dyn_manifest_open(LV2_Dyn_Manifest_Handle *handle,
2306 const LV2_Feature *const *features)
2307 {
2308 LV2Plugin* plugin =
2309 new LV2Plugin(LV2Plugin::numVoices(), 48000);
2310 *handle = (LV2_Dyn_Manifest_Handle)plugin;
2311 return 0;
2312 }
2313
2314 extern "C"
2315 LV2_SYMBOL_EXPORT
lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle,FILE * fp)2316 int lv2_dyn_manifest_get_subjects(LV2_Dyn_Manifest_Handle handle,
2317 FILE *fp)
2318 {
2319 fprintf(fp, "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n\
2320 <%s> a lv2:Plugin .\n", PLUGIN_URI);
2321 return 0;
2322 }
2323
2324 #include <string>
2325 #include <ctype.h>
2326
mangle(const string & s)2327 static string mangle(const string &s)
2328 {
2329 string t = s;
2330 size_t n = s.size();
2331 for (size_t i = 0; i < n; i++)
2332 if ((i == 0 && !isalpha(t[i]) && t[i] != '_') ||
2333 (!isalnum(t[i]) && t[i] != '_'))
2334 t[i] = '_';
2335 return t;
2336 }
2337
steps(float min,float max,float step)2338 static unsigned steps(float min, float max, float step)
2339 {
2340 if (step == 0.0) return 1;
2341 int n = (max-min)/step;
2342 if (n < 0) n = -n;
2343 if (n == 0) n = 1;
2344 return n;
2345 }
2346
2347 #if FAUST_META
is_xmlstring(const char * s)2348 static bool is_xmlstring(const char *s)
2349 {
2350 // This is just a basic sanity check. The string must not contain any
2351 // (unescaped) newlines, carriage returns or double quotes.
2352 return !strchr(s, '\n') && !strchr(s, '\r') && !strchr(s, '"');
2353 }
2354 #endif
2355
2356 extern "C"
2357 LV2_SYMBOL_EXPORT
lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle,FILE * fp,const char * uri)2358 int lv2_dyn_manifest_get_data(LV2_Dyn_Manifest_Handle handle,
2359 FILE *fp,
2360 const char *uri)
2361 {
2362 LV2Plugin* plugin = (LV2Plugin*)handle;
2363 int k = plugin->ui[0]->nports;
2364 int n = plugin->dsp[0]->getNumInputs(), m = plugin->dsp[0]->getNumOutputs();
2365 bool is_instr = plugin->maxvoices > 0, have_midi = is_instr;
2366 // Scan the global metadata for plugin name, description, license etc.
2367 const char *plugin_name = NULL, *plugin_author = NULL, *plugin_descr = NULL,
2368 *plugin_version = NULL, *plugin_license = NULL;
2369 #if FAUST_META
2370 plugin_name = plugin->pluginName();
2371 plugin_descr = plugin->pluginDescription();
2372 plugin_author = plugin->pluginAuthor();
2373 plugin_version = plugin->pluginVersion();
2374 plugin_license = plugin->pluginLicense();
2375 #endif
2376 if (!plugin_name || !*plugin_name) plugin_name = "harm_trem_ext";
2377 fprintf(fp, "@prefix doap: <http://usefulinc.com/ns/doap#> .\n\
2378 @prefix foaf: <http://xmlns.com/foaf/0.1/> .\n\
2379 @prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n\
2380 @prefix ui: <http://lv2plug.in/ns/extensions/ui#> .\n\
2381 @prefix epp: <http://lv2plug.in/ns/ext/port-props#> .\n\
2382 @prefix atom: <http://lv2plug.in/ns/ext/atom#> .\n\
2383 @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n\
2384 @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n\
2385 @prefix units: <http://lv2plug.in/ns/extensions/units#> .\n\
2386 <%s>\n\
2387 a lv2:Plugin%s ;\n\
2388 doap:name \"%s\" ;\n\
2389 lv2:binary <harm_trem_ext%s> ;\n\
2390 lv2:optionalFeature epp:supportsStrictBounds ;\n\
2391 lv2:optionalFeature lv2:hardRtCapable ;\n", PLUGIN_URI,
2392 is_instr?", lv2:InstrumentPlugin":"",
2393 plugin_name, DLLEXT);
2394 if (plugin_author && *plugin_author)
2395 fprintf(fp, "\
2396 doap:maintainer [ foaf:name \"%s\" ] ;\n", plugin_author);
2397 // doap:description just seems to be ignored by all LV2 hosts anyway, so we
2398 // rather use rdfs:comment now which works with Ardour at least.
2399 if (plugin_descr && *plugin_descr)
2400 fprintf(fp, "\
2401 rdfs:comment \"%s\" ;\n", plugin_descr);
2402 if (plugin_version && *plugin_version)
2403 fprintf(fp, "\
2404 doap:revision \"%s\" ;\n", plugin_version);
2405 if (plugin_license && *plugin_license)
2406 fprintf(fp, "\
2407 doap:license \"%s\" ;\n", plugin_license);
2408 #if FAUST_UI
2409 fprintf(fp, "\
2410 ui:ui <%sui> ;\n", PLUGIN_URI);
2411 #endif
2412 int idx = 0;
2413 // control ports
2414 for (int i = 0; i < k; i++, idx++) {
2415 int j = plugin->ctrls[i];
2416 assert(idx == plugin->ui[0]->elems[j].port);
2417 fprintf(fp, "%s [\n", idx==0?" lv2:port":" ,");
2418 const char *label = plugin->ui[0]->elems[j].label;
2419 assert(label);
2420 string sym = mangle(plugin->ui[0]->elems[j].label);
2421 switch (plugin->ui[0]->elems[j].type) {
2422 // active controls (input ports)
2423 case UI_BUTTON: case UI_CHECK_BUTTON:
2424 fprintf(fp, "\
2425 a lv2:InputPort ;\n\
2426 a lv2:ControlPort ;\n\
2427 lv2:index %d ;\n\
2428 lv2:symbol \"%s_%d\" ;\n\
2429 lv2:name \"%s\" ;\n\
2430 lv2:portProperty epp:hasStrictBounds ;\n\
2431 lv2:portProperty lv2:toggled ;\n\
2432 lv2:default 0.00000 ;\n\
2433 lv2:minimum 0.00000 ;\n\
2434 lv2:maximum 1.00000 ;\n", idx, sym.c_str(), idx, label);
2435 break;
2436 case UI_NUM_ENTRY: case UI_H_SLIDER: case UI_V_SLIDER:
2437 fprintf(fp, "\
2438 a lv2:InputPort ;\n\
2439 a lv2:ControlPort ;\n\
2440 lv2:index %d ;\n\
2441 lv2:symbol \"%s_%d\" ;\n\
2442 lv2:name \"%s\" ;\n\
2443 lv2:portProperty epp:hasStrictBounds ;\n\
2444 epp:rangeSteps %u ;\n\
2445 lv2:default %g ;\n\
2446 lv2:minimum %g ;\n\
2447 lv2:maximum %g ;\n", idx, sym.c_str(), idx, label,
2448 steps(plugin->ui[0]->elems[j].min,
2449 plugin->ui[0]->elems[j].max,
2450 plugin->ui[0]->elems[j].step),
2451 plugin->ui[0]->elems[j].init,
2452 plugin->ui[0]->elems[j].min,
2453 plugin->ui[0]->elems[j].max);
2454 break;
2455 // passive controls (output ports)
2456 case UI_H_BARGRAPH: case UI_V_BARGRAPH:
2457 fprintf(fp, "\
2458 a lv2:OutputPort ;\n\
2459 a lv2:ControlPort ;\n\
2460 lv2:index %d ;\n\
2461 lv2:symbol \"%s_%d\" ;\n\
2462 lv2:name \"%s\" ;\n\
2463 lv2:default %g ;\n\
2464 lv2:minimum %g ;\n\
2465 lv2:maximum %g ;\n", idx, sym.c_str(), idx, label,
2466 plugin->ui[0]->elems[j].min,
2467 plugin->ui[0]->elems[j].min,
2468 plugin->ui[0]->elems[j].max);
2469 break;
2470 default:
2471 assert(0 && "this can't happen");
2472 break;
2473 }
2474 // Scan for Faust control metadata we understand and add corresponding
2475 // hints to the LV2 description of the port.
2476 std::map< int, list<strpair> >::iterator it =
2477 plugin->ui[0]->metadata.find(j);
2478 if (it != plugin->ui[0]->metadata.end()) {
2479 for (std::list<strpair>::iterator jt = it->second.begin();
2480 jt != it->second.end(); jt++) {
2481 const char *key = jt->first, *val = jt->second;
2482 #if FAUST_MIDICC
2483 unsigned num;
2484 if (!strcmp(key, "midi") && sscanf(val, "ctrl %u", &num) == 1)
2485 have_midi = true;
2486 #endif
2487 if (!strcmp(key, "unit"))
2488 fprintf(fp, "\
2489 units:unit [\n\
2490 a units:Unit ;\n\
2491 units:name \"%s\" ;\n\
2492 units:symbol \"%s\" ;\n\
2493 units:render \"%%f %s\"\n\
2494 ] ;\n", val, val, val);
2495 if (strcmp(key, "lv2")) continue;
2496 if (!strcmp(val, "integer"))
2497 fprintf(fp, "\
2498 lv2:portProperty lv2:integer ;\n");
2499 else if (!strcmp(val, "reportsLatency"))
2500 fprintf(fp, "\
2501 lv2:portProperty lv2:reportsLatency ;\n\
2502 lv2:designation lv2:latency ;\n");
2503 else if (!strcmp(val, "hidden") || !strcmp(val, "notOnGUI"))
2504 fprintf(fp, "\
2505 lv2:portProperty epp:notOnGUI ;\n");
2506 else if (!strncmp(val, "scalepoint", 10) ||
2507 !strncmp(val, "scalePoint", 10)) {
2508 val += 10;
2509 if (!isspace(*val)) continue;
2510 char *label = (char*)malloc(strlen(val)+1);
2511 float point;
2512 int pos;
2513 while (sscanf(val, "%s %g%n", label, &point, &pos) == 2) {
2514 fprintf(fp, "\
2515 lv2:scalePoint [ rdfs:label \"%s\"; rdf:value %g ] ;\n",
2516 label, point);
2517 val += pos;
2518 }
2519 free(label);
2520 } else
2521 fprintf(stderr, "%s: bad port property '%s:%s'\n", PLUGIN_URI,
2522 key, val);
2523 }
2524 }
2525 fprintf(fp, " ]");
2526 }
2527 // audio inputs
2528 for (int i = 0; i < n; i++, idx++)
2529 fprintf(fp, "%s [\n\
2530 a lv2:InputPort ;\n\
2531 a lv2:AudioPort ;\n\
2532 lv2:index %d ;\n\
2533 lv2:symbol \"in%d\" ;\n\
2534 lv2:name \"in%d\" ;\n\
2535 ]", idx==0?" lv2:port":" ,", idx, i, i);
2536 // audio outputs
2537 for (int i = 0; i < m; i++, idx++)
2538 fprintf(fp, "%s [\n\
2539 a lv2:OutputPort ;\n\
2540 a lv2:AudioPort ;\n\
2541 lv2:index %d ;\n\
2542 lv2:symbol \"out%d\" ;\n\
2543 lv2:name \"out%d\" ;\n\
2544 ]", idx==0?" lv2:port":" ,", idx, i, i);
2545 if (have_midi) {
2546 // midi input
2547 fprintf(fp, "%s [\n\
2548 a lv2:InputPort ;\n\
2549 a atom:AtomPort ;\n\
2550 atom:bufferType atom:Sequence ;\n\
2551 atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ;\n\
2552 lv2:index %d ;\n\
2553 lv2:symbol \"midiin\" ;\n\
2554 lv2:name \"midiin\"\n\
2555 ]", idx==0?" lv2:port":" ,", idx);
2556 idx++;
2557 }
2558 if (is_instr) {
2559 // polyphony control
2560 fprintf(fp, "%s [\n\
2561 a lv2:InputPort ;\n\
2562 a lv2:ControlPort ;\n\
2563 lv2:index %d ;\n\
2564 lv2:symbol \"polyphony\" ;\n\
2565 lv2:name \"polyphony\" ;\n\
2566 lv2:portProperty epp:hasStrictBounds ;\n\
2567 # lv2:portProperty epp:expensive ;\n\
2568 lv2:portProperty lv2:integer ;\n\
2569 epp:rangeSteps %d ;\n\
2570 lv2:default %d ;\n\
2571 lv2:minimum 1 ;\n\
2572 lv2:maximum %d ;\n\
2573 ]", idx==0?" lv2:port":" ,", idx, plugin->maxvoices-1,
2574 plugin->maxvoices>1?plugin->maxvoices/2:1,
2575 plugin->maxvoices);
2576 idx++;
2577 #if FAUST_MTS
2578 if (plugin->n_tunings > 0) {
2579 // tuning control
2580 fprintf(fp, "%s [\n\
2581 a lv2:InputPort ;\n\
2582 a lv2:ControlPort ;\n\
2583 lv2:index %d ;\n\
2584 lv2:symbol \"tuning\" ;\n\
2585 lv2:name \"tuning\" ;\n\
2586 lv2:portProperty epp:hasStrictBounds ;\n\
2587 lv2:portProperty lv2:integer ;\n\
2588 epp:rangeSteps %d ;\n\
2589 lv2:default 0 ;\n\
2590 lv2:minimum 0 ;\n\
2591 lv2:maximum %d ;\n",
2592 idx==0?" lv2:port":" ,", idx, plugin->n_tunings, plugin->n_tunings);
2593 for (int i = 0; i <= plugin->n_tunings; i++)
2594 fprintf(fp, "\
2595 lv2:scalePoint [ rdfs:label \"%s\"; rdf:value %d ] ;\n",
2596 (i>0)?plugin->mts->tuning[i-1].name:"default", i);
2597 fprintf(fp, " ]");
2598 idx++;
2599 }
2600 #endif
2601 }
2602 fprintf(fp, "\n.\n");
2603 return 0;
2604 }
2605
2606 extern "C"
2607 LV2_SYMBOL_EXPORT
lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle)2608 void lv2_dyn_manifest_close(LV2_Dyn_Manifest_Handle handle)
2609 {
2610 LV2Plugin* plugin = (LV2Plugin*)handle;
2611 delete plugin;
2612 }
2613
main()2614 int main()
2615 {
2616 LV2_Dyn_Manifest_Handle handle;
2617 LV2_Feature **features = { NULL };
2618 int res = lv2_dyn_manifest_open(&handle, features);
2619 if (res) return res;
2620 res = lv2_dyn_manifest_get_data(handle, stdout, PLUGIN_URI);
2621 return res;
2622 }
2623
2624 #endif
2625