# HG changeset patch # User cannam # Date 1180710617 0 # Node ID 9d3272c7db60464bcb682b31c49bf3dbabb490dc # Parent 933fee59d33aac77f0433116fbc340cbabacb36c * 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. diff -r 933fee59d33a -r 9d3272c7db60 Makefile --- 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 " 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) diff -r 933fee59d33a -r 9d3272c7db60 README --- 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). + diff -r 933fee59d33a -r 9d3272c7db60 examples/AmplitudeFollower.h --- 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 diff -r 933fee59d33a -r 9d3272c7db60 examples/PercussionOnsetDetector.h --- 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. diff -r 933fee59d33a -r 9d3272c7db60 examples/SpectralCentroid.h --- 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 diff -r 933fee59d33a -r 9d3272c7db60 examples/ZeroCrossing.h --- 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 diff -r 933fee59d33a -r 9d3272c7db60 examples/plugins.cpp --- 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 +#include "vamp/vamp.h" +#include "vamp-sdk/PluginAdapter.h" -#include "PluginAdapter.h" #include "ZeroCrossing.h" #include "SpectralCentroid.h" #include "PercussionOnsetDetector.h" diff -r 933fee59d33a -r 9d3272c7db60 host/vamp-simple-host.cpp --- 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 #include -#include // 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 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 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 plugins = loader->listPlugins(); + typedef std::multimap + 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; - } - } -} diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/Plugin.h --- 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"; } diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/PluginBase.h --- 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 #include -#define VAMP_SDK_VERSION "1.0" +#define VAMP_SDK_VERSION "1.1" namespace Vamp { @@ -217,7 +217,13 @@ typedef std::vector 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(); } diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/PluginHostAdapter.h --- 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 - -#include "Plugin.h" +#include #include diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginChannelAdapter.cpp --- /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); + } +} + +} + +} + + diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginChannelAdapter.h --- /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 diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginInputDomainAdapter.cpp --- /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 + +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; + } + } +} + +} + +} + diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginInputDomainAdapter.h --- /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 diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginLoader.cpp --- /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 + +#ifdef _WIN32 + +#include +#include +#define PLUGIN_SUFFIX "dll" + +#else /* ! _WIN32 */ + +#include +#include + +#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::listPlugins() +{ + if (m_pluginLibraryNameMap.empty()) generateLibraryMap(); + + vector plugins; + for (map::iterator mi = + m_pluginLibraryNameMap.begin(); + mi != m_pluginLibraryNameMap.end(); ++mi) { + plugins.push_back(mi->first); + } + + return plugins; +} + +void +PluginLoader::generateLibraryMap() +{ + vector path = PluginHostAdapter::getPluginPath(); + + for (size_t i = 0; i < path.size(); ++i) { + + vector files = listFiles(path[i], PLUGIN_SUFFIX); + + for (vector::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 path = PluginHostAdapter::getPluginPath(); + string libfragment = "/lib/"; + vector catpath; + + string suffix = "cat"; + + for (vector::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::iterator i = catpath.begin(); + i != catpath.end(); ++i) { + + vector files = listFiles(*i, suffix); + + for (vector::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 +PluginLoader::listFiles(string dir, string extension) +{ + vector 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); +} + +} + +} diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginLoader.h --- /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 +#include +#include + +#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 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 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 m_pluginLibraryNameMap; + void generateLibraryMap(); + + std::map m_taxonomy; + void generateTaxonomy(); + + std::map 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 listFiles(std::string dir, std::string ext); + + static PluginLoader *m_instance; +}; + +} + +} + +#endif + diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginWrapper.cpp --- /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(); +} + +} + +} + diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/hostext/PluginWrapper.h --- /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 + +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 diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/vamp-hostsdk.pc.in --- 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} diff -r 933fee59d33a -r 9d3272c7db60 vamp-sdk/vamp-sdk.pc.in --- 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}