1 //
2 // C++ Implementation: plugin
3 //
4 // Description:
5 //
6 //  (C) Copyright 2000 Werner Schweer (ws@seh.de)
7 //
8 // Additions/modifications: Mathias Lundgren <lunar_shuttle@users.sf.net>, (C) 2004
9 //                          (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net)
10 //
11 //
12 //  This program is free software; you can redistribute it and/or
13 //  modify it under the terms of the GNU General Public License
14 //  as published by the Free Software Foundation; version 2 of
15 //  the License, or (at your option) any later version.
16 //
17 //  This program is distributed in the hope that it will be useful,
18 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 //  GNU General Public License for more details.
21 //
22 //  You should have received a copy of the GNU General Public License
23 //  along with this program; if not, write to the Free Software
24 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
25 //
26 //
27 
28 #include <QtCore>
29 #include <QtWidgets>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <dlfcn.h>
34 #include <string>
35 
36 #include "simpler_plugin.h"
37 #include "plugin_cache_reader.h"
38 
39 #define SS_LOG_MAX   0
40 #define SS_LOG_MIN -10
41 #define SS_LOG_OFFSET SS_LOG_MIN
42 
43 
44 #define SP_TRACE_FUNC   0
45 #define SP_DEBUG_LADSPA 0
46 
47 #define SP_TRACE_IN if (SP_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__);
48 #define SP_TRACE_OUT if (SP_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__);
49 #define SP_ERROR(string) fprintf(stderr, "SimplePlugin error: %s\n", string)
50 #define SP_DBG_LADSPA(string1) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1);
51 #define SP_DBG_LADSPA2(string1, string2) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2);
52 
53 // Turn on debugging messages.
54 //#define PLUGIN_DEBUGIN
55 
56 // Turn on constant stream of debugging messages.
57 //#define PLUGIN_DEBUGIN_PROCESS
58 
59 namespace MusESimplePlugin {
60 
61 PluginList plugins;
62 
63 //
64 // Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain)
65 //
SS_map_pluginparam2logdomain(int pluginparam_val)66 float SS_map_pluginparam2logdomain(int pluginparam_val)
67 {
68    float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX;
69    float scaled = (float) pluginparam_val * scale;
70    float mapped = scaled + SS_LOG_OFFSET;
71    return mapped;
72 }
73 //
74 // Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX]  (from log-> [0,127])
75 // (inverse func to the above)
SS_map_logdomain2pluginparam(float pluginparam_log)76 int SS_map_logdomain2pluginparam(float pluginparam_log)
77 {
78    float mapped = pluginparam_log - SS_LOG_OFFSET;
79    float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN);
80    int scaled  = (int) round(mapped * scale);
81    return scaled;
82 }
83 
84 //---------------------------------------------------------
85 //   initPlugins
86 //    search for LADSPA plugins
87 //---------------------------------------------------------
88 
SS_initPlugins(const QString & hostCachePath)89 void SS_initPlugins(const QString& hostCachePath)
90 {
91   SP_TRACE_IN
92 
93   MusEPlugin::PluginScanList scan_list;
94   // Read host plugin cache file. We only want ladspa plugins for now...
95   MusEPlugin::readPluginCacheFile(hostCachePath + "/scanner",
96                                   &scan_list,
97                                   false,
98                                   false,
99                                   MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA);
100   for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl)
101   {
102     const MusEPlugin::PluginScanInfoRef inforef = *isl;
103     const MusEPlugin::PluginScanInfoStruct& info = inforef->info();
104     switch(info._type)
105     {
106       case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA:
107       {
108         //if(MusEGlobal::loadPlugins)
109         {
110           // Make sure it doesn't already exist.
111           if(/*Plugin* pl =*/ plugins.find(PLUGIN_GET_QSTRING(info._completeBaseName),
112              PLUGIN_GET_QSTRING(info._label)))
113           {
114             //fprintf(stderr, "Ignoring LADSPA effect label:%s path:%s duplicate of path:%s\n",
115             //        info._label.toLatin1().constData(),
116             //        info._fi.filePath().toLatin1().constData(),
117             //        pl->filePath().toLatin1().constData());
118           }
119           else
120           {
121             plugins.push_back(new LadspaPlugin(info));
122           }
123         }
124       }
125       break;
126 
127       case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI:
128       case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST:
129       case MusEPlugin::PluginScanInfoStruct::PluginTypeVST:
130       case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2:
131       case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST:
132       case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS:
133       case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown:
134       case MusEPlugin::PluginScanInfoStruct::PluginTypeNone:
135       case MusEPlugin::PluginScanInfoStruct::PluginTypeAll:
136       break;
137     }
138   }
139 
140   SP_TRACE_OUT
141 }
142 
143 //---------------------------------------------------------
144 //   find
145 //---------------------------------------------------------
146 
find(const QString & file,const QString & name)147 Plugin* PluginList::find(const QString& file, const QString& name)
148       {
149       SP_TRACE_IN
150       for (iPlugin i = begin(); i != end(); ++i) {
151             if ((file == (*i)->lib()) && (name == (*i)->label())) {
152                   SP_TRACE_OUT
153                   return *i;
154                   }
155             }
156       //fprintf(stderr, "Plugin <%s> not found\n", name.toLatin1().constData());
157       SP_TRACE_OUT
158       return 0;
159       }
160 
~PluginList()161 PluginList::~PluginList()
162 {
163    //fprintf(stderr, "~PluginList\n");
164    //Cleanup plugins:
165    for (iPlugin i = plugins.begin(); i != plugins.end(); ++i)
166    {
167      if((*i)->references() != 0)
168      {
169        fprintf(stderr, "~PluginList: Plugin <%s> reference count not zero! Cannot delete.\n",
170               (*i)->name().toLatin1().constData());
171        continue;
172      }
173      //fprintf(stderr, "~PluginList: deleting plugin <%s>\n",
174      //       (*i)->name().toLatin1().constData());
175      delete (*i);
176    }
177 }
178 
179 //---------------------------------------------------------
180 //   Plugin
181 //---------------------------------------------------------
182 
Plugin(const MusEPlugin::PluginScanInfoStruct & info)183 Plugin::Plugin(const MusEPlugin::PluginScanInfoStruct& info)
184   : _fi(PLUGIN_GET_QSTRING(info.filePath())),
185     _libHandle(0),
186     _references(0),
187     _instNo(0),
188     _uniqueID(info._uniqueID),
189     _label(PLUGIN_GET_QSTRING(info._label)),
190     _name(PLUGIN_GET_QSTRING(info._name)),
191     _maker(PLUGIN_GET_QSTRING(info._maker)),
192     _copyright(PLUGIN_GET_QSTRING(info._copyright)),
193     _portCount(info._portCount),
194     _inports(info._inports),
195     _outports(info._outports),
196     _controlInPorts(info._controlInPorts),
197     _controlOutPorts(info._controlOutPorts),
198     _requiredFeatures(info._requiredFeatures) { }
199 
200 //---------------------------------------------------------
201 //   LadspaPlugin
202 //---------------------------------------------------------
203 
LadspaPlugin(const QFileInfo * f,const LADSPA_Descriptor_Function,const LADSPA_Descriptor * d)204 LadspaPlugin::LadspaPlugin(const QFileInfo* f,
205    const LADSPA_Descriptor_Function /*ldf*/,
206    const LADSPA_Descriptor* d)
207    : Plugin(f)
208       {
209       SP_TRACE_IN
210 
211       _plugin = NULL;
212 
213       _label = QString(d->Label);
214       _name = QString(d->Name);
215       _uniqueID = d->UniqueID;
216       _maker = QString(d->Maker);
217       _copyright = QString(d->Copyright);
218       _portCount = d->PortCount;
219 
220       for(unsigned long k = 0; k < _portCount; ++k)
221       {
222         LADSPA_PortDescriptor pd = d->PortDescriptors[k];
223         if(pd & LADSPA_PORT_AUDIO)
224         {
225           if(pd & LADSPA_PORT_INPUT)
226           {
227             ++_inports;
228           }
229           else
230           if(pd & LADSPA_PORT_OUTPUT)
231           {
232             ++_outports;
233           }
234         }
235         else
236         if(pd & LADSPA_PORT_CONTROL)
237         {
238           if(pd & LADSPA_PORT_INPUT)
239           {
240             ++_controlInPorts;
241           }
242           else
243           if(pd & LADSPA_PORT_OUTPUT)
244           {
245             ++_controlOutPorts;
246           }
247         }
248       }
249 
250       /*if (SP_DEBUG_LADSPA) {
251             printf("Label: %s\tLib: %s\tPortCount: %d\n", this->label().toLatin1().constData(), this->lib().toLatin1().constData(), plugin->PortCount);
252             printf("LADSPA_PORT_CONTROL|LADSPA_PORT_INPUT: %d\t", pIdx.size());
253             printf("Input ports: %d\t", iIdx.size());
254             printf("Output ports: %d\n\n", oIdx.size());
255             }*/
256 
257       if ((_inports != _outports) || (LADSPA_IS_INPLACE_BROKEN(d->Properties)))
258         _requiredFeatures |= MusECore::PluginNoInPlaceProcessing;
259 
260       SP_TRACE_OUT
261       }
262 
LadspaPlugin(const MusEPlugin::PluginScanInfoStruct & info)263 LadspaPlugin::LadspaPlugin(const MusEPlugin::PluginScanInfoStruct& info)
264   : Plugin(info), _plugin(NULL)
265 {
266    SP_TRACE_IN
267 
268   SP_TRACE_OUT
269 }
270 
271 //---------------------------------------------------------
272 //   createPluginI
273 //---------------------------------------------------------
274 
createPluginI(int chans,float sampleRate,unsigned int segmentSize,bool useDenormalBias,float denormalBias)275 PluginI* LadspaPlugin::createPluginI(int chans, float sampleRate, unsigned int segmentSize,
276                                            bool useDenormalBias, float denormalBias)
277 {
278    LadspaPluginI* plug_i = new LadspaPluginI();
279    if(plug_i->initPluginInstance(
280       this,
281       chans,
282       sampleRate,
283       segmentSize,
284       useDenormalBias,
285       denormalBias))
286    {
287      fprintf(stderr, "LadspaPlugin::createPluginI: cannot instantiate plugin <%s>\n",
288        name().toLatin1().constData());
289      // Make sure to delete the PluginI.
290      delete plug_i;
291      return 0;
292    }
293    return plug_i;
294 }
295 
296 
297 //---------------------------------------------------------
298 //   incReferences
299 //---------------------------------------------------------
300 
incReferences(int val)301 int LadspaPlugin::incReferences(int val)
302 {
303   #ifdef PLUGIN_DEBUGIN
304   fprintf(stderr, "LadspaPlugin::incReferences _references:%d val:%d\n", _references, val);
305   #endif
306 
307   int newref = _references + val;
308 
309   if(newref <= 0)
310   {
311     _references = 0;
312     if(_libHandle)
313     {
314       #ifdef PLUGIN_DEBUGIN
315       fprintf(stderr, "LadspaPlugin::incReferences no more instances, closing library\n");
316       #endif
317 
318       dlclose(_libHandle);
319     }
320 
321     _libHandle = 0;
322     _plugin = NULL;
323     _pIdx.clear();
324     _poIdx.clear();
325     _iIdx.clear();
326     _oIdx.clear();
327     _requiredFeatures = MusECore::PluginNoFeatures;
328 
329     return 0;
330   }
331 
332   if(_libHandle == 0)
333   {
334     _libHandle = dlopen(_fi.filePath().toLatin1().constData(), RTLD_NOW);
335 
336     if(_libHandle == 0)
337     {
338       fprintf(stderr, "LadspaPlugin::incReferences dlopen(%s) failed: %s\n",
339                     _fi.filePath().toLatin1().constData(), dlerror());
340       return 0;
341     }
342 
343     LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_libHandle, "ladspa_descriptor");
344     if(ladspadf)
345     {
346       const LADSPA_Descriptor* descr;
347       for(unsigned long i = 0;; ++i)
348       {
349         descr = ladspadf(i);
350         if(descr == NULL)
351           break;
352 
353         QString desc_label(descr->Label);
354         if(desc_label == label())
355         {
356           _plugin = descr;
357           break;
358         }
359       }
360     }
361 
362     if(_plugin != NULL)
363     {
364       _uniqueID = _plugin->UniqueID;
365 
366       _label = QString(_plugin->Label);
367       _name = QString(_plugin->Name);
368       _maker = QString(_plugin->Maker);
369       _copyright = QString(_plugin->Copyright);
370 
371       _portCount = _plugin->PortCount;
372       _inports = 0;
373       _outports = 0;
374       _controlInPorts = 0;
375       _controlOutPorts = 0;
376 
377       for(unsigned long k = 0; k < _portCount; ++k)
378       {
379         LADSPA_PortDescriptor pd = _plugin->PortDescriptors[k];
380         if(pd & LADSPA_PORT_AUDIO)
381         {
382           if(pd & LADSPA_PORT_INPUT)
383           {
384             ++_inports;
385             _iIdx.push_back(k);
386           }
387           else
388           if(pd & LADSPA_PORT_OUTPUT)
389           {
390             ++_outports;
391             _oIdx.push_back(k);
392           }
393         }
394         else
395         if(pd & LADSPA_PORT_CONTROL)
396         {
397           if(pd & LADSPA_PORT_INPUT)
398           {
399             ++_controlInPorts;
400             _pIdx.push_back(k);
401           }
402           else
403           if(pd & LADSPA_PORT_OUTPUT)
404           {
405             ++_controlOutPorts;
406             _poIdx.push_back(k);
407           }
408         }
409       }
410     }
411   }
412 
413   if(_plugin == NULL)
414   {
415     dlclose(_libHandle);
416     _libHandle = 0;
417     _references = 0;
418     fprintf(stderr, "LadspaPlugin::incReferences Error: %s no plugin!\n", _fi.filePath().toLatin1().constData());
419     return 0;
420   }
421 
422   if ((_inports != _outports) || (LADSPA_IS_INPLACE_BROKEN(_plugin->Properties)))
423     _requiredFeatures |= MusECore::PluginNoInPlaceProcessing;
424 
425   _references = newref;
426 
427   return _references;
428 }
429 
430 //---------------------------------------------------------
431 //   instantiate
432 //---------------------------------------------------------
433 
instantiate(float sampleRate,void *)434 void* LadspaPlugin::instantiate(float sampleRate, void*)
435 {
436   if(!_plugin)
437     return NULL;
438   bool success = false;
439   LADSPA_Handle h = _plugin->instantiate(_plugin, sampleRate);
440   success = (h != NULL);
441   if(success)
442   {
443     SP_DBG_LADSPA2("LadspaPlugin instantiated", label().toLatin1().constData());
444   }
445   return h;
446 }
447 
448 //---------------------------------------------------------
449 //   range
450 //---------------------------------------------------------
451 
port_range(unsigned long i,float sampleRate,float * min,float * max) const452 bool LadspaPlugin::port_range(unsigned long i, float sampleRate, float* min, float* max) const
453       {
454       if(!_plugin)
455         return false;
456       LADSPA_PortRangeHint range = _plugin->PortRangeHints[i];
457       LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
458       if (desc & LADSPA_HINT_TOGGLED) {
459             *min = 0.0;
460             *max = 1.0;
461             return true;
462             }
463       float m = 1.0;
464       if (desc & LADSPA_HINT_SAMPLE_RATE)
465             m = sampleRate;
466 
467       if (desc & LADSPA_HINT_BOUNDED_BELOW)
468             *min =  range.LowerBound * m;
469       else
470             *min = 0.0;
471       if (desc & LADSPA_HINT_BOUNDED_ABOVE)
472             *max =  range.UpperBound * m;
473       else
474             *max = 1.0;
475       return true;
476       }
477 
478 //---------------------------------------------------------
479 //   range
480 //---------------------------------------------------------
481 
range(unsigned long k,float sampleRate,float * min,float * max) const482 bool LadspaPlugin::range(unsigned long k, float sampleRate, float* min, float* max) const
483       {
484       SP_TRACE_IN
485       k = _pIdx[k];
486       SP_TRACE_OUT
487       return port_range(k, sampleRate, min, max);
488       }
489 
490 //---------------------------------------------------------
491 //   range
492 //---------------------------------------------------------
493 
rangeOut(unsigned long k,float sampleRate,float * min,float * max) const494 bool LadspaPlugin::rangeOut(unsigned long k, float sampleRate, float* min, float* max) const
495       {
496       SP_TRACE_IN
497       k = _poIdx[k];
498       SP_TRACE_OUT
499       return port_range(k, sampleRate, min, max);
500       }
501 
502 //---------------------------------------------------------
503 //   defaultValue
504 //---------------------------------------------------------
505 
defaultValue(unsigned long k) const506 float LadspaPlugin::defaultValue(unsigned long k) const
507       {
508       SP_TRACE_IN
509       if(!_plugin)
510         return 0.0f;
511       k = _pIdx[k];
512       LADSPA_PortRangeHint range = _plugin->PortRangeHints[k];
513       LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor;
514       LADSPA_Data val = 1.0;
515       if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh))
516             val = range.LowerBound;
517       else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh))
518             val = range.UpperBound;
519       else if (LADSPA_IS_HINT_DEFAULT_LOW(rh))
520             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
521                   val = exp(log(range.LowerBound) * .75 +
522                      log(range.UpperBound) * .25);
523             else
524                   val = range.LowerBound*.75 + range.UpperBound*.25;
525       else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh))
526             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
527                   val = exp(log(range.LowerBound) * .5 +
528                      log(range.UpperBound) * .5);
529             else
530                   val = range.LowerBound*.5 + range.UpperBound*.5;
531       else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh))
532             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
533                   val = exp(log(range.LowerBound) * .25 +
534                      log(range.UpperBound) * .75);
535             else
536                   val = range.LowerBound*.25 + range.UpperBound*.75;
537       else if (LADSPA_IS_HINT_DEFAULT_0(rh))
538             val = 0.0;
539       else if (LADSPA_IS_HINT_DEFAULT_1(rh))
540             val = 1.0;
541       else if (LADSPA_IS_HINT_DEFAULT_100(rh))
542             val = 100.0;
543       else if (LADSPA_IS_HINT_DEFAULT_440(rh))
544             val = 440.0;
545         // No default found. Make one up...
546       else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
547       {
548         if (LADSPA_IS_HINT_LOGARITHMIC(rh))
549           val = exp(log(range.LowerBound) * .5 +
550                 log(range.UpperBound) * .5);
551         else
552           val = range.LowerBound*.5 + range.UpperBound*.5;
553       }
554       else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh))
555           val = range.LowerBound;
556       else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
557       {
558           // Hm. What to do here... Just try 0.0 or the upper bound if less than zero.
559           //if(range.UpperBound > 0.0)
560           //  val = 0.0;
561           //else
562           //  val = range.UpperBound;
563           // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default.
564           val = range.UpperBound;
565           return true;
566       }
567 
568       SP_TRACE_OUT
569       return val;
570       }
571 
572 //---------------------------------------------------------
573 //   connectInport
574 //---------------------------------------------------------
connectInport(void * handle,unsigned long k,void * datalocation)575 void LadspaPlugin::connectInport(void* handle, unsigned long k, void* datalocation)
576       {
577       SP_TRACE_IN
578       if(!_plugin)
579         return;
580       _plugin->connect_port((LADSPA_Handle)handle, _iIdx[k], (LADSPA_Data*)datalocation);
581       SP_TRACE_OUT
582       }
583 
584 //---------------------------------------------------------
585 //   connectOutport
586 //---------------------------------------------------------
connectOutport(void * handle,unsigned long k,void * datalocation)587 void LadspaPlugin::connectOutport(void* handle, unsigned long k, void* datalocation)
588       {
589       SP_TRACE_IN
590       if(!_plugin)
591         return;
592       _plugin->connect_port((LADSPA_Handle)handle, _oIdx[k], (LADSPA_Data*)datalocation);
593       SP_TRACE_OUT
594       }
595 
596 //---------------------------------------------------------
597 //   connectCtrlInport
598 //---------------------------------------------------------
connectCtrlInport(void * handle,unsigned long k,void * datalocation)599 void LadspaPlugin::connectCtrlInport(void* handle, unsigned long k, void* datalocation)
600       {
601       SP_TRACE_IN
602       if(!_plugin)
603         return;
604       _plugin->connect_port((LADSPA_Handle)handle, _pIdx[k], (LADSPA_Data*)datalocation);
605       SP_TRACE_OUT
606       }
607 
608 //---------------------------------------------------------
609 //   connectCtrlOutport
610 //---------------------------------------------------------
connectCtrlOutport(void * handle,unsigned long k,void * datalocation)611 void LadspaPlugin::connectCtrlOutport(void* handle, unsigned long k, void* datalocation)
612       {
613       SP_TRACE_IN
614       if(!_plugin)
615         return;
616       _plugin->connect_port((LADSPA_Handle)handle, _poIdx[k], (LADSPA_Data*)datalocation);
617       SP_TRACE_OUT
618       }
619 
620 //---------------------------------------------------------
621 //   convertGuiControlValue
622 //  scale control value to gui-slider/checkbox representation
623 //---------------------------------------------------------
624 
convertGuiControlValue(unsigned long k,float sampleRate,int val) const625 float LadspaPlugin::convertGuiControlValue(unsigned long k, float sampleRate, int val) const
626       {
627       SP_TRACE_IN
628       float floatval = 0.0;
629       float min, max;
630       if(!range(k, sampleRate, &min, &max))
631         return floatval;
632 
633       if (isLog(k)) {
634             if (val > 0) {
635                   float logged = SS_map_pluginparam2logdomain(val);
636                   float e = expf(logged) * (max - min);
637                   e+=min;
638                   floatval = e;
639                   }
640             }
641       else if (isBool(k)) {
642             floatval = (float) val;
643             }
644       else if (isInt(k)) {
645             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
646             floatval = (float) round((((float) val) * scale) + min);
647             }
648       else {
649             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
650             floatval = (((float) val) * scale) + min;
651             }
652       SP_TRACE_OUT
653       return floatval;
654       }
655 
656 
657 //---------------------------------------------------------
658 //   PluginI
659 //---------------------------------------------------------
660 
init()661 void PluginI::init()
662       {
663       _plugin           = 0;
664       _sampleRate       = 44100.0f;
665       _dSampleRate      = _sampleRate;
666       _segmentSize      = 0;
667       _instances         = 0;
668       _controls          = 0;
669       _controlsOut       = 0;
670       _controlsOutDummy  = 0;
671       _audioInPorts      = 0;
672       _audioOutPorts     = 0;
673       _controlPorts      = 0;
674       _controlOutPorts   = 0;
675       _audioInSilenceBuf = 0;
676       _audioOutDummyBuf  = 0;
677       _hasLatencyOutPort = false;
678       _latencyOutPort = 0;
679       _on               = true;
680       }
681 
PluginI()682 PluginI::PluginI()
683       {
684       _id = -1;
685       init();
686       }
687 
688 //---------------------------------------------------------
689 //   PluginI
690 //---------------------------------------------------------
691 
~PluginI()692 PluginI::~PluginI()
693       {
694       if(_audioInSilenceBuf)
695         free(_audioInSilenceBuf);
696       if(_audioOutDummyBuf)
697         free(_audioOutDummyBuf);
698 
699       if(_controlsOutDummy)
700         delete[] _controlsOutDummy;
701       if(_controlsOut)
702         delete[] _controlsOut;
703       if(_controls)
704         delete[] _controls;
705       }
706 
707 //---------------------------------------------------------
708 //   setID
709 //---------------------------------------------------------
710 
setID(int i)711 void PluginI::setID(int i)
712 {
713   _id = i;
714 }
715 
716 //---------------------------------------------------------
717 //   getGuiControlValue
718 //  scale control value to gui-slider/checkbox representation
719 //---------------------------------------------------------
720 
getGuiControlValue(unsigned long parameter) const721 int PluginI::getGuiControlValue(unsigned long parameter) const
722       {
723       SP_TRACE_IN
724       float val = param(parameter);
725       float min, max;
726       range(parameter, &min, &max);
727       int intval;
728       if (isLog(parameter)) {
729             intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min));
730             }
731       else if (isBool(parameter)) {
732             intval = (int) val;
733             }
734       else {
735             float scale = SS_PLUGIN_PARAM_MAX / (max - min);
736             intval = (int) ((val - min) * scale);
737             }
738       SP_TRACE_OUT
739       return intval;
740       }
741 
742 //---------------------------------------------------------
743 //   convertGuiControlValue
744 //  scale control value to gui-slider/checkbox representation
745 //---------------------------------------------------------
746 
convertGuiControlValue(unsigned long parameter,int val) const747 float PluginI::convertGuiControlValue(unsigned long parameter, int val) const
748       {
749       SP_TRACE_IN
750       float floatval = 0;
751       float min, max;
752       range(parameter, &min, &max);
753 
754       if (isLog(parameter)) {
755             if (val > 0) {
756                   float logged = SS_map_pluginparam2logdomain(val);
757                   float e = expf(logged) * (max - min);
758                   e+=min;
759                   floatval = e;
760                   }
761             }
762       else if (isBool(parameter)) {
763             floatval = (float) val;
764             }
765       else if (isInt(parameter)) {
766             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
767             floatval = (float) round((((float) val) * scale) + min);
768             }
769       else {
770             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
771             floatval = (((float) val) * scale) + min;
772             }
773       SP_TRACE_OUT
774       return floatval;
775       }
776 
777 //---------------------------------------------------------
778 //   apply
779 //   If ports is 0, just process controllers only, not audio (do not 'run').
780 //---------------------------------------------------------
781 
apply(unsigned,unsigned long frames,unsigned long ports,float ** bufIn,float ** bufOut)782 void PluginI::apply(unsigned /*pos*/, unsigned long frames, unsigned long ports, float** bufIn, float** bufOut)
783 {
784 
785 #ifdef PLUGIN_DEBUGIN_PROCESS
786     fprintf(stderr, "PluginI::apply nsamp:%lu\n", n);
787 #endif
788 
789   if(!_plugin)
790     return;
791 
792   if(ports > 0)     // Don't bother if not 'running'.
793   {
794     connect(ports, 0, bufIn, bufOut);
795     process(frames);
796   }
797 }
798 
799 //---------------------------------------------------------
800 //   start
801 // activate and connect control ports
802 //---------------------------------------------------------
803 
start()804 bool PluginI::start()
805       {
806       if(!_plugin)
807         return false;
808       // Activate all the instances.
809       return activate();
810       }
811 
812 //---------------------------------------------------------
813 //   stop
814 // deactivate
815 //---------------------------------------------------------
stop()816 bool PluginI::stop()
817       {
818       if(!_plugin)
819         return false;
820       // Activate all the instances.
821       return deactivate();
822       }
823 
824 //---------------------------------------------------------
825 //   setParam
826 //---------------------------------------------------------
827 
setParam(unsigned long i,float val)828 void PluginI::setParam(unsigned long i, float val)
829 {
830   if(i >= _controlPorts)
831     return;
832   _controls[i]._val = val;
833 }
834 
835 // TODO
836 // //---------------------------------------------------------
837 // //   connect
838 // //---------------------------------------------------------
839 //
840 // void PluginI::connectInport(unsigned long k, void* datalocation)
841 //       {
842 //       if(!_plugin) return;
843 // //       const unsigned long ins = _plugin->inports();
844 // //       const unsigned long handle_idx = (k / ins) % instances;
845 // //       const unsigned long port_idx = k % ins;
846 // //       _plugin->connectInport(handle[handle_idx], port_idx, datalocation);
847 //
848 //       const unsigned long ins = _plugin->inports();
849 //       for (int i = 0; i < instances; ++i) {
850 //         for (unsigned long k = 0; k < audio; ++k) {
851 //       }
852 //
853 //       const unsigned long port_count = _plugin->portCount();
854 //       unsigned long port = 0;
855 //       for (int i = 0; i < instances; ++i) {
856 //             for (unsigned long k = 0; k < port_count; ++k) {
857 //                   if (_plugin->isAudioIn(k)) {
858 //                         //if(port < ports)
859 //                         if(port < channel)
860 //                           //_plugin->connectPort(handle[i], k, src[port] + offset);
861 //                           _plugin->connectInport(handle[i], k, datalocation);
862 //                         else
863 //                           // Connect to an input silence buffer.
864 //                           _plugin->connectInport(handle[i], k, _audioInSilenceBuf);
865 //                         ++port;
866 //                         }
867 //                   }
868 //             }
869 //       }
870 //
871 // void PluginI::connectOutport(unsigned long k, void* datalocation)
872 //       {
873 //       if(!_plugin) return;
874 // //       const unsigned long outs = _plugin->outports();
875 // //       const unsigned long handle_idx = (k / outs) % instances;
876 // //       const unsigned long port_idx = k % outs;
877 // //       _plugin->connectOutport(handle[handle_idx], port_idx, datalocation);
878 //
879 //       const unsigned long port_count = _plugin->portCount();
880 //       unsigned long port = 0;
881 //       for (int i = 0; i < instances; ++i) {
882 //             for (unsigned long k = 0; k < port_count; ++k) {
883 //                   if (_plugin->isAudioOut(k)) {
884 //                         //if(port < ports)
885 //                         if(port < channel)
886 //                           _plugin->connectOutport(handle[i], k, datalocation);
887 //                         else
888 //                           // Connect to a dummy buffer.
889 //                           _plugin->connectOutport(handle[i], k, _audioOutDummyBuf);
890 //                         ++port;
891 //                         }
892 //                   }
893 //             }
894 //       }
895 //
896 // void PluginI::connectCtrlInport(unsigned long k, void* datalocation)
897 //       {
898 //       if(!_plugin) return;
899 //       const unsigned long ctrl_ins = _plugin->parameter();
900 //       const unsigned long handle_idx = (k / ctrl_ins) % instances;
901 //       const unsigned long port_idx = k % ctrl_ins;
902 //       _plugin->connectCtrlInport(handle[handle_idx], port_idx, datalocation);
903 //       }
904 //
905 // void PluginI::connectCtrlOutport(unsigned long k, void* datalocation)
906 //       {
907 //       if(!_plugin) return;
908 //       const unsigned long ctrl_outs = _plugin->parameterOut();
909 //       const unsigned long handle_idx = (k / ctrl_outs) % instances;
910 //       const unsigned long port_idx = k % ctrl_outs;
911 //       _plugin->connectCtrlOutport(handle[handle_idx], port_idx, datalocation);
912 //       }
913 
914 //---------------------------------------------------------
915 //   latency
916 //---------------------------------------------------------
917 
latency() const918 float PluginI::latency() const
919 {
920   if(!_hasLatencyOutPort)
921     return 0.0;
922   return _controlsOut[_latencyOutPort]._val;
923 }
924 
925 
926 //---------------------------------------------------------
927 //   setControl
928 //    set plugin instance controller value by name
929 //---------------------------------------------------------
930 
setControl(const QString & s,float val)931 bool PluginI::setControl(const QString& s, float val)
932       {
933       if(!_plugin)
934         return true;
935       for (unsigned long i = 0; i < _controlPorts; ++i) {
936             if (QString(_plugin->getParameterName(i)) == s) {
937                   setParam(i, val);
938                   return false;
939                   }
940             }
941       fprintf(stderr, "PluginI:setControl(%s, %f) controller not found\n",
942          s.toLatin1().constData(), val);
943       return true;
944       }
945 
946 
947 
948 //---------------------------------------------------------
949 //   LadspaPluginI
950 //---------------------------------------------------------
951 
init()952 void LadspaPluginI::init()
953 {
954   _handle = 0;
955 }
956 
LadspaPluginI()957 LadspaPluginI::LadspaPluginI()
958   : PluginI()
959 {
960   init();
961 }
962 
963 //---------------------------------------------------------
964 //   LadspaPluginI
965 //---------------------------------------------------------
966 
~LadspaPluginI()967 LadspaPluginI::~LadspaPluginI()
968 {
969   if(_plugin) {
970     // Deactivate is pure virtual, it cannot be
971     //  called from the base destructor. Do it here.
972     deactivate();
973     _plugin->incReferences(-1);
974   }
975 
976   if(_handle)
977     delete[] _handle;
978 }
979 
980 //---------------------------------------------------------
981 //   initPluginInstance
982 //    return true on error
983 //---------------------------------------------------------
984 
initPluginInstance(Plugin * plug,int chans,float sampleRate,unsigned int segmentSize,bool useDenormalBias,float denormalBias)985 bool LadspaPluginI::initPluginInstance(Plugin* plug, int chans,
986                                  float sampleRate, unsigned int segmentSize,
987                                  bool useDenormalBias, float denormalBias)
988 {
989   _sampleRate = _dSampleRate = sampleRate;
990   _segmentSize = segmentSize;
991   _channel = chans;
992   if(plug == 0)
993   {
994     fprintf(stderr, "LadspaPluginI::initPluginInstance: zero plugin\n");
995     return true;
996   }
997   _plugin = plug;
998 
999   if (_plugin->incReferences(1)==0)
1000     return true;
1001 
1002   QString inst("-" + QString::number(_plugin->instNo()));
1003   _name  = _plugin->name() + inst;
1004   _label = _plugin->label() + inst;
1005 
1006   const unsigned long ins = _plugin->inports();
1007   const unsigned long outs = _plugin->outports();
1008   if(outs)
1009   {
1010     _instances = _channel / outs;
1011     // Ask for one more instance for remainder if required.
1012     const int re = _channel % outs;
1013     if(re != 0)
1014       ++_instances;
1015     if(_instances < 1)
1016       _instances = 1;
1017   }
1018   else
1019   if(ins)
1020   {
1021     _instances = _channel / ins;
1022     // Ask for one more instance for remainder if required.
1023     const int re = _channel % ins;
1024     if(re != 0)
1025       ++_instances;
1026     if(_instances < 1)
1027       _instances = 1;
1028   }
1029   else
1030     _instances = 1;
1031 
1032   _handle = new LADSPA_Handle[_instances];
1033   for(int i = 0; i < _instances; ++i)
1034     _handle[i]=NULL;
1035 
1036   for(int i = 0; i < _instances; ++i)
1037   {
1038     #ifdef PLUGIN_DEBUGIN
1039     fprintf(stderr, "LadspaPluginI::initPluginInstance instance:%d\n", i);
1040     #endif
1041 
1042     _handle[i] = _plugin->instantiate(_sampleRate, NULL);
1043     if(_handle[i] == NULL)
1044       return true;
1045   }
1046 
1047   const unsigned long port_count = _plugin->portCount();
1048 
1049   _audioInPorts = 0;
1050   _audioOutPorts = 0;
1051   _controlPorts = 0;
1052   _controlOutPorts = 0;
1053 
1054   unsigned long port = 0;
1055   for (int i = 0; i < _instances; ++i) {
1056         for (unsigned long k = 0; k < port_count; ++k) {
1057               if (_plugin->isAudioIn(k)) {
1058                     if(port < (unsigned long)_channel)
1059                       ++_audioInPorts;
1060                     ++port;
1061                     }
1062               }
1063         }
1064   port = 0;
1065   for (int i = 0; i < _instances; ++i) {
1066         for (unsigned long k = 0; k < port_count; ++k) {
1067               if (_plugin->isAudioOut(k)) {
1068                     if(port < (unsigned long)_channel)
1069                       ++_audioOutPorts;
1070                     ++port;
1071                     }
1072               }
1073         }
1074 
1075   for(unsigned long k = 0; k < port_count; ++k)
1076   {
1077     if(_plugin->isParameterIn(k))
1078       ++_controlPorts;
1079     else
1080     if(_plugin->isParameterOut(k))
1081       ++_controlOutPorts;
1082   }
1083 
1084   if(_controlPorts)
1085     _controls = new Port[_controlPorts];
1086   if(_controlOutPorts)
1087   {
1088     _controlsOut = new Port[_controlOutPorts];
1089     _controlsOutDummy = new Port[_controlOutPorts];
1090   }
1091 
1092   for(unsigned long k = 0; k < _controlPorts; ++k)
1093   {
1094     // Set the parameter input's initial value to the default.
1095     const float val = _plugin->defaultValue(k);
1096     _controls[k]._val = val;
1097     // All instances' parameter inputs share the same controls.
1098     // We don't have a mechanism to expose the other instances' inputs.
1099     for(int i = 0; i < _instances; ++i)
1100       _plugin->connectCtrlInport(_handle[i], k, &_controls[k]._val);
1101   }
1102 
1103   for(unsigned long k = 0; k < _controlOutPorts; ++k)
1104   {
1105     // Set the parameter output's initial value to zero.
1106     _controlsOut[k]._val = 0.0f;
1107     // Check for a latency port.
1108     const char* pname = _plugin->getParameterOutName(k);
1109     if(pname == QString("latency") || pname == QString("_latency"))
1110     {
1111       _hasLatencyOutPort = true;
1112       _latencyOutPort = k;
1113     }
1114     // Connect only the first instance's parameter output controls.
1115     // We don't have a mechanism to display the other instances' outputs.
1116     if(_instances > 0)
1117     {
1118       _plugin->connectCtrlOutport(_handle[0], k, &_controlsOut[k]._val);
1119       // Connect the rest to dummy ports.
1120       for(int i = 1; i < _instances; ++i)
1121         _plugin->connectCtrlOutport(_handle[i], k, &_controlsOutDummy[k]._val);
1122     }
1123   }
1124 
1125 #ifdef _WIN32
1126   _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float) * _segmentSize);
1127   if(_audioInSilenceBuf == NULL)
1128   {
1129       fprintf(stderr,
1130         "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n");
1131       abort();
1132   }
1133 #else
1134   int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * _segmentSize);
1135   if(rv != 0)
1136   {
1137       fprintf(stderr,
1138         "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv);
1139       abort();
1140   }
1141 #endif
1142 
1143   if(useDenormalBias)
1144   {
1145       for(unsigned q = 0; q < _segmentSize; ++q)
1146       {
1147         _audioInSilenceBuf[q] = denormalBias;
1148       }
1149   }
1150   else
1151   {
1152       memset(_audioInSilenceBuf, 0, sizeof(float) * _segmentSize);
1153   }
1154 
1155 #ifdef _WIN32
1156   _audioOutDummyBuf = (float *) _aligned_malloc(16, sizeof(float) * _segmentSize);
1157   if(_audioOutDummyBuf == NULL)
1158   {
1159       fprintf(stderr,
1160         "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n");
1161       abort();
1162   }
1163 #else
1164   rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * _segmentSize);
1165   if(rv != 0)
1166   {
1167       fprintf(stderr, "ERROR: LadspaPluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv);
1168       abort();
1169   }
1170 #endif
1171 
1172   // Don't activate yet.
1173   //activate();
1174   return false;
1175 }
1176 
1177 //---------------------------------------------------------
1178 //   setChannels
1179 //---------------------------------------------------------
1180 
setChannels(int chans)1181 void LadspaPluginI::setChannels(int chans)
1182 {
1183      _channel = chans;
1184 
1185      if(!_plugin)
1186        return;
1187 
1188       const unsigned long ins = _plugin->inports();
1189       const unsigned long outs = _plugin->outports();
1190       int ni = 1;
1191       if(outs)
1192       {
1193         ni = chans / outs;
1194         // Ask for one more instance for remainder if required.
1195         const int re = chans % outs;
1196         if(re != 0)
1197           ++ni;
1198       }
1199       else
1200       if(ins)
1201       {
1202         ni = chans / ins;
1203         // Ask for one more instance for remainder if required.
1204         const int re = chans % ins;
1205         if(re != 0)
1206           ++ni;
1207       }
1208 
1209       if(ni < 1)
1210         ni = 1;
1211 
1212       if (ni == _instances)
1213             return;
1214 
1215       LADSPA_Handle* handles = new LADSPA_Handle[ni];
1216 
1217       if(ni > _instances)
1218       {
1219         for(int i = 0; i < ni; ++i)
1220         {
1221           if(i < _instances)
1222             // Transfer existing handle from old array to new array.
1223             handles[i] = _handle[i];
1224           else
1225           {
1226             // Create a new plugin instance with handle.
1227             // Use the plugin's current sample rate.
1228             handles[i] = _plugin->instantiate(_sampleRate, NULL);
1229             if(handles[i] == NULL)
1230             {
1231               fprintf(stderr, "LadspaPluginI::setChannels: cannot instantiate instance %d\n", i);
1232 
1233               // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert().
1234               // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save.
1235               for(int k = i; k < ni; ++k)
1236                 handles[i] = NULL;
1237               ni = i + 1;
1238               //channel = ?;
1239               break;
1240             }
1241           }
1242         }
1243       }
1244       else
1245       {
1246         for(int i = 0; i < _instances; ++i)
1247         {
1248           if(i < ni)
1249             // Transfer existing handle from old array to new array.
1250             handles[i] = _handle[i];
1251           else
1252           {
1253             // Delete existing plugin instance.
1254             // Previously we deleted all the instances and rebuilt from scratch.
1255             // One side effect of this: Since a GUI is constructed only on the first handle,
1256             //  previously the native GUI would close when changing channels. Now it doesn't, which is good.
1257             _plugin->deactivate(_handle[i]);
1258             _plugin->cleanup(_handle[i]);
1259           }
1260         }
1261       }
1262 
1263       // Delete the old array, and set the new array.
1264       delete[] _handle;
1265       _handle = handles;
1266 
1267       // Connect new instances' ports:
1268       for(unsigned long k = 0; k < _controlPorts; ++k)
1269       {
1270         for(int i = _instances; i < ni; ++i)
1271         {
1272           // All instances' parameter inputs share the same controls.
1273           // We don't have a mechanism to expose the other instances' inputs.
1274           _plugin->connectCtrlInport(handles[i], k, &_controls[k]._val);
1275         }
1276       }
1277 
1278       for(unsigned long k = 0; k < _controlOutPorts; ++k)
1279       {
1280         // Connect only the first instance's parameter output controls.
1281         // We don't have a mechanism to display the other instances' outputs.
1282         if(_instances == 0 && ni > 0)
1283           // Only if the existing instances was zero. We are creating one(s) now.
1284           _plugin->connectCtrlOutport(_handle[0], k, &_controlsOut[k]._val);
1285         else
1286         {
1287           // Connect the rest to dummy ports.
1288           for(int i = _instances; i < ni; ++i)
1289             _plugin->connectCtrlOutport(_handle[i], k, &_controlsOutDummy[k]._val);
1290         }
1291       }
1292 
1293       // Activate new instances.
1294       for(int i = _instances; i < ni; ++i)
1295         _plugin->activate(_handle[i]);
1296 
1297       // Finally, set the new number of instances.
1298       _instances = ni;
1299 }
1300 
1301 //---------------------------------------------------------
1302 //   connect
1303 //---------------------------------------------------------
1304 
connect(unsigned long ports,unsigned long offset,float ** src,float ** dst)1305 void LadspaPluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst)
1306       {
1307       if(!_plugin) return;
1308 
1309       const unsigned long port_count = _plugin->portCount();
1310       unsigned long port = 0;
1311       for (int i = 0; i < _instances; ++i) {
1312             for (unsigned long k = 0; k < port_count; ++k) {
1313                   if (isAudioIn(k)) {
1314                         if(port < ports)
1315                           _plugin->connectPort(_handle[i], k, src[port] + offset);
1316                         else
1317                           // Connect to an input silence buffer.
1318                           _plugin->connectPort(_handle[i], k, _audioInSilenceBuf + offset);
1319                         ++port;
1320                         }
1321                   }
1322             }
1323       port = 0;
1324       for (int i = 0; i < _instances; ++i) {
1325             for (unsigned long k = 0; k < port_count; ++k) {
1326                   if (isAudioOut(k)) {
1327                         if(port < ports)
1328                           _plugin->connectPort(_handle[i], k, dst[port] + offset);
1329                         else
1330                           // Connect to a dummy buffer.
1331                           _plugin->connectPort(_handle[i], k, _audioOutDummyBuf + offset);
1332                         ++port;
1333                         }
1334                   }
1335             }
1336       }
1337 
1338 
1339 //---------------------------------------------------------
1340 //   deactivate
1341 //---------------------------------------------------------
1342 
deactivate()1343 bool LadspaPluginI::deactivate()
1344       {
1345       if(!_plugin)
1346         return false;
1347       for (int i = 0; i < _instances; ++i) {
1348             _plugin->deactivate(_handle[i]);
1349             _plugin->cleanup(_handle[i]);
1350             }
1351       return true;
1352       }
1353 
1354 //---------------------------------------------------------
1355 //   activate
1356 //---------------------------------------------------------
1357 
activate()1358 bool LadspaPluginI::activate()
1359       {
1360       if(!_plugin)
1361         return false;
1362       for (int i = 0; i < _instances; ++i)
1363             _plugin->activate(_handle[i]);
1364       return true;
1365       }
1366 
process(unsigned long frames)1367 void LadspaPluginI::process(unsigned long frames)
1368 {
1369   if(!_plugin)
1370     return;
1371   for(int i = 0; i < _instances; ++i)
1372     _plugin->apply(_handle[i], frames);
1373 }
1374 
1375 } // namespace MusESimplePlugin
1376