annotate plugin/LADSPAPluginFactory.cpp @ 1773:fadd9f8aaa27

This output is too annoying, in the perfectly innocuous case of reading from an aggregate model whose components are different lengths
author Chris Cannam
date Wed, 14 Aug 2019 13:54:23 +0100
parents 70e172e6cc59
children 5f8fbbde08ff
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7
Chris@52 8 This program is free software; you can redistribute it and/or
Chris@52 9 modify it under the terms of the GNU General Public License as
Chris@52 10 published by the Free Software Foundation; either version 2 of the
Chris@52 11 License, or (at your option) any later version. See the file
Chris@52 12 COPYING included with this distribution for more information.
Chris@0 13 */
Chris@0 14
Chris@0 15 /*
Chris@0 16 This is a modified version of a source file from the
Chris@0 17 Rosegarden MIDI and audio sequencer and notation editor.
Chris@17 18 This file copyright 2000-2006 Chris Cannam and Richard Bown.
Chris@0 19 */
Chris@0 20
Chris@0 21 #include "LADSPAPluginFactory.h"
Chris@0 22 #include <iostream>
Chris@0 23
Chris@0 24 #include <QDir>
Chris@0 25 #include <QFile>
Chris@0 26 #include <QTextStream>
Chris@0 27
Chris@0 28 #include <cmath>
Chris@0 29
Chris@0 30 #include "LADSPAPluginInstance.h"
Chris@0 31 #include "PluginIdentifier.h"
Chris@0 32
Chris@150 33 #include "system/System.h"
Chris@145 34 #include "base/Preferences.h"
Chris@408 35 #include "base/Profiler.h"
Chris@0 36
Chris@260 37 //#define DEBUG_LADSPA_PLUGIN_FACTORY 1
Chris@260 38
Chris@35 39 #ifdef HAVE_LRDF
Chris@0 40 #include "lrdf.h"
Chris@35 41 #endif // HAVE_LRDF
Chris@0 42
Chris@1480 43 using std::string;
Chris@0 44
Chris@0 45 LADSPAPluginFactory::LADSPAPluginFactory()
Chris@0 46 {
Chris@166 47 #ifdef HAVE_LRDF
Chris@166 48 lrdf_init();
Chris@166 49 #endif
Chris@0 50 }
Chris@0 51
Chris@0 52 LADSPAPluginFactory::~LADSPAPluginFactory()
Chris@0 53 {
Chris@0 54 for (std::set<RealTimePluginInstance *>::iterator i = m_instances.begin();
Chris@1429 55 i != m_instances.end(); ++i) {
Chris@1582 56 (*i)->setFactory(nullptr);
Chris@1429 57 delete *i;
Chris@0 58 }
Chris@0 59 m_instances.clear();
Chris@0 60 unloadUnusedLibraries();
Chris@166 61
Chris@166 62 #ifdef HAVE_LRDF
Chris@166 63 lrdf_cleanup();
Chris@166 64 #endif // HAVE_LRDF
Chris@0 65 }
Chris@0 66
Chris@0 67 const std::vector<QString> &
Chris@0 68 LADSPAPluginFactory::getPluginIdentifiers() const
Chris@0 69 {
Chris@0 70 return m_identifiers;
Chris@0 71 }
Chris@0 72
Chris@1464 73 QString
Chris@1464 74 LADSPAPluginFactory::getPluginLibraryPath(QString identifier)
Chris@1464 75 {
Chris@1464 76 return m_libraries[identifier];
Chris@1464 77 }
Chris@1464 78
Chris@0 79 void
Chris@0 80 LADSPAPluginFactory::enumeratePlugins(std::vector<QString> &list)
Chris@0 81 {
Chris@408 82 Profiler profiler("LADSPAPluginFactory::enumeratePlugins");
Chris@408 83
Chris@0 84 for (std::vector<QString>::iterator i = m_identifiers.begin();
Chris@1429 85 i != m_identifiers.end(); ++i) {
Chris@0 86
Chris@1429 87 const LADSPA_Descriptor *descriptor = getLADSPADescriptor(*i);
Chris@0 88
Chris@1429 89 if (!descriptor) {
Chris@1429 90 cerr << "WARNING: LADSPAPluginFactory::enumeratePlugins: couldn't get descriptor for identifier " << *i << endl;
Chris@1429 91 continue;
Chris@1429 92 }
Chris@1429 93
Chris@1429 94 list.push_back(*i);
Chris@1429 95 list.push_back(descriptor->Name);
Chris@1429 96 list.push_back(QString("%1").arg(descriptor->UniqueID));
Chris@1429 97 list.push_back(descriptor->Label);
Chris@1429 98 list.push_back(descriptor->Maker);
Chris@1429 99 list.push_back(descriptor->Copyright);
Chris@1429 100 list.push_back("false"); // is synth
Chris@1429 101 list.push_back("false"); // is grouped
Chris@165 102
Chris@1429 103 if (m_taxonomy.find(*i) != m_taxonomy.end() && m_taxonomy[*i] != "") {
Chris@1429 104 // cerr << "LADSPAPluginFactory: cat for " << *i << " found in taxonomy as " << m_taxonomy[descriptor->UniqueID] << endl;
Chris@1429 105 list.push_back(m_taxonomy[*i]);
Chris@1429 106 } else {
Chris@1429 107 list.push_back("");
Chris@1429 108 // cerr << "LADSPAPluginFactory: cat for " << *i << " not found (despite having " << m_fallbackCategories.size() << " fallbacks)" << endl;
Chris@1429 109
Chris@1429 110 }
Chris@0 111
Chris@1429 112 list.push_back(QString("%1").arg(descriptor->PortCount));
Chris@0 113
Chris@1429 114 for (int p = 0; p < (int)descriptor->PortCount; ++p) {
Chris@0 115
Chris@1429 116 int type = 0;
Chris@1429 117 if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) {
Chris@1429 118 type |= PortType::Control;
Chris@1429 119 } else {
Chris@1429 120 type |= PortType::Audio;
Chris@1429 121 }
Chris@1429 122 if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) {
Chris@1429 123 type |= PortType::Input;
Chris@1429 124 } else {
Chris@1429 125 type |= PortType::Output;
Chris@1429 126 }
Chris@0 127
Chris@1429 128 list.push_back(QString("%1").arg(p));
Chris@1429 129 list.push_back(descriptor->PortNames[p]);
Chris@1429 130 list.push_back(QString("%1").arg(type));
Chris@1429 131 list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p)));
Chris@1429 132 list.push_back(QString("%1").arg(getPortMinimum(descriptor, p)));
Chris@1429 133 list.push_back(QString("%1").arg(getPortMaximum(descriptor, p)));
Chris@1429 134 list.push_back(QString("%1").arg(getPortDefault(descriptor, p)));
Chris@1429 135 }
Chris@0 136 }
Chris@0 137
Chris@0 138 unloadUnusedLibraries();
Chris@0 139 }
Chris@1429 140
Chris@60 141 const RealTimePluginDescriptor *
Chris@60 142 LADSPAPluginFactory::getPluginDescriptor(QString identifier) const
Chris@60 143 {
Chris@60 144 std::map<QString, RealTimePluginDescriptor *>::const_iterator i =
Chris@60 145 m_rtDescriptors.find(identifier);
Chris@60 146
Chris@60 147 if (i != m_rtDescriptors.end()) {
Chris@60 148 return i->second;
Chris@60 149 }
Chris@60 150
Chris@1582 151 return nullptr;
Chris@60 152 }
Chris@60 153
Chris@0 154 float
Chris@0 155 LADSPAPluginFactory::getPortMinimum(const LADSPA_Descriptor *descriptor, int port)
Chris@0 156 {
Chris@0 157 LADSPA_PortRangeHintDescriptor d =
Chris@1429 158 descriptor->PortRangeHints[port].HintDescriptor;
Chris@0 159
Chris@1039 160 float minimum = 0.f;
Chris@1429 161
Chris@0 162 if (LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
Chris@1429 163 float lb = descriptor->PortRangeHints[port].LowerBound;
Chris@1429 164 minimum = lb;
Chris@0 165 } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
Chris@1429 166 float ub = descriptor->PortRangeHints[port].UpperBound;
Chris@1429 167 minimum = std::min(0.f, ub - 1.f);
Chris@0 168 }
Chris@0 169
Chris@0 170 if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
Chris@1429 171 minimum = float(minimum * m_sampleRate);
Chris@0 172 }
Chris@0 173
Chris@356 174 if (LADSPA_IS_HINT_LOGARITHMIC(d)) {
Chris@356 175 if (minimum == 0.f) minimum = 1.f;
Chris@356 176 }
Chris@356 177
Chris@0 178 return minimum;
Chris@0 179 }
Chris@0 180
Chris@0 181 float
Chris@0 182 LADSPAPluginFactory::getPortMaximum(const LADSPA_Descriptor *descriptor, int port)
Chris@0 183 {
Chris@0 184 LADSPA_PortRangeHintDescriptor d =
Chris@1429 185 descriptor->PortRangeHints[port].HintDescriptor;
Chris@0 186
Chris@1039 187 float maximum = 1.f;
Chris@0 188
Chris@0 189 if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
Chris@1429 190 float ub = descriptor->PortRangeHints[port].UpperBound;
Chris@1429 191 maximum = ub;
Chris@0 192 } else {
Chris@1429 193 float lb = descriptor->PortRangeHints[port].LowerBound;
Chris@1429 194 maximum = lb + 1.f;
Chris@0 195 }
Chris@0 196
Chris@0 197 if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
Chris@1429 198 maximum = float(maximum * m_sampleRate);
Chris@0 199 }
Chris@0 200
Chris@0 201 return maximum;
Chris@0 202 }
Chris@0 203
Chris@0 204 float
Chris@0 205 LADSPAPluginFactory::getPortDefault(const LADSPA_Descriptor *descriptor, int port)
Chris@0 206 {
Chris@0 207 float minimum = getPortMinimum(descriptor, port);
Chris@0 208 float maximum = getPortMaximum(descriptor, port);
Chris@0 209 float deft;
Chris@0 210
Chris@0 211 if (m_portDefaults.find(descriptor->UniqueID) !=
Chris@1429 212 m_portDefaults.end()) {
Chris@1429 213 if (m_portDefaults[descriptor->UniqueID].find(port) !=
Chris@1429 214 m_portDefaults[descriptor->UniqueID].end()) {
Chris@0 215
Chris@1429 216 deft = m_portDefaults[descriptor->UniqueID][port];
Chris@1429 217 if (deft < minimum) deft = minimum;
Chris@1429 218 if (deft > maximum) deft = maximum;
Chris@1429 219 return deft;
Chris@1429 220 }
Chris@0 221 }
Chris@0 222
Chris@0 223 LADSPA_PortRangeHintDescriptor d =
Chris@1429 224 descriptor->PortRangeHints[port].HintDescriptor;
Chris@0 225
Chris@0 226 bool logarithmic = LADSPA_IS_HINT_LOGARITHMIC(d);
Chris@0 227
Chris@356 228 float logmin = 0, logmax = 0;
Chris@356 229 if (logarithmic) {
Chris@356 230 float thresh = powf(10, -10);
Chris@356 231 if (minimum < thresh) logmin = -10;
Chris@356 232 else logmin = log10f(minimum);
Chris@356 233 if (maximum < thresh) logmax = -10;
Chris@356 234 else logmax = log10f(maximum);
Chris@356 235 }
Chris@356 236
Chris@690 237 // SVDEBUG << "LADSPAPluginFactory::getPortDefault: hint = " << d << endl;
Chris@356 238
Chris@0 239 if (!LADSPA_IS_HINT_HAS_DEFAULT(d)) {
Chris@1429 240
Chris@1429 241 deft = minimum;
Chris@1429 242
Chris@0 243 } else if (LADSPA_IS_HINT_DEFAULT_MINIMUM(d)) {
Chris@1429 244
Chris@1429 245 deft = minimum;
Chris@1429 246
Chris@0 247 } else if (LADSPA_IS_HINT_DEFAULT_LOW(d)) {
Chris@1429 248
Chris@1429 249 if (logarithmic) {
Chris@1429 250 deft = powf(10, logmin * 0.75f + logmax * 0.25f);
Chris@1429 251 } else {
Chris@1429 252 deft = minimum * 0.75f + maximum * 0.25f;
Chris@1429 253 }
Chris@1429 254
Chris@0 255 } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(d)) {
Chris@1429 256
Chris@1429 257 if (logarithmic) {
Chris@1429 258 deft = powf(10, logmin * 0.5f + logmax * 0.5f);
Chris@1429 259 } else {
Chris@1429 260 deft = minimum * 0.5f + maximum * 0.5f;
Chris@1429 261 }
Chris@1429 262
Chris@0 263 } else if (LADSPA_IS_HINT_DEFAULT_HIGH(d)) {
Chris@1429 264
Chris@1429 265 if (logarithmic) {
Chris@1429 266 deft = powf(10, logmin * 0.25f + logmax * 0.75f);
Chris@1429 267 } else {
Chris@1429 268 deft = minimum * 0.25f + maximum * 0.75f;
Chris@1429 269 }
Chris@1429 270
Chris@0 271 } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(d)) {
Chris@1429 272
Chris@1429 273 deft = maximum;
Chris@1429 274
Chris@0 275 } else if (LADSPA_IS_HINT_DEFAULT_0(d)) {
Chris@1429 276
Chris@1429 277 deft = 0.0;
Chris@1429 278
Chris@0 279 } else if (LADSPA_IS_HINT_DEFAULT_1(d)) {
Chris@1429 280
Chris@1429 281 deft = 1.0;
Chris@1429 282
Chris@0 283 } else if (LADSPA_IS_HINT_DEFAULT_100(d)) {
Chris@1429 284
Chris@1429 285 deft = 100.0;
Chris@1429 286
Chris@0 287 } else if (LADSPA_IS_HINT_DEFAULT_440(d)) {
Chris@1429 288
Chris@1429 289 // deft = 440.0;
Chris@1039 290 deft = (float)Preferences::getInstance()->getTuningFrequency();
Chris@1429 291
Chris@0 292 } else {
Chris@1429 293
Chris@1429 294 deft = minimum;
Chris@0 295 }
Chris@356 296
Chris@356 297 //!!! No -- the min and max have already been multiplied by the rate,
Chris@356 298 //so it would happen twice if we did it here -- and e.g. DEFAULT_440
Chris@356 299 //doesn't want to be multiplied by the rate either
Chris@0 300
Chris@356 301 // if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
Chris@1429 302 // deft *= m_sampleRate;
Chris@356 303 // }
Chris@0 304
Chris@0 305 return deft;
Chris@0 306 }
Chris@0 307
Chris@57 308 float
Chris@57 309 LADSPAPluginFactory::getPortQuantization(const LADSPA_Descriptor *descriptor, int port)
Chris@57 310 {
Chris@57 311 int displayHint = getPortDisplayHint(descriptor, port);
Chris@57 312 if (displayHint & PortHint::Toggled) {
Chris@1039 313 return float(lrintf(getPortMaximum(descriptor, port)) -
Chris@1039 314 lrintf(getPortMinimum(descriptor, port)));
Chris@57 315 }
Chris@57 316 if (displayHint & PortHint::Integer) {
Chris@57 317 return 1.0;
Chris@57 318 }
Chris@57 319 return 0.0;
Chris@57 320 }
Chris@57 321
Chris@0 322 int
Chris@0 323 LADSPAPluginFactory::getPortDisplayHint(const LADSPA_Descriptor *descriptor, int port)
Chris@0 324 {
Chris@0 325 LADSPA_PortRangeHintDescriptor d =
Chris@1429 326 descriptor->PortRangeHints[port].HintDescriptor;
Chris@0 327 int hint = PortHint::NoHint;
Chris@0 328
Chris@0 329 if (LADSPA_IS_HINT_TOGGLED(d)) hint |= PortHint::Toggled;
Chris@0 330 if (LADSPA_IS_HINT_INTEGER(d)) hint |= PortHint::Integer;
Chris@0 331 if (LADSPA_IS_HINT_LOGARITHMIC(d)) hint |= PortHint::Logarithmic;
Chris@0 332
Chris@0 333 return hint;
Chris@0 334 }
Chris@0 335
Chris@0 336
Chris@0 337 RealTimePluginInstance *
Chris@0 338 LADSPAPluginFactory::instantiatePlugin(QString identifier,
Chris@1429 339 int instrument,
Chris@1429 340 int position,
Chris@1429 341 sv_samplerate_t sampleRate,
Chris@1429 342 int blockSize,
Chris@1429 343 int channels)
Chris@0 344 {
Chris@408 345 Profiler profiler("LADSPAPluginFactory::instantiatePlugin");
Chris@408 346
Chris@0 347 const LADSPA_Descriptor *descriptor = getLADSPADescriptor(identifier);
Chris@0 348
Chris@0 349 if (descriptor) {
Chris@0 350
Chris@1429 351 LADSPAPluginInstance *instance =
Chris@1429 352 new LADSPAPluginInstance
Chris@1429 353 (this, instrument, identifier, position, sampleRate, blockSize, channels,
Chris@1429 354 descriptor);
Chris@0 355
Chris@1429 356 m_instances.insert(instance);
Chris@0 357
Chris@260 358 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@690 359 SVDEBUG << "LADSPAPluginFactory::instantiatePlugin("
Chris@687 360 << identifier << ": now have " << m_instances.size() << " instances" << endl;
Chris@260 361 #endif
Chris@78 362
Chris@1429 363 return instance;
Chris@0 364 }
Chris@0 365
Chris@1582 366 return nullptr;
Chris@0 367 }
Chris@0 368
Chris@0 369 void
Chris@0 370 LADSPAPluginFactory::releasePlugin(RealTimePluginInstance *instance,
Chris@1429 371 QString identifier)
Chris@0 372 {
Chris@408 373 Profiler profiler("LADSPAPluginFactory::releasePlugin");
Chris@408 374
Chris@0 375 if (m_instances.find(instance) == m_instances.end()) {
Chris@1429 376 cerr << "WARNING: LADSPAPluginFactory::releasePlugin: Not one of mine!"
Chris@1429 377 << endl;
Chris@1429 378 return;
Chris@0 379 }
Chris@0 380
Chris@0 381 QString type, soname, label;
Chris@0 382 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@0 383
Chris@0 384 m_instances.erase(instance);
Chris@0 385
Chris@0 386 bool stillInUse = false;
Chris@0 387
Chris@0 388 for (std::set<RealTimePluginInstance *>::iterator ii = m_instances.begin();
Chris@1429 389 ii != m_instances.end(); ++ii) {
Chris@1429 390 QString itype, isoname, ilabel;
Chris@1429 391 PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel);
Chris@1429 392 if (isoname == soname) {
Chris@260 393 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@1429 394 SVDEBUG << "LADSPAPluginFactory::releasePlugin: dll " << soname << " is still in use for plugin " << ilabel << endl;
Chris@260 395 #endif
Chris@1429 396 stillInUse = true;
Chris@1429 397 break;
Chris@1429 398 }
Chris@0 399 }
Chris@0 400
Chris@0 401 if (!stillInUse) {
Chris@118 402 if (soname != PluginIdentifier::BUILTIN_PLUGIN_SONAME) {
Chris@260 403 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@690 404 SVDEBUG << "LADSPAPluginFactory::releasePlugin: dll " << soname << " no longer in use, unloading" << endl;
Chris@260 405 #endif
Chris@118 406 unloadLibrary(soname);
Chris@118 407 }
Chris@0 408 }
Chris@78 409
Chris@260 410 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@690 411 SVDEBUG << "LADSPAPluginFactory::releasePlugin("
Chris@687 412 << identifier << ": now have " << m_instances.size() << " instances" << endl;
Chris@260 413 #endif
Chris@0 414 }
Chris@0 415
Chris@0 416 const LADSPA_Descriptor *
Chris@0 417 LADSPAPluginFactory::getLADSPADescriptor(QString identifier)
Chris@0 418 {
Chris@0 419 QString type, soname, label;
Chris@0 420 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@0 421
Chris@0 422 if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
Chris@1429 423 loadLibrary(soname);
Chris@1429 424 if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
Chris@1498 425 SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: loadLibrary failed for " << soname << endl;
Chris@1582 426 return nullptr;
Chris@1429 427 }
Chris@0 428 }
Chris@0 429
Chris@0 430 void *libraryHandle = m_libraryHandles[soname];
Chris@0 431
Chris@0 432 LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function)
Chris@1429 433 DLSYM(libraryHandle, "ladspa_descriptor");
Chris@0 434
Chris@0 435 if (!fn) {
Chris@1498 436 SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No descriptor function in library " << soname << endl;
Chris@1582 437 return nullptr;
Chris@0 438 }
Chris@0 439
Chris@1582 440 const LADSPA_Descriptor *descriptor = nullptr;
Chris@0 441
Chris@0 442 int index = 0;
Chris@0 443 while ((descriptor = fn(index))) {
Chris@1429 444 if (descriptor->Label == label) return descriptor;
Chris@1429 445 ++index;
Chris@0 446 }
Chris@0 447
Chris@1498 448 SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No such plugin as " << label << " in library " << soname << endl;
Chris@0 449
Chris@1582 450 return nullptr;
Chris@0 451 }
Chris@0 452
Chris@0 453 void
Chris@0 454 LADSPAPluginFactory::loadLibrary(QString soName)
Chris@0 455 {
Chris@0 456 void *libraryHandle = DLOPEN(soName, RTLD_NOW);
Chris@106 457 if (libraryHandle) {
Chris@106 458 m_libraryHandles[soName] = libraryHandle;
Chris@690 459 SVDEBUG << "LADSPAPluginFactory::loadLibrary: Loaded library \"" << soName << "\"" << endl;
Chris@106 460 return;
Chris@106 461 }
Chris@106 462
Chris@106 463 if (QFileInfo(soName).exists()) {
Chris@106 464 DLERROR();
Chris@1498 465 SVCERR << "LADSPAPluginFactory::loadLibrary: Library \"" << soName << "\" exists, but failed to load it" << endl;
Chris@106 466 return;
Chris@106 467 }
Chris@106 468
Chris@106 469 std::vector<QString> pathList = getPluginPath();
Chris@106 470
Chris@106 471 QString fileName = QFile(soName).fileName();
Chris@106 472 QString base = QFileInfo(soName).baseName();
Chris@106 473
Chris@106 474 for (std::vector<QString>::iterator i = pathList.begin();
Chris@1429 475 i != pathList.end(); ++i) {
Chris@106 476
Chris@260 477 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@690 478 SVDEBUG << "Looking at: " << (*i) << endl;
Chris@260 479 #endif
Chris@106 480
Chris@106 481 QDir dir(*i, PLUGIN_GLOB,
Chris@106 482 QDir::Name | QDir::IgnoreCase,
Chris@106 483 QDir::Files | QDir::Readable);
Chris@106 484
Chris@106 485 if (QFileInfo(dir.filePath(fileName)).exists()) {
Chris@260 486 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@1498 487 SVDEBUG << "Loading: " << fileName << endl;
Chris@260 488 #endif
Chris@106 489 libraryHandle = DLOPEN(dir.filePath(fileName), RTLD_NOW);
Chris@166 490 if (libraryHandle) {
Chris@166 491 m_libraryHandles[soName] = libraryHandle;
Chris@166 492 return;
Chris@166 493 }
Chris@106 494 }
Chris@106 495
Chris@1429 496 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@106 497 QString file = dir.filePath(dir[j]);
Chris@106 498 if (QFileInfo(file).baseName() == base) {
Chris@260 499 #ifdef DEBUG_LADSPA_PLUGIN_FACTORY
Chris@1498 500 SVDEBUG << "Loading: " << file << endl;
Chris@260 501 #endif
Chris@106 502 libraryHandle = DLOPEN(file, RTLD_NOW);
Chris@166 503 if (libraryHandle) {
Chris@166 504 m_libraryHandles[soName] = libraryHandle;
Chris@166 505 return;
Chris@166 506 }
Chris@106 507 }
Chris@106 508 }
Chris@106 509 }
Chris@106 510
Chris@1498 511 SVCERR << "LADSPAPluginFactory::loadLibrary: Failed to locate plugin library \"" << soName << "\"" << endl;
Chris@0 512 }
Chris@0 513
Chris@0 514 void
Chris@0 515 LADSPAPluginFactory::unloadLibrary(QString soName)
Chris@0 516 {
Chris@0 517 LibraryHandleMap::iterator li = m_libraryHandles.find(soName);
Chris@0 518 if (li != m_libraryHandles.end()) {
Chris@1429 519 // SVDEBUG << "unloading " << soname << endl;
Chris@1429 520 DLCLOSE(m_libraryHandles[soName]);
Chris@1429 521 m_libraryHandles.erase(li);
Chris@0 522 }
Chris@0 523 }
Chris@0 524
Chris@0 525 void
Chris@0 526 LADSPAPluginFactory::unloadUnusedLibraries()
Chris@0 527 {
Chris@0 528 std::vector<QString> toUnload;
Chris@0 529
Chris@0 530 for (LibraryHandleMap::iterator i = m_libraryHandles.begin();
Chris@1429 531 i != m_libraryHandles.end(); ++i) {
Chris@0 532
Chris@1429 533 bool stillInUse = false;
Chris@0 534
Chris@1429 535 for (std::set<RealTimePluginInstance *>::iterator ii = m_instances.begin();
Chris@1429 536 ii != m_instances.end(); ++ii) {
Chris@0 537
Chris@1429 538 QString itype, isoname, ilabel;
Chris@1429 539 PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel);
Chris@1429 540 if (isoname == i->first) {
Chris@1429 541 stillInUse = true;
Chris@1429 542 break;
Chris@1429 543 }
Chris@1429 544 }
Chris@0 545
Chris@1429 546 if (!stillInUse) toUnload.push_back(i->first);
Chris@0 547 }
Chris@0 548
Chris@0 549 for (std::vector<QString>::iterator i = toUnload.begin();
Chris@1429 550 i != toUnload.end(); ++i) {
Chris@118 551 if (*i != PluginIdentifier::BUILTIN_PLUGIN_SONAME) {
Chris@118 552 unloadLibrary(*i);
Chris@118 553 }
Chris@0 554 }
Chris@0 555 }
Chris@0 556
Chris@0 557
Chris@0 558 // It is only later, after they've gone,
Chris@0 559 // I realize they have delivered a letter.
Chris@0 560 // It's a letter from my wife. "What are you doing
Chris@0 561 // there?" my wife asks. "Are you drinking?"
Chris@0 562 // I study the postmark for hours. Then it, too, begins to fade.
Chris@0 563 // I hope someday to forget all this.
Chris@0 564
Chris@0 565
Chris@0 566 std::vector<QString>
Chris@0 567 LADSPAPluginFactory::getPluginPath()
Chris@0 568 {
Chris@0 569 std::vector<QString> pathList;
Chris@1480 570 string path;
Chris@0 571
Chris@1497 572 (void)getEnvUtf8("LADSPA_PATH", path);
Chris@0 573
Chris@0 574 if (path == "") {
Chris@186 575
Chris@186 576 path = DEFAULT_LADSPA_PATH;
Chris@186 577
Chris@1480 578 string home;
Chris@1480 579 if (getEnvUtf8("HOME", home)) {
Chris@1480 580 string::size_type f;
Chris@1480 581 while ((f = path.find("$HOME")) != string::npos &&
Chris@186 582 f < path.length()) {
Chris@186 583 path.replace(f, 5, home);
Chris@186 584 }
Chris@186 585 }
Chris@186 586
Chris@186 587 #ifdef _WIN32
Chris@1480 588 string pfiles;
Chris@1480 589 if (!getEnvUtf8("ProgramFiles", pfiles)) {
Chris@1480 590 pfiles = "C:\\Program Files";
Chris@1480 591 }
Chris@1480 592
Chris@1480 593 string::size_type f;
Chris@1480 594 while ((f = path.find("%ProgramFiles%")) != string::npos &&
Chris@186 595 f < path.length()) {
Chris@186 596 path.replace(f, 14, pfiles);
Chris@186 597 }
Chris@186 598 #endif
Chris@0 599 }
Chris@0 600
Chris@1480 601 string::size_type index = 0, newindex = 0;
Chris@0 602
Chris@223 603 while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) {
Chris@1429 604 pathList.push_back(path.substr(index, newindex - index).c_str());
Chris@1429 605 index = newindex + 1;
Chris@0 606 }
Chris@0 607
Chris@0 608 pathList.push_back(path.substr(index).c_str());
Chris@0 609
Chris@0 610 return pathList;
Chris@0 611 }
Chris@0 612
Chris@0 613
Chris@0 614 std::vector<QString>
Chris@0 615 LADSPAPluginFactory::getLRDFPath(QString &baseUri)
Chris@0 616 {
Chris@150 617 std::vector<QString> lrdfPaths;
Chris@150 618
Chris@150 619 #ifdef HAVE_LRDF
Chris@0 620 std::vector<QString> pathList = getPluginPath();
Chris@0 621
Chris@0 622 lrdfPaths.push_back("/usr/local/share/ladspa/rdf");
Chris@0 623 lrdfPaths.push_back("/usr/share/ladspa/rdf");
Chris@0 624
Chris@0 625 for (std::vector<QString>::iterator i = pathList.begin();
Chris@1429 626 i != pathList.end(); ++i) {
Chris@1429 627 lrdfPaths.push_back(*i + "/rdf");
Chris@0 628 }
Chris@0 629
Chris@0 630 baseUri = LADSPA_BASE;
Chris@953 631 #else
Chris@953 632 baseUri = "";
Chris@150 633 #endif
Chris@150 634
Chris@0 635 return lrdfPaths;
Chris@0 636 }
Chris@0 637
Chris@0 638 void
Chris@0 639 LADSPAPluginFactory::discoverPlugins()
Chris@0 640 {
Chris@408 641 Profiler profiler("LADSPAPluginFactory::discoverPlugins");
Chris@408 642
Chris@0 643 std::vector<QString> pathList = getPluginPath();
Chris@0 644
Chris@690 645 // SVDEBUG << "LADSPAPluginFactory::discoverPlugins - "
Chris@1429 646 // << "discovering plugins; path is ";
Chris@259 647 // for (std::vector<QString>::iterator i = pathList.begin();
Chris@1429 648 // i != pathList.end(); ++i) {
Chris@1429 649 // SVDEBUG << "[" << i-<< "] ";
Chris@259 650 // }
Chris@690 651 // SVDEBUG << endl;
Chris@0 652
Chris@35 653 #ifdef HAVE_LRDF
Chris@166 654 // read the description files
Chris@0 655 //
Chris@0 656 QString baseUri;
Chris@0 657 std::vector<QString> lrdfPaths = getLRDFPath(baseUri);
Chris@0 658
Chris@0 659 bool haveSomething = false;
Chris@0 660
Chris@0 661 for (size_t i = 0; i < lrdfPaths.size(); ++i) {
Chris@1429 662 QDir dir(lrdfPaths[i], "*.rdf;*.rdfs");
Chris@1429 663 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@1429 664 if (!lrdf_read_file(QString("file:" + lrdfPaths[i] + "/" + dir[j]).toStdString().c_str())) {
Chris@1429 665 // cerr << "LADSPAPluginFactory: read RDF file " << (lrdfPaths[i] + "/" + dir[j]) << endl;
Chris@1429 666 haveSomething = true;
Chris@1429 667 }
Chris@1429 668 }
Chris@0 669 }
Chris@0 670
Chris@0 671 if (haveSomething) {
Chris@1429 672 generateTaxonomy(baseUri + "Plugin", "");
Chris@0 673 }
Chris@35 674 #endif // HAVE_LRDF
Chris@0 675
Chris@0 676 generateFallbackCategories();
Chris@0 677
Chris@1246 678 auto candidates =
Chris@1179 679 PluginScan::getInstance()->getCandidateLibrariesFor(getPluginType());
Chris@0 680
Chris@1246 681 for (auto c: candidates) {
Chris@1246 682 discoverPluginsFrom(c.libraryPath);
Chris@0 683 }
Chris@0 684 }
Chris@0 685
Chris@0 686 void
Chris@929 687 LADSPAPluginFactory::discoverPluginsFrom(QString soname)
Chris@0 688 {
Chris@0 689 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@0 690
Chris@0 691 if (!libraryHandle) {
Chris@843 692 cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: couldn't load plugin library "
Chris@843 693 << soname << " - " << DLERROR() << endl;
Chris@0 694 return;
Chris@0 695 }
Chris@0 696
Chris@0 697 LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function)
Chris@1429 698 DLSYM(libraryHandle, "ladspa_descriptor");
Chris@0 699
Chris@0 700 if (!fn) {
Chris@1429 701 cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: No descriptor function in " << soname << endl;
Chris@1429 702 return;
Chris@0 703 }
Chris@0 704
Chris@1582 705 const LADSPA_Descriptor *descriptor = nullptr;
Chris@0 706
Chris@0 707 int index = 0;
Chris@0 708 while ((descriptor = fn(index))) {
Chris@0 709
Chris@60 710 RealTimePluginDescriptor *rtd = new RealTimePluginDescriptor;
Chris@60 711 rtd->name = descriptor->Name;
Chris@60 712 rtd->label = descriptor->Label;
Chris@60 713 rtd->maker = descriptor->Maker;
Chris@60 714 rtd->copyright = descriptor->Copyright;
Chris@60 715 rtd->category = "";
Chris@60 716 rtd->isSynth = false;
Chris@60 717 rtd->parameterCount = 0;
Chris@60 718 rtd->audioInputPortCount = 0;
Chris@166 719 rtd->audioOutputPortCount = 0;
Chris@60 720 rtd->controlOutputPortCount = 0;
Chris@60 721
Chris@1429 722 QString identifier = PluginIdentifier::createIdentifier
Chris@1429 723 ("ladspa", soname, descriptor->Label);
Chris@165 724
Chris@35 725 #ifdef HAVE_LRDF
Chris@1582 726 char *def_uri = nullptr;
Chris@1582 727 lrdf_defaults *defs = nullptr;
Chris@1429 728
Chris@165 729 if (m_lrdfTaxonomy[descriptor->UniqueID] != "") {
Chris@165 730 m_taxonomy[identifier] = m_lrdfTaxonomy[descriptor->UniqueID];
Chris@843 731 // cerr << "set id \"" << identifier << "\" to cat \"" << m_taxonomy[identifier] << "\" from LRDF" << endl;
Chris@843 732 // cout << identifier << "::" << m_taxonomy[identifier] << endl;
Chris@165 733 }
Chris@165 734
Chris@1429 735 QString category = m_taxonomy[identifier];
Chris@1429 736
Chris@1429 737 if (category == "") {
Chris@1480 738 string name = rtd->name;
Chris@1429 739 if (name.length() > 4 &&
Chris@1429 740 name.substr(name.length() - 4) == " VST") {
Chris@1429 741 category = "VST effects";
Chris@1429 742 m_taxonomy[identifier] = category;
Chris@1429 743 }
Chris@1429 744 }
Chris@1429 745
Chris@60 746 rtd->category = category.toStdString();
Chris@60 747
Chris@1429 748 // cerr << "Plugin id is " << descriptor->UniqueID
Chris@1429 749 // << ", category is \"" << (category ? category : QString("(none)"))
Chris@1429 750 // << "\", name is " << descriptor->Name
Chris@1429 751 // << ", label is " << descriptor->Label
Chris@1429 752 // << endl;
Chris@1429 753
Chris@1429 754 def_uri = lrdf_get_default_uri(descriptor->UniqueID);
Chris@1429 755 if (def_uri) {
Chris@1429 756 defs = lrdf_get_setting_values(def_uri);
Chris@1429 757 }
Chris@0 758
Chris@1429 759 unsigned int controlPortNumber = 1;
Chris@1429 760
Chris@1429 761 for (int i = 0; i < (int)descriptor->PortCount; i++) {
Chris@1429 762
Chris@1429 763 if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) {
Chris@1429 764
Chris@1429 765 if (def_uri && defs) {
Chris@1429 766
Chris@1429 767 for (unsigned int j = 0; j < defs->count; j++) {
Chris@1429 768 if (defs->items[j].pid == controlPortNumber) {
Chris@1429 769 // cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << descriptor->PortNames[i] << endl;
Chris@1429 770 m_portDefaults[descriptor->UniqueID][i] =
Chris@1429 771 defs->items[j].value;
Chris@1429 772 }
Chris@1429 773 }
Chris@1429 774 }
Chris@1429 775
Chris@1429 776 ++controlPortNumber;
Chris@1429 777 }
Chris@1429 778 }
Chris@35 779 #endif // HAVE_LRDF
Chris@0 780
Chris@1429 781 for (int i = 0; i < (int)descriptor->PortCount; i++) {
Chris@1429 782 if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) {
Chris@60 783 if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
Chris@60 784 ++rtd->parameterCount;
Chris@60 785 } else {
Chris@60 786 if (strcmp(descriptor->PortNames[i], "latency") &&
Chris@60 787 strcmp(descriptor->PortNames[i], "_latency")) {
Chris@60 788 ++rtd->controlOutputPortCount;
Chris@60 789 rtd->controlOutputPortNames.push_back
Chris@60 790 (descriptor->PortNames[i]);
Chris@60 791 }
Chris@60 792 }
Chris@60 793 } else {
Chris@60 794 if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
Chris@60 795 ++rtd->audioInputPortCount;
Chris@166 796 } else if (LADSPA_IS_PORT_OUTPUT(descriptor->PortDescriptors[i])) {
Chris@166 797 ++rtd->audioOutputPortCount;
Chris@60 798 }
Chris@60 799 }
Chris@60 800 }
Chris@60 801
Chris@1429 802 m_identifiers.push_back(identifier);
Chris@0 803
Chris@1464 804 m_libraries[identifier] = soname;
Chris@1464 805
Chris@60 806 m_rtDescriptors[identifier] = rtd;
Chris@60 807
Chris@1429 808 ++index;
Chris@0 809 }
Chris@0 810
Chris@0 811 if (DLCLOSE(libraryHandle) != 0) {
Chris@843 812 cerr << "WARNING: LADSPAPluginFactory::discoverPlugins - can't unload " << libraryHandle << endl;
Chris@0 813 return;
Chris@0 814 }
Chris@0 815 }
Chris@0 816
Chris@0 817 void
Chris@0 818 LADSPAPluginFactory::generateFallbackCategories()
Chris@0 819 {
Chris@0 820 std::vector<QString> pluginPath = getPluginPath();
Chris@0 821 std::vector<QString> path;
Chris@0 822
Chris@0 823 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@1429 824 if (pluginPath[i].contains("/lib/")) {
Chris@1429 825 QString p(pluginPath[i]);
Chris@165 826 path.push_back(p);
Chris@1429 827 p.replace("/lib/", "/share/");
Chris@1429 828 path.push_back(p);
Chris@1429 829 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << p << endl;
Chris@1429 830 }
Chris@1429 831 path.push_back(pluginPath[i]);
Chris@1429 832 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << pluginPath[i] << endl;
Chris@0 833 }
Chris@0 834
Chris@0 835 for (size_t i = 0; i < path.size(); ++i) {
Chris@0 836
Chris@1429 837 QDir dir(path[i], "*.cat");
Chris@0 838
Chris@1429 839 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl;
Chris@1429 840 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@0 841
Chris@1429 842 QFile file(path[i] + "/" + dir[j]);
Chris@0 843
Chris@1429 844 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl;
Chris@0 845
Chris@1429 846 if (file.open(QIODevice::ReadOnly)) {
Chris@1429 847 // cerr << "...opened" << endl;
Chris@1429 848 QTextStream stream(&file);
Chris@1429 849 QString line;
Chris@0 850
Chris@1429 851 while (!stream.atEnd()) {
Chris@1429 852 line = stream.readLine();
Chris@1429 853 // cerr << "line is: \"" << line << "\"" << endl;
Chris@1429 854 QString id = PluginIdentifier::canonicalise
Chris@165 855 (line.section("::", 0, 0));
Chris@1429 856 QString cat = line.section("::", 1, 1);
Chris@1429 857 m_taxonomy[id] = cat;
Chris@1429 858 // cerr << "set id \"" << id << "\" to cat \"" << cat << "\"" << endl;
Chris@1429 859 }
Chris@1429 860 }
Chris@1429 861 }
Chris@0 862 }
Chris@0 863 }
Chris@0 864
Chris@0 865 void
Chris@0 866 LADSPAPluginFactory::generateTaxonomy(QString uri, QString base)
Chris@0 867 {
Chris@35 868 #ifdef HAVE_LRDF
Chris@0 869 lrdf_uris *uris = lrdf_get_instances(uri.toStdString().c_str());
Chris@0 870
Chris@1582 871 if (uris != nullptr) {
Chris@1429 872 for (unsigned int i = 0; i < uris->count; ++i) {
Chris@1429 873 m_lrdfTaxonomy[lrdf_get_uid(uris->items[i])] = base;
Chris@1429 874 }
Chris@1429 875 lrdf_free_uris(uris);
Chris@0 876 }
Chris@0 877
Chris@0 878 uris = lrdf_get_subclasses(uri.toStdString().c_str());
Chris@0 879
Chris@1582 880 if (uris != nullptr) {
Chris@1429 881 for (unsigned int i = 0; i < uris->count; ++i) {
Chris@1429 882 char *label = lrdf_get_label(uris->items[i]);
Chris@1429 883 generateTaxonomy(uris->items[i],
Chris@1429 884 base + (base.length() > 0 ? " > " : "") + label);
Chris@1429 885 }
Chris@1429 886 lrdf_free_uris(uris);
Chris@0 887 }
Chris@953 888 #else
Chris@953 889 // avoid unused parameter
Chris@953 890 (void)uri;
Chris@953 891 (void)base;
Chris@0 892 #endif
Chris@0 893 }
Chris@0 894
Chris@165 895 QString
Chris@165 896 LADSPAPluginFactory::getPluginCategory(QString identifier)
Chris@165 897 {
Chris@165 898 return m_taxonomy[identifier];
Chris@165 899 }
Chris@0 900