# HG changeset patch # User Chris Cannam # Date 1477389866 -3600 # Node ID 9ae2ce9190e6dab707da8d610e77ead5a8b1e68c # Parent a2091d148d7fd9e32dcb6b2aac0cc5725d12497d# Parent e699bdeef63c387d8466b909ff414d925778d88a Merge diff -r a2091d148d7f -r 9ae2ce9190e6 base/Preferences.cpp --- a/base/Preferences.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/base/Preferences.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -41,6 +41,7 @@ m_propertyBoxLayout(VerticallyStacked), m_windowType(HanningWindow), m_resampleQuality(1), + m_runPluginsInProcess(true), m_omitRecentTemps(true), m_tempDirRoot(""), m_fixedSampleRate(0), @@ -65,6 +66,7 @@ m_windowType = WindowType (settings.value("window-type", int(HanningWindow)).toInt()); m_resampleQuality = settings.value("resample-quality", 1).toInt(); + m_runPluginsInProcess = settings.value("run-vamp-plugins-in-process", true).toBool(); m_fixedSampleRate = settings.value("fixed-sample-rate", 0).toDouble(); m_resampleOnLoad = settings.value("resample-on-load", false).toBool(); m_normaliseAudio = settings.value("normalise-audio", false).toBool(); @@ -266,6 +268,10 @@ return m_resampleQuality; } + if (name == "Run Vamp Plugins In Process") { + return m_runPluginsInProcess; + } + if (name == "Omit Temporaries from Recent Files") { if (deflt) *deflt = 1; return m_omitRecentTemps ? 1 : 0; @@ -414,6 +420,8 @@ setWindowType(WindowType(value)); } else if (name == "Resample Quality") { setResampleQuality(value); + } else if (name == "Run Vamp Plugins In Process") { + setRunPluginsInProcess(value ? true : false); } else if (name == "Omit Temporaries from Recent Files") { setOmitTempsFromRecentFiles(value ? true : false); } else if (name == "Background Mode") { @@ -519,6 +527,19 @@ } void +Preferences::setRunPluginsInProcess(bool run) +{ + if (m_runPluginsInProcess != run) { + m_runPluginsInProcess = run; + QSettings settings; + settings.beginGroup("Preferences"); + settings.setValue("run-vamp-plugins-in-process", run); + settings.endGroup(); + emit propertyChanged("Run Vamp Plugins In Process"); + } +} + +void Preferences::setOmitTempsFromRecentFiles(bool omit) { if (m_omitRecentTemps != omit) { diff -r a2091d148d7f -r 9ae2ce9190e6 base/Preferences.h --- a/base/Preferences.h Mon Oct 24 17:53:33 2016 +0100 +++ b/base/Preferences.h Tue Oct 25 11:04:26 2016 +0100 @@ -53,6 +53,8 @@ WindowType getWindowType() const { return m_windowType; } int getResampleQuality() const { return m_resampleQuality; } + bool getRunPluginsInProcess() const { return m_runPluginsInProcess; } + //!!! harmonise with PaneStack enum PropertyBoxLayout { VerticallyStacked, @@ -114,6 +116,7 @@ void setPropertyBoxLayout(PropertyBoxLayout layout); void setWindowType(WindowType type); void setResampleQuality(int quality); + void setRunPluginsInProcess(bool r); void setOmitTempsFromRecentFiles(bool omit); void setTemporaryDirectoryRoot(QString tempDirRoot); void setFixedSampleRate(sv_samplerate_t); @@ -151,6 +154,7 @@ PropertyBoxLayout m_propertyBoxLayout; WindowType m_windowType; int m_resampleQuality; + bool m_runPluginsInProcess; bool m_omitRecentTemps; QString m_tempDirRoot; sv_samplerate_t m_fixedSampleRate; diff -r a2091d148d7f -r 9ae2ce9190e6 files.pri --- a/files.pri Mon Oct 24 17:53:33 2016 +0100 +++ b/files.pri Tue Oct 25 11:04:26 2016 +0100 @@ -104,6 +104,8 @@ plugin/FeatureExtractionPluginFactory.h \ plugin/LADSPAPluginFactory.h \ plugin/LADSPAPluginInstance.h \ + plugin/NativeVampPluginFactory.h \ + plugin/PiperVampPluginFactory.h \ plugin/PluginIdentifier.h \ plugin/PluginXml.h \ plugin/RealTimePluginFactory.h \ @@ -215,6 +217,8 @@ plugin/FeatureExtractionPluginFactory.cpp \ plugin/LADSPAPluginFactory.cpp \ plugin/LADSPAPluginInstance.cpp \ + plugin/NativeVampPluginFactory.cpp \ + plugin/PiperVampPluginFactory.cpp \ plugin/PluginIdentifier.cpp \ plugin/PluginXml.cpp \ plugin/RealTimePluginFactory.cpp \ diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/FeatureExtractionPluginFactory.cpp --- a/plugin/FeatureExtractionPluginFactory.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/plugin/FeatureExtractionPluginFactory.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. + This file copyright 2006-2016 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -13,171 +13,32 @@ COPYING included with this distribution for more information. */ -#include "FeatureExtractionPluginFactory.h" -#include "PluginIdentifier.h" +#include "PiperVampPluginFactory.h" +#include "NativeVampPluginFactory.h" -#include "system/System.h" +#include +#include -#include "PluginScan.h" - -#ifdef _WIN32 -#undef VOID -#undef ERROR -#define CAPNP_LITE 1 -#endif -#include "vamp-client/AutoPlugin.h" - -#include -#include -#include -#include - -#include - -#include "base/Profiler.h" - -#include "vamp-client/ProcessQtTransport.h" -#include "vamp-client/CapnpRRClient.h" - -using namespace std; - -//#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 - -static FeatureExtractionPluginFactory *_nativeInstance = 0; +#include "base/Preferences.h" FeatureExtractionPluginFactory * -FeatureExtractionPluginFactory::instance(QString pluginType) +FeatureExtractionPluginFactory::instance() { - if (pluginType == "vamp") { - if (!_nativeInstance) { -// SVDEBUG << "FeatureExtractionPluginFactory::instance(" << pluginType// << "): creating new FeatureExtractionPluginFactory" << endl; - _nativeInstance = new FeatureExtractionPluginFactory(); - } - return _nativeInstance; + static QMutex mutex; + static FeatureExtractionPluginFactory *instance = 0; + + QMutexLocker locker(&mutex); + + if (!instance) { + + if (Preferences::getInstance()->getRunPluginsInProcess()) { + cerr << "creating native instance" << endl; + instance = new NativeVampPluginFactory(); + } else { + cerr << "creating piper instance" << endl; + instance = new PiperVampPluginFactory(); + } } - else return 0; + return instance; } - -FeatureExtractionPluginFactory * -FeatureExtractionPluginFactory::instanceFor(QString identifier) -{ - QString type, soName, label; - PluginIdentifier::parseIdentifier(identifier, type, soName, label); - return instance(type); -} - -FeatureExtractionPluginFactory::FeatureExtractionPluginFactory() : - m_serverName("piper-cpp/bin/piper-vamp-server") //!!! -{ -} - -vector -FeatureExtractionPluginFactory::getAllPluginIdentifiers() -{ - FeatureExtractionPluginFactory *factory; - vector rv; - - factory = instance("vamp"); - if (factory) { - vector tmp = factory->getPluginIdentifiers(); - for (size_t i = 0; i < tmp.size(); ++i) { -// cerr << "identifier: " << tmp[i] << endl; - rv.push_back(tmp[i]); - } - } - - // Plugins can change the locale, revert it to default. - RestoreStartupLocale(); - - return rv; -} - -vector -FeatureExtractionPluginFactory::getPluginIdentifiers() -{ - Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers"); - - QMutexLocker locker(&m_mutex); - - if (m_pluginData.empty()) { - populate(); - } - - vector rv; - - for (const auto &d: m_pluginData) { - rv.push_back(QString("vamp:") + QString::fromStdString(d.pluginKey)); - } - - return rv; -} - -Vamp::Plugin * -FeatureExtractionPluginFactory::instantiatePlugin(QString identifier, - sv_samplerate_t inputSampleRate) -{ - Profiler profiler("FeatureExtractionPluginFactory::instantiatePlugin"); - - QString type, soname, label; - PluginIdentifier::parseIdentifier(identifier, type, soname, label); - std::string pluginKey = (soname + ":" + label).toStdString(); - - auto ap = new piper_vamp::client::AutoPlugin - (m_serverName, pluginKey, float(inputSampleRate), 0); - - if (!ap->isOK()) { - delete ap; - return 0; - } else { - return ap; - } -} - -piper_vamp::PluginStaticData -FeatureExtractionPluginFactory::getPluginStaticData(QString identifier) -{ - QString type, soname, label; - PluginIdentifier::parseIdentifier(identifier, type, soname, label); - std::string pluginKey = (soname + ":" + label).toStdString(); - - for (const auto &d: m_pluginData) { - if (d.pluginKey == pluginKey) { - return d; - } - } - return {}; -} - -QString -FeatureExtractionPluginFactory::getPluginCategory(QString identifier) -{ - if (m_taxonomy.find(identifier) != m_taxonomy.end()) { - return m_taxonomy[identifier]; - } else { - return {}; - } -} - -void -FeatureExtractionPluginFactory::populate() -{ - piper_vamp::client::ProcessQtTransport transport(m_serverName); - piper_vamp::client::CapnpRRClient client(&transport); - piper_vamp::ListResponse lr = client.listPluginData(); - m_pluginData = lr.available; - - for (const auto &pd: m_pluginData) { - - QString identifier = - QString("vamp:") + QString::fromStdString(pd.pluginKey); - - QStringList catlist; - for (const auto &cs: pd.category) { - catlist.push_back(QString::fromStdString(cs)); - } - - m_taxonomy[identifier] = catlist.join(" > "); - } -} - diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/FeatureExtractionPluginFactory.h --- a/plugin/FeatureExtractionPluginFactory.h Mon Oct 24 17:53:33 2016 +0100 +++ b/plugin/FeatureExtractionPluginFactory.h Tue Oct 25 11:04:26 2016 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. + This file copyright 2006-2016 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -13,51 +13,55 @@ COPYING included with this distribution for more information. */ -#ifndef _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_ -#define _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_ - -#include -#include -#include -#include +#ifndef SV_FEATURE_EXTRACTION_PLUGIN_FACTORY_H +#define SV_FEATURE_EXTRACTION_PLUGIN_FACTORY_H #include -#include "base/Debug.h" +#include "vamp-support/PluginStaticData.h" + #include "base/BaseTypes.h" -#include "vamp-support/PluginStaticData.h" +#include class FeatureExtractionPluginFactory { public: - FeatureExtractionPluginFactory(); + static FeatureExtractionPluginFactory *instance(); + virtual ~FeatureExtractionPluginFactory() { } - static FeatureExtractionPluginFactory *instance(QString pluginType); - static FeatureExtractionPluginFactory *instanceFor(QString identifier); - static std::vector getAllPluginIdentifiers(); + /** + * Return all installed plugin identifiers. + */ + virtual std::vector getPluginIdentifiers(QString &errorMessage) { + return instance()->getPluginIdentifiers(errorMessage); + } - virtual std::vector getPluginIdentifiers(); + /** + * Return static data for the given plugin. + */ + virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier) { + return instance()->getPluginStaticData(identifier); + } - virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier); - - // We don't set blockSize or channels on this -- they're - // negotiated and handled via initialize() on the plugin + /** + * Instantiate (load) and return pointer to the plugin with the + * given identifier, at the given sample rate. We don't set + * blockSize or channels on this -- they're negotiated and handled + * via initialize() on the plugin itself after loading. + */ virtual Vamp::Plugin *instantiatePlugin(QString identifier, - sv_samplerate_t inputSampleRate); + sv_samplerate_t inputSampleRate) { + return instance()->instantiatePlugin(identifier, inputSampleRate); + } /** * Get category metadata about a plugin (without instantiating it). */ - virtual QString getPluginCategory(QString identifier); - -protected: - std::string m_serverName; - QMutex m_mutex; - std::vector m_pluginData; - std::map m_taxonomy; - void populate(); + virtual QString getPluginCategory(QString identifier) { + return instance()->getPluginCategory(identifier); + } }; #endif diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/NativeVampPluginFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/NativeVampPluginFactory.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -0,0 +1,432 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2016 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "NativeVampPluginFactory.h" +#include "PluginIdentifier.h" + +#include +#include + +#include "system/System.h" + +#include "PluginScan.h" + +#include +#include +#include +#include + +#include + +#include "base/Profiler.h" + +#include +#include + +using namespace std; + +//#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 + +class PluginDeletionNotifyAdapter : public Vamp::HostExt::PluginWrapper { +public: + PluginDeletionNotifyAdapter(Vamp::Plugin *plugin, + NativeVampPluginFactory *factory) : + PluginWrapper(plugin), m_factory(factory) { } + virtual ~PluginDeletionNotifyAdapter(); +protected: + NativeVampPluginFactory *m_factory; +}; + +PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter() +{ + // see notes in vamp-sdk/hostext/PluginLoader.cpp from which this is drawn + Vamp::Plugin *p = m_plugin; + delete m_plugin; + m_plugin = 0; + // acceptable use after free here, as pluginDeleted uses p only as + // pointer key and does not deref it + if (m_factory) m_factory->pluginDeleted(p); +} + +vector +NativeVampPluginFactory::getPluginPath() +{ + if (!m_pluginPath.empty()) return m_pluginPath; + + vector p = Vamp::PluginHostAdapter::getPluginPath(); + for (size_t i = 0; i < p.size(); ++i) m_pluginPath.push_back(p[i].c_str()); + return m_pluginPath; +} + +vector +NativeVampPluginFactory::getPluginIdentifiers(QString &) +{ + Profiler profiler("NativeVampPluginFactory::getPluginIdentifiers"); + + QMutexLocker locker(&m_mutex); + + if (!m_identifiers.empty()) { + return m_identifiers; + } + + QStringList candidates = PluginScan::getInstance()->getCandidateLibrariesFor + (PluginScan::VampPlugin); + + for (QString soname : candidates) { + + void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + + if (!libraryHandle) { + cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; + continue; + } + + VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) + DLSYM(libraryHandle, "vampGetPluginDescriptor"); + + if (!fn) { + cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } + continue; + } + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::getPluginIdentifiers: Vamp descriptor found" << endl; +#endif + + const VampPluginDescriptor *descriptor = 0; + int index = 0; + + map known; + bool ok = true; + + while ((descriptor = fn(VAMP_API_VERSION, index))) { + + if (known.find(descriptor->identifier) != known.end()) { + cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Plugin library " + << soname + << " returns the same plugin identifier \"" + << descriptor->identifier << "\" at indices " + << known[descriptor->identifier] << " and " + << index << endl; + cerr << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; + ok = false; + break; + } else { + known[descriptor->identifier] = index; + } + + ++index; + } + + if (ok) { + + index = 0; + + while ((descriptor = fn(VAMP_API_VERSION, index))) { + + QString id = PluginIdentifier::createIdentifier + ("vamp", soname, descriptor->identifier); + m_identifiers.push_back(id); +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; +#endif + ++index; + } + } + + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + } + } + + generateTaxonomy(); + + // Plugins can change the locale, revert it to default. + RestoreStartupLocale(); + + return m_identifiers; +} + +QString +NativeVampPluginFactory::findPluginFile(QString soname, QString inDir) +{ + QString file = ""; + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile(\"" + << soname << "\", \"" << inDir << "\")" + << endl; +#endif + + if (inDir != "") { + + QDir dir(inDir, PLUGIN_GLOB, + QDir::Name | QDir::IgnoreCase, + QDir::Files | QDir::Readable); + if (!dir.exists()) return ""; + + file = dir.filePath(QFileInfo(soname).fileName()); + + if (QFileInfo(file).exists() && QFileInfo(file).isFile()) { + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile: " + << "found trivially at " << file << endl; +#endif + + return file; + } + + for (unsigned int j = 0; j < dir.count(); ++j) { + file = dir.filePath(dir[j]); + if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) { + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile: " + << "found \"" << soname << "\" at " << file << endl; +#endif + + return file; + } + } + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile (with dir): " + << "not found" << endl; +#endif + + return ""; + + } else { + + QFileInfo fi(soname); + + if (fi.isAbsolute() && fi.exists() && fi.isFile()) { +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile: " + << "found trivially at " << soname << endl; +#endif + return soname; + } + + if (fi.isAbsolute() && fi.absolutePath() != "") { + file = findPluginFile(soname, fi.absolutePath()); + if (file != "") return file; + } + + vector path = getPluginPath(); + for (vector::iterator i = path.begin(); + i != path.end(); ++i) { + if (*i != "") { + file = findPluginFile(soname, *i); + if (file != "") return file; + } + } + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::findPluginFile: " + << "not found" << endl; +#endif + + return ""; + } +} + +Vamp::Plugin * +NativeVampPluginFactory::instantiatePlugin(QString identifier, + sv_samplerate_t inputSampleRate) +{ + Profiler profiler("NativeVampPluginFactory::instantiatePlugin"); + + Vamp::Plugin *rv = 0; + Vamp::PluginHostAdapter *plugin = 0; + + const VampPluginDescriptor *descriptor = 0; + int index = 0; + + QString type, soname, label; + PluginIdentifier::parseIdentifier(identifier, type, soname, label); + if (type != "vamp") { +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; +#endif + return 0; + } + + QString found = findPluginFile(soname); + + if (found == "") { + cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find library file " << soname << endl; + return 0; + } else if (found != soname) { + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::instantiatePlugin: Given library name was " << soname << ", found at " << found << endl; + cerr << soname << " -> " << found << endl; +#endif + + } + + soname = found; + + void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + + if (!libraryHandle) { + cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to load library " << soname << ": " << DLERROR() << endl; + return 0; + } + + VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction) + DLSYM(libraryHandle, "vampGetPluginDescriptor"); + + if (!fn) { + cerr << "NativeVampPluginFactory::instantiatePlugin: No descriptor function in " << soname << endl; + goto done; + } + + while ((descriptor = fn(VAMP_API_VERSION, index))) { + if (label == descriptor->identifier) break; + ++index; + } + + if (!descriptor) { + cerr << "NativeVampPluginFactory::instantiatePlugin: Failed to find plugin \"" << label << "\" in library " << soname << endl; + goto done; + } + + plugin = new Vamp::PluginHostAdapter(descriptor, float(inputSampleRate)); + + if (plugin) { + m_handleMap[plugin] = libraryHandle; + rv = new PluginDeletionNotifyAdapter(plugin, this); + } + +// SVDEBUG << "NativeVampPluginFactory::instantiatePlugin: Constructed Vamp plugin, rv is " << rv << endl; + + //!!! need to dlclose() when plugins from a given library are unloaded + +done: + if (!rv) { + if (DLCLOSE(libraryHandle) != 0) { + cerr << "WARNING: NativeVampPluginFactory::instantiatePlugin: Failed to unload library " << soname << endl; + } + } + +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "NativeVampPluginFactory::instantiatePlugin: Instantiated plugin " << label << " from library " << soname << ": descriptor " << descriptor << ", rv "<< rv << ", label " << rv->getName() << ", outputs " << rv->getOutputDescriptors().size() << endl; +#endif + + return rv; +} + +void +NativeVampPluginFactory::pluginDeleted(Vamp::Plugin *plugin) +{ + void *handle = m_handleMap[plugin]; + if (handle) { +#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "unloading library " << handle << " for plugin " << plugin << endl; +#endif + DLCLOSE(handle); + } + m_handleMap.erase(plugin); +} + +QString +NativeVampPluginFactory::getPluginCategory(QString identifier) +{ + return m_taxonomy[identifier]; +} + +void +NativeVampPluginFactory::generateTaxonomy() +{ + vector pluginPath = getPluginPath(); + vector path; + + for (size_t i = 0; i < pluginPath.size(); ++i) { + if (pluginPath[i].contains("/lib/")) { + QString p(pluginPath[i]); + path.push_back(p); + p.replace("/lib/", "/share/"); + path.push_back(p); + } + path.push_back(pluginPath[i]); + } + + for (size_t i = 0; i < path.size(); ++i) { + + QDir dir(path[i], "*.cat"); + +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; + for (unsigned int j = 0; j < dir.count(); ++j) { + + QFile file(path[i] + "/" + dir[j]); + +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; + + if (file.open(QIODevice::ReadOnly)) { +// cerr << "...opened" << endl; + QTextStream stream(&file); + QString line; + + while (!stream.atEnd()) { + line = stream.readLine(); +// cerr << "line is: \"" << line << "\"" << endl; + QString id = PluginIdentifier::canonicalise + (line.section("::", 0, 0)); + QString cat = line.section("::", 1, 1); + m_taxonomy[id] = cat; +// cerr << "NativeVampPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl; + } + } + } + } +} + +piper_vamp::PluginStaticData +NativeVampPluginFactory::getPluginStaticData(QString identifier) +{ + QMutexLocker locker(&m_mutex); + + if (m_pluginData.find(identifier) != m_pluginData.end()) { + return m_pluginData[identifier]; + } + + QString type, soname, label; + PluginIdentifier::parseIdentifier(identifier, type, soname, label); + std::string pluginKey = (soname + ":" + label).toStdString(); + + std::vector catlist; + for (auto s: getPluginCategory(identifier).split(" > ")) { + catlist.push_back(s.toStdString()); + } + + Vamp::Plugin *p = instantiatePlugin(identifier, 44100); + if (!p) return {}; + + auto psd = piper_vamp::PluginStaticData::fromPlugin(pluginKey, + catlist, + p); + + delete p; + + m_pluginData[identifier] = psd; + return psd; +} + diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/NativeVampPluginFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/NativeVampPluginFactory.h Tue Oct 25 11:04:26 2016 +0100 @@ -0,0 +1,68 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2016 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_NATIVE_VAMP_PLUGIN_FACTORY_H +#define SV_NATIVE_VAMP_PLUGIN_FACTORY_H + +#include "FeatureExtractionPluginFactory.h" + +#include +#include + +#include "base/Debug.h" + +#include + +/** + * FeatureExtractionPluginFactory type for Vamp plugins hosted + * in-process. + */ +class NativeVampPluginFactory : public FeatureExtractionPluginFactory +{ +public: + virtual ~NativeVampPluginFactory() { } + + virtual std::vector getPluginIdentifiers(QString &errorMessage) + override; + + virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier) + override; + + virtual Vamp::Plugin *instantiatePlugin(QString identifier, + sv_samplerate_t inputSampleRate) + override; + + /** + * Get category metadata about a plugin (without instantiating it). + */ + virtual QString getPluginCategory(QString identifier) override; + +protected: + QMutex m_mutex; + std::vector m_pluginPath; + std::vector m_identifiers; + std::map m_taxonomy; // identifier -> category string + std::map m_pluginData; // identifier -> data (created opportunistically) + + friend class PluginDeletionNotifyAdapter; + void pluginDeleted(Vamp::Plugin *); + std::map m_handleMap; + + QString findPluginFile(QString soname, QString inDir = ""); + std::vector getPluginPath(); + void generateTaxonomy(); +}; + +#endif diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/PiperVampPluginFactory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PiperVampPluginFactory.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -0,0 +1,170 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "PiperVampPluginFactory.h" +#include "PluginIdentifier.h" + +#include "system/System.h" + +#include "PluginScan.h" + +#ifdef _WIN32 +#undef VOID +#undef ERROR +#define CAPNP_LITE 1 +#endif + +#include "vamp-client/AutoPlugin.h" + +#include +#include +#include +#include +#include + +#include + +#include "base/Profiler.h" + +#include "vamp-client/ProcessQtTransport.h" +#include "vamp-client/CapnpRRClient.h" + +using namespace std; + +//#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 + +PiperVampPluginFactory::PiperVampPluginFactory() : + // No server unless we find one - don't run arbitrary stuff from the path: + m_serverName() +{ + // Server must exist either in the same directory as this one or + // (preferably) a subdirectory called "piper-bin". + //!!! todo: merge this with plugin scan checker thingy used in main.cpp? + QString myDir = QCoreApplication::applicationDirPath(); + QString name = "piper-vamp-server"; + QString path = myDir + "/piper-bin/" + name; + QString suffix = ""; +#ifdef _WIN32 + suffix = ".exe"; +#endif + if (!QFile(path + suffix).exists()) { + cerr << "NOTE: Piper Vamp server not found at " << (path + suffix) + << ", trying in my own directory" << endl; + path = myDir + "/" + name; + } + if (!QFile(path + suffix).exists()) { + cerr << "NOTE: Piper Vamp server not found at " << (path + suffix) + << endl; + } else { + m_serverName = (path + suffix).toStdString(); + } +} + +vector +PiperVampPluginFactory::getPluginIdentifiers(QString &errorMessage) +{ + Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers"); + + QMutexLocker locker(&m_mutex); + + if (m_serverName == "") { + errorMessage = QObject::tr("External plugin host executable does not appear to be installed"); + return {}; + } + + if (m_pluginData.empty()) { + populate(errorMessage); + } + + vector rv; + + for (const auto &d: m_pluginData) { + rv.push_back(QString("vamp:") + QString::fromStdString(d.second.pluginKey)); + } + + return rv; +} + +Vamp::Plugin * +PiperVampPluginFactory::instantiatePlugin(QString identifier, + sv_samplerate_t inputSampleRate) +{ + Profiler profiler("PiperVampPluginFactory::instantiatePlugin"); + + auto psd = getPluginStaticData(identifier); + if (psd.pluginKey == "") { + return 0; + } + + auto ap = new piper_vamp::client::AutoPlugin + (m_serverName, psd.pluginKey, float(inputSampleRate), 0); + if (!ap->isOK()) { + delete ap; + return 0; + } + + return ap; +} + +piper_vamp::PluginStaticData +PiperVampPluginFactory::getPluginStaticData(QString identifier) +{ + if (m_pluginData.find(identifier) != m_pluginData.end()) { + return m_pluginData[identifier]; + } else { + return {}; + } +} + +QString +PiperVampPluginFactory::getPluginCategory(QString identifier) +{ + if (m_taxonomy.find(identifier) != m_taxonomy.end()) { + return m_taxonomy[identifier]; + } else { + return {}; + } +} + +void +PiperVampPluginFactory::populate(QString &errorMessage) +{ + if (m_serverName == "") return; + + piper_vamp::client::ProcessQtTransport transport(m_serverName); + if (!transport.isOK()) { + errorMessage = QObject::tr("Could not start external plugin host"); + return; + } + + piper_vamp::client::CapnpRRClient client(&transport); + piper_vamp::ListResponse lr = client.listPluginData(); + + for (const auto &pd: lr.available) { + + QString identifier = + QString("vamp:") + QString::fromStdString(pd.pluginKey); + + m_pluginData[identifier] = pd; + + QStringList catlist; + for (const auto &cs: pd.category) { + catlist.push_back(QString::fromStdString(cs)); + } + + m_taxonomy[identifier] = catlist.join(" > "); + } +} + diff -r a2091d148d7f -r 9ae2ce9190e6 plugin/PiperVampPluginFactory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PiperVampPluginFactory.h Tue Oct 25 11:04:26 2016 +0100 @@ -0,0 +1,58 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2016 Chris Cannam and QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef SV_PIPER_VAMP_PLUGIN_FACTORY_H +#define SV_PIPER_VAMP_PLUGIN_FACTORY_H + +#include "FeatureExtractionPluginFactory.h" + +#include +#include +#include + +#include "base/Debug.h" + +/** + * FeatureExtractionPluginFactory type for Vamp plugins hosted in a + * separate process using Piper protocol. + */ +class PiperVampPluginFactory : public FeatureExtractionPluginFactory +{ +public: + PiperVampPluginFactory(); + + virtual ~PiperVampPluginFactory() { } + + virtual std::vector getPluginIdentifiers(QString &errorMessage) + override; + + virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier) + override; + + virtual Vamp::Plugin *instantiatePlugin(QString identifier, + sv_samplerate_t inputSampleRate) + override; + + virtual QString getPluginCategory(QString identifier) override; + +protected: + QMutex m_mutex; + std::string m_serverName; + std::map m_pluginData; // identifier -> data + std::map m_taxonomy; // identifier -> category string + void populate(QString &errorMessage); +}; + +#endif diff -r a2091d148d7f -r 9ae2ce9190e6 svcore.pro --- a/svcore.pro Mon Oct 24 17:53:33 2016 +0100 +++ b/svcore.pro Tue Oct 25 11:04:26 2016 +0100 @@ -31,3 +31,4 @@ HEADERS = $$(SVCORE_HEADERS) SOURCES = $$(SVCORE_SOURCES) + diff -r a2091d148d7f -r 9ae2ce9190e6 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -16,6 +16,7 @@ #include "FeatureExtractionModelTransformer.h" #include "plugin/FeatureExtractionPluginFactory.h" + #include "plugin/PluginXml.h" #include @@ -93,7 +94,7 @@ QString pluginId = primaryTransform.getPluginIdentifier(); FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); + FeatureExtractionPluginFactory::instance(); if (!factory) { m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); @@ -731,9 +732,6 @@ if (m_abandoned) break; - cerr << "calling process() from thread " - << QThread::currentThreadId() << endl; - Vamp::Plugin::FeatureSet features = m_plugin->process (buffers, RealTime::frame2RealTime(blockFrame, sampleRate).toVampRealTime()); diff -r a2091d148d7f -r 9ae2ce9190e6 transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/transform/ModelTransformerFactory.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -93,17 +93,7 @@ Vamp::PluginBase *plugin = 0; - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - cerr << "getConfigurationForTransform: instantiating Vamp plugin" << endl; - - Vamp::Plugin *vp = - FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin - (id, float(inputModel->getSampleRate())); - - plugin = vp; - - } else if (RealTimePluginFactory::instanceFor(id)) { + if (RealTimePluginFactory::instanceFor(id)) { RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id); @@ -120,6 +110,16 @@ (id, 0, 0, sampleRate, blockSize, channels); plugin = rtp; + + } else { + + cerr << "getConfigurationForTransform: instantiating Vamp plugin" << endl; + + Vamp::Plugin *vp = + FeatureExtractionPluginFactory::instance()->instantiatePlugin + (id, float(inputModel->getSampleRate())); + + plugin = vp; } if (plugin) { @@ -171,20 +171,15 @@ QString id = transforms[0].getPluginIdentifier(); - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - transformer = - new FeatureExtractionModelTransformer(input, transforms); - - } else if (RealTimePluginFactory::instanceFor(id)) { + if (RealTimePluginFactory::instanceFor(id)) { transformer = new RealTimeEffectModelTransformer(input, transforms[0]); } else { - SVDEBUG << "ModelTransformerFactory::createTransformer: Unknown transform \"" - << transforms[0].getIdentifier() << "\"" << endl; - return transformer; + + transformer = + new FeatureExtractionModelTransformer(input, transforms); } if (transformer) transformer->setObjectName(transforms[0].getIdentifier()); diff -r a2091d148d7f -r 9ae2ce9190e6 transform/Transform.cpp --- a/transform/Transform.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/transform/Transform.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -202,12 +202,10 @@ Transform::Type Transform::getType() const { - if (FeatureExtractionPluginFactory::instanceFor(getPluginIdentifier())) { - return FeatureExtraction; - } else if (RealTimePluginFactory::instanceFor(getPluginIdentifier())) { + if (RealTimePluginFactory::instanceFor(getPluginIdentifier())) { return RealTimeEffect; } else { - return UnknownType; + return FeatureExtraction; } } diff -r a2091d148d7f -r 9ae2ce9190e6 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Mon Oct 24 17:53:33 2016 +0100 +++ b/transform/TransformFactory.cpp Tue Oct 25 11:04:26 2016 +0100 @@ -16,6 +16,7 @@ #include "TransformFactory.h" #include "plugin/FeatureExtractionPluginFactory.h" + #include "plugin/RealTimePluginFactory.h" #include "plugin/RealTimePluginInstance.h" #include "plugin/PluginXml.h" @@ -402,22 +403,21 @@ void TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms) { - std::vector plugs = - FeatureExtractionPluginFactory::getAllPluginIdentifiers(); + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instance(); + + QString errorMessage; + std::vector plugs = factory->getPluginIdentifiers(errorMessage); + if (errorMessage != "") { + m_errorString = tr("Failed to list Vamp plugins: %1").arg(errorMessage); + } + if (m_exiting) return; for (int i = 0; i < (int)plugs.size(); ++i) { QString pluginId = plugs[i]; - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); - - if (!factory) { - cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId << endl; - continue; - } - piper_vamp::PluginStaticData psd = factory->getPluginStaticData(pluginId); if (psd.pluginKey == "") { @@ -794,8 +794,8 @@ // cerr << "TransformFactory::instantiateDefaultPluginFor: identifier \"" // << identifier << "\" is a feature extraction transform" << endl; - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instance(); if (factory) { plugin = factory->instantiatePlugin(pluginId, rate); @@ -914,22 +914,7 @@ { QString id = identifier.section(':', 0, 2); - if (FeatureExtractionPluginFactory::instanceFor(id)) { - - Vamp::Plugin *plugin = - FeatureExtractionPluginFactory::instanceFor(id)-> - instantiatePlugin(id, 44100); - if (!plugin) return false; - - min = (int)plugin->getMinChannelCount(); - max = (int)plugin->getMaxChannelCount(); - delete plugin; - - return true; - - } else if (RealTimePluginFactory::instanceFor(id)) { - - // don't need to instantiate + if (RealTimePluginFactory::instanceFor(id)) { const RealTimePluginDescriptor *descriptor = RealTimePluginFactory::instanceFor(id)-> @@ -940,6 +925,17 @@ max = descriptor->audioInputPortCount; return true; + + } else { + + auto psd = FeatureExtractionPluginFactory::instance()-> + getPluginStaticData(id); + if (psd.pluginKey == "") return false; + + min = (int)psd.minChannelCount; + max = (int)psd.maxChannelCount; + + return true; } return false; diff -r a2091d148d7f -r 9ae2ce9190e6 transform/TransformFactory.h --- a/transform/TransformFactory.h Mon Oct 24 17:53:33 2016 +0100 +++ b/transform/TransformFactory.h Tue Oct 25 11:04:26 2016 +0100 @@ -196,6 +196,10 @@ void setParametersFromPluginConfigurationXml(Transform &transform, QString xml); + QString getStartupFailureReport() const { + return m_errorString; + } + protected: typedef std::map TransformDescriptionMap; @@ -205,6 +209,8 @@ TransformDescriptionMap m_uninstalledTransforms; bool m_uninstalledTransformsPopulated; + QString m_errorString; + void populateTransforms(); void populateUninstalledTransforms(); void populateFeatureExtractionPlugins(TransformDescriptionMap &);