annotate plugin/FeatureExtractionPluginFactory.cpp @ 498:fdf5930b7ccc

* Bring FeatureWriter and RDFFeatureWriter into the fold (from Runner) so that we can use them to export features from SV as well
author Chris Cannam
date Fri, 28 Nov 2008 13:47:11 +0000
parents a70dcfed59c1
children d7f3dfe6f9a4
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@259 61 // std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
Chris@259 62 // << "): creating new FeatureExtractionPluginFactory" << std::endl;
Chris@0 63 _nativeInstance = new FeatureExtractionPluginFactory();
Chris@0 64 }
Chris@0 65 return _nativeInstance;
Chris@0 66 }
Chris@0 67
Chris@0 68 else return 0;
Chris@0 69 }
Chris@0 70
Chris@0 71 FeatureExtractionPluginFactory *
Chris@0 72 FeatureExtractionPluginFactory::instanceFor(QString identifier)
Chris@0 73 {
Chris@0 74 QString type, soName, label;
Chris@0 75 PluginIdentifier::parseIdentifier(identifier, type, soName, label);
Chris@0 76 return instance(type);
Chris@0 77 }
Chris@0 78
Chris@0 79 std::vector<QString>
Chris@66 80 FeatureExtractionPluginFactory::getPluginPath()
Chris@66 81 {
Chris@117 82 if (!m_pluginPath.empty()) return m_pluginPath;
Chris@117 83
Chris@186 84 std::vector<std::string> p = Vamp::PluginHostAdapter::getPluginPath();
Chris@186 85 for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str());
Chris@186 86 return m_pluginPath;
Chris@66 87 }
Chris@66 88
Chris@66 89 std::vector<QString>
Chris@0 90 FeatureExtractionPluginFactory::getAllPluginIdentifiers()
Chris@0 91 {
Chris@0 92 FeatureExtractionPluginFactory *factory;
Chris@0 93 std::vector<QString> rv;
Chris@0 94
Chris@66 95 factory = instance("vamp");
Chris@0 96 if (factory) {
Chris@0 97 std::vector<QString> tmp = factory->getPluginIdentifiers();
Chris@0 98 for (size_t i = 0; i < tmp.size(); ++i) {
Chris@358 99 // std::cerr << "identifier: " << tmp[i].toStdString() << std::endl;
Chris@0 100 rv.push_back(tmp[i]);
Chris@0 101 }
Chris@0 102 }
Chris@0 103
Chris@0 104 // Plugins can change the locale, revert it to default.
Chris@0 105 setlocale(LC_ALL, "C");
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@249 120 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::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@249 132 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname.toStdString() << std::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@71 138 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 139 continue;
Chris@66 140 }
Chris@66 141
Chris@249 142 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 143 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << std::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@66 150 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 151 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 152 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << 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@249 158 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << std::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@251 176 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << std::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@251 196 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id.toStdString() << " at index " << index << std::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@66 203 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << 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@249 219 std::cerr << "FeatureExtractionPluginFactory::findPluginFile(\""
Chris@249 220 << soname.toStdString() << "\", \"" << inDir.toStdString() << "\")"
Chris@249 221 << std::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@249 236 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 237 << "found trivially at " << file.toStdString() << std::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@249 248 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@262 249 << "found \"" << soname.toStdString() << "\" at " << file.toStdString() << std::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@249 257 std::cerr << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
Chris@249 258 << "not found" << std::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@249 269 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 270 << "found trivially at " << soname.toStdString() << std::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@249 290 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 291 << "not found" << std::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@0 313 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::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@66 320 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << 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@249 325 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@249 326 std::cerr << soname.toStdString() << " -> " << found.toStdString() << 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@71 336 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << 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@66 344 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::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@66 354 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << 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@117 365 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::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@66 372 std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 373 }
Chris@66 374 }
Chris@73 375
Chris@73 376 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Instantiated plugin " << label.toStdString() << " from library " << soname.toStdString() << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << std::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@299 386 // std::cerr << "unloading library " << handle << " for plugin " << plugin << std::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@165 418 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i].toStdString() << " has " << dir.count() << " .cat files" << std::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@165 423 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i].toStdString() + "/" + dir[j].toStdString()) << std::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@165 432 // std::cerr << "line is: \"" << line.toStdString() << "\"" << 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@165 437 // std::cerr << "FeatureExtractionPluginFactory: set id \"" << id.toStdString() << "\" to cat \"" << cat.toStdString() << "\"" << std::endl;
Chris@165 438 }
Chris@165 439 }
Chris@165 440 }
Chris@165 441 }
Chris@165 442 }