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 <unistd.h>
32 #include <dlfcn.h>
33 #include <string>
34 #include "ssplugin.h"
35 #include "common.h"
36 
37 #define SS_LOG_MAX   0
38 #define SS_LOG_MIN -10
39 #define SS_LOG_OFFSET SS_LOG_MIN
40 
41 
42 //
43 // Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain)
44 //
SS_map_pluginparam2logdomain(int pluginparam_val)45 float SS_map_pluginparam2logdomain(int pluginparam_val)
46 {
47    float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX;
48    float scaled = (float) pluginparam_val * scale;
49    float mapped = scaled + SS_LOG_OFFSET;
50    return mapped;
51 }
52 //
53 // 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])
54 // (inverse func to the above)
SS_map_logdomain2pluginparam(float pluginparam_log)55 int SS_map_logdomain2pluginparam(float pluginparam_log)
56 {
57    float mapped = pluginparam_log - SS_LOG_OFFSET;
58    float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN);
59    int scaled  = (int) round(mapped * scale);
60    return scaled;
61 }
62 
63 PluginList plugins;
64 
65 
Plugin(const QFileInfo * f)66 Plugin::Plugin(const QFileInfo* f)
67    : fi(*f)
68       {
69       }
70 
71 //---------------------------------------------------------
72 //   loadPluginLib
73 //---------------------------------------------------------
74 
loadPluginLib(QFileInfo * fi)75 static void loadPluginLib(QFileInfo* fi)
76       {
77       SS_TRACE_IN
78       if (SS_DEBUG_LADSPA) {
79             printf("loadPluginLib: %s\n", fi->fileName().toLatin1().constData());
80             }
81       void* handle = dlopen(fi->filePath().toLatin1().constData(), RTLD_NOW);
82       if (handle == 0) {
83             fprintf(stderr, "dlopen(%s) failed: %s\n",
84               fi->filePath().toLatin1().constData(), dlerror());
85             return;
86             }
87       LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor");
88 
89       if (!ladspa) {
90             const char *txt = dlerror();
91             if (txt) {
92                   fprintf(stderr,
93                         "Unable to find ladspa_descriptor() function in plugin "
94                         "library file \"%s\": %s.\n"
95                         "Are you sure this is a LADSPA plugin file?\n",
96                         fi->filePath().toLatin1().constData(),
97                         txt);
98                   return;//exit(1);
99                   }
100             }
101       const LADSPA_Descriptor* descr;
102       for (int i = 0;; ++i) {
103             descr = ladspa(i);
104             if (descr == NULL)
105                   break;
106             plugins.push_back(new LadspaPlugin(fi, ladspa, descr));
107             }
108       SS_TRACE_OUT
109       }
110 
111 //---------------------------------------------------------
112 //   loadPluginDir
113 //---------------------------------------------------------
114 
loadPluginDir(const QString & s)115 static void loadPluginDir(const QString& s)
116       {
117       SS_TRACE_IN
118       QDir pluginDir(s, QString("*.so"), 0, QDir::Files);
119       if (pluginDir.exists()) {
120             QFileInfoList list = pluginDir.entryInfoList();
121             int n = list.size();
122             for (int i = 0; i < n; ++i) {
123                   QFileInfo fi = list.at(i);
124                   loadPluginLib(&fi);
125                   }
126             }
127       SS_TRACE_OUT
128       }
129 
130 //---------------------------------------------------------
131 //   initPlugins
132 //    search for LADSPA plugins
133 //---------------------------------------------------------
134 
SS_initPlugins()135 void SS_initPlugins()
136       {
137       SS_TRACE_IN
138       //loadPluginDir(museGlobalLib + QString("/plugins"));
139 
140       std::string s;
141       const char* ladspaPath = getenv("LADSPA_PATH");
142       if (ladspaPath == 0)
143       {
144           const char* home = getenv("HOME");
145           s = std::string(home) + std::string("/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa");
146           ladspaPath = s.c_str();
147       }
148       const char* p = ladspaPath;
149       while (*p != '\0') {
150             const char* pe = p;
151             while (*pe != ':' && *pe != '\0')
152                   pe++;
153 
154             int n = pe - p;
155             if (n) {
156                   char* buffer = new char[n + 1];
157                   strncpy(buffer, p, n);
158                   buffer[n] = '\0';
159                   loadPluginDir(QString(buffer));
160                   delete[] buffer;
161                   }
162             p = pe;
163             if (*p == ':')
164                   p++;
165             }
166       SS_TRACE_OUT
167       }
168 
169 
170 //---------------------------------------------------------
171 //   LadspaPlugin
172 //---------------------------------------------------------
173 
LadspaPlugin(const QFileInfo * f,const LADSPA_Descriptor_Function ldf,const LADSPA_Descriptor * d)174 LadspaPlugin::LadspaPlugin(const QFileInfo* f,
175    const LADSPA_Descriptor_Function ldf,
176    const LADSPA_Descriptor* d)
177    : Plugin(f), ladspa(ldf), plugin(d)
178       {
179       SS_TRACE_IN
180       _inports        = 0;
181       _outports       = 0;
182       _parameter      = 0;
183       handle          = 0;
184       active          = false;
185       controls        = 0;
186       inputs          = 0;
187       outputs         = 0;
188 
189       for (unsigned k = 0; k < plugin->PortCount; ++k) {
190             LADSPA_PortDescriptor pd = d->PortDescriptors[k];
191             static const int CI = LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT;
192             if ((pd &  CI) == CI) {
193                   ++_parameter;
194                   pIdx.push_back(k);
195                   }
196             else if (pd &  LADSPA_PORT_INPUT) {
197                   ++_inports;
198                   iIdx.push_back(k);
199                   }
200             else if (pd &  LADSPA_PORT_OUTPUT) {
201                   ++_outports;
202                   oIdx.push_back(k);
203                   }
204             }
205 
206       /*if (SS_DEBUG_LADSPA) {
207             printf("Label: %s\tLib: %s\tPortCount: %d\n", this->label().toLatin1().constData(), this->lib().toLatin1().constData(), plugin->PortCount);
208             printf("LADSPA_PORT_CONTROL|LADSPA_PORT_INPUT: %d\t", pIdx.size());
209             printf("Input ports: %d\t", iIdx.size());
210             printf("Output ports: %d\n\n", oIdx.size());
211             }*/
212 
213       LADSPA_Properties properties = plugin->Properties;
214       _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(properties);
215       if (_inports != _outports)
216             _inPlaceCapable = false;
217       SS_TRACE_OUT
218       }
219 
220 //---------------------------------------------------------
221 //   ~LadspaPlugin
222 //---------------------------------------------------------
~LadspaPlugin()223 LadspaPlugin::~LadspaPlugin()
224       {
225       SS_TRACE_IN
226       if (active) {
227             stop();
228             }
229       if (handle) {
230          SS_DBG_LADSPA2("Cleaning up ", this->label().toLatin1().constData());
231          plugin->cleanup(handle);
232          }
233 
234       //Free ports:
235       if (controls)
236             delete controls;
237       if (inputs)
238             delete inputs;
239       if (outputs)
240             delete outputs;
241       SS_TRACE_OUT
242       }
243 
244 //---------------------------------------------------------
245 //   instantiate
246 //---------------------------------------------------------
247 
instantiate()248 bool LadspaPlugin::instantiate()
249       {
250       bool success = false;
251       handle = plugin->instantiate(plugin, SS_samplerate);
252       success = (handle != NULL);
253       if (success)
254             SS_DBG_LADSPA2("Plugin instantiated", label().toLatin1().constData());
255       return success;
256       }
257 
258 //---------------------------------------------------------
259 //   start
260 // activate and connect control ports
261 //---------------------------------------------------------
262 
start()263 bool LadspaPlugin::start()
264       {
265       SS_TRACE_IN
266       if (handle) {
267             if (plugin->activate) {
268                   plugin->activate(handle);
269                   SS_DBG_LADSPA("Plugin activated");
270                   }
271             active = true;
272             }
273       else {
274             SS_DBG_LADSPA("Error trying to activate plugin - plugin not instantiated!");
275             SS_TRACE_OUT
276             return false;
277             }
278 
279       //Connect ports:
280       controls = new Port[_parameter];
281 
282       for (int k = 0; k < _parameter; ++k) {
283             double val = defaultValue(k);
284             controls[k].val    = val;
285             plugin->connect_port(handle, pIdx[k], &controls[k].val);
286             }
287 
288       outputs  = new Port[_outports];
289       inputs   = new Port[_inports];
290 
291       SS_TRACE_OUT
292       return true;
293       }
294 
295 //---------------------------------------------------------
296 //   stop
297 // deactivate
298 //---------------------------------------------------------
stop()299 void LadspaPlugin::stop()
300       {
301       SS_TRACE_IN
302       if (handle) {
303             SS_DBG_LADSPA2("Trying to stop plugin", label().toLatin1().constData());
304             if (plugin->deactivate) {
305                   SS_DBG_LADSPA2("Deactivating ", label().toLatin1().constData());
306                   plugin->deactivate(handle);
307                   active = false;
308                   }
309             }
310       else
311             SS_DBG_LADSPA("Warning - tried to stop plugin, but plugin was never started...\n");
312       SS_TRACE_OUT
313       }
314 
315 //---------------------------------------------------------
316 //   range
317 //---------------------------------------------------------
318 
range(int i,float * min,float * max) const319 void LadspaPlugin::range(int i, float* min, float* max) const
320       {
321       SS_TRACE_IN
322       i = pIdx[i];
323       LADSPA_PortRangeHint range = plugin->PortRangeHints[i];
324       LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor;
325       if (desc & LADSPA_HINT_TOGGLED) {
326             *min = 0.0;
327             *max = 1.0;
328             return;
329             }
330       float m = 1.0;
331       if (desc & LADSPA_HINT_SAMPLE_RATE)
332             m = (float) SS_samplerate;
333 
334       if (desc & LADSPA_HINT_BOUNDED_BELOW)
335             *min =  range.LowerBound * m;
336       else
337             *min = 0.0;
338       if (desc & LADSPA_HINT_BOUNDED_ABOVE)
339             *max =  range.UpperBound * m;
340       else
341             *max = 1.0;
342       SS_TRACE_OUT
343       }
344 
345 //---------------------------------------------------------
346 //   defaultValue
347 //---------------------------------------------------------
348 
defaultValue(int k) const349 float LadspaPlugin::defaultValue(int k) const
350       {
351       SS_TRACE_IN
352       k = pIdx[k];
353       LADSPA_PortRangeHint range = plugin->PortRangeHints[k];
354       LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor;
355       double val = 1.0;
356       if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh))
357             val = range.LowerBound;
358       else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh))
359             val = range.UpperBound;
360       else if (LADSPA_IS_HINT_DEFAULT_LOW(rh))
361             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
362                   val = exp(log(range.LowerBound) * .75 +
363                      log(range.UpperBound) * .25);
364             else
365                   val = range.LowerBound*.75 + range.UpperBound*.25;
366       else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh))
367             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
368                   val = exp(log(range.LowerBound) * .5 +
369                      log(range.UpperBound) * .5);
370             else
371                   val = range.LowerBound*.5 + range.UpperBound*.5;
372       else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh))
373             if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor))
374                   val = exp(log(range.LowerBound) * .25 +
375                      log(range.UpperBound) * .75);
376             else
377                   val = range.LowerBound*.25 + range.UpperBound*.75;
378       else if (LADSPA_IS_HINT_DEFAULT_0(rh))
379             val = 0.0;
380       else if (LADSPA_IS_HINT_DEFAULT_1(rh))
381             val = 1.0;
382       else if (LADSPA_IS_HINT_DEFAULT_100(rh))
383             val = 100.0;
384       else if (LADSPA_IS_HINT_DEFAULT_440(rh))
385             val = 440.0;
386         // No default found. Make one up...
387       else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
388       {
389         if (LADSPA_IS_HINT_LOGARITHMIC(rh))
390           val = exp(log(range.LowerBound) * .5 +
391                 log(range.UpperBound) * .5);
392         else
393           val = range.LowerBound*.5 + range.UpperBound*.5;
394       }
395       else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh))
396           val = range.LowerBound;
397       else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh))
398       {
399           // Hm. What to do here... Just try 0.0 or the upper bound if less than zero.
400           //if(range.UpperBound > 0.0)
401           //  val = 0.0;
402           //else
403           //  val = range.UpperBound;
404           // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default.
405           val = range.UpperBound;
406           return true;
407       }
408 
409       SS_TRACE_OUT
410       return val;
411       }
412 
413 //---------------------------------------------------------
414 //   find
415 //---------------------------------------------------------
416 
find(const QString & file,const QString & name)417 Plugin* PluginList::find(const QString& file, const QString& name)
418       {
419       SS_TRACE_IN
420       for (iPlugin i = begin(); i != end(); ++i) {
421             if ((file == (*i)->lib()) && (name == (*i)->label())) {
422                   SS_TRACE_OUT
423                   return *i;
424                   }
425             }
426       printf("Plugin <%s> not found\n", name.toLatin1().constData());
427       SS_TRACE_OUT
428       return 0;
429       }
430 
431 //---------------------------------------------------------
432 //   connectInport
433 //---------------------------------------------------------
connectInport(int k,LADSPA_Data * datalocation)434 void LadspaPlugin::connectInport(int k, LADSPA_Data* datalocation)
435       {
436       SS_TRACE_IN
437       plugin->connect_port(handle, iIdx[k], datalocation);
438       SS_TRACE_OUT
439       }
440 
441 //---------------------------------------------------------
442 //   connectOutport
443 //---------------------------------------------------------
connectOutport(int k,LADSPA_Data * datalocation)444 void LadspaPlugin::connectOutport(int k, LADSPA_Data* datalocation)
445       {
446       SS_TRACE_IN
447       plugin->connect_port(handle, oIdx[k], datalocation);
448       SS_TRACE_OUT
449       }
450 
451 //---------------------------------------------------------
452 //   process
453 //---------------------------------------------------------
process(unsigned long frames)454 void LadspaPlugin::process(unsigned long frames)
455       {
456       plugin->run(handle, frames);
457       }
458 
459 //---------------------------------------------------------
460 //   setParam
461 //---------------------------------------------------------
462 
setParam(int k,float val)463 void LadspaPlugin::setParam(int k, float val)
464       {
465       SS_TRACE_IN
466       controls[k].val = val;
467       SS_TRACE_OUT
468       }
469 
470 //---------------------------------------------------------
471 //   getGuiControlValue
472 //  scale control value to gui-slider/checkbox representation
473 //---------------------------------------------------------
474 
getGuiControlValue(int param) const475 int LadspaPlugin::getGuiControlValue(int param) const
476       {
477       SS_TRACE_IN
478       float val = getControlValue(param);
479       float min, max;
480       range(param, &min, &max);
481       int intval;
482       if (isLog(param)) {
483             intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min));
484             }
485       else if (isBool(param)) {
486             intval = (int) val;
487             }
488       else {
489             float scale = SS_PLUGIN_PARAM_MAX / (max - min);
490             intval = (int) ((val - min) * scale);
491             }
492       SS_TRACE_OUT
493       return intval;
494       }
495 
496 //---------------------------------------------------------
497 //   convertGuiControlValue
498 //  scale control value to gui-slider/checkbox representation
499 //---------------------------------------------------------
500 
convertGuiControlValue(int parameter,int val) const501 float LadspaPlugin::convertGuiControlValue(int parameter, int val) const
502       {
503       SS_TRACE_IN
504       float floatval = 0;
505       float min, max;
506       range(parameter, &min, &max);
507 
508       if (isLog(parameter)) {
509             if (val > 0) {
510                   float logged = SS_map_pluginparam2logdomain(val);
511                   float e = expf(logged) * (max - min);
512                   e+=min;
513                   floatval = e;
514                   }
515             }
516       else if (isBool(parameter)) {
517             floatval = (float) val;
518             }
519       else if (isInt(parameter)) {
520             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
521             floatval = (float) round((((float) val) * scale) + min);
522             }
523       else {
524             float scale = (max - min) / SS_PLUGIN_PARAM_MAX;
525             floatval = (((float) val) * scale) + min;
526             }
527       SS_TRACE_OUT
528       return floatval;
529       }
530