annotate plugin/FeatureExtractionPluginFactory.cpp @ 1164:0f90a357cb2a

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