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;
+
+	
+}
+
+
+
+
+
+
+
+