annotate plugin/NativeVampPluginFactory.cpp @ 1247:8f076d02569a piper

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