annotate plugin/LADSPAPluginFactory.cpp @ 458:f60360209e5c

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