changeset 64:9d3272c7db60

* Merge from host-factory-stuff branch: this adds several helper classes in the hostext directory that should make a host's life much easier. This will become version 1.1 of the SDK, eventually.
author cannam
date Fri, 01 Jun 2007 15:10:17 +0000
parents 933fee59d33a
children a712ed15d158
files Makefile README examples/AmplitudeFollower.h examples/PercussionOnsetDetector.h examples/SpectralCentroid.h examples/ZeroCrossing.h examples/plugins.cpp host/vamp-simple-host.cpp vamp-sdk/Plugin.h vamp-sdk/PluginBase.h vamp-sdk/PluginHostAdapter.h vamp-sdk/hostext/PluginChannelAdapter.cpp vamp-sdk/hostext/PluginChannelAdapter.h vamp-sdk/hostext/PluginInputDomainAdapter.cpp vamp-sdk/hostext/PluginInputDomainAdapter.h vamp-sdk/hostext/PluginLoader.cpp vamp-sdk/hostext/PluginLoader.h vamp-sdk/hostext/PluginWrapper.cpp vamp-sdk/hostext/PluginWrapper.h vamp-sdk/vamp-hostsdk.pc.in vamp-sdk/vamp-sdk.pc.in
diffstat 21 files changed, 2102 insertions(+), 468 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Fri Mar 30 17:14:16 2007 +0000
+++ b/Makefile	Fri Jun 01 15:10:17 2007 +0000
@@ -3,13 +3,55 @@
 # libraries, example plugins, and the test host.  Please adjust to
 # suit your operating system requirements.
 
+APIDIR		= vamp
 SDKDIR		= vamp-sdk
-APIDIR		= vamp
+HOSTEXTDIR      = vamp-sdk/hostext
 EXAMPLEDIR	= examples
 HOSTDIR		= host
 
+###
+### Start of user-serviceable parts
+###
 
-### Start of user-serviceable parts
+# Default build target (or use "make <target>" to select one).
+# Targets are:
+#   all       -- build everything
+#   sdk       -- build all the Vamp SDK libraries for plugins and hosts
+#   plugins   -- build the example plugins (and the SDK if required)
+#   host      -- build the simple Vamp plugin host (and the SDK if required)
+#   test      -- build the host and example plugins, and run a quick test
+#   clean     -- remove binary targets
+#   distclean -- remove all targets
+#
+default:	all
+
+# Compile flags
+#
+CXXFLAGS	:= $(CXXFLAGS) -g -Wall -I.
+
+# Libraries required for the plugins.
+# (Note that it is desirable to statically link libstdc++ if possible,
+# because our plugin exposes only a C API so there are no boundary
+# compatibility problems.)
+#
+PLUGIN_LIBS	= $(SDKDIR)/libvamp-sdk.a
+#PLUGIN_LIBS	= vamp-sdk/libvamp-sdk.a $(shell g++ -print-file-name=libstdc++.a)
+
+# Flags required to tell the compiler to link to a dynamically loadable object
+#
+PLUGIN_LDFLAGS	= -shared -Wl,-Bsymbolic -static-libgcc
+
+# File extension for a dynamically loadable object
+#
+PLUGIN_EXT	= .so
+
+## For OS/X with g++:
+#PLUGIN_LDFLAGS	= -dynamiclib
+#PLUGIN_EXT	= .dylib
+
+# Libraries required for the host.
+#
+HOST_LIBS	= $(SDKDIR)/libvamp-hostsdk.a -lsndfile -ldl
 
 # Locations for "make install".  This will need quite a bit of 
 # editing for non-Linux platforms.  Of course you don't necessarily
@@ -18,6 +60,7 @@
 INSTALL_PREFIX	 	  := /usr/local
 INSTALL_API_HEADERS	  := $(INSTALL_PREFIX)/include/vamp
 INSTALL_SDK_HEADERS	  := $(INSTALL_PREFIX)/include/vamp-sdk
+INSTALL_HOSTEXT_HEADERS	  := $(INSTALL_PREFIX)/include/vamp-sdk/hostext
 INSTALL_SDK_LIBS	  := $(INSTALL_PREFIX)/lib
 
 INSTALL_SDK_LIBNAME	  := libvamp-sdk.so.1.0.0
@@ -34,33 +77,6 @@
 
 INSTALL_PKGCONFIG	  := $(INSTALL_PREFIX)/lib/pkgconfig
 
-# Compile flags
-#
-CXXFLAGS	:= $(CXXFLAGS) -O2 -Wall -I$(SDKDIR) -I$(APIDIR) -I.
-
-# Libraries required for the host at link time
-#
-HOST_LIBS	= vamp-sdk/libvamp-hostsdk.a -lsndfile -ldl
-
-# Libraries required for the plugin.  Note that we can (and actively
-# want to) statically link libstdc++, because our plugin exposes only
-# a C API so there are no boundary compatibility problems.
-#
-PLUGIN_LIBS	= vamp-sdk/libvamp-sdk.a
-#PLUGIN_LIBS	= vamp-sdk/libvamp-sdk.a $(shell g++ -print-file-name=libstdc++.a)
-
-# Flags required to tell the compiler to link to a dynamically loadable object
-#
-PLUGIN_LDFLAGS	= -shared -Wl,-Bsymbolic -static-libgcc
-
-# File extension for a dynamically loadable object
-#
-PLUGIN_EXT	= .so
-
-## For OS/X with g++:
-#PLUGIN_LDFLAGS	= -dynamiclib
-#PLUGIN_EXT	= .dylib
-
 ### End of user-serviceable parts
 
 
@@ -79,12 +95,22 @@
 		$(SDKDIR)/PluginHostAdapter.h \
 		$(SDKDIR)/RealTime.h
 
+HOSTEXT_HEADERS = \
+		$(HOSTEXTDIR)/PluginChannelAdapter.h \
+		$(HOSTEXTDIR)/PluginInputDomainAdapter.h \
+		$(HOSTEXTDIR)/PluginLoader.h \
+		$(HOSTEXTDIR)/PluginWrapper.h
+
 SDK_OBJECTS	= \
 		$(SDKDIR)/PluginAdapter.o \
 		$(SDKDIR)/RealTime.o
 
 HOSTSDK_OBJECTS	= \
 		$(SDKDIR)/PluginHostAdapter.o \
+		$(HOSTEXTDIR)/PluginChannelAdapter.o \
+		$(HOSTEXTDIR)/PluginInputDomainAdapter.o \
+		$(HOSTEXTDIR)/PluginLoader.o \
+		$(HOSTEXTDIR)/PluginWrapper.o \
 		$(SDKDIR)/RealTime.o
 
 SDK_STATIC	= \
@@ -127,28 +153,34 @@
 HOST_TARGET	= \
 		$(HOSTDIR)/vamp-simple-host
 
-all:		$(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC) $(PLUGIN_TARGET) $(HOST_TARGET) test
+sdk:		$(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC)
+
+plugins:	$(PLUGIN_TARGET)
+
+host:		$(HOST_TARGET)
+
+all:		sdk plugins host test
 
 $(SDK_STATIC):	$(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS)
 		$(AR) r $@ $(SDK_OBJECTS)
 
