annotate plugin/FeatureExtractionPluginFactory.cpp @ 1182:a1f410f895d3 3.0-integration

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