annotate plugin/FeatureExtractionPluginFactory.cpp @ 404:4884fba80e00

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