annotate plugin/FeatureExtractionPluginFactory.cpp @ 1008:d9e0e59a1581

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