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