annotate plugin/FeatureExtractionPluginFactory.cpp @ 299:576be0d0d218

* Merge transform directory from sv-match-alignment branch (the previous comment included notes for this stuff, but I missed it in the actual merge) * Fix crash when a transform fails to create an output model and the thread that created the transform then deletes its input model thinking it's no longer needed, even though the transform run thread is still using it -- fix is to wait() on the transform before returning the null output model
author Chris Cannam
date Fri, 28 Sep 2007 16:15:06 +0000
parents 57d7889f626c
children 9203b82a8c53
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@0 98 rv.push_back(tmp[i]);
Chris@0 99 }
Chris@0 100 }
Chris@0 101
Chris@0 102 // Plugins can change the locale, revert it to default.
Chris@0 103 setlocale(LC_ALL, "C");
Chris@0 104 return rv;
Chris@0 105 }
Chris@0 106
Chris@0 107 std::vector<QString>
Chris@0 108 FeatureExtractionPluginFactory::getPluginIdentifiers()
Chris@0 109 {
Chris@0 110 std::vector<QString> rv;
Chris@66 111 std::vector<QString> path = getPluginPath();
Chris@66 112
Chris@66 113 for (std::vector<QString>::iterator i = path.begin(); i != path.end(); ++i) {
Chris@66 114
Chris@249 115 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 116 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: scanning directory " << i->toStdString() << std::endl;
Chris@249 117 #endif
Chris@66 118
Chris@66 119 QDir pluginDir(*i, PLUGIN_GLOB,
Chris@66 120 QDir::Name | QDir::IgnoreCase,
Chris@66 121 QDir::Files | QDir::Readable);
Chris@66 122
Chris@66 123 for (unsigned int j = 0; j < pluginDir.count(); ++j) {
Chris@66 124
Chris@66 125 QString soname = pluginDir.filePath(pluginDir[j]);
Chris@66 126
Chris@249 127 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 128 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: trying potential library " << soname.toStdString() << std::endl;
Chris@249 129 #endif
Chris@249 130
Chris@66 131 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 132
Chris@66 133 if (!libraryHandle) {
Chris@71 134 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 135 continue;
Chris@66 136 }
Chris@66 137
Chris@249 138 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 139 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: It's a library all right, checking for descriptor" << std::endl;
Chris@249 140 #endif
Chris@249 141
Chris@66 142 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 143 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 144
Chris@66 145 if (!fn) {
Chris@66 146 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 147 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 148 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 149 }
Chris@66 150 continue;
Chris@66 151 }
Chris@66 152
Chris@249 153 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 154 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Vamp descriptor found" << std::endl;
Chris@249 155 #endif
Chris@249 156
Chris@66 157 const VampPluginDescriptor *descriptor = 0;
Chris@66 158 int index = 0;
Chris@66 159
Chris@251 160 std::map<std::string, int> known;
Chris@251 161 bool ok = true;
Chris@251 162
Chris@239 163 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 164
Chris@251 165 if (known.find(descriptor->identifier) != known.end()) {
Chris@251 166 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Plugin library "
Chris@251 167 << soname.toStdString()
Chris@251 168 << " returns the same plugin identifier \""
Chris@251 169 << descriptor->identifier << "\" at indices "
Chris@251 170 << known[descriptor->identifier] << " and "
Chris@251 171 << index << std::endl;
Chris@251 172 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << std::endl;
Chris@251 173 ok = false;
Chris@251 174 break;
Chris@251 175 } else {
Chris@251 176 known[descriptor->identifier] = index;
Chris@251 177 }
Chris@251 178
Chris@251 179 ++index;
Chris@251 180 }
Chris@251 181
Chris@251 182 if (ok) {
Chris@251 183
Chris@251 184 index = 0;
Chris@251 185
Chris@251 186 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@251 187
Chris@251 188 QString id = PluginIdentifier::createIdentifier
Chris@251 189 ("vamp", soname, descriptor->identifier);
Chris@251 190 rv.push_back(id);
Chris@249 191 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@251 192 std::cerr << "FeatureExtractionPluginFactory::getPluginIdentifiers: Found plugin id " << id.toStdString() << " at index " << index << std::endl;
Chris@249 193 #endif
Chris@251 194 ++index;
Chris@251 195 }
Chris@66 196 }
Chris@66 197
Chris@66 198 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 199 std::cerr << "WARNING: FeatureExtractionPluginFactory::getPluginIdentifiers: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 200 }
Chris@66 201 }
Chris@66 202 }
Chris@66 203
Chris@165 204 generateTaxonomy();
Chris@165 205
Chris@0 206 return rv;
Chris@0 207 }
Chris@0 208
Chris@66 209 QString
Chris@66 210 FeatureExtractionPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@66 211 {
Chris@66 212 QString file = "";
Chris@66 213
Chris@249 214 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 215 std::cerr << "FeatureExtractionPluginFactory::findPluginFile(\""
Chris@249 216 << soname.toStdString() << "\", \"" << inDir.toStdString() << "\")"
Chris@249 217 << std::endl;
Chris@249 218 #endif
Chris@249 219
Chris@66 220 if (inDir != "") {
Chris@66 221
Chris@66 222 QDir dir(inDir, PLUGIN_GLOB,
Chris@66 223 QDir::Name | QDir::IgnoreCase,
Chris@66 224 QDir::Files | QDir::Readable);
Chris@66 225 if (!dir.exists()) return "";
Chris@66 226
Chris@66 227 file = dir.filePath(QFileInfo(soname).fileName());
Chris@249 228
Chris@249 229 if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
Chris@249 230
Chris@249 231 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 232 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 233 << "found trivially at " << file.toStdString() << std::endl;
Chris@249 234 #endif
Chris@249 235
Chris@66 236 return file;
Chris@66 237 }
Chris@66 238
Chris@66 239 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@66 240 file = dir.filePath(dir[j]);
Chris@66 241 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@249 242
Chris@249 243 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 244 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@262 245 << "found \"" << soname.toStdString() << "\" at " << file.toStdString() << std::endl;
Chris@249 246 #endif
Chris@249 247
Chris@66 248 return file;
Chris@66 249 }
Chris@66 250 }
Chris@66 251
Chris@249 252 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 253 std::cerr << "FeatureExtractionPluginFactory::findPluginFile (with dir): "
Chris@249 254 << "not found" << std::endl;
Chris@249 255 #endif
Chris@249 256
Chris@66 257 return "";
Chris@66 258
Chris@66 259 } else {
Chris@66 260
Chris@66 261 QFileInfo fi(soname);
Chris@249 262
Chris@249 263 if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
Chris@249 264 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 265 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 266 << "found trivially at " << soname.toStdString() << std::endl;
Chris@249 267 #endif
Chris@249 268 return soname;
Chris@249 269 }
Chris@66 270
Chris@66 271 if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@66 272 file = findPluginFile(soname, fi.absolutePath());
Chris@66 273 if (file != "") return file;
Chris@66 274 }
Chris@66 275
Chris@66 276 std::vector<QString> path = getPluginPath();
Chris@66 277 for (std::vector<QString>::iterator i = path.begin();
Chris@66 278 i != path.end(); ++i) {
Chris@66 279 if (*i != "") {
Chris@66 280 file = findPluginFile(soname, *i);
Chris@66 281 if (file != "") return file;
Chris@66 282 }
Chris@66 283 }
Chris@66 284
Chris@249 285 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 286 std::cerr << "FeatureExtractionPluginFactory::findPluginFile: "
Chris@249 287 << "not found" << std::endl;
Chris@249 288 #endif
Chris@249 289
Chris@66 290 return "";
Chris@66 291 }
Chris@66 292 }
Chris@66 293
Chris@66 294 Vamp::Plugin *
Chris@0 295 FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
Chris@0 296 float inputSampleRate)
Chris@0 297 {
Chris@66 298 Vamp::Plugin *rv = 0;
Chris@298 299 Vamp::PluginHostAdapter *plugin = 0;
Chris@66 300
Chris@66 301 const VampPluginDescriptor *descriptor = 0;
Chris@66 302 int index = 0;
Chris@66 303
Chris@66 304 QString type, soname, label;
Chris@66 305 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@71 306 if (type != "vamp") {
Chris@0 307 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
Chris@0 308 return 0;
Chris@0 309 }
Chris@0 310
Chris@66 311 QString found = findPluginFile(soname);
Chris@66 312
Chris@66 313 if (found == "") {
Chris@66 314 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find library file " << soname.toStdString() << std::endl;
Chris@117 315 return 0;
Chris@66 316 } else if (found != soname) {
Chris@249 317
Chris@249 318 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@249 319 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Given library name was " << soname.toStdString() << ", found at " << found.toStdString() << std::endl;
Chris@249 320 std::cerr << soname.toStdString() << " -> " << found.toStdString() << std::endl;
Chris@249 321 #endif
Chris@249 322
Chris@249 323 }
Chris@0 324
Chris@66 325 soname = found;
Chris@66 326
Chris@66 327 void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
Chris@66 328
Chris@66 329 if (!libraryHandle) {
Chris@71 330 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to load library " << soname.toStdString() << ": " << DLERROR() << std::endl;
Chris@66 331 return 0;
Chris@19 332 }
Chris@19 333
Chris@66 334 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@66 335 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@66 336
Chris@66 337 if (!fn) {
Chris@66 338 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: No descriptor function in " << soname.toStdString() << std::endl;
Chris@66 339 goto done;
Chris@0 340 }
Chris@0 341
Chris@239 342 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@238 343 if (label == descriptor->identifier) break;
Chris@66 344 ++index;
Chris@47 345 }
Chris@47 346
Chris@66 347 if (!descriptor) {
Chris@66 348 std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Failed to find plugin \"" << label.toStdString() << "\" in library " << soname.toStdString() << std::endl;
Chris@66 349 goto done;
Martin@37 350 }
Martin@37 351
Chris@298 352 plugin = new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
Chris@298 353
Chris@298 354 if (plugin) {
Chris@298 355 m_handleMap[plugin] = libraryHandle;
Chris@298 356 rv = new PluginDeletionNotifyAdapter(plugin, this);
Chris@298 357 }
Chris@66 358
Chris@117 359 // std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << std::endl;
Chris@79 360
Chris@66 361 //!!! need to dlclose() when plugins from a given library are unloaded
Chris@66 362
Chris@66 363 done:
Chris@66 364 if (!rv) {
Chris@66 365 if (DLCLOSE(libraryHandle) != 0) {
Chris@66 366 std::cerr << "WARNING: FeatureExtractionPluginFactory::instantiatePlugin: Failed to unload library " << soname.toStdString() << std::endl;
Chris@66 367 }
Chris@66 368 }
Chris@73 369
Chris@73 370 // 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 371
Chris@66 372 return rv;
Chris@0 373 }
Chris@0 374
Chris@298 375 void
Chris@298 376 FeatureExtractionPluginFactory::pluginDeleted(Vamp::Plugin *plugin)
Chris@298 377 {
Chris@298 378 void *handle = m_handleMap[plugin];
Chris@298 379 if (handle) {
Chris@299 380 // std::cerr << "unloading library " << handle << " for plugin " << plugin << std::endl;
Chris@298 381 DLCLOSE(handle);
Chris@298 382 }
Chris@298 383 m_handleMap.erase(plugin);
Chris@298 384 }
Chris@298 385
Chris@165 386 QString
Chris@165 387 FeatureExtractionPluginFactory::getPluginCategory(QString identifier)
Chris@165 388 {
Chris@165 389 return m_taxonomy[identifier];
Chris@165 390 }
Chris@165 391
Chris@165 392 void
Chris@165 393 FeatureExtractionPluginFactory::generateTaxonomy()
Chris@165 394 {
Chris@165 395 std::vector<QString> pluginPath = getPluginPath();
Chris@165 396 std::vector<QString> path;
Chris@165 397
Chris@165 398 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@165 399 if (pluginPath[i].contains("/lib/")) {
Chris@165 400 QString p(pluginPath[i]);
Chris@165 401 path.push_back(p);
Chris@165 402 p.replace("/lib/", "/share/");
Chris@165 403 path.push_back(p);
Chris@165 404 }
Chris@165 405 path.push_back(pluginPath[i]);
Chris@165 406 }
Chris@165 407
Chris@165 408 for (size_t i = 0; i < path.size(); ++i) {
Chris@165 409
Chris@165 410 QDir dir(path[i], "*.cat");
Chris@165 411
Chris@165 412 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i].toStdString() << " has " << dir.count() << " .cat files" << std::endl;
Chris@165 413 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@165 414
Chris@165 415 QFile file(path[i] + "/" + dir[j]);
Chris@165 416
Chris@165 417 // std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i].toStdString() + "/" + dir[j].toStdString()) << std::endl;
Chris@165 418
Chris@165 419 if (file.open(QIODevice::ReadOnly)) {
Chris@165 420 // std::cerr << "...opened" << std::endl;
Chris@165 421 QTextStream stream(&file);
Chris@165 422 QString line;
Chris@165 423
Chris@165 424 while (!stream.atEnd()) {
Chris@165 425 line = stream.readLine();
Chris@165 426 // std::cerr << "line is: \"" << line.toStdString() << "\"" << std::endl;
Chris@165 427 QString id = PluginIdentifier::canonicalise
Chris@165 428 (line.section("::", 0, 0));
Chris@165 429 QString cat = line.section("::", 1, 1);
Chris@165 430 m_taxonomy[id] = cat;
Chris@165 431 // std::cerr << "FeatureExtractionPluginFactory: set id \"" << id.toStdString() << "\" to cat \"" << cat.toStdString() << "\"" << std::endl;
Chris@165 432 }
Chris@165 433 }
Chris@165 434 }
Chris@165 435 }
Chris@165 436 }