annotate plugin/FeatureExtractionPluginFactory.cpp @ 1179:6b1af0f05f06 pluginscan

Make use of, and warn for, the plugin checker for all types of plugin. Haven't yet resolved the question of how to install and find it.
author Chris Cannam
date Thu, 14 Apr 2016 14:03:18 +0100
parents bf05d9259dbc
children 98664afd518b
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@1179 122 QStringList candidates =
Chris@1179 123 PluginScan::getInstance()->getCandidateVampLibraries();
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 }