annotate plugin/LADSPAPluginFactory.cpp @ 1879:652c5360e682

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