changeset 1225:ba16388b937d piper

Restore native-Vamp factory and make the choice between Piper and Native a preference
author Chris Cannam
date Fri, 21 Oct 2016 11:49:27 +0100
parents ab050519c4ba
children 91ff08313375
files base/Preferences.cpp base/Preferences.h plugin/FeatureExtractionPluginFactory.cpp plugin/FeatureExtractionPluginFactory.h plugin/NativeVampPluginFactory.cpp plugin/NativeVampPluginFactory.h plugin/PiperVampPluginFactory.cpp plugin/PiperVampPluginFactory.h svcore.pro transform/FeatureExtractionModelTransformer.cpp transform/ModelTransformerFactory.cpp transform/Transform.cpp transform/TransformFactory.cpp
diffstat 13 files changed, 760 insertions(+), 303 deletions(-) [+]
line wrap: on
line diff
--- a/base/Preferences.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ b/base/Preferences.cpp	Fri Oct 21 11:49:27 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) {
--- a/base/Preferences.h	Thu Oct 20 18:31:02 2016 +0100
+++ b/base/Preferences.h	Fri Oct 21 11:49:27 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;
--- a/plugin/FeatureExtractionPluginFactory.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-/* -*- 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 "FeatureExtractionPluginFactory.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 <QDir>
-#include <QFile>
-#include <QFileInfo>
-#include <QTextStream>
-
-#include <iostream>
-
-#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;
-
-FeatureExtractionPluginFactory *
-FeatureExtractionPluginFactory::instance(QString pluginType)
-{
-    if (pluginType == "vamp") {
-	if (!_nativeInstance) {
-//	    SVDEBUG << "FeatureExtractionPluginFactory::instance(" << pluginType//		      << "): creating new FeatureExtractionPluginFactory" << endl;
-	    _nativeInstance = new FeatureExtractionPluginFactory();
-	}
-	return _nativeInstance;
-    }
-
-    else return 0;
-}
-
-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<QString>
-FeatureExtractionPluginFactory::getAllPluginIdentifiers()
-{
-    FeatureExtractionPluginFactory *factory;
-    vector<QString> rv;
-    
-    factory = instance("vamp");
-    if (factory) {
-	vector<QString> 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<QString>
-FeatureExtractionPluginFactory::getPluginIdentifiers()
-{
-    Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
-
-    QMutexLocker locker(&m_mutex);
-
-    if (m_pluginData.empty()) {
-        populate();
-    }
-
-    vector<QString> 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(" > ");
-    }
-}
-
--- a/plugin/FeatureExtractionPluginFactory.h	Thu Oct 20 18:31:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/* -*- 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.
-    
-    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 _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_
-#define _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_
-
-#include <QString>
-#include <QMutex>
-#include <vector>
-#include <map>
-
-#include <vamp-hostsdk/Plugin.h>
-
-#include "base/Debug.h"
-#include "base/BaseTypes.h"
-
-#include "vamp-support/PluginStaticData.h"
-
-class FeatureExtractionPluginFactory
-{
-public:
-    FeatureExtractionPluginFactory();
-    virtual ~FeatureExtractionPluginFactory() { }
-
-    static FeatureExtractionPluginFactory *instance(QString pluginType);
-    static FeatureExtractionPluginFactory *instanceFor(QString identifier);
-    static std::vector<QString> getAllPluginIdentifiers();
-
-    virtual std::vector<QString> getPluginIdentifiers();
-
-    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
-    virtual Vamp::Plugin *instantiatePlugin(QString identifier,
-                                            sv_samplerate_t 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<piper_vamp::PluginStaticData> m_pluginData;
-    std::map<QString, QString> m_taxonomy;
-    void populate();
-};
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/NativeVampPluginFactory.cpp	Fri Oct 21 11:49:27 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 <vamp-hostsdk/PluginHostAdapter.h>
+#include <vamp-hostsdk/PluginWrapper.h>
+
+#include "system/System.h"
+
+#include "PluginScan.h"
+
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+
+#include <iostream>
+
+#include "base/Profiler.h"
+
+#include <QMutex>
+#include <QMutexLocker>
+
+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<QString>
+NativeVampPluginFactory::getPluginPath()
+{
+    if (!m_pluginPath.empty()) return m_pluginPath;
+
+    vector<string> 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<QString>
+NativeVampPluginFactory::getPluginIdentifiers()
+{
+    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<string, int> 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<QString> path = getPluginPath();
+        for (vector<QString>::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<QString> pluginPath = getPluginPath();
+    vector<QString> 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<std::string> 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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/NativeVampPluginFactory.h	Fri Oct 21 11:49:27 2016 +0100
@@ -0,0 +1,66 @@
+/* -*- 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 <vector>
+#include <map>
+
+#include "base/Debug.h"
+
+#include <QMutex>
+
+/**
+ * FeatureExtractionPluginFactory type for Vamp plugins hosted
+ * in-process.
+ */
+class NativeVampPluginFactory : public FeatureExtractionPluginFactory
+{
+public:
+    virtual ~NativeVampPluginFactory() { }
+
+    virtual std::vector<QString> getPluginIdentifiers();
+    
+    virtual QString findPluginFile(QString soname, QString inDir = "");
+
+    virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier);
+
+    virtual Vamp::Plugin *instantiatePlugin(QString identifier,
+                                            sv_samplerate_t inputSampleRate);
+
+    /**
+     * Get category metadata about a plugin (without instantiating it).
+     */
+    virtual QString getPluginCategory(QString identifier);
+
+protected:
+    QMutex m_mutex;
+    std::vector<QString> m_pluginPath;
+    std::vector<QString> m_identifiers;
+    std::map<QString, QString> m_taxonomy; // identifier -> category string
+    std::map<QString, piper_vamp::PluginStaticData> m_pluginData; // identifier -> data (created opportunistically)
+
+    friend class PluginDeletionNotifyAdapter;
+    void pluginDeleted(Vamp::Plugin *);
+    std::map<Vamp::Plugin *, void *> m_handleMap;
+    
+    std::vector<QString> getPluginPath();
+    void generateTaxonomy();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PiperVampPluginFactory.cpp	Fri Oct 21 11:49:27 2016 +0100
@@ -0,0 +1,135 @@
+/* -*- 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 <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+
+#include <iostream>
+
+#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() :
+    m_serverName("piper-cpp/bin/piper-vamp-server") //!!!
+{
+}
+
+vector<QString>
+PiperVampPluginFactory::getPluginIdentifiers()
+{
+    Profiler profiler("PiperVampPluginFactory::getPluginIdentifiers");
+
+    QMutexLocker locker(&m_mutex);
+
+    if (m_pluginData.empty()) {
+        populate();
+    }
+
+    vector<QString> 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()
+{
+    piper_vamp::client::ProcessQtTransport transport(m_serverName);
+    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(" > ");
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PiperVampPluginFactory.h	Fri Oct 21 11:49:27 2016 +0100
@@ -0,0 +1,57 @@
+/* -*- 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 <QMutex>
+#include <vector>
+#include <map>
+
+#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<QString> getPluginIdentifiers() 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<QString, piper_vamp::PluginStaticData> m_pluginData; // identifier -> data
+    std::map<QString, QString> m_taxonomy; // identifier -> category string
+    void populate();
+};
+
+#endif
--- a/svcore.pro	Thu Oct 20 18:31:02 2016 +0100
+++ b/svcore.pro	Fri Oct 21 11:49:27 2016 +0100
@@ -237,6 +237,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 \
@@ -258,6 +260,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 \
--- a/transform/FeatureExtractionModelTransformer.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ b/transform/FeatureExtractionModelTransformer.cpp	Fri Oct 21 11:49:27 2016 +0100
@@ -16,6 +16,9 @@
 #include "FeatureExtractionModelTransformer.h"
 
 #include "plugin/FeatureExtractionPluginFactory.h"
+#include "plugin/NativeVampPluginFactory.h"
+#include "plugin/PiperVampPluginFactory.h"
+
 #include "plugin/PluginXml.h"
 #include <vamp-hostsdk/Plugin.h>
 
@@ -92,8 +95,7 @@
 
     QString pluginId = primaryTransform.getPluginIdentifier();
 
-    FeatureExtractionPluginFactory *factory =
-	FeatureExtractionPluginFactory::instanceFor(pluginId);
+    FeatureExtractionPluginFactory *factory = PiperVampPluginFactory::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 +733,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());
 
--- a/transform/ModelTransformerFactory.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ b/transform/ModelTransformerFactory.cpp	Fri Oct 21 11:49:27 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());
--- a/transform/Transform.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ b/transform/Transform.cpp	Fri Oct 21 11:49:27 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;
     }
 }
 
--- a/transform/TransformFactory.cpp	Thu Oct 20 18:31:02 2016 +0100
+++ b/transform/TransformFactory.cpp	Fri Oct 21 11:49:27 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,17 @@
 void
 TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
 {
-    std::vector<QString> plugs =
-	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
+    FeatureExtractionPluginFactory *factory =
+        FeatureExtractionPluginFactory::instance();
+    
+    std::vector<QString> plugs = factory->getPluginIdentifiers();
+    
     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 +790,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 +910,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 +921,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;