annotate plugin/FeatureExtractionPluginFactory.cpp @ 690:1424aa29ae95

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