-$(HOSTSDK_STATIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS)
+$(HOSTSDK_STATIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS)
 		$(AR) r $@ $(HOSTSDK_OBJECTS)
 
 $(SDK_DYNAMIC):	$(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS)
 		$(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(SDK_OBJECTS)
 
-$(HOSTSDK_DYNAMIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS)
+$(HOSTSDK_DYNAMIC):	$(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS)
 		$(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(HOSTSDK_OBJECTS)
 
-$(PLUGIN_TARGET):	$(PLUGIN_OBJECTS) $(SDK_TARGET) $(PLUGIN_HEADERS)
+$(PLUGIN_TARGET):	$(PLUGIN_OBJECTS) $(SDK_STATIC) $(PLUGIN_HEADERS)
 		$(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(PLUGIN_OBJECTS) $(PLUGIN_LIBS)
 
-$(HOST_TARGET):	$(HOST_OBJECTS) $(HOSTSDK_TARGET) $(HOST_HEADERS)
+$(HOST_TARGET):	$(HOST_OBJECTS) $(HOSTSDK_STATIC) $(HOST_HEADERS)
 		$(CXX) $(LDFLAGS) $(HOST_LDFLAGS) -o $@ $(HOST_OBJECTS) $(HOST_LIBS)
 
-test:		$(HOST_TARGET) $(PLUGIN_TARGET)
-		$(HOST_TARGET) $(PLUGIN_TARGET)
+test:		plugins host
+		VAMP_PATH=$(EXAMPLEDIR) $(HOST_TARGET) -l
 
 clean:		
 		rm -f $(SDK_OBJECTS) $(HOSTSDK_OBJECTS) $(PLUGIN_OBJECTS) $(HOST_OBJECTS)
@@ -159,11 +191,13 @@
 install:	$(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC) $(PLUGIN_TARGET) $(HOST_TARGET)
 		mkdir -p $(INSTALL_API_HEADERS)
 		mkdir -p $(INSTALL_SDK_HEADERS)
+		mkdir -p $(INSTALL_HOSTEXT_HEADERS)
 		mkdir -p $(INSTALL_SDK_LIBS)
 		mkdir -p $(INSTALL_PKGCONFIG)
 		cp $(API_HEADERS) $(INSTALL_API_HEADERS)
 		cp $(SDK_HEADERS) $(INSTALL_SDK_HEADERS)
 		cp $(HOSTSDK_HEADERS) $(INSTALL_SDK_HEADERS)
+		cp $(HOSTEXT_HEADERS) $(INSTALL_HOSTEXT_HEADERS)
 		cp $(SDK_STATIC) $(INSTALL_SDK_LIBS)
 		cp $(HOSTSDK_STATIC) $(INSTALL_SDK_LIBS)
 		cp $(SDK_DYNAMIC) $(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LIBNAME)
--- a/README	Fri Mar 30 17:14:16 2007 +0000
+++ b/README	Fri Jun 01 15:10:17 2007 +0000
@@ -77,6 +77,28 @@
 plugin's C API back into a Vamp::Plugin object.  Hosts should link
 with -lvamp-hostsdk.
 
+ * vamp-sdk/hostext
+
+Additional C++ classes to make a host's life easier.
+
+Vamp::HostExt::PluginLoader provides a very simple interface for a
+host to discover, load, and find out category information about the
+available plugins.  Most "casual" Vamp hosts will probably want to use
+this class.
+
+Vamp::HostExt::PluginInputDomainAdapter provides a simple means for
+hosts to handle plugins that expect frequency-domain input, without
+having to convert the input themselves.
+
+Vamp::HostExt::PluginChannelAdapter provides a simple means for hosts
+to use plugins that do not necessarily support the same number of
+audio channels as they have available, without having to apply a
+channel management / mixdown policy themselves.
+
+The PluginLoader can use the input domain and channel adapters
+automatically to make the entire conversion process transparent to the
+host if required.
+
  * examples
 
 Example plugins implemented using the C++ classes.  ZeroCrossing
@@ -92,7 +114,11 @@
 
 A simple command-line Vamp host, capable of loading a plugin and using
 it to process a complete audio file, with its default parameters.
-Requires libsndfile.
+Requires libsndfile (http://www.mega-nerd.com/libsndfile/).
+
+If you don't have libsndfile, you may want to edit the Makefile to
+change the default build target from "all" to "sdk" so as to compile
+only the SDK.
 
 
 Plugin Lookup and Categorisation
@@ -186,11 +212,14 @@
 =======
 
 Vamp and the Vamp SDK were designed and made at the Centre for Digital
-Music at Queen Mary, University of London.  The SDK code was written
-by Chris Cannam, copyright (c) 2005-2006 Chris Cannam.  Mark Sandler
-and Christian Landone provided ideas and direction, and Mark Levy, Dan
-Stowell, Martin Gasser and Craig Sapp provided testing and other input
-for the 1.0 API and SDK.  The API reuses some ideas from several prior
-plugin systems, notably DSSI (http://dssi.sourceforge.net) and FEAPI
-(http://feapi.sourceforge.net).
+Music at Queen Mary, University of London.
 
+The SDK was written by Chris Cannam, copyright (c) 2005-2007
+Chris Cannam and QMUL.
+
+Mark Sandler and Christian Landone provided ideas and direction, and
+Mark Levy, Dan Stowell, Martin Gasser and Craig Sapp provided testing
+and other input for the 1.0 API and SDK.  The API also uses some ideas
+from prior plugin systems, notably DSSI (http://dssi.sourceforge.net)
+and FEAPI (http://feapi.sourceforge.net).
+
--- a/examples/AmplitudeFollower.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/examples/AmplitudeFollower.h	Fri Jun 01 15:10:17 2007 +0000
@@ -37,7 +37,7 @@
 #ifndef _AMPLITUDE_FOLLOWER_PLUGIN_H_
 #define _AMPLITUDE_FOLLOWER_PLUGIN_H_
 
-#include "Plugin.h"
+#include "vamp-sdk/Plugin.h"
 
 /**
  * Example plugin implementing the SuperCollider amplitude follower
--- a/examples/PercussionOnsetDetector.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/examples/PercussionOnsetDetector.h	Fri Jun 01 15:10:17 2007 +0000
@@ -37,7 +37,7 @@
 #ifndef _PERCUSSION_ONSET_DETECTOR_PLUGIN_H_
 #define _PERCUSSION_ONSET_DETECTOR_PLUGIN_H_
 
-#include "Plugin.h"
+#include "vamp-sdk/Plugin.h"
 
 /**
  * Example plugin that detects percussive events.
--- a/examples/SpectralCentroid.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/examples/SpectralCentroid.h	Fri Jun 01 15:10:17 2007 +0000
@@ -37,7 +37,7 @@
 #ifndef _SPECTRAL_CENTROID_PLUGIN_H_
 #define _SPECTRAL_CENTROID_PLUGIN_H_
 
-#include "Plugin.h"
+#include "vamp-sdk/Plugin.h"
 
 /**
  * Example plugin that calculates the centre of gravity of the
--- a/examples/ZeroCrossing.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/examples/ZeroCrossing.h	Fri Jun 01 15:10:17 2007 +0000
@@ -37,7 +37,7 @@
 #ifndef _ZERO_CROSSING_PLUGIN_H_
 #define _ZERO_CROSSING_PLUGIN_H_
 
-#include "Plugin.h"
+#include "vamp-sdk/Plugin.h"
 
 /**
  * Example plugin that calculates the positions and density of
--- a/examples/plugins.cpp	Fri Mar 30 17:14:16 2007 +0000
+++ b/examples/plugins.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -34,9 +34,9 @@
     authorization.
 */
 
-#include <vamp/vamp.h>
+#include "vamp/vamp.h"
+#include "vamp-sdk/PluginAdapter.h"
 
-#include "PluginAdapter.h"
 #include "ZeroCrossing.h"
 #include "SpectralCentroid.h"
 #include "PercussionOnsetDetector.h"
--- a/host/vamp-simple-host.cpp	Fri Mar 30 17:14:16 2007 +0000
+++ b/host/vamp-simple-host.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -35,12 +35,14 @@
     authorization.
 */
 
-#include "PluginHostAdapter.h"
-#include "vamp.h"
+#include "vamp-sdk/PluginHostAdapter.h"
+#include "vamp-sdk/hostext/PluginChannelAdapter.h"
+#include "vamp-sdk/hostext/PluginInputDomainAdapter.h"
+#include "vamp-sdk/hostext/PluginLoader.h"
+#include "vamp/vamp.h"
 
 #include <iostream>
 #include <sndfile.h>
-#include <dirent.h> // POSIX directory open and read
 
 #include "system.h"
 
@@ -52,59 +54,62 @@
 using std::string;
 using std::vector;
 
-#define HOST_VERSION "1.0"
+using Vamp::HostExt::PluginLoader;
+
+#define HOST_VERSION "1.1"
 
 void printFeatures(int, int, int, Vamp::Plugin::FeatureSet);
 void transformInput(float *, size_t);
 void fft(unsigned int, bool, double *, double *, double *, double *);
-void printPluginPath();
+void printPluginPath(bool verbose);
+void enumeratePlugins();
+void listPluginsInLibrary(string soname);
+int runPlugin(string myname, string soname, string id, string output,
+              int outputNo, string inputFile);
 
-#ifdef HAVE_OPENDIR
-void enumeratePlugins();
-#endif
-
-/*
-    A very simple Vamp plugin host.  Given the name of a plugin
-    library and the name of a sound file on the command line, it loads
-    the first plugin in the library and runs it on the sound file,
-    dumping the plugin's first output to stdout.
-*/
+void usage(const char *name)
+{
+    cerr << "\n"
+         << name << ": A simple Vamp plugin host.\n\n"
+        "Centre for Digital Music, Queen Mary, University of London.\n"
+        "Copyright 2006-2007 Chris Cannam and QMUL.\n"
+        "Freely redistributable; published under a BSD-style license.\n\n"
+        "Usage:\n\n"
+        "  " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin[:output] file.wav\n"
+        "  " << name << " pluginlibrary[." << PLUGIN_SUFFIX << "]:plugin file.wav [outputno]\n\n"
+        "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
+        "       audio data in \"file.wav\"; retrieve the named \"output\", or output\n"
+        "       number \"outputno\" (the first output by default) and dump it to\n"
+        "       standard output.\n\n"
+        "  " << name << " -l\n\n"
+        "    -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
+        "  " << name << " -p\n\n"
+        "    -- Print out the Vamp plugin search path.\n\n"
+        "  " << name << " -v\n\n"
+        "    -- Display version information only.\n\n"
+         << endl;
+    exit(2);
+}
 
 int main(int argc, char **argv)
 {
-    if (argc < 2 || argc > 4) {
-        char *scooter = argv[0];
-        char *name = 0;
-        while (scooter && *scooter) {
-            if (*scooter == '/' || *scooter == '\\') name = ++scooter;
-            else ++scooter;
-        }
-        if (!name || !*name) name = argv[0];
-        cerr << "\n"
-             << name << ": A simple Vamp plugin host.\n\n"
-            "Centre for Digital Music, Queen Mary, University of London.\n"
-            "Copyright 2006 Chris Cannam and QMUL.\n"
-            "Freely redistributable; published under a BSD-style license.\n\n"
-            "Usage:\n\n"
-            "  " << name << " pluginlibrary." << PLUGIN_SUFFIX << "\n\n"
-            "    -- Load \"pluginlibrary\" and list the Vamp plugins it contains.\n\n"
-            "  " << name << " pluginlibrary." << PLUGIN_SUFFIX << ":plugin file.wav [outputno]\n\n"
-            "    -- Load plugin id \"plugin\" from \"pluginlibrary\" and run it on the\n"
-            "       audio data in \"file.wav\", dumping the output from \"outputno\"\n"
-            "       (default 0) to standard output.\n\n"
-#ifdef HAVE_OPENDIR
-            "  " << name << " -l\n\n"
-            "    -- List the plugin libraries and Vamp plugins in the plugin search path.\n\n"
-#endif
-            "  " << name << " -p\n\n"
-            "    -- Print out the Vamp plugin search path.\n\n"
-	    "  " << name << " -v\n\n"
-	    "    -- Display version information only.\n\n"
-	    "Note that this host does not use the plugin search path when loadinga plugin.\nIf a plugin library is specified, it should be with a full file path.\n"
-             << endl;
-        return 2;
+    char *scooter = argv[0];
+    char *name = 0;
+    while (scooter && *scooter) {
+        if (*scooter == '/' || *scooter == '\\') name = ++scooter;
+        else ++scooter;
     }
+    if (!name || !*name) name = argv[0];
     
+    if (argc < 2 || argc > 4 ||
+        (argc == 2 &&
+         (!strcmp(argv[1], "-?") ||
+          !strcmp(argv[1], "-h") ||
+          !strcmp(argv[1], "--help")))) {
+
+        usage(name); // does not return
+    }
+
     if (argc == 2 && !strcmp(argv[1], "-v")) {
 	cout << "Simple Vamp plugin host version: " << HOST_VERSION << endl
 	     << "Vamp API version: " << VAMP_API_VERSION << endl
@@ -113,89 +118,57 @@
     }
     
     if (argc == 2 && !strcmp(argv[1], "-l")) {
-#ifdef HAVE_OPENDIR
+        printPluginPath(true);
         enumeratePlugins();
-#endif
         return 0;
     }
     if (argc == 2 && !strcmp(argv[1], "-p")) {
-        printPluginPath();
+        printPluginPath(false);
         return 0;
     }
 
-    cerr << endl << argv[0] << ": Running..." << endl;
+    cerr << endl << name << ": Running..." << endl;
 
     string soname = argv[1];
     string plugid = "";
+    string output = "";
+    int outputNo = -1;
     string wavname;
     if (argc >= 3) wavname = argv[2];
 
-    int sep = soname.find(":");
-    if (sep >= 0 && sep < int(soname.length())) {
+    string::size_type sep = soname.find(':');
+
+    if (sep != string::npos) {
         plugid = soname.substr(sep + 1);
         soname = soname.substr(0, sep);
-    }
 
-    void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
-
-    if (!libraryHandle) {
-        cerr << argv[0] << ": Failed to open plugin library " 
-                  << soname << ": " << DLERROR() << endl;
-        return 1;
-    }
-
-    cerr << argv[0] << ": Opened plugin library " << soname << endl;
-
-    VampGetPluginDescriptorFunction fn = (VampGetPluginDescriptorFunction)
-        DLSYM(libraryHandle, "vampGetPluginDescriptor");
-    
-    if (!fn) {
-        cerr << argv[0] << ": No Vamp descriptor function in library "
-                  << soname << endl;
-        DLCLOSE(libraryHandle);
-        return 1;
-    }
-
-    cerr << argv[0] << ": Found plugin descriptor function" << endl;
-
-    int index = 0;
-    int plugnumber = -1;
-    const VampPluginDescriptor *descriptor = 0;
-
-    while ((descriptor = fn(VAMP_API_VERSION, index))) {
-
-        Vamp::PluginHostAdapter plugin(descriptor, 48000);
-        cerr << argv[0] << ": Plugin " << (index+1)
-                  << " is \"" << plugin.getIdentifier() << "\"" << endl;
-
-        if (plugin.getIdentifier() == plugid) plugnumber = index;
-        
-        ++index;
-    }
-
-    cerr << argv[0] << ": Done\n" << endl;
-
-    if (wavname == "") {
-        DLCLOSE(libraryHandle);
-        return 0;
-    }
-
-    if (plugnumber < 0) {
-        if (plugid != "") {
-            cerr << "ERROR: No such plugin as " << plugid << " in library"
-                 << endl;
-            DLCLOSE(libraryHandle);
-            return 0;
-        } else {
-            plugnumber = 0;
+        sep = plugid.find(':');
+        if (sep != string::npos) {
+            output = plugid.substr(sep + 1);
+            plugid = plugid.substr(0, sep);
         }
     }
 
-    descriptor = fn(VAMP_API_VERSION, plugnumber);
-    if (!descriptor) {
-        DLCLOSE(libraryHandle);
-        return 0;
+    if (plugid == "") {
+        usage(name);
     }
+
+    if (argc == 4) outputNo = atoi(argv[3]);
+
+    if (output != "" && outputNo != -1) {
+        usage(name);
+    }
+
+    return runPlugin(name, soname, plugid, output, outputNo, wavname);
+}
+
+
+int runPlugin(string myname, string soname, string id,
+              string output, int outputNo, string wavname)
+{
+    PluginLoader *loader = PluginLoader::getInstance();
+
+    PluginLoader::PluginKey key = loader->composePluginKey(soname, id);
     
     SNDFILE *sndfile;
     SF_INFO sfinfo;
@@ -203,48 +176,25 @@
 
     sndfile = sf_open(wavname.c_str(), SFM_READ, &sfinfo);
     if (!sndfile) {
-	cerr << "ERROR: Failed to open input file \"" << wavname << "\": "
-	     << sf_strerror(sndfile) << endl;
-        DLCLOSE(libraryHandle);
+	cerr << myname << ": ERROR: Failed to open input file \""
+             << wavname << "\": " << sf_strerror(sndfile) << endl;
 	return 1;
     }
 
-    Vamp::PluginHostAdapter *plugin =
-        new Vamp::PluginHostAdapter(descriptor, sfinfo.samplerate);
+    Vamp::Plugin *plugin = loader->loadPlugin
+        (key, sfinfo.samplerate, PluginLoader::ADAPT_ALL);
+    if (!plugin) {
+        cerr << myname << ": ERROR: Failed to load plugin \"" << id
+             << "\" from library \"" << soname << "\"" << endl;
+        sf_close(sndfile);
+        return 1;
+    }
 
-    cerr << "Running " << plugin->getIdentifier() << "..." << endl;
+    cerr << "Running plugin: \"" << plugin->getIdentifier() << "\"..." << endl;
 
     int blockSize = plugin->getPreferredBlockSize();
     int stepSize = plugin->getPreferredStepSize();
 
-    cerr << "Preferred block size = " << blockSize << ", step size = "
-         << stepSize << endl;
-
-    if (blockSize == 0) blockSize = 1024;
-
-    bool rightBlockSize = true;
-
-    if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
-
-        int p = 1, b = blockSize;
-        while (b) {
-            p <<= 1;
-            b >>= 1;
-        }
-        if (p != blockSize * 2) {
-            cerr << "WARNING: Plugin requested non-power-of-two block size of "
-                 << blockSize << ",\nwhich is not supported by this host.  ";
-            blockSize = p;
-            cerr << "Rounding up to " << blockSize << "." << endl;
-            rightBlockSize = false;
-        }
-        if (stepSize == 0) stepSize = blockSize / 2;
-
-    } else {
-
-        if (stepSize == 0) stepSize = blockSize;
-    }
-
     int channels = sfinfo.channels;
 
     float *filebuf = new float[blockSize * channels];
@@ -257,48 +207,47 @@
     int minch = plugin->getMinChannelCount();
     int maxch = plugin->getMaxChannelCount();
     cerr << "Plugin accepts " << minch << " -> " << maxch << " channel(s)" << endl;
+    cerr << "Sound file has " << channels << " (will mix/augment if necessary)" << endl;
 
     Vamp::Plugin::OutputList outputs = plugin->getOutputDescriptors();
     Vamp::Plugin::OutputDescriptor od;
 
     int returnValue = 1;
 
-    int output = 0;
-    if (argc == 4) output = atoi(argv[3]);
-
-    bool mix = false;
-
-    if (minch > channels || maxch < channels) {
-        if (minch == 1) {
-            cerr << "WARNING: Sound file has " << channels << " channels, mixing down to 1" << endl;
-            mix = true;
-            channels = 1;
-        } else {
-            cerr << "ERROR: Sound file has " << channels << " channels, out of range for plugin" << endl;
-            goto done;
-        }
-    }
-
     if (outputs.empty()) {
-	cerr << "Plugin has no outputs!" << endl;
+	cerr << "ERROR: Plugin has no outputs!" << endl;
         goto done;
     }
 
-    if (int(outputs.size()) <= output) {
-	cerr << "Output " << output << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
-        goto done;
-    }        
+    if (outputNo < 0) {
 
-    od = outputs[output];
-    cerr << "Output is " << od.identifier << endl;
+        for (size_t oi = 0; oi < outputs.size(); ++oi) {
+            if (outputs[oi].identifier == output) {
+                outputNo = oi;
+                break;
+            }
+        }
+
+        if (outputNo < 0) {
+            cerr << "ERROR: Non-existent output \"" << output << "\" requested" << endl;
+            goto done;
+        }
+
+    } else {
+
+        if (int(outputs.size()) <= outputNo) {
+            cerr << "ERROR: Output " << outputNo << " requested, but plugin has only " << outputs.size() << " output(s)" << endl;
+            goto done;
+        }        
+    }
+
+    od = outputs[outputNo];
+    cerr << "Output is: \"" << od.identifier << "\"" << endl;
 
     if (!plugin->initialise(channels, stepSize, blockSize)) {
         cerr << "ERROR: Plugin initialise (channels = " << channels
              << ", stepSize = " << stepSize << ", blockSize = "
              << blockSize << ") failed." << endl;
-        if (!rightBlockSize) {
-            cerr << "(Probably because I couldn't provide the plugin's preferred block size.)" << endl;
-        }
         goto done;
     }
 
@@ -317,141 +266,134 @@
         }
 
         for (int c = 0; c < channels; ++c) {
-            for (int j = 0; j < blockSize; ++j) {
+            int j = 0;
+            while (j < count) {
+                plugbuf[c][j] = filebuf[j * sfinfo.channels + c];
+                ++j;
+            }
+            while (j < blockSize) {
                 plugbuf[c][j] = 0.0f;
-            }
-        }
-
-        for (int j = 0; j < blockSize && j < count; ++j) {
-            int tc = 0;
-            for (int c = 0; c < sfinfo.channels; ++c) {
-                tc = c;
-                if (mix) tc = 0;
-                plugbuf[tc][j] += filebuf[j * sfinfo.channels + c];
-            }
-            if (mix) {
-                plugbuf[0][j] /= sfinfo.channels;
-            }
-        }
-
-        if (plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
-            for (int c = 0; c < sfinfo.channels; ++c) {
-                transformInput(plugbuf[c], blockSize);
-                if (mix) break;
+                ++j;
             }
         }
 
         printFeatures
-            (i, sfinfo.samplerate, output, plugin->process
+            (i, sfinfo.samplerate, outputNo, plugin->process
              (plugbuf, Vamp::RealTime::frame2RealTime(i, sfinfo.samplerate)));
     }
 
-    printFeatures(sfinfo.frames, sfinfo.samplerate, output,
+    printFeatures(sfinfo.frames, sfinfo.samplerate, outputNo,
                   plugin->getRemainingFeatures());
 
     returnValue = 0;
 
 done:
     delete plugin;
-
-    DLCLOSE(libraryHandle);
     sf_close(sndfile);
     return returnValue;
 }
 
 void
-printPluginPath()
+printPluginPath(bool verbose)
 {
+    if (verbose) {
+        cout << "\nVamp plugin search path: ";
+    }
+
     vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
     for (size_t i = 0; i < path.size(); ++i) {
-        cerr << path[i] << endl;
+        if (verbose) {
+            cout << "[" << path[i] << "]";
+        } else {
+            cout << path[i] << endl;
+        }
     }
+
+    if (verbose) cout << endl;
 }
 
-#ifdef HAVE_OPENDIR
-
 void
 enumeratePlugins()
 {
-    cerr << endl << "Vamp plugin libraries found in search path:" << endl;
-    vector<string> path = Vamp::PluginHostAdapter::getPluginPath();
-    for (size_t i = 0; i < path.size(); ++i) {
-        cerr << "\n" << path[i] << ":" << endl;
-        DIR *d = opendir(path[i].c_str());
-        if (!d) {
-            perror("Failed to open directory");
-            continue;
+    PluginLoader *loader = PluginLoader::getInstance();
+
+    cout << "\nVamp plugin libraries found in search path:" << endl;
+
+    std::vector<PluginLoader::PluginKey> plugins = loader->listPlugins();
+    typedef std::multimap<std::string, PluginLoader::PluginKey>
+        LibraryMap;
+    LibraryMap libraryMap;
+
+    for (size_t i = 0; i < plugins.size(); ++i) {
+        std::string path = loader->getLibraryPathForPlugin(plugins[i]);
+        libraryMap.insert(LibraryMap::value_type(path, plugins[i]));
+    }
+
+    std::string prevPath = "";
+    int index = 0;
+
+    for (LibraryMap::iterator i = libraryMap.begin();
+         i != libraryMap.end(); ++i) {
+        
+        std::string path = i->first;
+        PluginLoader::PluginKey key = i->second;
+
+        if (path != prevPath) {
+            prevPath = path;
+            index = 0;
+            cout << "\n  " << path << ":" << endl;
         }
-        struct dirent *e = 0;
-        while ((e = readdir(d))) {
-//            cerr << "reading: " << e->d_name << endl;
-            if (!(e->d_type & DT_REG)) {
-//                cerr << e->d_name << ": not a regular file" << endl;
-                continue;
+
+        Vamp::Plugin *plugin = loader->loadPlugin(key, 48000);
+        if (plugin) {
+
+            char c = char('A' + index);
+            if (c > 'Z') c = char('a' + (index - 26));
+
+            cout << "    [" << c << "] [v"
+                 << plugin->getVampApiVersion() << "] "
+                 << plugin->getName() << ", \""
+                 << plugin->getIdentifier() << "\"" << " ["
+                 << plugin->getMaker() << "]" << endl;
+
+            PluginLoader::PluginCategoryHierarchy category =
+                loader->getPluginCategory(key);
+            if (!category.empty()) {
+                cout << "       ";
+                for (size_t ci = 0; ci < category.size(); ++ci) {
+                    cout << " > " << category[ci];
+                }
+                cout << endl;
             }
-            int len = strlen(e->d_name);
-            if (len < int(strlen(PLUGIN_SUFFIX) + 2) ||
-                e->d_name[len - strlen(PLUGIN_SUFFIX) - 1] != '.' ||
-                strcmp(e->d_name + len - strlen(PLUGIN_SUFFIX), PLUGIN_SUFFIX)) {
-//                cerr << e->d_name << ": not a library file" << endl;
-                continue;
+
+            if (plugin->getDescription() != "") {
+                cout << "        - " << plugin->getDescription() << endl;
             }
-            char *fp = new char[path[i].length() + len + 3];
-            sprintf(fp, "%s/%s", path[i].c_str(), e->d_name);
-            void *handle = DLOPEN(string(fp), RTLD_LAZY);
-            if (handle) {
-                VampGetPluginDescriptorFunction fn =
-                    (VampGetPluginDescriptorFunction)DLSYM
-                    (handle, "vampGetPluginDescriptor");
-                if (fn) {
-                    cerr << "\n  " << e->d_name << ":" << endl;
-                    int index = 0;
-                    const VampPluginDescriptor *descriptor = 0;
-                    while ((descriptor = fn(VAMP_API_VERSION, index))) {
-                        Vamp::PluginHostAdapter plugin(descriptor, 48000);
-                        char c = char('A' + index);
-                        if (c > 'Z') c = char('a' + (index - 26));
-                        cerr << "    [" << c << "] [v"
-                             << plugin.getVampApiVersion() << "] "
-                             << plugin.getName()
-                             << ", \"" << plugin.getIdentifier() << "\""
-                             << " [" << plugin.getMaker()
-                             << "]" << endl;
-                        if (plugin.getDescription() != "") {
-                            cerr << "        - " << plugin.getDescription() << endl;
-                        }
-                        Vamp::Plugin::OutputList outputs =
-                            plugin.getOutputDescriptors();
-                        if (outputs.size() > 1) {
-                            for (size_t j = 0; j < outputs.size(); ++j) {
-                                cerr << "         (" << j << ") "
-                                     << outputs[j].name
-                                     << ", \"" << outputs[j].identifier << "\""
-                                     << endl;
-                                if (outputs[j].description != "") {
-                                    cerr << "             - " 
-                                         << outputs[j].description << endl;
-                                }
-                            }
-                        }
-                        ++index;
+
+            Vamp::Plugin::OutputList outputs =
+                plugin->getOutputDescriptors();
+
+            if (outputs.size() > 1) {
+                for (size_t j = 0; j < outputs.size(); ++j) {
+                    cout << "         (" << j << ") "
+                         << outputs[j].name << ", \""
+                         << outputs[j].identifier << "\"" << endl;
+                    if (outputs[j].description != "") {
+                        cout << "             - " 
+                             << outputs[j].description << endl;
                     }
-                } else {
-//                    cerr << e->d_name << ": no Vamp descriptor function" << endl;
                 }
-                DLCLOSE(handle);
-            } else {
-                cerr << "\n" << e->d_name << ": unable to load library (" << DLERROR() << ")" << endl;
-            }                
+            }
+
+            ++index;
+
+            delete plugin;
         }
-        closedir(d);
     }
-    cerr << endl;
+
+    cout << endl;
 }
 
-#endif
-
-
 void
 printFeatures(int frame, int sr, int output, Vamp::Plugin::FeatureSet features)
 {
@@ -468,149 +410,5 @@
     }
 }
 
-void
-transformInput(float *buffer, size_t size)
-{
-    double *inbuf = new double[size * 2];
-    double *outbuf = new double[size * 2];
-
-    // Copy across with Hanning window
-    for (size_t i = 0; i < size; ++i) {
-        inbuf[i] = double(buffer[i]) * (0.50 - 0.50 * cos(2 * M_PI * i / size));
-        inbuf[i + size] = 0.0;
-    }
-    
-    for (size_t i = 0; i < size/2; ++i) {
-        double temp = inbuf[i];
-        inbuf[i] = inbuf[i + size/2];
-        inbuf[i + size/2] = temp;
-    }
-
-    fft(size, false, inbuf, inbuf + size, outbuf, outbuf + size);
-
-    for (size_t i = 0; i <= size/2; ++i) {
-        buffer[i * 2] = outbuf[i];
-        buffer[i * 2 + 1] = outbuf[i + size];
-    }
-    
-    delete[] inbuf;
-    delete[] outbuf;
-}
-
-void
-fft(unsigned int n, bool inverse, double *ri, double *ii, double *ro, double *io)
-{
-    if (!ri || !ro || !io) return;
-
-    unsigned int bits;
-    unsigned int i, j, k, m;
-    unsigned int blockSize, blockEnd;
-
-    double tr, ti;
-
-    if (n < 2) return;
-    if (n & (n-1)) return;
-
-    double angle = 2.0 * M_PI;
-    if (inverse) angle = -angle;
-
-    for (i = 0; ; ++i) {
-	if (n & (1 << i)) {
-	    bits = i;
-	    break;
-	}
-    }
-
-    static unsigned int tableSize = 0;
-    static int *table = 0;
-
-    if (tableSize != n) {
-
-	delete[] table;
-
-	table = new int[n];
-
-	for (i = 0; i < n; ++i) {
-	
-	    m = i;
-
-	    for (j = k = 0; j < bits; ++j) {
-		k = (k << 1) | (m & 1);
-		m >>= 1;
-	    }
-
-	    table[i] = k;
-	}
-
-	tableSize = n;
-    }
-
-    if (ii) {
-	for (i = 0; i < n; ++i) {
-	    ro[table[i]] = ri[i];
-	    io[table[i]] = ii[i];
-	}
-    } else {
-	for (i = 0; i < n; ++i) {
-	    ro[table[i]] = ri[i];
-	    io[table[i]] = 0.0;
-	}
-    }
-
-    blockEnd = 1;
-
-    for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
-
-	double delta = angle / (double)blockSize;
-	double sm2 = -sin(-2 * delta);
-	double sm1 = -sin(-delta);
-	double cm2 = cos(-2 * delta);
-	double cm1 = cos(-delta);
-	double w = 2 * cm1;
-	double ar[3], ai[3];
-
-	for (i = 0; i < n; i += blockSize) {
-
-	    ar[2] = cm2;
-	    ar[1] = cm1;
-
-	    ai[2] = sm2;
-	    ai[1] = sm1;
-
-	    for (j = i, m = 0; m < blockEnd; j++, m++) {
-
-		ar[0] = w * ar[1] - ar[2];
-		ar[2] = ar[1];
-		ar[1] = ar[0];
-
-		ai[0] = w * ai[1] - ai[2];
-		ai[2] = ai[1];
-		ai[1] = ai[0];
-
-		k = j + blockEnd;
-		tr = ar[0] * ro[k] - ai[0] * io[k];
-		ti = ar[0] * io[k] + ai[0] * ro[k];
-
-		ro[k] = ro[j] - tr;
-		io[k] = io[j] - ti;
-
-		ro[j] += tr;
-		io[j] += ti;
-	    }
-	}
-
-	blockEnd = blockSize;
-    }
-
-    if (inverse) {
-
-	double denom = (double)n;
-
-	for (i = 0; i < n; i++) {
-	    ro[i] /= denom;
-	    io[i] /= denom;
-	}
-    }
-}
 
         
--- a/vamp-sdk/Plugin.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/vamp-sdk/Plugin.h	Fri Jun 01 15:10:17 2007 +0000
@@ -358,6 +358,7 @@
      * real and imaginary component floats corresponding to bins
      * 0..(blockSize/2) of the FFT output, where bin 0 contains the DC
      * output and bin blockSize/2 corresponds to the Nyquist output.
+     * There will therefore be blockSize+2 floats per channel in total.
      * The timestamp will be the real time in seconds of the centre of
      * the FFT input window (i.e. the very first block passed to
      * process might contain the FFT of half a block of zero samples
@@ -379,7 +380,7 @@
 
     /**
      * Used to distinguish between Vamp::Plugin and other potential
-     * sibling subclasses of PluginBase.  Do not implement this
+     * sibling subclasses of PluginBase.  Do not reimplement this
      * function in your subclass.
      */
     virtual std::string getType() const { return "Feature Extraction Plugin"; }
--- a/vamp-sdk/PluginBase.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/vamp-sdk/PluginBase.h	Fri Jun 01 15:10:17 2007 +0000
@@ -40,7 +40,7 @@
 #include <string>
 #include <vector>
 
-#define VAMP_SDK_VERSION "1.0"
+#define VAMP_SDK_VERSION "1.1"
 
 namespace Vamp {
 
@@ -217,7 +217,13 @@
     typedef std::vector<std::string> ProgramList;
 
     /**
-     * Get the program settings available in this plugin.
+     * Get the program settings available in this plugin.  A program
+     * is a named shorthand for a set of parameter values; changing
+     * the program may cause the plugin to alter the values of its
+     * published parameters (and/or non-public internal processing
+     * parameters).  The host should re-read the plugin's parameter
+     * values after setting a new program.
+     *
      * The programs must have unique names.
      */
     virtual ProgramList getPrograms() const { return ProgramList(); }
--- a/vamp-sdk/PluginHostAdapter.h	Fri Mar 30 17:14:16 2007 +0000
+++ b/vamp-sdk/PluginHostAdapter.h	Fri Jun 01 15:10:17 2007 +0000
@@ -38,8 +38,7 @@
 #define _VAMP_PLUGIN_HOST_ADAPTER_H_
 
 #include <vamp/vamp.h>
-
-#include "Plugin.h"
+#include <vamp-sdk/Plugin.h>
 
 #include <vector>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginChannelAdapter.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,183 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include "PluginChannelAdapter.h"
+
+namespace Vamp {
+
+namespace HostExt {
+
+PluginChannelAdapter::PluginChannelAdapter(Plugin *plugin) :
+    PluginWrapper(plugin),
+    m_blockSize(0),
+    m_inputChannels(0),
+    m_pluginChannels(0),
+    m_buffer(0),
+    m_forwardPtrs(0)
+{
+}
+
+PluginChannelAdapter::~PluginChannelAdapter()
+{
+    if (m_buffer) {
+        if (m_inputChannels > m_pluginChannels) {
+            delete[] m_buffer[0];
+        } else {
+            for (size_t i = 0; i < m_pluginChannels - m_inputChannels; ++i) {
+                delete[] m_buffer[i];
+            }
+        }
+        delete[] m_buffer;
+        m_buffer = 0;
+    }
+
+    if (m_forwardPtrs) {
+        delete[] m_forwardPtrs;
+        m_forwardPtrs = 0;
+    }
+}
+
+bool
+PluginChannelAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    m_blockSize = blockSize;
+
+    size_t minch = m_plugin->getMinChannelCount();
+    size_t maxch = m_plugin->getMaxChannelCount();
+
+    m_inputChannels = channels;
+
+    if (m_inputChannels < minch) {
+
+        m_forwardPtrs = new const float *[minch];
+
+        if (m_inputChannels > 1) {
+            // We need a set of zero-valued buffers to add to the
+            // forwarded pointers
+            m_buffer = new float*[minch - channels];
+            for (size_t i = 0; i < minch; ++i) {
+                m_buffer[i] = new float[blockSize];
+                for (size_t j = 0; j < blockSize; ++j) {
+                    m_buffer[i][j] = 0.f;
+                }
+            }
+        }
+
+        m_pluginChannels = minch;
+
+        std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
+
+    } else if (m_inputChannels > maxch) {
+
+        // We only need m_buffer if we are mixing down to a single
+        // channel -- otherwise we can just forward the same float* as
+        // passed in to process(), expecting the excess to be ignored
+
+        if (maxch == 1) {
+            m_buffer = new float *[1];
+            m_buffer[0] = new float[blockSize];
+
+            std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl;
+
+        } else {
+            
+            std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
+        }
+
+        m_pluginChannels = maxch;
+
+    } else {
+ 
+        std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl;
+        m_pluginChannels = m_inputChannels;
+    }
+
+    return m_plugin->initialise(m_pluginChannels, stepSize, blockSize);
+}
+
+PluginChannelAdapter::FeatureSet
+PluginChannelAdapter::process(const float *const *inputBuffers,
+                              RealTime timestamp)
+{
+    std::cerr << "PluginChannelAdapter::process: " << m_inputChannels << " -> " << m_pluginChannels << " channels" << std::endl;
+
+    if (m_inputChannels < m_pluginChannels) {
+
+        if (m_inputChannels == 1) {
+            for (size_t i = 0; i < m_pluginChannels; ++i) {
+                m_forwardPtrs[i] = inputBuffers[0];
+            }
+        } else {
+            for (size_t i = 0; i < m_inputChannels; ++i) {
+                m_forwardPtrs[i] = inputBuffers[i];
+            }
+            for (size_t i = m_inputChannels; i < m_pluginChannels; ++i) {
+                m_forwardPtrs[i] = m_buffer[i - m_inputChannels];
+            }
+        }
+
+        return m_plugin->process(m_forwardPtrs, timestamp);
+
+    } else if (m_inputChannels > m_pluginChannels) {
+
+        if (m_pluginChannels == 1) {
+            for (size_t j = 0; j < m_blockSize; ++j) {
+                m_buffer[0][j] = inputBuffers[0][j];
+            }
+            for (size_t i = 1; i < m_inputChannels; ++i) {
+                for (size_t j = 0; j < m_blockSize; ++j) {
+                    m_buffer[0][j] += inputBuffers[i][j];
+                }
+            }
+            for (size_t j = 0; j < m_blockSize; ++j) {
+                m_buffer[0][j] /= m_inputChannels;
+            }
+            return m_plugin->process(m_buffer, timestamp);
+        } else {
+            return m_plugin->process(inputBuffers, timestamp);
+        }
+
+    } else {
+
+        return m_plugin->process(inputBuffers, timestamp);
+    }
+}
+
+}
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginChannelAdapter.h	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,127 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef _VAMP_PLUGIN_CHANNEL_ADAPTER_H_
+#define _VAMP_PLUGIN_CHANNEL_ADAPTER_H_
+
+#include "PluginWrapper.h"
+
+namespace Vamp {
+
+namespace HostExt {
+
+/**
+ * PluginChannelAdapter is a Vamp plugin adapter that implements a
+ * policy for management of plugins that expect a different number of
+ * input channels from the number actually available in the source
+ * audio data.
+ *
+ * A host using PluginChannelAdapter may ignore the getMinChannelCount
+ * and getMaxChannelCount reported by the plugin, and still expect the
+ * plugin to run.
+ *
+ * PluginChannelAdapter implements the following policy:
+ *
+ * -- If the plugin supports the provided number of channels directly,
+ * PluginChannelAdapter will just run the plugin as normal.
+ *
+ * -- If the plugin only supports exactly one channel but more than
+ * one channel is provided, PluginChannelAdapter will use the mean of
+ * the channels.  This ensures that the resulting values remain within
+ * the same magnitude range as expected for mono data.
+ *
+ * -- If the plugin requires more than one channel but exactly one is
+ * provided, the provided channel will be duplicated across all the
+ * plugin input channels.
+ *
+ * If none of the above apply:
+ * 
+ * -- If the plugin requires more channels than are provided, the
+ * minimum acceptable number of channels will be produced by adding
+ * empty (zero valued) channels to those provided.
+ *
+ * -- If the plugin requires fewer channels than are provided, the
+ * maximum acceptable number of channels will be produced by
+ * discarding the excess channels.
+ *
+ * Hosts requiring a different channel policy from the above will need
+ * to implement it themselves, instead of using PluginChannelAdapter.
+ *
+ * Note that PluginChannelAdapter does not override the minimum and
+ * maximum channel counts returned by the wrapped plugin.  The host
+ * will need to be aware that it is using a PluginChannelAdapter, and
+ * be prepared to ignore these counts as necessary.  (This contrasts
+ * with the approach used in PluginInputDomainAdapter, which aims to
+ * make the host completely unaware of which underlying input domain
+ * is in fact in use.)
+ * 
+ * (The rationale for this is that a host may wish to use the
+ * PluginChannelAdapter but still discriminate in some way on the
+ * basis of the number of channels actually supported.  For example, a
+ * simple stereo audio host may prefer to reject plugins that require
+ * more than two channels on the grounds that doesn't actually
+ * understand what they are for, rather than allow the channel adapter
+ * to make a potentially meaningless channel conversion for them.)
+ *
+ * In every respect other than its management of channels, the
+ * PluginChannelAdapter behaves identically to the plugin that it
+ * wraps.  The wrapped plugin will be deleted when the wrapper is
+ * deleted.
+ */
+
+class PluginChannelAdapter : public PluginWrapper
+{
+public:
+    PluginChannelAdapter(Plugin *plugin); // I take ownership of plugin
+    virtual ~PluginChannelAdapter();
+
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+
+    FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+
+protected:
+    size_t m_blockSize;
+    size_t m_inputChannels;
+    size_t m_pluginChannels;
+    float **m_buffer;
+    const float **m_forwardPtrs;
+};
+
+}
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginInputDomainAdapter.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,376 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include "PluginInputDomainAdapter.h"
+
+#include <cmath>
+
+namespace Vamp {
+
+namespace HostExt {
+
+PluginInputDomainAdapter::PluginInputDomainAdapter(Plugin *plugin) :
+    PluginWrapper(plugin),
+    m_channels(0),
+    m_blockSize(0),
+    m_freqbuf(0)
+{
+}
+
+PluginInputDomainAdapter::~PluginInputDomainAdapter()
+{
+}
+    
+bool
+PluginInputDomainAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    if (m_plugin->getInputDomain() == TimeDomain) {
+
+        m_blockSize = blockSize;
+        m_channels = channels;
+
+        return m_plugin->initialise(channels, stepSize, blockSize);
+    }
+
+    if (blockSize < 2) {
+        std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not supported" << std::endl;
+        return false;
+    }                
+        
+    if (blockSize & (blockSize-1)) {
+        std::cerr << "ERROR: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported" << std::endl;
+        return false;
+    }
+
+    if (m_channels > 0) {
+        for (size_t c = 0; c < m_channels; ++c) {
+            delete[] m_freqbuf[c];
+        }
+        delete[] m_freqbuf;
+        delete[] m_ri;
+        delete[] m_ro;
+        delete[] m_io;
+    }
+
+    m_blockSize = blockSize;
+    m_channels = channels;
+
+    m_freqbuf = new float *[m_channels];
+    for (size_t c = 0; c < m_channels; ++c) {
+        m_freqbuf[c] = new float[m_blockSize + 2];
+    }
+    m_ri = new double[m_blockSize];
+    m_ro = new double[m_blockSize];
+    m_io = new double[m_blockSize];
+
+    return m_plugin->initialise(channels, stepSize, blockSize);
+}
+
+Plugin::InputDomain
+PluginInputDomainAdapter::getInputDomain() const
+{
+    return TimeDomain;
+}
+
+size_t
+PluginInputDomainAdapter::getPreferredStepSize() const
+{
+    size_t step = m_plugin->getPreferredStepSize();
+
+    if (step == 0 && (m_plugin->getInputDomain() == FrequencyDomain)) {
+        step = getPreferredBlockSize() / 2;
+    }
+
+    return step;
+}
+
+size_t
+PluginInputDomainAdapter::getPreferredBlockSize() const
+{
+    size_t block = m_plugin->getPreferredBlockSize();
+
+    if (m_plugin->getInputDomain() == FrequencyDomain) {
+        if (block == 0) {
+            block = 1024;
+        } else {
+            block = makeBlockSizeAcceptable(block);
+        }
+    }
+
+    return block;
+}
+
+size_t
+PluginInputDomainAdapter::makeBlockSizeAcceptable(size_t blockSize) const
+{
+    if (blockSize < 2) {
+
+        std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: blocksize < 2 not" << std::endl
+                  << "supported, increasing from " << blockSize << " to 2" << std::endl;
+        blockSize = 2;
+        
+    } else if (blockSize & (blockSize-1)) {
+            
+        // not a power of two, can't handle that with our current fft
+        // implementation
+
+        size_t nearest = blockSize;
+        size_t power = 0;
+        while (nearest > 1) {
+            nearest >>= 1;
+            ++power;
+        }
+        nearest = 1;
+        while (power) {
+            nearest <<= 1;
+            --power;
+        }
+        
+        if (blockSize - nearest > (nearest*2) - blockSize) {
+            nearest = nearest*2;
+        }
+        
+        std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
+        blockSize = nearest;
+    }
+
+    return blockSize;
+}
+
+Plugin::FeatureSet
+PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime timestamp)
+{
+    if (m_plugin->getInputDomain() == TimeDomain) {
+        return m_plugin->process(inputBuffers, timestamp);
+    }
+
+    // The timestamp supplied should be (according to the Vamp::Plugin
+    // spec) the time of the start of the time-domain input block.
+    // However, we want to pass to the plugin an FFT output calculated
+    // from the block of samples _centred_ on that timestamp.
+    // 
+    // We have two options:
+    // 
+    // 1. Buffer the input, calculating the fft of the values at the
+    // passed-in block minus blockSize/2 rather than starting at the
+    // passed-in block.  So each time we call process on the plugin,
+    // we are passing in the same timestamp as was passed to our own
+    // process plugin, but not (the frequency domain representation
+    // of) the same set of samples.  Advantages: avoids confusion in
+    // the host by ensuring the returned values have timestamps
+    // comparable with that passed in to this function (in fact this
+    // is pretty much essential for one-value-per-block outputs);
+    // consistent with hosts such as SV that deal with the
+    // frequency-domain transform themselves.  Disadvantages: means
+    // making the not necessarily correct assumption that the samples
+    // preceding the first official block are all zero (or some other
+    // known value).
+    //
+    // 2. Increase the passed-in timestamps by half the blocksize.  So
+    // when we call process, we are passing in the frequency domain
+    // representation of the same set of samples as passed to us, but
+    // with a different timestamp.  Advantages: simplicity; avoids
+    // iffy assumption mentioned above.  Disadvantages: inconsistency
+    // with SV in cases where stepSize != blockSize/2; potential
+    // confusion arising from returned timestamps being calculated
+    // from the adjusted input timestamps rather than the original
+    // ones (and inaccuracy where the returned timestamp is implied,
+    // as in one-value-per-block).
+    //
+    // Neither way is ideal, but I don't think either is strictly
+    // incorrect either.  I think this is just a case where the same
+    // plugin can legitimately produce differing results from the same
+    // input data, depending on how that data is packaged.
+    // 
+    // We'll go for option 2, adjusting the timestamps.  Note in
+    // particular that this means some results can differ from those
+    // produced by SV.
+
+    std::cerr << "PluginInputDomainAdapter: sampleRate " << m_inputSampleRate << ", blocksize " << m_blockSize << ", adjusting time from " << timestamp;
+
+    timestamp = timestamp + RealTime::frame2RealTime(m_blockSize/2,
+                                                     m_inputSampleRate);
+
+    std::cerr << " to " << timestamp << std::endl;
+
+    for (size_t c = 0; c < m_channels; ++c) {
+
+        for (size_t i = 0; i < m_blockSize; ++i) {
+            // Hanning window
+            m_ri[i] = double(inputBuffers[c][i])
+                * (0.50 - 0.50 * cos((2 * M_PI * i)
+                                     / m_blockSize));
+        }
+
+        for (size_t i = 0; i < m_blockSize/2; ++i) {
+            // FFT shift
+            double value = m_ri[i];
+            m_ri[i] = m_ri[i + m_blockSize/2];
+            m_ri[i + m_blockSize/2] = value;
+        }
+
+        fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
+
+        for (size_t i = 0; i <= m_blockSize/2; ++i) {
+            m_freqbuf[c][i * 2] = m_ro[i];
+            m_freqbuf[c][i * 2 + 1] = m_io[i];
+        }
+    }
+
+    return m_plugin->process(m_freqbuf, timestamp);
+}
+
+void
+PluginInputDomainAdapter::fft(unsigned int n, bool inverse,
+                              double *ri, double *ii, double *ro, double *io)
+{
+    if (!ri || !ro || !io) return;
+
+    unsigned int bits;
+    unsigned int i, j, k, m;
+    unsigned int blockSize, blockEnd;
+
+    double tr, ti;
+
+    if (n < 2) return;
+    if (n & (n-1)) return;
+
+    double angle = 2.0 * M_PI;
+    if (inverse) angle = -angle;
+
+    for (i = 0; ; ++i) {
+	if (n & (1 << i)) {
+	    bits = i;
+	    break;
+	}
+    }
+
+    static unsigned int tableSize = 0;
+    static int *table = 0;
+
+    if (tableSize != n) {
+
+	delete[] table;
+
+	table = new int[n];
+
+	for (i = 0; i < n; ++i) {
+	
+	    m = i;
+
+	    for (j = k = 0; j < bits; ++j) {
+		k = (k << 1) | (m & 1);
+		m >>= 1;
+	    }
+
+	    table[i] = k;
+	}
+
+	tableSize = n;
+    }
+
+    if (ii) {
+	for (i = 0; i < n; ++i) {
+	    ro[table[i]] = ri[i];
+	    io[table[i]] = ii[i];
+	}
+    } else {
+	for (i = 0; i < n; ++i) {
+	    ro[table[i]] = ri[i];
+	    io[table[i]] = 0.0;
+	}
+    }
+
+    blockEnd = 1;
+
+    for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
+
+	double delta = angle / (double)blockSize;
+	double sm2 = -sin(-2 * delta);
+	double sm1 = -sin(-delta);
+	double cm2 = cos(-2 * delta);
+	double cm1 = cos(-delta);
+	double w = 2 * cm1;
+	double ar[3], ai[3];
+
+	for (i = 0; i < n; i += blockSize) {
+
+	    ar[2] = cm2;
+	    ar[1] = cm1;
+
+	    ai[2] = sm2;
+	    ai[1] = sm1;
+
+	    for (j = i, m = 0; m < blockEnd; j++, m++) {
+
+		ar[0] = w * ar[1] - ar[2];
+		ar[2] = ar[1];
+		ar[1] = ar[0];
+
+		ai[0] = w * ai[1] - ai[2];
+		ai[2] = ai[1];
+		ai[1] = ai[0];
+
+		k = j + blockEnd;
+		tr = ar[0] * ro[k] - ai[0] * io[k];
+		ti = ar[0] * io[k] + ai[0] * ro[k];
+
+		ro[k] = ro[j] - tr;
+		io[k] = io[j] - ti;
+
+		ro[j] += tr;
+		io[j] += ti;
+	    }
+	}
+
+	blockEnd = blockSize;
+    }
+
+    if (inverse) {
+
+	double denom = (double)n;
+
+	for (i = 0; i < n; i++) {
+	    ro[i] /= denom;
+	    io[i] /= denom;
+	}
+    }
+}
+
+}
+        
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginInputDomainAdapter.h	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,108 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef _VAMP_PLUGIN_INPUT_DOMAIN_ADAPTER_H_
+#define _VAMP_PLUGIN_INPUT_DOMAIN_ADAPTER_H_
+
+#include "PluginWrapper.h"
+
+namespace Vamp {
+
+namespace HostExt {
+
+/**
+ * PluginInputDomainAdapter is a Vamp plugin adapter that converts
+ * time-domain input into frequency-domain input for plugins that need
+ * it.  This permits a host to use time- and frequency-domain plugins
+ * interchangeably without needing to handle the conversion itself.
+ *
+ * This adapter uses a basic Hanning windowed FFT that supports
+ * power-of-two block sizes only.  If a frequency domain plugin
+ * requests a non-power-of-two blocksize, the adapter will adjust it
+ * to a nearby power of two instead.  Thus, getPreferredBlockSize()
+ * will always return a power of two if the wrapped plugin is a
+ * frequency domain one.  If the plugin doesn't accept the adjusted
+ * power of two block size, initialise() will fail.
+ *
+ * The adapter provides no way for the host to discover whether the
+ * underlying plugin is actually a time or frequency domain plugin
+ * (except that if the preferred block size is not a power of two, it
+ * must be a time domain plugin).
+ *
+ * The FFT implementation is simple and self-contained, but unlikely
+ * to be the fastest available: a host can usually do better if it
+ * cares enough.
+ *
+ * In every respect other than its input domain handling, the
+ * PluginInputDomainAdapter behaves identically to the plugin that it
+ * wraps.  The wrapped plugin will be deleted when the wrapper is
+ * deleted.
+ */
+
+class PluginInputDomainAdapter : public PluginWrapper
+{
+public:
+    PluginInputDomainAdapter(Plugin *plugin); // I take ownership of plugin
+    virtual ~PluginInputDomainAdapter();
+    
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+
+    InputDomain getInputDomain() const;
+
+    size_t getPreferredStepSize() const;
+    size_t getPreferredBlockSize() const;
+
+    FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+
+protected:
+    size_t m_channels;
+    size_t m_blockSize;
+    float **m_freqbuf;
+    double *m_ri;
+    double *m_ro;
+    double *m_io;
+
+    void fft(unsigned int n, bool inverse,
+             double *ri, double *ii, double *ro, double *io);
+
+    size_t makeBlockSizeAcceptable(size_t) const;
+};
+
+}
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginLoader.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,452 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include "vamp-sdk/PluginHostAdapter.h"
+#include "PluginLoader.h"
+#include "PluginInputDomainAdapter.h"
+#include "PluginChannelAdapter.h"
+
+#include <fstream>
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <tchar.h>
+#define PLUGIN_SUFFIX "dll"
+
+#else /* ! _WIN32 */
+
+#include <dirent.h>
+#include <dlfcn.h>
+
+#ifdef __APPLE__
+#define PLUGIN_SUFFIX "dylib"
+#else /* ! __APPLE__ */
+#define PLUGIN_SUFFIX "so"
+#endif /* ! __APPLE__ */
+
+#endif /* ! _WIN32 */
+
+using namespace std;
+
+namespace Vamp {
+	
+namespace HostExt {
+
+PluginLoader *
+PluginLoader::m_instance = 0;
+
+PluginLoader::PluginLoader()
+{
+}
+
+PluginLoader::~PluginLoader()
+{
+}
+
+PluginLoader *
+PluginLoader::getInstance()
+{
+    if (!m_instance) m_instance = new PluginLoader();
+    return m_instance;
+}
+
+vector<PluginLoader::PluginKey>
+PluginLoader::listPlugins() 
+{
+    if (m_pluginLibraryNameMap.empty()) generateLibraryMap();
+
+    vector<PluginKey> plugins;
+    for (map<PluginKey, string>::iterator mi =
+             m_pluginLibraryNameMap.begin();
+         mi != m_pluginLibraryNameMap.end(); ++mi) {
+        plugins.push_back(mi->first);
+    }
+
+    return plugins;
+}
+
+void
+PluginLoader::generateLibraryMap()
+{
+    vector<string> path = PluginHostAdapter::getPluginPath();
+
+    for (size_t i = 0; i < path.size(); ++i) {
+        
+        vector<string> files = listFiles(path[i], PLUGIN_SUFFIX);
+
+        for (vector<string>::iterator fi = files.begin();
+             fi != files.end(); ++fi) {
+            
+            string fullPath = path[i];
+            fullPath = splicePath(fullPath, *fi);
+            void *handle = loadLibrary(fullPath);
+            if (!handle) continue;
+            
+            VampGetPluginDescriptorFunction fn =
+                (VampGetPluginDescriptorFunction)lookupInLibrary
+                (handle, "vampGetPluginDescriptor");
+            
+            if (!fn) {
+                unloadLibrary(handle);
+                continue;
+            }
+            
+            int index = 0;
+            const VampPluginDescriptor *descriptor = 0;
+            
+            while ((descriptor = fn(VAMP_API_VERSION, index))) {
+                PluginKey key = composePluginKey(*fi, descriptor->identifier);
+                if (m_pluginLibraryNameMap.find(key) ==
+                    m_pluginLibraryNameMap.end()) {
+                    m_pluginLibraryNameMap[key] = fullPath;
+                }
+                ++index;
+            }
+            
+            unloadLibrary(handle);
+        }
+    }
+}
+
+PluginLoader::PluginKey
+PluginLoader::composePluginKey(string libraryName, string identifier)
+{
+    string basename = libraryName;
+
+    string::size_type li = basename.rfind('/');
+    if (li != string::npos) basename = basename.substr(li + 1);
+
+    li = basename.find('.');
+    if (li != string::npos) basename = basename.substr(0, li);
+
+    return basename + ":" + identifier;
+}
+
+PluginLoader::PluginCategoryHierarchy
+PluginLoader::getPluginCategory(PluginKey plugin)
+{
+    if (m_taxonomy.empty()) generateTaxonomy();
+    if (m_taxonomy.find(plugin) == m_taxonomy.end()) return PluginCategoryHierarchy();
+    return m_taxonomy[plugin];
+}
+
+string
+PluginLoader::getLibraryPathForPlugin(PluginKey plugin)
+{
+    if (m_pluginLibraryNameMap.empty()) generateLibraryMap();
+    if (m_pluginLibraryNameMap.find(plugin) == m_pluginLibraryNameMap.end()) return "";
+    return m_pluginLibraryNameMap[plugin];
+}    
+
+Plugin *
+PluginLoader::loadPlugin(PluginKey key, float inputSampleRate, int adapterFlags)
+{
+    string fullPath = getLibraryPathForPlugin(key);
+    if (fullPath == "") return 0;
+    
+    string::size_type ki = key.find(':');
+    if (ki == string::npos) {
+        //!!! flag error
+        return 0;
+    }
+
+    string identifier = key.substr(ki + 1);
+    
+    void *handle = loadLibrary(fullPath);
+    if (!handle) return 0;
+    
+    VampGetPluginDescriptorFunction fn =
+        (VampGetPluginDescriptorFunction)lookupInLibrary
+        (handle, "vampGetPluginDescriptor");
+
+    if (!fn) {
+        unloadLibrary(handle);
+        return 0;
+    }
+
+    int index = 0;
+    const VampPluginDescriptor *descriptor = 0;
+
+    while ((descriptor = fn(VAMP_API_VERSION, index))) {
+
+        if (string(descriptor->identifier) == identifier) {
+
+            Vamp::PluginHostAdapter *plugin =
+                new Vamp::PluginHostAdapter(descriptor, inputSampleRate);
+
+            Plugin *adapter = new PluginDeletionNotifyAdapter(plugin, this);
+
+            m_pluginLibraryHandleMap[adapter] = handle;
+
+            if (adapterFlags & ADAPT_INPUT_DOMAIN) {
+                if (adapter->getInputDomain() == Plugin::FrequencyDomain) {
+                    adapter = new PluginInputDomainAdapter(adapter);
+                }
+            }
+
+            if (adapterFlags & ADAPT_CHANNEL_COUNT) {
+                adapter = new PluginChannelAdapter(adapter);
+            }
+
+            return adapter;
+        }
+
+        ++index;
+    }
+
+    cerr << "Vamp::HostExt::PluginLoader: Plugin \""
+         << identifier << "\" not found in library \""
+         << fullPath << "\"" << endl;
+
+    return 0;
+}
+
+void
+PluginLoader::generateTaxonomy()
+{
+//    cerr << "PluginLoader::generateTaxonomy" << endl;
+
+    vector<string> path = PluginHostAdapter::getPluginPath();
+    string libfragment = "/lib/";
+    vector<string> catpath;
+
+    string suffix = "cat";
+
+    for (vector<string>::iterator i = path.begin();
+         i != path.end(); ++i) {
+
+        // It doesn't matter that we're using literal forward-slash in
+        // this bit, as it's only relevant if the path contains
+        // "/lib/", which is only meaningful and only plausible on
+        // systems with forward-slash delimiters
+        
+        string dir = *i;
+        string::size_type li = dir.find(libfragment);
+
+        if (li != string::npos) {
+            catpath.push_back
+                (dir.substr(0, li)
+                 + "/share/"
+                 + dir.substr(li + libfragment.length()));
+        }
+
+        catpath.push_back(dir);
+    }
+
+    char buffer[1024];
+
+    for (vector<string>::iterator i = catpath.begin();
+         i != catpath.end(); ++i) {
+        
+        vector<string> files = listFiles(*i, suffix);
+
+        for (vector<string>::iterator fi = files.begin();
+             fi != files.end(); ++fi) {
+
+            string filepath = splicePath(*i, *fi);
+            ifstream is(filepath.c_str(), ifstream::in | ifstream::binary);
+
+            if (is.fail()) {
+//                cerr << "failed to open: " << filepath << endl;
+                continue;
+            }
+
+//            cerr << "opened: " << filepath << endl;
+
+            while (!!is.getline(buffer, 1024)) {
+
+                string line(buffer);
+
+//                cerr << "line = " << line << endl;
+
+                string::size_type di = line.find("::");
+                if (di == string::npos) continue;
+
+                string id = line.substr(0, di);
+                string encodedCat = line.substr(di + 2);
+
+                if (id.substr(0, 5) != "vamp:") continue;
+                id = id.substr(5);
+
+                while (encodedCat.length() >= 1 &&
+                       encodedCat[encodedCat.length()-1] == '\r') {
+                    encodedCat = encodedCat.substr(0, encodedCat.length()-1);
+                }
+
+//                cerr << "id = " << id << ", cat = " << encodedCat << endl;
+
+                PluginCategoryHierarchy category;
+                string::size_type ai;
+                while ((ai = encodedCat.find(" > ")) != string::npos) {
+                    category.push_back(encodedCat.substr(0, ai));
+                    encodedCat = encodedCat.substr(ai + 3);
+                }
+                if (encodedCat != "") category.push_back(encodedCat);
+
+                m_taxonomy[id] = category;
+            }
+        }
+    }
+}    
+
+void *
+PluginLoader::loadLibrary(string path)
+{
+    void *handle = 0;
+#ifdef _WIN32
+    handle = LoadLibrary(path.c_str());
+    if (!handle) {
+        cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
+             << path << "\"" << endl;
+    }
+#else
+    handle = dlopen(path.c_str(), RTLD_LAZY);
+    if (!handle) {
+        cerr << "Vamp::HostExt::PluginLoader: Unable to load library \""
+             << path << "\": " << dlerror() << endl;
+    }
+#endif
+    return handle;
+}
+
+void
+PluginLoader::unloadLibrary(void *handle)
+{
+#ifdef _WIN32
+    FreeLibrary((HINSTANCE)handle);
+#else
+    dlclose(handle);
+#endif
+}
+
+void *
+PluginLoader::lookupInLibrary(void *handle, const char *symbol)
+{
+#ifdef _WIN32
+    return (void *)GetProcAddress((HINSTANCE)handle, symbol);
+#else
+    return (void *)dlsym(handle, symbol);
+#endif
+}
+
+string
+PluginLoader::splicePath(string a, string b)
+{
+#ifdef _WIN32
+    return a + "\\" + b;
+#else
+    return a + "/" + b;
+#endif
+}
+
+vector<string>
+PluginLoader::listFiles(string dir, string extension)
+{
+    vector<string> files;
+    size_t extlen = extension.length();
+
+#ifdef _WIN32
+
+    string expression = dir + "\\*." + extension;
+    WIN32_FIND_DATA data;
+    HANDLE fh = FindFirstFile(expression.c_str(), &data);
+    if (fh == INVALID_HANDLE_VALUE) return files;
+
+    bool ok = true;
+    while (ok) {
+        files.push_back(data.cFileName);
+        ok = FindNextFile(fh, &data);
+    }
+
+    FindClose(fh);
+
+#else
+    DIR *d = opendir(dir.c_str());
+    if (!d) return files;
+            
+    struct dirent *e = 0;
+    while ((e = readdir(d))) {
+        
+        if (!(e->d_type & DT_REG) || !e->d_name) continue;
+        
+        size_t len = strlen(e->d_name);
+        if (len < extlen + 2 ||
+            e->d_name + len - extlen - 1 != "." + extension) {
+            continue;
+        }
+
+        files.push_back(e->d_name);
+    }
+
+    closedir(d);
+#endif
+
+    return files;
+}
+
+void
+PluginLoader::pluginDeleted(PluginDeletionNotifyAdapter *adapter)
+{
+    void *handle = m_pluginLibraryHandleMap[adapter];
+    if (handle) unloadLibrary(handle);
+    m_pluginLibraryHandleMap.erase(adapter);
+}
+
+PluginLoader::PluginDeletionNotifyAdapter::PluginDeletionNotifyAdapter(Plugin *plugin,
+                                                                       PluginLoader *loader) :
+    PluginWrapper(plugin),
+    m_loader(loader)
+{
+}
+
+PluginLoader::PluginDeletionNotifyAdapter::~PluginDeletionNotifyAdapter()
+{
+    // We need to delete the plugin before calling pluginDeleted, as
+    // the delete call may require calling through to the descriptor
+    // (for e.g. cleanup) but pluginDeleted may unload the required
+    // library for the call.  To prevent a double deletion when our
+    // parent's destructor runs (after this one), be sure to set
+    // m_plugin to 0 after deletion.
+    delete m_plugin;
+    m_plugin = 0;
+
+    if (m_loader) m_loader->pluginDeleted(this);
+}
+
+}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginLoader.h	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,219 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef _VAMP_PLUGIN_LOADER_H_
+#define _VAMP_PLUGIN_LOADER_H_
+
+#include <vector>
+#include <string>
+#include <map>
+
+#include "PluginWrapper.h"
+
+namespace Vamp {
+
+class Plugin;
+
+namespace HostExt {
+
+/**
+ * Vamp::HostExt::PluginLoader is a convenience class for discovering
+ * and loading Vamp plugins using the typical plugin-path, library
+ * naming, and categorisation conventions described in the Vamp SDK
+ * documentation.  This class is intended to greatly simplify the task
+ * of becoming a Vamp plugin host for any C++ application.
+ * 
+ * Hosts are not required by the Vamp specification to use the same
+ * plugin search path and naming conventions as implemented by this
+ * class, and are certainly not required to use this actual class.
+ * But it's recommended, for sound practical reasons.
+ */
+
+class PluginLoader
+{
+public:
+    /**
+     * PluginLoader is a singleton class.  This function returns a
+     * pointer to the single instance of it.  Use this to obtain your
+     * loader object.
+     */
+    static PluginLoader *getInstance();
+
+    /**
+     * PluginKey is a string type that is used to identify a plugin
+     * uniquely within the scope of "the current system".  It consists
+     * of the base name of the plugin library, a colon separator, and
+     * the identifier string for the plugin.  It is only meaningful in
+     * the context of a given plugin path (the one returned by
+     * PluginHostAdapter::getPluginPath()).
+     *
+     * Use composePluginKey to construct a plugin key from a known
+     * plugin library name and identifier.
+     */
+    typedef std::string PluginKey;
+
+    /**
+     * PluginKeyList is a sequence of plugin keys, such as returned by
+     * a plugin lookup function.
+     */
+    typedef std::vector<PluginKey> PluginKeyList;
+
+    /**
+     * PluginCategoryHierarchy is a sequence of general->specific
+     * category names, as may be associated with a single plugin.
+     * This sequence describes the location of a plugin within a
+     * category forest, containing the human-readable names of the
+     * plugin's category tree root, followed by each of the nodes down
+     * to the leaf containing the plugin.
+     */
+    typedef std::vector<std::string> PluginCategoryHierarchy;
+
+    /**
+     * Search for all available Vamp plugins, and return a list of
+     * them in the order in which they were found.
+     */
+    PluginKeyList listPlugins();
+
+    /**
+     * AdapterFlags contains a set of values that may be OR'd together
+     * to indicate in which circumstances PluginLoader should use a
+     * plugin adapter to make a plugin easier to use for a host that
+     * does not want to cater for complex features.
+     *
+     * The available flags are:
+     * 
+     * ADAPT_INPUT_DOMAIN - If the plugin expects frequency domain
+     * input, wrap it in a PluginInputDomainAdapter that automatically
+     * converts the plugin to one that expects time-domain input.
+     * This enables a host to accommodate time- and frequency-domain
+     * plugins without needing to do any conversion itself.
+     *
+     * ADAPT_CHANNEL_COUNT - Wrap the plugin in a PluginChannelAdapter
+     * to handle any mismatch between the number of channels of audio
+     * the plugin can handle and the number available in the host.
+     * This enables a host to use plugins that may require the input
+     * to be mixed down to mono, etc., without having to worry about
+     * doing that itself.
+     * 
+     * See PluginInputDomainAdapter and PluginChannelAdapter for more
+     * details of the classes that the loader may use if these flags
+     * are set.
+     */
+    enum AdapterFlags {
+        ADAPT_INPUT_DOMAIN  = 0x01,
+        ADAPT_CHANNEL_COUNT = 0x02,
+        ADAPT_ALL           = 0xff
+    };
+
+    /**
+     * Load a Vamp plugin, given its identifying key.  If the plugin
+     * could not be loaded, returns 0.
+     *
+     * adapterFlags is a bitwise OR of the values in the AdapterFlags
+     * enum, indicating under which circumstances an adapter should be
+     * used to wrap the original plugin.  See AdapterFlags for more
+     * details.  If adapterFlags is 0, no optional adapters will be
+     * used.
+     *
+     * The returned plugin should be deleted (using the standard C++
+     * delete) after use.
+     */
+    Plugin *loadPlugin(PluginKey key,
+                       float inputSampleRate,
+                       int adapterFlags = 0);
+
+    /**
+     * Given a Vamp plugin library name and plugin identifier, return
+     * the corresponding plugin key in a form suitable for passing in to
+     * loadPlugin.
+     */
+    PluginKey composePluginKey(std::string libraryName,
+                               std::string identifier);
+
+    /**
+     * Return the category hierarchy for a Vamp plugin, given its
+     * identifying key.  See PluginCategoryHierarchy documentation for
+     * more details.
+     *
+     * If the plugin has no category information, return an empty
+     * hierarchy.
+     */
+    PluginCategoryHierarchy getPluginCategory(PluginKey plugin);
+
+    /**
+     * Return the file path of the dynamic library from which the
+     * given plugin will be loaded (if available).
+     */
+    std::string getLibraryPathForPlugin(PluginKey plugin);
+
+protected:
+    PluginLoader();
+    virtual ~PluginLoader();
+
+    class PluginDeletionNotifyAdapter : public PluginWrapper {
+    public:
+        PluginDeletionNotifyAdapter(Plugin *plugin, PluginLoader *loader);
+        virtual ~PluginDeletionNotifyAdapter();
+    protected:
+        PluginLoader *m_loader;
+    };
+
+    virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
+
+    std::map<PluginKey, std::string> m_pluginLibraryNameMap;
+    void generateLibraryMap();
+
+    std::map<PluginKey, PluginCategoryHierarchy> m_taxonomy;
+    void generateTaxonomy();
+
+    std::map<Plugin *, void *> m_pluginLibraryHandleMap;
+
+    void *loadLibrary(std::string path);
+    void unloadLibrary(void *handle);
+    void *lookupInLibrary(void *handle, const char *symbol);
+
+    std::string splicePath(std::string a, std::string b);
+    std::vector<std::string> listFiles(std::string dir, std::string ext);
+
+    static PluginLoader *m_instance;
+};
+
+}
+
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginWrapper.cpp	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,200 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#include "PluginWrapper.h"
+
+namespace Vamp {
+
+namespace HostExt {
+
+class PluginRateExtractor : public Plugin
+{
+public:
+    float getRate() const { return m_inputSampleRate; }
+};
+
+PluginWrapper::PluginWrapper(Plugin *plugin) :
+    Plugin(((PluginRateExtractor *)plugin)->getRate()),
+    m_plugin(plugin)
+{
+}
+
+PluginWrapper::~PluginWrapper()
+{
+    delete m_plugin;
+}
+
+bool
+PluginWrapper::initialise(size_t channels, size_t stepSize, size_t blockSize)
+{
+    return m_plugin->initialise(channels, stepSize, blockSize);
+}
+
+void
+PluginWrapper::reset()
+{
+    m_plugin->reset();
+}
+
+Plugin::InputDomain
+PluginWrapper::getInputDomain() const
+{
+    return m_plugin->getInputDomain();
+}
+
+unsigned int
+PluginWrapper::getVampApiVersion() const
+{
+    return m_plugin->getVampApiVersion();
+}
+
+std::string
+PluginWrapper::getIdentifier() const
+{
+    return m_plugin->getIdentifier();
+}
+
+std::string
+PluginWrapper::getName() const
+{
+    return m_plugin->getName();
+}
+
+std::string
+PluginWrapper::getDescription() const
+{
+    return m_plugin->getDescription();
+}
+
+std::string
+PluginWrapper::getMaker() const
+{
+    return m_plugin->getMaker();
+}
+
+int
+PluginWrapper::getPluginVersion() const
+{
+    return m_plugin->getPluginVersion();
+}
+
+std::string
+PluginWrapper::getCopyright() const
+{
+    return m_plugin->getCopyright();
+}
+
+PluginBase::ParameterList
+PluginWrapper::getParameterDescriptors() const
+{
+    return m_plugin->getParameterDescriptors();
+}
+
+float
+PluginWrapper::getParameter(std::string parameter) const
+{
+    return m_plugin->getParameter(parameter);
+}
+
+void
+PluginWrapper::setParameter(std::string parameter, float value)
+{
+    m_plugin->setParameter(parameter, value);
+}
+
+PluginBase::ProgramList
+PluginWrapper::getPrograms() const
+{
+    return m_plugin->getPrograms();
+}
+
+std::string
+PluginWrapper::getCurrentProgram() const
+{
+    return m_plugin->getCurrentProgram();
+}
+
+void
+PluginWrapper::selectProgram(std::string program)
+{
+    m_plugin->selectProgram(program);
+}
+
+size_t
+PluginWrapper::getPreferredStepSize() const
+{
+    return m_plugin->getPreferredStepSize();
+}
+
+size_t
+PluginWrapper::getPreferredBlockSize() const
+{
+    return m_plugin->getPreferredBlockSize();
+}
+
+size_t
+PluginWrapper::getMinChannelCount() const
+{
+    return m_plugin->getMinChannelCount();
+}
+
+size_t PluginWrapper::getMaxChannelCount() const
+{
+    return m_plugin->getMaxChannelCount();
+}
+
+Plugin::OutputList
+PluginWrapper::getOutputDescriptors() const
+{
+    return m_plugin->getOutputDescriptors();
+}
+
+Plugin::FeatureSet
+PluginWrapper::process(const float *const *inputBuffers, RealTime timestamp)
+{
+    return m_plugin->process(inputBuffers, timestamp);
+}
+
+Plugin::FeatureSet
+PluginWrapper::getRemainingFeatures()
+{
+    return m_plugin->getRemainingFeatures();
+}
+
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vamp-sdk/hostext/PluginWrapper.h	Fri Jun 01 15:10:17 2007 +0000
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef _VAMP_PLUGIN_WRAPPER_H_
+#define _VAMP_PLUGIN_WRAPPER_H_
+
+#include <vamp-sdk/Plugin.h>
+
+namespace Vamp {
+
+namespace HostExt {
+
+/**
+ * PluginWrapper is a simple base class for adapter plugins.  It takes
+ * a pointer to a "to be wrapped" Vamp plugin on construction, and
+ * provides implementations of all the Vamp plugin methods that simply
+ * delegate through to the wrapped plugin.  A subclass can therefore
+ * override only the methods that are meaningful for the particular
+ * adapter.
+ */
+
+class PluginWrapper : public Plugin
+{
+public:
+    virtual ~PluginWrapper();
+    
+    bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+    void reset();
+
+    InputDomain getInputDomain() const;
+
+    unsigned int getVampApiVersion() const;
+    std::string getIdentifier() const;
+    std::string getName() const;
+    std::string getDescription() const;
+    std::string getMaker() const;
+    int getPluginVersion() const;
+    std::string getCopyright() const;
+
+    ParameterList getParameterDescriptors() const;
+    float getParameter(std::string) const;
+    void setParameter(std::string, float);
+
+    ProgramList getPrograms() const;
+    std::string getCurrentProgram() const;
+    void selectProgram(std::string);
+
+    size_t getPreferredStepSize() const;
+    size_t getPreferredBlockSize() const;
+
+    size_t getMinChannelCount() const;
+    size_t getMaxChannelCount() const;
+
+    OutputList getOutputDescriptors() const;
+
+    FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
+
+    FeatureSet getRemainingFeatures();
+
+protected:
+    PluginWrapper(Plugin *plugin); // I take ownership of plugin
+    Plugin *m_plugin;
+};
+
+}
+
+}
+
+#endif
--- a/vamp-sdk/vamp-hostsdk.pc.in	Fri Mar 30 17:14:16 2007 +0000
+++ b/vamp-sdk/vamp-hostsdk.pc.in	Fri Jun 01 15:10:17 2007 +0000
@@ -4,7 +4,7 @@
 includedir=${prefix}/include
 
 Name: vamp-hostsdk
-Version: 1.0.0
+Version: 1.1.0
 Description: Development library for Vamp audio analysis plugin hosts
 Libs: -L${libdir} -lvamp-hostsdk
 Cflags: -I${includedir} 
--- a/vamp-sdk/vamp-sdk.pc.in	Fri Mar 30 17:14:16 2007 +0000
+++ b/vamp-sdk/vamp-sdk.pc.in	Fri Jun 01 15:10:17 2007 +0000
@@ -4,7 +4,7 @@
 includedir=${prefix}/include
 
 Name: vamp-sdk
-Version: 1.0.0
+Version: 1.1.0
 Description: Development library for Vamp audio analysis plugins
 Libs: -L${libdir} -lvamp-sdk
 Cflags: -I${includedir}