annotate plugin/NativeVampPluginFactory.cpp @ 1246:75aefcc9f07d piper

Use plugin scan results to inform the list requests issued to Piper servers
author Chris Cannam
date Thu, 03 Nov 2016 14:14:09 +0000
parents 5d886b7b4029
children 8f076d02569a
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@1225 94 cerr << "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@1225 102 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl;
Chris@1225 103 if (DLCLOSE(libraryHandle) != 0) {
Chris@1225 104 cerr << "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@1225 122 cerr << "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@1225 128 cerr << "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@1225 155 cerr << "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@1225 276 cerr << "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@1225 292 cerr << "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@1225 300 cerr << "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@1225 310 cerr << "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@1225 328 cerr << "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