Mercurial > hg > vampy
changeset 0:e20e214bdfb5
Added VAMP-Python binding project vampy
author | fazekasgy |
---|---|
date | Tue, 11 Mar 2008 19:47:34 +0000 |
parents | |
children | dc88002ce687 |
files | Example VamPy plugins/PyZeroCrossing.py Makefile PyPlugScanner.cpp PyPlugScanner.h PyPlugin.cpp PyPlugin.h README pyvamp-main.cpp |
diffstat | 8 files changed, 2150 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Example VamPy plugins/PyZeroCrossing.py Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,145 @@ +'''PyZeroCrossing.py - Example plugin designed to demonstrate''' +'''how to call a python class using the VamPy VAMP plugin''' + +#from time import * +#import sys + +class PyZeroCrossing: + + def __init__(self): + self.m_imputSampleRate = 44100 + self.m_stepSize = 0 + self.m_blockSize = 0 + self.m_channels = 0 + self.previousSample = 0.0 + self.threshold = 0.05 + + def initialise(self,channels,stepSize,blockSize,inputSampleRate): + self.m_channels = channels + self.m_stepSize = stepSize + self.m_blockSize = blockSize + self.m_inputSampleRate = inputSampleRate + return True + + def getMaker(self): + return 'VamPy Example Plugins' + + def getName(self): + return 'Zero Crossing (VamPy)' + + def getIdentifier(self): + return 'python-zc' + + def getMaxChannelCount(self): + return 1 + + def getInputDomain(self): + return 'TimeDomain' + + def getOutputDescriptors(self): + + #python dictionary + output0={ + 'identifier':'vampy-counts', + 'name':'Number of Zero Crossings', + 'description':'Number of zero crossings per audio frame', + 'unit':' ', + 'hasFixedBinCount':True, + 'binCount':1, + #'binNames':['1 Hz',1.5,'2 Hz',3,'4 Hz'], + 'hasKnownExtents':False, + #'minValue':0.0, + #'maxValue':0.0, + 'isQuantized':True, + 'quantizeStep':1.0, + 'sampleType':'OneSamplePerStep' + #'sampleRate':48000.0 + } + + output1={ + 'identifier':'vampy-crossings', + 'name':'Zero Crossing Locations', + 'description':'The locations of zero crossing points', + 'unit':'discrete', + 'hasFixedBinCount':True, + 'binCount':0, + 'sampleType':'VariableSampleRate' + #'sampleRate':48000.0 + } + + #return a list of dictionaries + return [output0,output1] + + def getParameterDescriptors(self): + paramlist1={ + 'identifier':'threshold', + 'name':'Noise threshold: ', + 'description':'Return null or delete this function if not needed.', + 'unit':'v', + 'minValue':0.0, + 'maxValue':0.5, + 'defaultValue':0.05, + 'isQuantized':False + } + return [paramlist1] + + def setParameter(self,paramid,newval): + if paramid == 'threshold' : + self.threshold = newval + return + + def getParameter(self,paramid): + if paramid == 'threshold' : + return self.threshold + else: + return 0.0 + + def process(self,inbuf): + crossing = False + prev = self.previousSample + count = 0.0; + + #we have two outputs defined thus we have to declare + #them as empty dictionaries in our output list + #in order to be able to return variable rate outputs + output0=[] + output1=[] + + if abs(sum(inbuf)) > self.threshold : + for x in range(len(inbuf)-1) : + + crossing = False + sample = inbuf[x] + if sample <= 0.0 : + if prev > 0.0 : crossing = True + else : + if sample > 0.0 : + if prev <= 0.0 : crossing = True + + if crossing == True : + count = count + 1 + feature1={ + 'hasTimestamp':True, #bool + #for now return sample position and convert to RealTime in C code + 'timeStamp':x + #'values':[count] + #'label':label + } + output1.append(feature1) + + prev = sample + self.previousSample = prev + else : + count = 0.0 + self.previousSample = inbuf[len(inbuf)-2] + + feature0={ + 'hasTimestamp':False, + 'values':[count], #strictly must be a list + 'label':str(count) + } + output0.append(feature0) + + #return a LIST of list of dictionaries + return [output0,output1] + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,289 @@ + +# Makefile for the Vamp plugin SDK. This builds the SDK objects, +# libraries, example plugins, and the test host. Please adjust to +# suit your operating system requirements. + +APIDIR = sdk/vamp +SDKDIR = sdk/vamp-sdk +HOSTEXTDIR = sdk/vamp-sdk/hostext +#EXAMPLEDIR = sdk/examples +HOSTDIR = sdk/host +EXAMPLEDIR = . + +### +### 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 +# sdkstatic -- build only the static versions of the SDK libraries +# 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 +# cleanplug -- remove compiled plugin files + +default: plugins + +# Compile flags +# +CXXFLAGS := $(CXXFLAGS) -O2 -Wall -I. -fPIC + +# ar, ranlib +# +AR := ar +RANLIB := ranlib + +# 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 = $(SDKDIR)/libvamp-sdk.a $(shell g++ -print-file-name=libstdc++.a) + +# File extension for a dynamically loadable object +# +#PLUGIN_EXT = .so +#PLUGIN_EXT = .dll +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 +# have to use "make install". +# +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.dylib.1.1.0 +INSTALL_SDK_LINK_ABI := libvamp-sdk.dylib.1 +INSTALL_SDK_LINK_DEV := libvamp-sdk.dylib +INSTALL_SDK_STATIC := libvamp-sdk.o +INSTALL_SDK_LA := libvamp-sdk.la + +INSTALL_HOSTSDK_LIBNAME := libvamp-hostsdk.dylib.2.0.0 +INSTALL_HOSTSDK_LINK_ABI := libvamp-hostsdk.dylib.2 +INSTALL_HOSTSDK_LINK_DEV := libvamp-hostsdk.dylib +INSTALL_HOSTSDK_STATIC := libvamp-hostsdk.a +INSTALL_HOSTSDK_LA := libvamp-hostsdk.la + +INSTALL_PKGCONFIG := $(INSTALL_PREFIX)/lib/pkgconfig + +# Install plugins to this location +# +LIBRARY_PREFIX :=/Library +INSTALL_PLUGIN :=$(LIBRARY_PREFIX)/Audio/Plug-Ins/Vamp + +# Flags required to tell the compiler to create a dynamically loadable object +# +#DYNAMIC_LDFLAGS = --static-libgcc -shared -Wl,-Bsymbolic +DYNAMIC_LDFLAGS = -dynamiclib +PLUGIN_LDFLAGS = $(DYNAMIC_LDFLAGS) +SDK_DYNAMIC_LDFLAGS = $(DYNAMIC_LDFLAGS) -Wl,-soname=$(INSTALL_SDK_LINK_ABI) +HOSTSDK_DYNAMIC_LDFLAGS = $(DYNAMIC_LDFLAGS) -Wl,-soname=$(INSTALL_HOSTSDK_LINK_ABI) + +## For OS/X with g++: +#DYNAMIC_LDFLAGS = -dynamiclib +PLUGIN_LDFLAGS = $(DYNAMIC_LDFLAGS) +SDK_DYNAMIC_LDFLAGS = $(DYNAMIC_LDFLAGS) +HOSTSDK_DYNAMIC_LDFLAGS = $(DYNAMIC_LDFLAGS) + + +### End of user-serviceable parts + +# Plugin headers, compiler objects and targets + +PLUGIN_HEADERS = \ + $(EXAMPLEDIR)/PyPlugScanner.h \ + $(EXAMPLEDIR)/PyPlugin.h + +PLUGIN_OBJECTS = \ + $(EXAMPLEDIR)/PyPlugScanner.o \ + $(EXAMPLEDIR)/PyPlugin.o \ + $(EXAMPLEDIR)/pyvamp-main.o + +PLUGIN_NAME = vamp-pyvamp-plugin + +PLUGIN_TARGET = \ + $(EXAMPLEDIR)/$(PLUGIN_NAME)$(PLUGIN_EXT) + +PLUGIN_DESCRIPTORS = \ + $(EXAMPLEDIR)/exampleplugin.n3 + +# We need to link with python +# Links the actual python executable. forces to export symbol _PyMac_Error +PYTHON_LINK_AS_SHARED := \ + -u _PyMac_Error /Library/Frameworks/Python.framework/Versions/2.5/Python + +API_HEADERS = \ + $(APIDIR)/vamp.h + +SDK_HEADERS = \ + $(SDKDIR)/Plugin.h \ + $(SDKDIR)/PluginAdapter.h \ + $(SDKDIR)/PluginBase.h \ + $(SDKDIR)/RealTime.h + +HOSTSDK_HEADERS = \ + $(SDKDIR)/Plugin.h \ + $(SDKDIR)/PluginBase.h \ + $(SDKDIR)/PluginHostAdapter.h \ + $(SDKDIR)/RealTime.h + +HOSTEXT_HEADERS = \ + $(HOSTEXTDIR)/PluginBufferingAdapter.h \ + $(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)/PluginBufferingAdapter.o \ + $(HOSTEXTDIR)/PluginChannelAdapter.o \ + $(HOSTEXTDIR)/PluginInputDomainAdapter.o \ + $(HOSTEXTDIR)/PluginLoader.o \ + $(HOSTEXTDIR)/PluginWrapper.o \ + $(SDKDIR)/RealTime.o + +SDK_STATIC = \ + $(SDKDIR)/libvamp-sdk.a + +HOSTSDK_STATIC = \ + $(SDKDIR)/libvamp-hostsdk.a + +SDK_DYNAMIC = \ + $(SDKDIR)/libvamp-sdk$(PLUGIN_EXT) + +HOSTSDK_DYNAMIC = \ + $(SDKDIR)/libvamp-hostsdk$(PLUGIN_EXT) + +SDK_LA = \ + $(SDKDIR)/libvamp-sdk.la + +HOSTSDK_LA = \ + $(SDKDIR)/libvamp-hostsdk.la + +HOST_HEADERS = \ + $(HOSTDIR)/system.h + +HOST_OBJECTS = \ + $(HOSTDIR)/vamp-simple-host.o + +HOST_TARGET = \ + $(HOSTDIR)/vamp-simple-host + +#Primary Make rules +# + +sdk: sdkstatic $(SDK_DYNAMIC) $(HOSTSDK_DYNAMIC) + +sdkstatic: + $(SDK_STATIC) $(HOSTSDK_STATIC) + $(RANLIB) $(SDK_STATIC) + $(RANLIB) $(HOSTSDK_STATIC) + +plugins: $(PLUGIN_TARGET) + +host: $(HOST_TARGET) + +all: sdk plugins host test + +#This is where all the stuff gets built +# + +$(SDK_STATIC): $(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS) + $(AR) r $@ $(SDK_OBJECTS) + +$(HOSTSDK_STATIC): $(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS) + $(AR) r $@ $(HOSTSDK_OBJECTS) + +$(SDK_DYNAMIC): $(SDK_OBJECTS) $(API_HEADERS) $(SDK_HEADERS) + $(CXX) $(LDFLAGS) $(SDK_DYNAMIC_LDFLAGS) -o $@ $(SDK_OBJECTS) + +$(HOSTSDK_DYNAMIC): $(HOSTSDK_OBJECTS) $(API_HEADERS) $(HOSTSDK_HEADERS) $(HOSTEXT_HEADERS) + $(CXX) $(LDFLAGS) $(HOSTSDK_DYNAMIC_LDFLAGS) -o $@ $(HOSTSDK_OBJECTS) + +$(PLUGIN_TARGET): $(PLUGIN_OBJECTS) $(SDK_STATIC) $(PLUGIN_HEADERS) + $(CXX) $(LDFLAGS) $(PLUGIN_LDFLAGS) -o $@ $(PLUGIN_OBJECTS) $(PLUGIN_LIBS) $(PYTHON_LINK_AS_SHARED) + +$(HOST_TARGET): $(HOST_OBJECTS) $(HOSTSDK_STATIC) $(HOST_HEADERS) + $(CXX) $(LDFLAGS) $(HOST_LDFLAGS) -o $@ $(HOST_OBJECTS) $(HOST_LIBS) + +test: plugins host + VAMP_PATH=$(EXAMPLEDIR) $(HOST_TARGET) -l + +_clean: + rm -f $(SDK_OBJECTS) $(HOSTSDK_OBJECTS) $(PLUGIN_OBJECTS) $(HOST_OBJECTS) + +cleanplug : + rm -f $(PLUGIN_OBJECTS) + rm -f $(EXAMPLEDIR)/$(PLUGIN_NAME)$(PLUGIN_EXT) +# rm -f $(LIBRARY_PREFIX)/$(INSTALL_PLUGIN)/$(PLUGIN_NAME)$(PLUGIN_EXT) + + +distclean: clean + rm -f $(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC) $(PLUGIN_TARGET) $(HOST_TARGET) *~ */*~ + +_install: $(SDK_STATIC) $(SDK_DYNAMIC) $(HOSTSDK_STATIC) $(HOSTSDK_DYNAMIC) $(PLUGIN_TARGET) $(HOST_TARGET) + mkdir -p $(DESTDIR)$(INSTALL_API_HEADERS) + mkdir -p $(DESTDIR)$(INSTALL_SDK_HEADERS) + mkdir -p $(DESTDIR)$(INSTALL_HOSTEXT_HEADERS) + mkdir -p $(DESTDIR)$(INSTALL_SDK_LIBS) + mkdir -p $(DESTDIR)$(INSTALL_PKGCONFIG) + cp $(API_HEADERS) $(DESTDIR)$(INSTALL_API_HEADERS) + cp $(SDK_HEADERS) $(DESTDIR)$(INSTALL_SDK_HEADERS) + cp $(HOSTSDK_HEADERS) $(DESTDIR)$(INSTALL_SDK_HEADERS) + cp $(HOSTEXT_HEADERS) $(DESTDIR)$(INSTALL_HOSTEXT_HEADERS) + cp $(SDK_STATIC) $(DESTDIR)$(INSTALL_SDK_LIBS) + cp $(HOSTSDK_STATIC) $(DESTDIR)$(INSTALL_SDK_LIBS) + cp $(SDK_DYNAMIC) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LIBNAME) + cp $(HOSTSDK_DYNAMIC) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LIBNAME) + rm -f $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LINK_ABI) + ln -s $(INSTALL_SDK_LIBNAME) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LINK_ABI) + rm -f $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LINK_ABI) + ln -s $(INSTALL_HOSTSDK_LIBNAME) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LINK_ABI) + rm -f $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LINK_DEV) + ln -s $(INSTALL_SDK_LIBNAME) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LINK_DEV) + rm -f $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LINK_DEV) + ln -s $(INSTALL_HOSTSDK_LIBNAME) $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LINK_DEV) + sed "s,%PREFIX%,$(INSTALL_PREFIX)," $(APIDIR)/vamp.pc.in \ + > $(DESTDIR)$(INSTALL_PKGCONFIG)/vamp.pc + sed "s,%PREFIX%,$(INSTALL_PREFIX)," $(SDKDIR)/vamp-sdk.pc.in \ + > $(DESTDIR)$(INSTALL_PKGCONFIG)/vamp-sdk.pc + sed "s,%PREFIX%,$(INSTALL_PREFIX)," $(SDKDIR)/vamp-hostsdk.pc.in \ + > $(DESTDIR)$(INSTALL_PKGCONFIG)/vamp-hostsdk.pc + sed -e "s,%LIBNAME%,$(INSTALL_SDK_LIBNAME),g" \ + -e "s,%LINK_ABI%,$(INSTALL_SDK_LINK_ABI),g" \ + -e "s,%LINK_DEV%,$(INSTALL_SDK_LINK_DEV),g" \ + -e "s,%STATIC%,$(INSTALL_SDK_STATIC),g" \ + -e "s,%LIBS%,$(INSTALL_SDK_LIBS),g" $(SDK_LA).in \ + > $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_SDK_LA) + sed -e "s,%LIBNAME%,$(INSTALL_HOSTSDK_LIBNAME),g" \ + -e "s,%LINK_ABI%,$(INSTALL_HOSTSDK_LINK_ABI),g" \ + -e "s,%LINK_DEV%,$(INSTALL_HOSTSDK_LINK_DEV),g" \ + -e "s,%STATIC%,$(INSTALL_HOSTSDK_STATIC),g" \ + -e "s,%LIBS%,$(INSTALL_SDK_LIBS),g" $(HOSTSDK_LA).in \ + > $(DESTDIR)$(INSTALL_SDK_LIBS)/$(INSTALL_HOSTSDK_LA) + +installplug: + mkdir -p $(INSTALL_PLUGIN) + rm -f $(INSTALL_PLUGIN)/$(PLUGIN_NAME)$(PLUGIN_EXT) + cp $(PLUGIN_NAME)$(PLUGIN_EXT) $(INSTALL_PLUGIN)/$(PLUGIN_NAME)$(PLUGIN_EXT) +# cp $(PLUGIN_DESCRIPTORS) $(INSTALL_PLUGIN) + +installplugin: installplug
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyPlugScanner.cpp Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,255 @@ +/** + * This VAMP plugin is a wrapper for Python Scripts. (VamPy) + * Centre for Digital Music, Queen Mary, University of London. + * Copyright 2008, George Fazekas. + +*/ + + +#include "/usr/include/python/Python.h" +#include "PyPlugScanner.h" + +//#include <fstream> +//#include <cctype> + +#ifdef _WIN32 +#include <windows.h> +#include <tchar.h> +#define pathsep ("\\") +#else +#include <dirent.h> +#include <dlfcn.h> +#define pathsep ("/") +#endif +#define joinPath(a,b) ( (a)+pathsep+(b) ) + +using std::string; +using std::vector; +using std::cerr; +using std::endl; + +PyPlugScanner::PyPlugScanner() +{ + +} + +PyPlugScanner *PyPlugScanner::m_instance = NULL; +bool PyPlugScanner::m_hasInstance = false; + +PyPlugScanner* +PyPlugScanner::getInstance() +{ + if (!m_hasInstance) { + m_instance = new PyPlugScanner(); + m_hasInstance = true; + } + return m_instance; +} + +void +PyPlugScanner::setPath(vector<string> path) +{ + m_path=path; +} + +// TODO: This should return all scripts for all valid paths +// Validate python classes here? +// For now, we assume that each script on the path has one valid class +vector<string> +PyPlugScanner::getPyPlugs() +{ + //foreach m_path listFiles then return vector<pyPlugs> + //format: FullPathString/FileName.py:ClassName + + vector<string> pyPlugs; + string pluginKey; + PyObject *pyClassInstance; + + for (size_t i = 0; i < m_path.size(); ++i) { + + vector<string> files = listFiles(m_path[i],"py"); + + for (vector<string>::iterator fi = files.begin(); + fi != files.end(); ++fi) { + string script = *fi; + if (!script.empty()) { + string classname=script.substr(0,script.rfind('.')); + pluginKey=joinPath(m_path[i],script)+":"+classname; + pyClassInstance = getScriptInstance(m_path[i],classname); + if (pyClassInstance == NULL) + cerr << "Warning: Syntax error in VamPy plugin: " + << classname << ". Avoiding plugin." << endl; + else { + pyPlugs.push_back(pluginKey); + m_pyInstances.push_back(pyClassInstance); + } + //pyPlugs.push_back(pluginKey); + } + } + } + +return pyPlugs; + +} + + +//For now return one class instance found in each script +vector<PyObject*> +PyPlugScanner::getPyInstances() +{ +return m_pyInstances; + +} + + +//Validate +//This should not be called more than once! +PyObject* +PyPlugScanner::getScriptInstance(string path, string classname) +{ + + //Add plugin path to active Python Path + string pyCmd = "import sys\nsys.path.append('" + path + "')\n"; + PyRun_SimpleString(pyCmd.c_str()); + + //Assign an object to the source code + PyObject *pySource = PyString_FromString(classname.c_str()); + + //Import it as a module into the py interpreter + PyObject *pyModule = PyImport_Import(pySource); + PyObject* pyError = PyErr_Occurred(); + if (! pyError == 0) { + cerr << "ERROR: error importing source: " << classname << endl; + PyErr_Print(); + Py_DECREF(pySource); + Py_CLEAR(pyModule); // safer if pyModule==NULL + return NULL; + } + Py_DECREF(pySource); + + //Read the namespace of the module into a dictionary object (borrowed reference) + PyObject *pyDict = PyModule_GetDict(pyModule); + Py_DECREF(pyModule); + + //Get the PluginClass from the module (borrowed reference) + PyObject *pyClass = PyDict_GetItemString(pyDict, classname.c_str()); + + //Check if class is present and a callable method is implemented + if (pyClass && PyCallable_Check(pyClass)) { + + //Create an instance + PyObject *pyInstance = PyObject_CallObject(pyClass, NULL); + //cerr << "__(getInstance) PyPlugin Class: " << m_class << " successfully created.__" << endl; + return pyInstance; + } else return NULL; +} + + + +// Return a list of files in dir with given extension +// Code taken from hostext/PluginLoader.cpp +vector<string> +PyPlugScanner::listFiles(string dir, string extension) +{ + vector<string> files; + +#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 + + size_t extlen = extension.length(); + 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_type != DT_UNKNOWN)) continue; + + if (!e->d_name) continue; + + size_t len = strlen(e->d_name); + if (len < extlen + 2 || + e->d_name + len - extlen - 1 != "." + extension) { + continue; + } + //cerr << "pyscripts: " << e->d_name << endl; + files.push_back(e->d_name); + } + + closedir(d); +#endif + + return files; +} + +//Return correct plugin directories as per platform +//Code taken from vamp-sdk/PluginHostAdapter.cpp +std::vector<std::string> +PyPlugScanner::getAllValidPath() +{ + std::vector<std::string> path; + std::string envPath; + + char *cpath = getenv("VAMP_PATH"); + if (cpath) envPath = cpath; + +#ifdef _WIN32 +#define PATH_SEPARATOR ';' +#define DEFAULT_VAMP_PATH "%ProgramFiles%\\Vamp Plugins" +#else +#define PATH_SEPARATOR ':' +#ifdef __APPLE__ +#define DEFAULT_VAMP_PATH "$HOME/Library/Audio/Plug-Ins/Vamp:/Library/Audio/Plug-Ins/Vamp" +#else +#define DEFAULT_VAMP_PATH "$HOME/vamp:$HOME/.vamp:/usr/local/lib/vamp:/usr/lib/vamp" +#endif +#endif + + if (envPath == "") { + envPath = DEFAULT_VAMP_PATH; + char *chome = getenv("HOME"); + if (chome) { + std::string home(chome); + std::string::size_type f; + while ((f = envPath.find("$HOME")) != std::string::npos && + f < envPath.length()) { + envPath.replace(f, 5, home); + } + } +#ifdef _WIN32 + char *cpfiles = getenv("ProgramFiles"); + if (!cpfiles) cpfiles = "C:\\Program Files"; + std::string pfiles(cpfiles); + std::string::size_type f; + while ((f = envPath.find("%ProgramFiles%")) != std::string::npos && + f < envPath.length()) { + envPath.replace(f, 14, pfiles); + } +#endif + } + + std::string::size_type index = 0, newindex = 0; + + while ((newindex = envPath.find(PATH_SEPARATOR, index)) < envPath.size()) { + path.push_back(envPath.substr(index, newindex - index)); + index = newindex + 1; + } + + path.push_back(envPath.substr(index)); + + return path; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyPlugScanner.h Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,73 @@ +/* + Vamp + + An API for audio analysis and feature extraction plugins. + + Centre for Digital Music, Queen Mary, University of London. + Copyright 2006-2007 Chris Cannam and QMUL. + + 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. +*/ + + +/* + Objective: We want to find available pyVamp plugins here + Future: We may have multiple plugins per script +*/ + +#ifndef _VAMP_PYPLUG_SCANNER_H_ +#define _VAMP_PYPLUG_SCANNER_H_ + +#include "/usr/include/python/Python.h" +#include <iostream> +#include <vector> +#include <string> +#include <map> +//#include <fstream> + +class PyPlugScanner +{ +public: + ~PyPlugScanner() { m_hasInstance = false; } + static PyPlugScanner *getInstance(); + std::vector<std::string> getPyPlugs(); + std::vector<PyObject*> getPyInstances(); + void setPath(std::vector<std::string> path); + std::vector<std::string> getAllValidPath(); + +protected: + PyPlugScanner(); + PyObject *getScriptInstance(std::string path, std::string classname); + std::vector<std::string> listFiles(std::string dir, std::string ext); + static bool m_hasInstance; + static PyPlugScanner *m_instance; + std::string m_dir; + std::vector<std::string> m_path; + std::vector<PyObject*> m_pyInstances; +}; + +#endif + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyPlugin.cpp Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,985 @@ +/* + 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. +*/ + + + +/** + * This VAMP plugin is a wrapper for Python Scripts. (vampy) + * Centre for Digital Music, Queen Mary, University of London. + * Copyright 2008, George Fazekas. + +TODO: needs more complete error checking + needs correct implementation of Python threading + more efficient data conversion using the buffering interface or ctypes + VAMP programs not implemented + support multiple plugins per script in scanner + ensure proper cleanup, host do a good job though + +*/ + +//#include "Python.h" +#include "/usr/include/python/Python.h" +#include "PyPlugin.h" + +#ifdef _WIN32 +#define pathsep ('\\') +#else +#define pathsep ('/') +#endif + +#define _DEBUG + +using std::string; +using std::vector; +using std::cerr; +using std::endl; +using std::map; + +extern volatile bool mutex; + +// Maps to associate strings with enum values +static std::map<std::string, eOutDescriptors> outKeys; +static std::map<std::string, eSampleTypes> sampleKeys; +static std::map<std::string, eFeatureFields> ffKeys; +static std::map<std::string, p::eParmDescriptors> parmKeys; + +void initMaps() +{ + outKeys["identifier"] = identifier; + outKeys["name"] = name; + outKeys["description"] = description; + outKeys["unit"] = unit; + outKeys["hasFixedBinCount"] = hasFixedBinCount; + outKeys["binCount"] = binCount; + outKeys["binNames"] = binNames; + outKeys["hasKnownExtents"] = hasKnownExtents; + outKeys["minValue"] = minValue; + outKeys["maxValue"] = maxValue; + outKeys["isQuantized"] = isQuantized; + outKeys["quantizeStep"] = quantizeStep; + outKeys["sampleType"] = sampleType; + outKeys["sampleRate"] = sampleRate; + + sampleKeys["OneSamplePerStep"] = OneSamplePerStep; + sampleKeys["FixedSampleRate"] = FixedSampleRate; + sampleKeys["VariableSampleRate"] = VariableSampleRate; + + ffKeys["hasTimestamp"] = hasTimestamp; + ffKeys["timeStamp"] = timeStamp; + ffKeys["values"] = values; + ffKeys["label"] = label; + + parmKeys["identifier"] = p::identifier; + parmKeys["name"] = p::name; + parmKeys["description"] = p::description; + parmKeys["unit"] = p::unit; + parmKeys["minValue"] = p::minValue; + parmKeys["maxValue"] = p::maxValue; + parmKeys["defaultValue"] = p::defaultValue; + parmKeys["isQuantized"] = p::isQuantized; + +} + + +//missing API helper: convert Python list to C++ vector of strings +//TODO: these could be templates if we need more of this kind +std::vector<std::string> PyList_To_StringVector (PyObject *inputList) { + + std::vector<std::string> Output; + std::string ListElement; + PyObject *pyString = NULL; + + if (!PyList_Check(inputList)) return Output; + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { + //Get next list item (Borrowed Reference) + pyString = PyList_GET_ITEM(inputList,i); + ListElement = (string) PyString_AsString(PyObject_Str(pyString)); + Output.push_back(ListElement); + } + return Output; +} + +//missing API helper: convert Python list to C++ vector of floats +std::vector<float> PyList_As_FloatVector (PyObject *inputList) { + + std::vector<float> Output; + float ListElement; + PyObject *pyFloat = NULL; + + if (!PyList_Check(inputList)) return Output; + + for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { + //Get next list item (Borrowed Reference) + pyFloat = PyList_GET_ITEM(inputList,k); + ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); + Output.push_back(ListElement); + } + + return Output; +} + +/* TODO: find out why this produces error, also + do sg more clever about handling RealTime +Vamp::RealTime +PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) { +Vamp::RealTime result = +Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate); +return result; +} +*/ + + +PyPlugin::PyPlugin(std::string pluginKey,float inputSampleRate, PyObject *pyInstance) : + Plugin(inputSampleRate), + m_pyInstance(pyInstance), + m_stepSize(0), + m_previousSample(0.0f), + m_plugin(pluginKey), + m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), + m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))) +{ + +/*TODO: this is a nasty way of ensuring the process is +finished before we create a new instance accessing the Python/C API. +The Python/C API is not fully thread safe. +Calling into a python class while the process is doing heavy +computation may cause segmentation fault. +Manipulating the GIL and thread states only gave me a grief so far.*/ + + if (mutex) { + cerr << "PyPlugin::PyPlugin:" << m_class + << " Warning: Waiting for clear signal from parallel process." << endl; + while (mutex) { } + } +} + +PyPlugin::~PyPlugin() +{ + cerr << "PyPlugin::PyPlugin:" << m_class + << " Instance deleted." << endl; + //for safety only. has been cleared after process. + mutex = false; +} + + +string +PyPlugin::getIdentifier() const +{ + char method[]="getIdentifier"; + cerr << "[call] " << method << endl; + string rString="VampPy-x"; + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method + PyObject *pyString = + PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rString; + } + + rString=PyString_AsString(pyString); + Py_CLEAR(pyString); + } + cerr << "Warning: Plugin must return a unique identifier." << endl; + return rString; +} + + +string +PyPlugin::getName() const +{ + + char method[]="getName"; + cerr << "[call] " << method << endl; + string rString="VamPy Plugin (Noname)"; + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method + PyObject *pyString = + PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rString; + } + + rString=PyString_AsString(pyString); + Py_CLEAR(pyString); + } + return rString; +} + +string +PyPlugin::getDescription() const +{ + char method[]="getDescription"; + cerr << "[call] " << method << endl; + string rString="Not given. (Hint: Implement getDescription method.)"; + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method + PyObject *pyString = + PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rString; + } + + rString=PyString_AsString(pyString); + Py_CLEAR(pyString); + } + return rString; +} + +string +PyPlugin::getMaker() const +{ + char method[]="getMaker"; + cerr << "[call] " << method << endl; + string rString="Generic VamPy Plugin."; + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method + PyObject *pyString = + PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rString; + } + + rString=PyString_AsString(pyString); + Py_CLEAR(pyString); + } + return rString; +} + +int +PyPlugin::getPluginVersion() const +{ + return 2; +} + +string +PyPlugin::getCopyright() const +{ + char method[]="getCopyright"; + cerr << "[call] " << method << endl; + string rString="BSD License"; + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method + PyObject *pyString = + PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rString; + } + + + rString=PyString_AsString(pyString); + Py_CLEAR(pyString); + } + return rString; +} + + +bool +PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) +{ + if (channels < getMinChannelCount() || + channels > getMaxChannelCount()) return false; + + m_stepSize = std::min(stepSize, blockSize); + char method[]="initialise"; + cerr << "[call] " << method << endl; + + //Check if the method is implemented in Python else return false + if (PyObject_HasAttrString(m_pyInstance,method)) { + + PyObject *pyMethod = PyString_FromString(method); + PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels); + PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize); + PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize); + PyObject *pyInputSampleRate = PyFloat_FromDouble((double)m_inputSampleRate); + + //Call the method + PyObject *pyBool = + PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL); + + Py_DECREF(pyMethod); + Py_DECREF(pyChannels); + Py_DECREF(pyStepSize); + Py_DECREF(pyBlockSize); + Py_DECREF(pyInputSampleRate); + + //Check return value + if (!PyBool_Check(pyBool)) { + Py_CLEAR(pyBool); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected Bool return value." << endl; + return false; + } + + if (pyBool == Py_True) { + Py_CLEAR(pyBool); + return true; + } else { + Py_CLEAR(pyBool); + return false;} + } + return true; +} + +void +PyPlugin::reset() +{ + m_previousSample = 0.0f; +} + +PyPlugin::InputDomain PyPlugin::getInputDomain() const +{ + char method[]="getInputDomain"; + cerr << "[call] " << method << endl; + PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain + + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyString_Check(pyString)) { + Py_CLEAR(pyString); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rValue; + } + + string domain = (string) PyString_AsString(pyString); + if (domain == "FrequencyDomain") rValue = FrequencyDomain; + Py_CLEAR(pyString); + } + return rValue; +} + +size_t PyPlugin::getPreferredBlockSize() const +{ + char method[]="getPreferredBlockSize"; + cerr << "[call] " << method << endl; + size_t rValue=0; //not set by default + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyInt_Check(pyInt)) { + Py_CLEAR(pyInt); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected Integer return value." << endl; + return rValue; + } + + rValue=(size_t)PyInt_AS_LONG(pyInt); + Py_CLEAR(pyInt); + } + return rValue; +} + +//size_t PyPlugin::getPreferredStepSize() const { return 0; } +size_t PyPlugin::getPreferredStepSize() const +{ + char method[]="getPreferredStepSize"; + cerr << "[call] " << method << endl; + size_t rValue=0; //not set by default + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyInt_Check(pyInt)) { + Py_CLEAR(pyInt); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected Integer return value." << endl; + return rValue; + } + + rValue=(size_t)PyInt_AS_LONG(pyInt); + Py_CLEAR(pyInt); + } + return rValue; +} + +size_t PyPlugin::getMinChannelCount() const +{ + char method[]="getMinChannelCount"; + cerr << "[call] " << method << endl; + size_t rValue=1; //default value + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyInt_Check(pyInt)) { + Py_CLEAR(pyInt); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rValue; + } + + rValue=(size_t)PyInt_AS_LONG(pyInt); + Py_CLEAR(pyInt); + } + return rValue; +} + +size_t PyPlugin::getMaxChannelCount() const +{ + char method[]="getMaxChannelCount"; + cerr << "[call] " << method << endl; + size_t rValue=1; //default value + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); + + //Check return value + if (!PyInt_Check(pyInt)) { + Py_CLEAR(pyInt); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected String return value." << endl; + return rValue; + } + + rValue=(size_t)PyInt_AS_LONG(pyInt); + Py_CLEAR(pyInt); + } + return rValue; +} + + +PyPlugin::OutputList +PyPlugin::getOutputDescriptors() const +{ + //PyEval_AcquireThread(newThreadState); + OutputList list; + OutputDescriptor od; + char method[]="getOutputDescriptors"; + cerr << "[call] " << method << endl; + + //Check if the method is implemented in Python + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method: must return list object (new reference) + PyObject *pyList = + PyObject_CallMethod(m_pyInstance,method, NULL); + + //Check return type + if (! PyList_Check(pyList) ) { + Py_CLEAR(pyList); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected List return type." << endl; + return list; + } + + //These will all be borrowed references (no need to DECREF) + PyObject *pyDict, *pyKey, *pyValue; + + //Parse Output List + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + + //Get i-th VAMP output descriptor (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyList,i); + + //We only care about dictionaries holding output descriptors + if ( !PyDict_Check(pyDict) ) continue; + + Py_ssize_t pyPos = NULL; + initMaps(); + + //Python Sequence Iterator + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + switch (outKeys[PyString_AsString(pyKey)]) + { + case not_found : + cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; + break; + case identifier: + od.identifier = PyString_AsString(pyValue); + break; + case name: + od.name = PyString_AsString(pyValue); + break; + case description: + od.description = PyString_AsString(pyValue); + break; + case unit: + od.unit = PyString_AsString(pyValue); + break; + case hasFixedBinCount: + od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue); + break; + case binCount: + od.binCount = (size_t) PyInt_AS_LONG(pyValue); + break; + case binNames: + od.binNames = PyList_To_StringVector(pyValue); + break; + case hasKnownExtents: + od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue); + break; + case minValue: + od.minValue = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case maxValue: + od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case isQuantized: + od.isQuantized = (bool) PyInt_AS_LONG(pyValue); + break; + case quantizeStep: + od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case sampleType: + od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; + break; + case sampleRate: + od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); + break; + default : + cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; + } // switch + } // while dict keys + list.push_back(od); + } // for each memeber in outputlist + Py_CLEAR(pyList); + } // if method is implemented + //PyEval_ReleaseThread(newThreadState); + return list; +} + +PyPlugin::ParameterList +PyPlugin::getParameterDescriptors() const +{ + ParameterList list; + ParameterDescriptor pd; + char method[]="getParameterDescriptors"; + cerr << "[call] " << method << endl; + + //Check if the method is implemented in Python + if ( PyObject_HasAttrString(m_pyInstance,method) ) { + + //Call the method: must return list object (new reference) + PyObject *pyList = + PyObject_CallMethod(m_pyInstance,method, NULL); + + //Check return type + if (! PyList_Check(pyList) ) { + Py_CLEAR(pyList); + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected List return type." << endl; + return list; + } + + + //These will all be borrowed references (no need to DECREF) + PyObject *pyDict, *pyKey, *pyValue; + + //Parse Output List + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + + //Get i-th VAMP output descriptor (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyList,i); + + //We only care about dictionaries holding output descriptors + if ( !PyDict_Check(pyDict) ) continue; + + Py_ssize_t pyPos = NULL; + initMaps(); + + //Python Sequence Iterator + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + switch (parmKeys[PyString_AsString(pyKey)]) + { + case not_found : + cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; + break; + case p::identifier: + pd.identifier = PyString_AsString(pyValue); + break; + case p::name: + pd.name = PyString_AsString(pyValue); + break; + case p::description: + pd.description = PyString_AsString(pyValue); + break; + case p::unit: + pd.unit = PyString_AsString(pyValue); + break; + case p::minValue: + pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case p::maxValue: + pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case p::defaultValue: + pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue); + break; + case p::isQuantized: + pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); + break; + default : + cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; + } // switch + } // while dict keys + list.push_back(pd); + } // for each memeber in outputlist + Py_CLEAR(pyList); + } // if + return list; +} + +void PyPlugin::setParameter(std::string paramid, float newval) +{ + char method[]="setParameter"; + cerr << "[call] " << method << endl; + + //Check if the method is implemented in Python + if (PyObject_HasAttrString(m_pyInstance,method)) { + + PyObject *pyMethod = PyString_FromString(method); + PyObject *pyParamid = PyString_FromString(paramid.c_str()); + PyObject *pyNewval = PyFloat_FromDouble((double)newval); + + //Call the method + PyObject *pyBool = + PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL); + + //This only happens if there is a syntax error or so + if (pyBool == NULL) { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Error setting parameter: " << paramid << endl; + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + } + + Py_DECREF(pyMethod); + Py_DECREF(pyParamid); + Py_DECREF(pyNewval); + } +} + +float PyPlugin::getParameter(std::string paramid) const +{ + char method[]="getParameter"; + cerr << "[call] " << method << endl; + float rValue = 0.0f; + + //Check if the method is implemented in Python + if (PyObject_HasAttrString(m_pyInstance,method)) { + + PyObject *pyMethod = PyString_FromString(method); + PyObject *pyParamid = PyString_FromString(paramid.c_str()); + + //Call the method + PyObject *pyFloat = + PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL); + + //Check return type + if (! PyFloat_Check(pyFloat) ) { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected Float return type." << endl; + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + Py_CLEAR(pyFloat); + return rValue; + } + + rValue = (float) PyFloat_AS_DOUBLE(pyFloat); + + Py_DECREF(pyMethod); + Py_DECREF(pyParamid); + Py_DECREF(pyFloat); + } + + return rValue; +} + +#ifdef _DEBUG +static int proccounter = 0; +#endif + +PyPlugin::FeatureSet +PyPlugin::process(const float *const *inputBuffers, + Vamp::RealTime timestamp) +{ + if (m_stepSize == 0) { + cerr << "ERROR: PyPlugin::process: " + << "Plugin has not been initialised" << endl; + return FeatureSet(); + } + mutex = true; + static char method[]="process"; + +#ifdef _DEBUG + cerr << "[call] " << method << " frame:" << proccounter << endl; + proccounter++; + //cerr << "C: proc..." << proccounter << " " << endl; +#endif + + //proces::Check if the method is implemented in Python + if ( PyObject_HasAttrString(m_pyInstance,method) ) + { + + //Pack method name into Python Object + PyObject *pyMethod = PyString_FromString(method); + + //Declare new list object + PyObject *pyFloat, *pyList; + pyList = PyList_New((Py_ssize_t) m_stepSize); + + //Pack samples into a Python List Object + //pyFloat will always be new references, + //these will be discarded when the list is deallocated + for (size_t i = 0; i < m_stepSize; ++i) { + pyFloat=PyFloat_FromDouble((double) inputBuffers[0][i]); + PyList_SET_ITEM(pyList, (Py_ssize_t) i, pyFloat); + } + + //Call python process (returns new reference) + PyObject *pyOutputList = + PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyList,NULL); + + //Check return type + if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { + if (pyOutputList == NULL) { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Unexpected result." << endl; + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + } else { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected List return type." << endl; + } + Py_CLEAR(pyMethod); + Py_CLEAR(pyOutputList); + mutex = false; + return FeatureSet(); + } + + Py_DECREF(pyMethod); + // Py_DECREF(pyList); + // This appears to be tracked by the cyclic garbage collector + // hence decrefing produces GC error +#ifdef _DEBUG + cerr << "Process Returned Features" << endl; +#endif + // These will ALL be borrowed references + PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; + + FeatureSet returnFeatures; + + //Parse Output List for each element (FeatureSet) + for (Py_ssize_t i = 0; + i < PyList_GET_SIZE(pyOutputList); ++i) { + //cerr << "output (FeatureSet): " << i << endl; + + //Get i-th FeatureList (Borrowed Reference) + pyFeatureList = PyList_GET_ITEM(pyOutputList,i); + + //Parse FeatureList for each element (Feature) + for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { + //cerr << "element (FeatureList): " << j << endl; + + //Get j-th Feature (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyFeatureList,j); + + //We only care about dictionaries holding a Feature struct + if ( !PyDict_Check(pyDict) ) continue; + + Py_ssize_t pyPos = NULL; + bool emptyFeature = true; + Feature feature; + + //process::Python Sequence Iterator for dictionary + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + emptyFeature = false; + switch (ffKeys[PyString_AsString(pyKey)]) + { + case not_found : + cerr << "Unknown key in VAMP FeatureSet: " + << PyString_AsString(pyKey) << endl; + break; + case hasTimestamp: + feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); + break; + case timeStamp: + feature.timestamp = timestamp + + Vamp::RealTime::frame2RealTime + ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); + break; + case values: + feature.values = PyList_As_FloatVector(pyValue); + break; + case label: + feature.label = PyString_AsString(pyValue); + break; + default : + cerr << "Invalid key in VAMP FeatureSet: " + << PyString_AsString(pyKey) << endl; + } // switch + + } // while + if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; + else returnFeatures[i].push_back(feature); + + }// for j = FeatureList + + }//for i = FeatureSet + Py_CLEAR(pyOutputList); + mutex = false; + return returnFeatures; + + }//if (has_attribute) + mutex = false; + return FeatureSet(); +} + + + +PyPlugin::FeatureSet +PyPlugin::getRemainingFeatures() +{ + static char method[]="getRemainingFeatures"; + cerr << "[call] " << method << endl; + + //check if the method is implemented + mutex = true; + if ( ! PyObject_HasAttrString(m_pyInstance,method) ) { + return FeatureSet(); + } + + PyObject *pyMethod = PyString_FromString(method); + + PyObject *pyOutputList = + PyObject_CallMethod(m_pyInstance,method, NULL); + + //Check return type + if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { + if (pyOutputList == NULL) { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Unexpected result." << endl; + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + } else { + cerr << "ERROR: In Python plugin [" << m_class << "::" << method + << "] Expected List return type." << endl; + } + Py_CLEAR(pyMethod); + Py_CLEAR(pyOutputList); + mutex = false; + return FeatureSet(); + } + Py_DECREF(pyMethod); + + PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; + FeatureSet returnFeatures; + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { + + pyFeatureList = PyList_GET_ITEM(pyOutputList,i); + + for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { + + pyDict = PyList_GET_ITEM(pyFeatureList,j); + + if ( !PyDict_Check(pyDict) ) continue; + + Py_ssize_t pyPos = NULL; + bool emptyFeature = true; + Feature feature; + + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + emptyFeature = false; + switch (ffKeys[PyString_AsString(pyKey)]) + { + case not_found : + cerr << "Unknown key in VAMP FeatureSet: " + << PyString_AsString(pyKey) << endl; + break; + case hasTimestamp: + feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); + break; + // TODO: clarify what to do here + case timeStamp: + feature.timestamp = + Vamp::RealTime::frame2RealTime + ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); + break; + case values: + feature.values = PyList_As_FloatVector(pyValue); + break; + case label: + feature.label = PyString_AsString(pyValue); + break; + } // switch + } // while + if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; + else returnFeatures[i].push_back(feature); + }// for j + }//for i + Py_CLEAR(pyOutputList); + mutex = false; + return returnFeatures; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyPlugin.h Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,136 @@ +/* + 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. +*/ + + /** + * This plugin abstracts appropriate Python Scripts as a VAMP plugin. + */ + +#ifndef _PYTHON_WRAPPER_PLUGIN_H_ +#define _PYTHON_WRAPPER_PLUGIN_H_ + +#include "vamp-sdk/Plugin.h" +#include "/usr/include/python/Python.h" + +//fields in OutputDescriptor +enum eOutDescriptors { + not_found, + identifier, + name, + description, + unit, + hasFixedBinCount, + binCount, + binNames, + hasKnownExtents, + minValue, + maxValue, + isQuantized, + quantizeStep, + sampleType, + sampleRate, + endNode + }; + +namespace p { +enum eParmDescriptors { + not_found, + identifier, + name, + description, + unit, + minValue, + maxValue, + defaultValue, + isQuantized + }; +} + +enum eSampleTypes { + OneSamplePerStep, + FixedSampleRate, + VariableSampleRate + }; + +enum eFeatureFields { + unknown, + hasTimestamp, + timeStamp, + values, + label + }; + + +class PyPlugin : public Vamp::Plugin +{ +public: + PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyInstance); + virtual ~PyPlugin(); + + bool initialise(size_t channels, size_t stepSize, size_t blockSize); + void reset(); + //virtuals: + InputDomain getInputDomain() const; + size_t getPreferredBlockSize() const; + size_t getPreferredStepSize() const; + size_t getMinChannelCount() const; + size_t getMaxChannelCount() const; + + std::string getIdentifier() const; + std::string getName() const; + std::string getDescription() const; + std::string getMaker() const; + int getPluginVersion() const; + std::string getCopyright() const; + + OutputList getOutputDescriptors() const; + ParameterList getParameterDescriptors() const; + float getParameter(std::string paramid) const; + void setParameter(std::string paramid, float newval); + +FeatureSet process(const float *const *inputBuffers, + Vamp::RealTime timestamp); + + FeatureSet getRemainingFeatures(); + +protected: + PyObject *m_pyInstance; + size_t m_stepSize; + float m_previousSample; + std::string m_plugin; + std::string m_class; + std::string m_path; +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,69 @@ + + * VamPy is a VAMP plugin wrapper for Python Scripts. + +WHAT IS IT FOR? + + you can write VAMP plugins in Python which can do the same as the native C++ plugins + plus more if you're using advanced Python modules. + + you might get into VAMP development more easily this way. + you can use it for prototyping your plugin before writing in C++ + + +LIMITATIONS: + + this is really just a proof of concept. The implementation is not the most efficient. + only tested on MacOSX, but in theory should work on other platforms with small fixes + The Python C/API is not fully thread safe and correct threading is not yet implemented. + (This can cause problems resulting in nasty crashes.) + + Error checking is incomplete. + You better not make a mistake in your Python script, although in most cases you can see what + the problem is if you start the host (Sonic Visualiser) from the command line. + + +HOW DOES IT WORK: + + (1) Make sure you have Python installed. + (2) Compile the C++ source or ask me for a binary (MacOS). + (3) Copy the library in your Vamp plugin directory like any other VAMP plugins: + eg. /Library/Audio/Plug-Ins/VAMP + (4) Write your python plugins and copy them to the same place. + (5) Each plugin must contain a single class with the same name as your script file. + e.g. PyZeroCrossing.py -> calss PyZeroCrossing + -Scripts with syntax errors in them are ignored. + -Scripts not having the same class as the filename are ignored. + -Other unknown scripts are likely to cause a nasty crash. + (Don't put other python scripts in your VAMP directory.) + + +WHY PYTHON? + + Python is a general purpose high level scripting language. + It is interpreted, so you don't need to compile your plugins + It has very high level libraries. e.g. you can stream audio from your VamPy plugin (it works for me...) + Supports functional programming + + +COMPILING AND LINKING: + (1) include Python.h wherever it is on your machine + (2) this plugin needs to be linked against the Python binary: + + example on on MacOSX: + g++ -O2 -Wall -I. -fPIC -c -o pyvamp-main.o pyvamp-main.cpp + g++ -dynamiclib -o vamp-pyvamp-plugin.dylib ./PyPlugScanner.o ./PyPlugin.o ./pyvamp-main.o sdk/vamp-sdk/libvamp-sdk.a + -u _PyMac_Error /Library/Frameworks/Python.framework/Versions/2.5/Python + + OR: + + (3) There is a modified Makefile from the VAMP-SDK that compiles this plugin by default. + + +TODO: needs more complete error checking + needs correct implementation of Python threading + more efficient data conversion using the buffering interface or ctypes + VAMP 'programs' not implemented + support multiple classes per script in scanner + ensure proper cleanup, (host do a good job though) + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyvamp-main.cpp Tue Mar 11 19:47:34 2008 +0000 @@ -0,0 +1,198 @@ +/* -*- 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. +*/ + +/** + * This VAMP plugin is a wrapper for Python Scripts. (VamPy) + * Centre for Digital Music, Queen Mary, University of London. + * Copyright 2008, George Fazekas. + +TODO: needs more complete error checking + needs correct implementation of Python threading + more efficient data conversion using the buffering interface or ctypes + VAMP 'programs' not implemented + support multiple plugins per script in scanner + ensure proper cleanup, (host do a good job though) + +COMPILING AND LINKING: + (1) include Python.h wherever it is on your machine + (2) this plugin needs to be linked against the Python binary: + + example on on MacOSX: + g++ -O2 -Wall -I. -fPIC -c -o pyvamp-main.o pyvamp-main.cpp + g++ -dynamiclib -o vamp-pyvamp-plugin.dylib ./PyPlugScanner.o ./PyPlugin.o ./pyvamp-main.o sdk/vamp-sdk/libvamp-sdk.a + ... -u _PyMac_Error /Library/Frameworks/Python.framework/Versions/2.5/Python +*/ + +//#include "Python.h" +#include "/usr/include/python/Python.h" +#include "vamp/vamp.h" +#include "vamp-sdk/PluginAdapter.h" +#include "PyPlugScanner.h" +#include "PyPlugin.h" + +#ifdef _WIN32 +#define pathsep ('\\') +#include <windows.h> +#include <tchar.h> +#else +#define pathsep ('/') +#include <dirent.h> +#include <dlfcn.h> +#endif + +using std::cerr; +using std::endl; +using std::string; +using std::vector; + +volatile bool mutex = false; +static int adinstcount; + +class PyPluginAdapter : public Vamp::PluginAdapterBase +{ +public: + PyPluginAdapter(std::string pyPlugId, PyObject* pyInstance) : + PluginAdapterBase(), + m_plug(pyPlugId), + m_pyInstance(pyInstance) + { + cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; + adinstcount++; + m_instanceCount = 0; + } + + ~PyPluginAdapter() + { + } + +protected: + Vamp::Plugin *createPlugin(float inputSampleRate) { + + std::string pclass = m_plug.substr(m_plug.rfind(':')+1,m_plug.size()-1); + std::string ppath = m_plug.substr(0,m_plug.rfind(pathsep)); + PyPlugin *plugin = new PyPlugin(m_plug,inputSampleRate,m_pyInstance); + m_instanceCount++; + cerr << "PyPluginAdapter::createPlugin:" << pclass << " (instance: " << m_instanceCount << ")" << endl; + return plugin; + + } + + std::string m_plug; + bool m_haveInitialized; + PyObject *m_pyInstance; + int m_instanceCount; + +}; + + +static std::vector<PyPluginAdapter *> adapters; +static bool haveScannedPlugins = false; + +const VampPluginDescriptor +*vampGetPluginDescriptor(unsigned int version,unsigned int index) +{ + if (version < 1) return 0; + + int isPythonInitialized = Py_IsInitialized(); + //cerr << "# isPythonInitialized: " << isPythonInitialized << endl; + //cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; + + if (!haveScannedPlugins) { + + if (!isPythonInitialized) { + + string pythonPath = + (string) Py_GetExecPrefix() + pathsep + + (string) Py_GetProgramName(); + + void *pylib = 0; + + cerr << "Loading Python Interpreter at: " << pythonPath << endl; + //Preloading the binary allows the load of shared libs //dlopen("/Library/Frameworks/Python.framework/Versions/2.5/Python", RTLD_NOW|RTLD_GLOBAL); +#ifdef _WIN32 + //TODO: check how to do RTLD_NOW on Windows + pylib = LoadLibrary(pythonPath.c_str()); +#else + pylib = dlopen(pythonPath.c_str(), RTLD_NOW|RTLD_GLOBAL); +#endif + if (!pylib) cerr << "Warning: Could not preload Python." + << " Dynamic lodading in scripts will fail." << endl; + Py_Initialize(); + PyEval_InitThreads(); + } else { + //Py_InitializeEx(1); + } + + vector<string> pyPlugs; + vector<string> pyPath; + vector<PyObject *> pyInstances; + static PyPlugScanner *scanner; + + //Scanning Plugins + cerr << "Scanning PyPlugins" << endl; + scanner = PyPlugScanner::getInstance(); + pyPath=scanner->getAllValidPath(); + //add this as extra path for development + pyPath.push_back("/Users/Shared/Development/vamp-experiments"); + scanner->setPath(pyPath); + pyPlugs = scanner->getPyPlugs(); + cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl; + //TODO: this will support multiple classes per script + pyInstances = scanner->getPyInstances(); + cerr << "Found " << pyInstances.size() << " Instances ...OK" << endl; + + for (size_t i = 0; i < pyPlugs.size(); ++i) { + adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyInstances[i])); + } + haveScannedPlugins=true; + } + + cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl; + if (index<adapters.size()) { + const VampPluginDescriptor *tmp = adapters[index]->getDescriptor(); + return tmp; + } else return 0; + + +} + + + + + + + +