annotate plugin/NativeVampPluginFactory.cpp @ 1234:2d9e2771805a project-file-rework

Error handling
author Chris Cannam
date Thu, 27 Oct 2016 14:01:35 +0100
parents 5d886b7b4029
children 75aefcc9f07d
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@1225 84 QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor
Chris@1225 85 (PluginScan::VampPlugin);
Chris@1225 86
Chris@1225 87 for (QString soname : candidates) {
Chris@1225 88
Chris@1225 89 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
Chris@1225 90
Chris@1225 91 if (!libraryHandle) {
Chris@1225 92 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl;
Chris@1225 93 continue;
Chris@1225 94 }
Chris@1225 95
Chris@1225 96 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@1225 97 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@1225 98
Chris@1225 99 if (!fn) {
Chris@1225 100 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl;
Chris@1225 101 if (DLCLOSE(libraryHandle) != 0) {
Chris@1225 102 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
Chris@1225 103 }
Chris@1225 104 continue;
Chris@1225 105 }
Chris@1225 106
Chris@1225 107 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 108 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl;
Chris@1225 109 #endif
Chris@1225 110
Chris@1225 111 const VampPluginDescriptor *descriptor = 0;
Chris@1225 112 int index = 0;
Chris@1225 113
Chris@1225 114 map<string, int> known;
Chris@1225 115 bool ok = true;
Chris@1225 116
Chris@1225 117 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@1225 118
Chris@1225 119 if (known.find(descriptor->identifier) != known.end()) {
Chris@1225 120 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Plugin library "
Chris@1225 121 << soname
Chris@1225 122 << " returns the same plugin identifier \""
Chris@1225 123 << descriptor->identifier << "\" at indices "
Chris@1225 124 << known[descriptor->identifier] << " and "
Chris@1225 125 << index << endl;
Chris@1225 126 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl;
Chris@1225 127 ok = false;
Chris@1225 128 break;
Chris@1225 129 } else {
Chris@1225 130 known[descriptor->identifier] = index;
Chris@1225 131 }
Chris@1225 132
Chris@1225 133 ++index;
Chris@1225 134 }
Chris@1225 135
Chris@1225 136 if (ok) {
Chris@1225 137
Chris@1225 138 index = 0;
Chris@1225 139
Chris@1225 140 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@1225 141
Chris@1225 142 QString id = PluginIdentifier::createIdentifier
Chris@1225 143 ("vamp", soname, descriptor->identifier);
Chris@1225 144 m_identifiers.push_back(id);
Chris@1225 145 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 146 cerr << "NativeVampPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl;
Chris@1225 147 #endif
Chris@1225 148 ++index;
Chris@1225 149 }
Chris@1225 150 }
Chris@1225 151
Chris@1225 152 if (DLCLOSE(libraryHandle) != 0) {
Chris@1225 153 cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl;
Chris@1225 154 }
Chris@0 155 }
Chris@0 156
Chris@1225 157 generateTaxonomy();
Chris@1225 158
Chris@0 159 // Plugins can change the locale, revert it to default.
Chris@608 160 RestoreStartupLocale();
Chris@608 161
Chris@1225 162 return m_identifiers;
Chris@1225 163 }
Chris@1225 164
Chris@1225 165 QString
Chris@1225 166 NativeVampPluginFactory::findPluginFile(QString soname, QString inDir)
Chris@1225 167 {
Chris@1225 168 QString file = "";
Chris@1225 169
Chris@1225 170 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 171 cerr << "NativeVampPluginFactory::findPluginFile(\""
Chris@1225 172 << soname << "\", \"" << inDir << "\")"
Chris@1225 173 << endl;
Chris@1225 174 #endif
Chris@1225 175
Chris@1225 176 if (inDir != "") {
Chris@1225 177
Chris@1225 178 QDir dir(inDir, PLUGIN_GLOB,
Chris@1225 179 QDir::Name | QDir::IgnoreCase,
Chris@1225 180 QDir::Files | QDir::Readable);
Chris@1225 181 if (!dir.exists()) return "";
Chris@1225 182
Chris@1225 183 file = dir.filePath(QFileInfo(soname).fileName());
Chris@1225 184
Chris@1225 185 if (QFileInfo(file).exists() && QFileInfo(file).isFile()) {
Chris@1225 186
Chris@1225 187 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 188 cerr << "NativeVampPluginFactory::findPluginFile: "
Chris@1225 189 << "found trivially at " << file << endl;
Chris@1225 190 #endif
Chris@1225 191
Chris@1225 192 return file;
Chris@1225 193 }
Chris@1225 194
Chris@1225 195 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@1225 196 file = dir.filePath(dir[j]);
Chris@1225 197 if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) {
Chris@1225 198
Chris@1225 199 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 200 cerr << "NativeVampPluginFactory::findPluginFile: "
Chris@1225 201 << "found \"" << soname << "\" at " << file << endl;
Chris@1225 202 #endif
Chris@1225 203
Chris@1225 204 return file;
Chris@1225 205 }
Chris@1225 206 }
Chris@1225 207
Chris@1225 208 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 209 cerr << "NativeVampPluginFactory::findPluginFile (with dir): "
Chris@1225 210 << "not found" << endl;
Chris@1225 211 #endif
Chris@1225 212
Chris@1225 213 return "";
Chris@1225 214
Chris@1225 215 } else {
Chris@1225 216
Chris@1225 217 QFileInfo fi(soname);
Chris@1225 218
Chris@1225 219 if (fi.isAbsolute() && fi.exists() && fi.isFile()) {
Chris@1225 220 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 221 cerr << "NativeVampPluginFactory::findPluginFile: "
Chris@1225 222 << "found trivially at " << soname << endl;
Chris@1225 223 #endif
Chris@1225 224 return soname;
Chris@1225 225 }
Chris@1225 226
Chris@1225 227 if (fi.isAbsolute() && fi.absolutePath() != "") {
Chris@1225 228 file = findPluginFile(soname, fi.absolutePath());
Chris@1225 229 if (file != "") return file;
Chris@1225 230 }
Chris@1225 231
Chris@1225 232 vector<QString> path = getPluginPath();
Chris@1225 233 for (vector<QString>::iterator i = path.begin();
Chris@1225 234 i != path.end(); ++i) {
Chris@1225 235 if (*i != "") {
Chris@1225 236 file = findPluginFile(soname, *i);
Chris@1225 237 if (file != "") return file;
Chris@1225 238 }
Chris@1225 239 }
Chris@1225 240
Chris@1225 241 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 242 cerr << "NativeVampPluginFactory::findPluginFile: "
Chris@1225 243 << "not found" << endl;
Chris@1225 244 #endif
Chris@1225 245
Chris@1225 246 return "";
Chris@1225 247 }
Chris@1225 248 }
Chris@1225 249
Chris@1225 250 Vamp::Plugin *
Chris@1225 251 NativeVampPluginFactory::instantiatePlugin(QString identifier,
Chris@1225 252 sv_samplerate_t inputSampleRate)
Chris@1225 253 {
Chris@1225 254 Profiler profiler("NativeVampPluginFactory::instantiatePlugin");
Chris@1225 255
Chris@1225 256 Vamp::Plugin *rv = 0;
Chris@1225 257 Vamp::PluginHostAdapter *plugin = 0;
Chris@1225 258
Chris@1225 259 const VampPluginDescriptor *descriptor = 0;
Chris@1225 260 int index = 0;
Chris@1225 261
Chris@1225 262 QString type, soname, label;
Chris@1225 263 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@1225 264 if (type != "vamp") {
Chris@1225 265 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 266 cerr << "NativeVampPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl;
Chris@1225 267 #endif
Chris@1225 268 return 0;
Chris@1225 269 }
Chris@1225 270
Chris@1225 271 QString found = findPluginFile(soname);
Chris@1225 272
Chris@1225 273 if (found == "") {
Chris@1225 274 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl;
Chris@1225 275 return 0;
Chris@1225 276 } else if (found != soname) {
Chris@1225 277
Chris@1225 278 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 279 cerr << "NativeVampPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl;
Chris@1225 280 cerr << soname << " -> " << found << endl;
Chris@1225 281 #endif
Chris@1225 282
Chris@1225 283 }
Chris@1225 284
Chris@1225 285 soname = found;
Chris@1225 286
Chris@1225 287 void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL);
Chris@1225 288
Chris@1225 289 if (!libraryHandle) {
Chris@1225 290 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl;
Chris@1225 291 return 0;
Chris@1225 292 }
Chris@1225 293
Chris@1225 294 VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
Chris@1225 295 DLSYM(libraryHandle, "vampGetPluginDescriptor");
Chris@1225 296
Chris@1225 297 if (!fn) {
Chris@1225 298 cerr << "NativeVampPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl;
Chris@1225 299 goto done;
Chris@1225 300 }
Chris@1225 301
Chris@1225 302 while ((descriptor = fn(VAMP_API_VERSION, index))) {
Chris@1225 303 if (label == descriptor->identifier) break;
Chris@1225 304 ++index;
Chris@1225 305 }
Chris@1225 306
Chris@1225 307 if (!descriptor) {
Chris@1225 308 cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl;
Chris@1225 309 goto done;
Chris@1225 310 }
Chris@1225 311
Chris@1225 312 plugin = new Vamp::PluginHostAdapter(descriptor, float(inputSampleRate));
Chris@1225 313
Chris@1225 314 if (plugin) {
Chris@1225 315 m_handleMap[plugin] = libraryHandle;
Chris@1225 316 rv = new PluginDeletionNotifyAdapter(plugin, this);
Chris@1225 317 }
Chris@1225 318
Chris@1225 319 // SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << endl;
Chris@1225 320
Chris@1225 321 //!!! need to dlclose() when plugins from a given library are unloaded
Chris@1225 322
Chris@1225 323 done:
Chris@1225 324 if (!rv) {
Chris@1225 325 if (DLCLOSE(libraryHandle) != 0) {
Chris@1225 326 cerr << "WARNING: NativeVampPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl;
Chris@1225 327 }
Chris@1225 328 }
Chris@1225 329
Chris@1225 330 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 331 cerr << "NativeVampPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl;
Chris@1225 332 #endif
Chris@1225 333
Chris@0 334 return rv;
Chris@0 335 }
Chris@0 336
Chris@1225 337 void
Chris@1225 338 NativeVampPluginFactory::pluginDeleted(Vamp::Plugin *plugin)
Chris@0 339 {
Chris@1225 340 void *handle = m_handleMap[plugin];
Chris@1225 341 if (handle) {
Chris@1225 342 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
Chris@1225 343 cerr << "unloading library " << handle << " for plugin " << plugin << endl;
Chris@1225 344 #endif
Chris@1225 345 DLCLOSE(handle);
Chris@1225 346 }
Chris@1225 347 m_handleMap.erase(plugin);
Chris@1225 348 }
Chris@408 349
Chris@1225 350 QString
Chris@1225 351 NativeVampPluginFactory::getPluginCategory(QString identifier)
Chris@1225 352 {
Chris@1225 353 return m_taxonomy[identifier];
Chris@1225 354 }
Chris@1225 355
Chris@1225 356 void
Chris@1225 357 NativeVampPluginFactory::generateTaxonomy()
Chris@1225 358 {
Chris@1225 359 vector<QString> pluginPath = getPluginPath();
Chris@1225 360 vector<QString> path;
Chris@1225 361
Chris@1225 362 for (size_t i = 0; i < pluginPath.size(); ++i) {
Chris@1225 363 if (pluginPath[i].contains("/lib/")) {
Chris@1225 364 QString p(pluginPath[i]);
Chris@1225 365 path.push_back(p);
Chris@1225 366 p.replace("/lib/", "/share/");
Chris@1225 367 path.push_back(p);
Chris@1225 368 }
Chris@1225 369 path.push_back(pluginPath[i]);
Chris@1225 370 }
Chris@1225 371
Chris@1225 372 for (size_t i = 0; i < path.size(); ++i) {
Chris@1225 373
Chris@1225 374 QDir dir(path[i], "*.cat");
Chris@1225 375
Chris@1225 376 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl;
Chris@1225 377 for (unsigned int j = 0; j < dir.count(); ++j) {
Chris@1225 378
Chris@1225 379 QFile file(path[i] + "/" + dir[j]);
Chris@1225 380
Chris@1225 381 // SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl;
Chris@1225 382
Chris@1225 383 if (file.open(QIODevice::ReadOnly)) {
Chris@1225 384 // cerr << "...opened" << endl;
Chris@1225 385 QTextStream stream(&file);
Chris@1225 386 QString line;
Chris@1225 387
Chris@1225 388 while (!stream.atEnd()) {
Chris@1225 389 line = stream.readLine();
Chris@1225 390 // cerr << "line is: \"" << line << "\"" << endl;
Chris@1225 391 QString id = PluginIdentifier::canonicalise
Chris@1225 392 (line.section("::", 0, 0));
Chris@1225 393 QString cat = line.section("::", 1, 1);
Chris@1225 394 m_taxonomy[id] = cat;
Chris@1225 395 // cerr << "NativeVampPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl;
Chris@1225 396 }
Chris@1225 397 }
Chris@1225 398 }
Chris@1225 399 }
Chris@1225 400 }
Chris@1225 401
Chris@1225 402 piper_vamp::PluginStaticData
Chris@1225 403 NativeVampPluginFactory::getPluginStaticData(QString identifier)
Chris@1225 404 {
Chris@1209 405 QMutexLocker locker(&m_mutex);
Chris@1209 406
Chris@1225 407 if (m_pluginData.find(identifier) != m_pluginData.end()) {
Chris@1225 408 return m_pluginData[identifier];
Chris@1209 409 }
Chris@1210 410
Chris@66 411 QString type, soname, label;
Chris@66 412 PluginIdentifier::parseIdentifier(identifier, type, soname, label);
Chris@1210 413 std::string pluginKey = (soname + ":" + label).toStdString();
Chris@0 414
Chris@1225 415 std::vector<std::string> catlist;
Chris@1225 416 for (auto s: getPluginCategory(identifier).split(" > ")) {
Chris@1225 417 catlist.push_back(s.toStdString());
Chris@1225 418 }
Chris@1225 419
Chris@1225 420 Vamp::Plugin *p = instantiatePlugin(identifier, 44100);
Chris@1225 421 if (!p) return {};
Chris@66 422
Chris@1225 423 auto psd = piper_vamp::PluginStaticData::fromPlugin(pluginKey,
Chris@1225 424 catlist,
Chris@1225 425 p);
Chris@1225 426
Chris@1225 427 delete p;
Chris@1225 428
Chris@1225 429 m_pluginData[identifier] = psd;
Chris@1225 430 return psd;
Chris@298 431 }
Chris@298 432