annotate plugin/FeatureExtractionPluginFactory.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 824a87450f81
children a70dcfed59c1
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@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "FeatureExtractionPluginFactory.h"
Chris@0 17 #include "PluginIdentifier.h"
Chris@0 18
Chris@66 19 #include "vamp/vamp.h"
Chris@66 20 #include "vamp-sdk/PluginHostAdapter.h"
Chris@298 21 #include "vamp-sdk/hostext/PluginWrapper.h"
Chris@66 22
Chris@150 23 #include "system/System.h"
Chris@66 24
Chris@66 25 #include <QDir>
Chris@66 26 #include <QFile>
Chris@66 27 #include <QFileInfo>
Chris@165 28 #include <QTextStream>
Chris@66 29
Chris@0 30 #include <iostream>
Chris@0 31
Chris@408 32 #include "base/Profiler.h"
Chris@408 33
Chris@249 34 //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1
Chris@249 35
Chris@298 36 class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper {
Chris@298 37 public:
Chris@298 38 PluginDeletionNotifyAdapter(Vamp::Plugin *plugin,
Chris@298 39 FeatureExtractionPluginFactory *factory) :
Chris@298 40 PluginWrapper(plugin), m_factory(factory) { }
Chris@298 41 virtual ~PluginDeletionNotifyAdapter();
Chris@298 42 protected:
Chris@298 43 FeatureExtractionPluginFactory *m_factory;
Chris@298 44 };
Chris@298 45
Chris@298 46 PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
Chris@298 47 {
Chris@298 48 // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn
Chris@298 49 Vamp::Plugin *p = m_plugin;
Chris@298 50 delete m_plugin;
Chris@298 51 m_plugin = 0;
Chris@298 52 if (m_factory) m_factory->pluginDeleted(p);
Chris@298 53 }
Chris@298 54
Chris@0 55 static FeatureExtractionPluginFactory *_nativeInstance = 0;
Chris@0 56
Chris@0 57 FeatureExtractionPluginFactory *
Chris@0 58 FeatureExtractionPluginFactory::instance(QString pluginType)
Chris@0 59 {
Chris@71 60 if (pluginType == "vamp") {
Chris@0 61 if (!_nativeInstance) {
Chris@259 62 // std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
Chris@259 63 // << "): creating new FeatureExtractionPluginFactory" << std::endl;
Chris@0 64 _nativeInstance = new FeatureExtractionPluginFactory();
Chris@0 65 }
Chris@0 66 return _nativeInstance;
Chris@0 67 }
Chris@0 68
Chris@0 69 else return 0;
Chris@0 70 }
Chris@0 71
Chris@0 72 FeatureExtractionPluginFactory *
Chris@0 73 FeatureExtractionPluginFactory::instanceFor(QString identifier)
Chris@0 74 {
Chris@0 75 QString type, soName, label;
Chris@0 76 PluginIdentifier::parseIdentifier(identifier, type, soName, label);
Chris@0 77 return instance(type);
Chris@0 78 }
Chris@0 79
Chris@0 80 std::vector<QString>
Chris@66 81 FeatureExtractionPluginFactory::getPluginPath()
Chris@66 82 {
Chris@117 83 if (!m_pluginPath.empty()) return m_pluginPath;
Chris@117 84
Chris@186 85 std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath();
Chris@186 86 for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
Chris@186 87 return m_pluginPath;
Chris@66 88 }
Chris@66 89
Chris@66 90 std::vector<QString>
Chris@0 91 FeatureExtractionPluginFactory::getAllPluginIdentifiers()
Chris@0 92 {
Chris@0 93 FeatureExtractionPluginFactory *factory;
Chris@0 94 std::vector<QString> rv;
Chris@0 95
Chris@66 96 factory = instance("vamp");
Chris@0 97 if (factory) {
Chris@0 98 std::vector<QString> tmp = factory->getPluginIdentifiers();
Chris@0 99 for (size_t i = 0; i < tmp.size(); ++i) {
Chris@358 100 // std::cerr << "identifier: " << tmp[i].toStdString() << std::endl;
Chris@0 101 rv.push_back(tmp[i]);
Chris@0 102 }
Chris@0 103 }
Chris@0 104
Chris@0 105 // Plugins can change the locale, revert it to default.
Chris@0 106 setlocale(LC_ALL, "C");
Chris@0 107 return rv;
Chris@0 108 }
Chris@0 109
Chris@0 110 std::vector<QString>
Chris@0 111 FeatureExtractionPluginFactory::getPluginIdentifiers()
Chris@0 112 {
Chris@408 113 Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
Chris@408 114
Chris@0 115 std::vector<QString> rv;
Chris@66 116 std::vector<QString> path = getPluginPath();
Chris@66 117
Chris@66 118 for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
Chris@66 119
Chris@249 120 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 121 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl;
Chris@249 122 #endif
Chris@66 123
Chris@66 124 QDir pluginDir(*i, PLUGIN_GLOB,
Chris@66 125 QDir::Name | QDir::IgnoreCase,
Chris@66 126 QDir::Files | QDir::Readable);
Chris@66 127
Chris@66 128 for (unsigned int j = 0; j < pluginDir.count(); ++j) {
Chris@66 129
Chris@66 130 QString soname = pluginDir.filePath(pluginDir[j]);
Chris@66 131
Chris@249 132 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 133 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname.toStdString() << std::endl;
Chris@249 134 #endif
Chris@249 135
Chris@435 136 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
Chris@66 137
Chris@66 138 if (!libraryHandle) {
Chris@71 139 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 140 continue;
Chris@66 141 }
Chris@66 142
Chris@249 143 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 144 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << std::endl;
Chris@249 145 #endif
Chris@249 146
Chris@66 147 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 148 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 149
Chris@66 150 if (!fn) {
Chris@66 151 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 152 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 153 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 154 }
Chris@66 155 continue;
Chris@66 156 }
Chris@66 157
Chris@249 158 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 159 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << std::endl;
Chris@249 160 #endif
Chris@249 161
Chris@66 162 const VampPluginDescriptor *descriptor = 0;
Chris@66 163 int index = 0;
Chris@66 164
Chris@251 165 std::map<std::string, int> known;
Chris@251 166 bool ok = true;
Chris@251 167
Chris@239 168 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 169
Chris@251 170 if (known.find(descriptor->identifier) != known.end()) {
Chris@251 171 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library "
Chris@251 172 << soname.toStdString()
Chris@251 173 << " returns the same plugin identifier \""
Chris@251 174 << descriptor->identifier << "\" at indices "
Chris@251 175 << known[descriptor->identifier] << " and "
Chris@251 176 << index << std::endl;
Chris@251 177 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << std::endl;
Chris@251 178 ok = false;
Chris@251 179 break;
Chris@251 180 } else {
Chris@251 181 known[descriptor->identifier] = index;
Chris@251 182 }
Chris@251 183
Chris@251 184 ++index;
Chris@251 185 }
Chris@251 186
Chris@251 187 if (ok) {
Chris@251 188
Chris@251 189 index = 0;
Chris@251 190
Chris@251 191 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 192
Chris@251 193 QString id = PluginIdentifier::createIdentifier
Chris@251 194 ("vamp", soname, descriptor->identifier);
Chris@251 195 rv.push_back(id);
Chris@249 196 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@251 197 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id.toStdString() << " at index " << index << std::endl;
Chris@249 198 #endif
Chris@251 199 ++index;
Chris@251 200 }
Chris@66 201 }
Chris@66 202
Chris@66 203 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 204 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 205 }
Chris@66 206 }
Chris@66 207 }
Chris@66 208
Chris@165 209 generateTaxonomy();
Chris@165 210
Chris@0 211 return rv;
Chris@0 212 }
Chris@0 213
Chris@66 214 QString
Chris@66 215 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@66 216 {
Chris@66 217 QString file = "";
Chris@66 218
Chris@249 219 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 220 std::cerr << "FeatureExtractionPluginFactory::findPluginFile(\""
Chris@249 221 << soname.toStdString() << "\", \"" << inDir.toStdString() << "\")"
Chris@249 222 << std::endl;
Chris@249 223 #endif
Chris@249 224
Chris@66 225 if (inDir != "") {
Chris@66 226
Chris@66 227 QDir dir(inDir, PLUGIN_GLOB,
Chris@66 228 QDir::Name | QDir::IgnoreCase,
Chris@66 229 QDir::Files | QDir::Readable);
Chris@66 230 if (!dir.exists()) return "";
Chris@66 231
Chris@66 232 file = dir.filePath(QFileInfo(soname).fileName());
Chris@249 233
Chris@249 234 if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
Chris@249 235
Chris@249 236 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 237 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 238 << "found trivially at " << file.toStdString() << std::endl;
Chris@249 239 #endif
Chris@249 240
Chris@66 241 return file;
Chris@66 242 }
Chris@66 243
Chris@66 244 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@66 245 file = dir.filePath(dir[j]);
Chris@66 246 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@249 247
Chris@249 248 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 249 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@262 250 << "found \"" << soname.toStdString() << "\" at " << file.toStdString() << std::endl;
Chris@249 251 #endif
Chris@249 252
Chris@66 253 return file;
Chris@66 254 }
Chris@66 255 }
Chris@66 256
Chris@249 257 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 258 std::cerr << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
Chris@249 259 << "not found" << std::endl;
Chris@249 260 #endif
Chris@249 261
Chris@66 262 return "";
Chris@66 263
Chris@66 264 } else {
Chris@66 265
Chris@66 266 QFileInfo fi(soname);
Chris@249 267
Chris@249 268 if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
Chris@249 269 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 270 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 271 << "found trivially at " << soname.toStdString() << std::endl;
Chris@249 272 #endif
Chris@249 273 return soname;
Chris@249 274 }
Chris@66 275
Chris@66 276 if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@66 277 file = findPluginFile(soname, fi.absolutePath());
Chris@66 278 if (file != "") return file;
Chris@66 279 }
Chris@66 280
Chris@66 281 std::vector<QString> path = getPluginPath();
Chris@66 282 for (std::vector<QString>::iterator i = path.begin();
Chris@66 283 i != path.end(); ++i) {
Chris@66 284 if (*i != "") {
Chris@66 285 file = findPluginFile(soname, *i);
Chris@66 286 if (file != "") return file;
Chris@66 287 }
Chris@66 288 }
Chris@66 289
Chris@249 290 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 291 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 292 << "not found" << std::endl;
Chris@249 293 #endif
Chris@249 294
Chris@66 295 return "";
Chris@66 296 }
Chris@66 297 }
Chris@66 298
Chris@66 299 Vamp::Plugin *
Chris@0 300 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
Chris@0 301 float inputSampleRate)
Chris@0 302 {
Chris@408 303 Profiler profiler("FeatureExtractionPluginFactory::instantiatePlugin");
Chris@408 304
Chris@66 305 Vamp::Plugin *rv = 0;
Chris@298 306 Vamp::PluginHostAdapter *plugin = 0;
Chris@66 307
Chris@66 308 const VampPluginDescriptor *descriptor = 0;
Chris@66 309 int index = 0;
Chris@66 310
Chris@66 311 QString type, soname, label;
Chris@66 312 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@71 313 if (type != "vamp") {
Chris@0 314 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
Chris@0 315 return 0;
Chris@0 316 }
Chris@0 317
Chris@66 318 QString found = findPluginFile(soname);
Chris@66 319
Chris@66 320 if (found == "") {
Chris@66 321 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl;
Chris@117 322 return 0;
Chris@66 323 } else if (found != soname) {
Chris@249 324
Chris@249 325 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 326 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@249 327 std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl;
Chris@249 328 #endif
Chris@249 329
Chris@249 330 }
Chris@0 331
Chris@66 332 soname = found;
Chris@66 333
Chris@435 334 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
Chris@66 335
Chris@66 336 if (!libraryHandle) {
Chris@71 337 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 338 return 0;
Chris@19 339 }
Chris@19 340
Chris@66 341 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 342 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 343
Chris@66 344 if (!fn) {
Chris@66 345 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 346 goto done;
Chris@0 347 }
Chris@0 348
Chris@239 349 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@238 350 if (label == descriptor->identifier) break;
Chris@66 351 ++index;
Chris@47 352 }
Chris@47 353
Chris@66 354 if (!descriptor) {
Chris@66 355 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl;
Chris@66 356 goto done;
Martin@37 357 }
Martin@37 358
Chris@298 359 plugin = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Chris@298 360
Chris@298 361 if (plugin) {
Chris@298 362 m_handleMap[plugin] = libraryHandle;
Chris@298 363 rv = new PluginDeletionNotifyAdapter(plugin, this);
Chris@298 364 }
Chris@66 365
Chris@117 366 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl;
Chris@79 367
Chris@66 368 //!!! need to dlclose() when plugins from a given library are unloaded
Chris@66 369
Chris@66 370 done:
Chris@66 371 if (!rv) {
Chris@66 372 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 373 std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 374 }
Chris@66 375 }
Chris@73 376
Chris@73 377 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label.toStdString() << " from library " << soname.toStdString() << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << std::endl;
Chris@73 378
Chris@66 379 return rv;
Chris@0 380 }
Chris@0 381
Chris@298 382 void
Chris@298 383 FeatureExtractionPluginFactory::pluginDeleted(Vamp::Plugin *plugin)
Chris@298 384 {
Chris@298 385 void *handle = m_handleMap[plugin];
Chris@298 386 if (handle) {
Chris@299 387 // std::cerr << "unloading library " << handle << " for plugin " << plugin << std::endl;
Chris@298 388 DLCLOSE(handle);
Chris@298 389 }
Chris@298 390 m_handleMap.erase(plugin);
Chris@298 391 }
Chris@298 392
Chris@165 393 QString
Chris@165 394 FeatureExtractionPluginFactory::getPluginCategory(QString identifier)
Chris@165 395 {
Chris@165 396 return m_taxonomy[identifier];
Chris@165 397 }
Chris@165 398
Chris@165 399 void
Chris@165 400 FeatureExtractionPluginFactory::generateTaxonomy()
Chris@165 401 {
Chris@165 402 std::vector<QString> pluginPath = getPluginPath();
Chris@165 403 std::vector<QString> path;
Chris@165 404
Chris@165 405 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@165 406 if (pluginPath[i].contains("/lib/")) {
Chris@165 407 QString p(pluginPath[i]);
Chris@165 408 path.push_back(p);
Chris@165 409 p.replace("/lib/", "/share/");
Chris@165 410 path.push_back(p);
Chris@165 411 }
Chris@165 412 path.push_back(pluginPath[i]);
Chris@165 413 }
Chris@165 414
Chris@165 415 for (size_t i = 0; i < path.size(); ++i) {
Chris@165 416
Chris@165 417 QDir dir(path[i], "*.cat");
Chris@165 418
Chris@165 419 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i].toStdString() << " has " << dir.count() << " .cat files" << std::endl;
Chris@165 420 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@165 421
Chris@165 422 QFile file(path[i] + "/" + dir[j]);
Chris@165 423
Chris@165 424 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i].toStdString() + "/" + dir[j].toStdString()) << std::endl;
Chris@165 425
Chris@165 426 if (file.open(QIODevice::ReadOnly)) {
Chris@165 427 // std::cerr << "...opened" << std::endl;
Chris@165 428 QTextStream stream(&file);
Chris@165 429 QString line;
Chris@165 430
Chris@165 431 while (!stream.atEnd()) {
Chris@165 432 line = stream.readLine();
Chris@165 433 // std::cerr << "line is: \"" << line.toStdString() << "\"" << std::endl;
Chris@165 434 QString id = PluginIdentifier::canonicalise
Chris@165 435 (line.section("::", 0, 0));
Chris@165 436 QString cat = line.section("::", 1, 1);
Chris@165 437 m_taxonomy[id] = cat;
Chris@165 438 // std::cerr << "FeatureExtractionPluginFactory: set id \"" << id.toStdString() << "\" to cat \"" << cat.toStdString() << "\"" << std::endl;
Chris@165 439 }
Chris@165 440 }
Chris@165 441 }
Chris@165 442 }
Chris@165 443 }