diff PyPlugin.h @ 37:27bab3a16c9a vampy2final

new branch Vampy2final
author fazekasgy
date Mon, 05 Oct 2009 11:28:00 +0000
parents
children 8b2eddf686da
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PyPlugin.h	Mon Oct 05 11:28:00 2009 +0000
@@ -0,0 +1,491 @@
+/*
+
+ * Vampy : This plugin is a wrapper around the Vamp plugin API.
+ * It allows for writing Vamp plugins in Python.
+
+ * Centre for Digital Music, Queen Mary University of London.
+ * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources 
+ * for licence information.)
+
+*/
+
+/*
+    Vamp
+
+    An API for audio analysis and feature extraction plugins.
+
+    Centre for Digital Music, Queen Mary, University of London.
+    Copyright 2006 Chris Cannam.
+  
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of the Centre for
+    Digital Music; Queen Mary, University of London; and Chris Cannam
+    shall not be used in advertising or otherwise to promote the sale,
+    use or other dealings in this Software without prior written
+    authorization.
+*/
+
+#ifndef _PYTHON_WRAPPER_PLUGIN_H_
+#define _PYTHON_WRAPPER_PLUGIN_H_
+
+#define _CLASS_METHOD_ m_class << "::" << method
+#define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: "
+#define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " "
+#define DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented. Returning default value."
+#define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True")
+
+#include <Python.h>
+#include "PyExtensionModule.h"
+#include "PyTypeInterface.h"
+#include "vamp-sdk/Plugin.h"
+#include "Mutex.h"
+
+using std::string;
+using std::cerr;
+using std::endl;
+
+enum eProcessType {
+	not_implemented,
+	legacyProcess,
+	numpyProcess,
+	numpy_bufferProcess,
+	numpy_arrayProcess
+	};
+
+class PyPlugin : public Vamp::Plugin
+{
+public:
+	PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount);
+	virtual ~PyPlugin();
+
+	bool initialise(size_t channels, size_t stepSize, size_t blockSize);
+	void reset();
+
+	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:
+	static Mutex m_pythonInterpreterMutex;
+	PyObject *m_pyClass;
+	PyObject *m_pyInstance;
+	int &m_instcount;
+	size_t m_stepSize;
+	size_t m_blockSize;
+	size_t m_channels;
+	std::string m_plugin;
+	std::string m_class;
+	std::string m_path;
+	eProcessType m_processType;
+	PyObject *m_pyProcess;
+	PyObject *m_pyProcessCallable;
+	mutable InputDomain m_inputDomain;
+	PyTypeInterface m_ti;
+	int m_vampyFlags;
+	bool m_quitOnErrorFlag;
+	bool m_debugFlag;
+	bool m_useRealTimeFlag;
+
+	void setProcessType();
+	
+	FeatureSet processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp);
+
+	bool getBooleanFlag(char flagName[],bool) const;
+	int getBinaryFlags(char flagName[], eVampyFlags) const;
+	void typeErrorHandler(char *method) const;
+
+	/// simple 'void return' call with no args
+	void genericMethodCall(char *method) const
+	{
+		if (m_debugFlag) cerr << DEBUG_NAME << endl;
+		if ( PyObject_HasAttrString(m_pyInstance,method) ) 
+		{
+			PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL);
+			if (!pyValue) {
+				cerr << PLUGIN_ERROR << "Failed to call method." << endl;
+				if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+			}
+		}
+	}
+
+	/// 'no arg with default return value' call
+	template<typename RET> 
+	RET &genericMethodCall(char *method, RET &rValue) const
+	{
+		if (m_debugFlag) cerr << DEBUG_NAME << endl;
+		if ( PyObject_HasAttrString(m_pyInstance,method) ) 
+		{
+			PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL);
+			if (!pyValue) {
+				cerr << PLUGIN_ERROR << "Failed to call method." << endl;
+				if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+				return rValue;
+			}
+
+            /// convert the returned value
+			m_ti.PyValue_To_rValue(pyValue,rValue);
+			if (!m_ti.error) {
+				Py_DECREF(pyValue);
+			} else {
+				Py_CLEAR(pyValue);
+				typeErrorHandler(method);
+			}
+			return rValue;
+		}
+		if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
+		return rValue;
+	}
+
+	/// unary call
+	template<typename RET,typename A1>
+	RET genericMethodCallArgs(char *method, A1 arg1) const
+	{
+		RET rValue = RET();
+		if (m_debugFlag) cerr << DEBUG_NAME << endl;
+		if (!PyObject_HasAttrString(m_pyInstance,method)) {
+			if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
+			return rValue;
+		}
+		
+		/// prepare arguments for fast method call
+		PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
+		PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod);
+		PyObject* pyArgs = PyTuple_New(1);
+		if (!(pyArgs && pyCallable && pyMethod)) {
+			cerr << PLUGIN_ERROR << "Failed to prepare argument for calling method." << endl;
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
+		if (m_ti.error) {
+			cerr << PLUGIN_ERROR << "Failed to convert argument for calling method." << endl;
+			typeErrorHandler(method);
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		PyTuple_SET_ITEM(pyArgs, 0, pyArg1);
+		Py_INCREF(pyArg1);	
+		
+        /// call the method
+		PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL);
+		if (!pyValue) 
+		{
+			cerr << PLUGIN_ERROR << "Failed to call method." << endl;
+			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+			
+		Py_DECREF(pyMethod);
+		Py_DECREF(pyCallable);
+		Py_DECREF(pyArg1);
+		Py_DECREF(pyArgs);    
+		
+		/// convert the returned value
+		m_ti.PyValue_To_rValue(pyValue,rValue);
+		if (!m_ti.error) {
+			Py_DECREF(pyValue);
+		} else {
+			Py_CLEAR(pyValue);
+			typeErrorHandler(method);
+		}
+		return rValue;
+	}
+
+	/// binary call
+	template<typename RET,typename A1,typename A2>
+	RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2) const
+	{
+		RET rValue = RET();
+		if (m_debugFlag) cerr << DEBUG_NAME << endl;
+		if (!PyObject_HasAttrString(m_pyInstance,method)) {
+			if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
+			return rValue;
+		}
+		
+		/// prepare arguments for fast method call
+		PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
+		PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod);
+		PyObject* pyArgs = PyTuple_New(2);
+		if (!(pyArgs && pyCallable && pyMethod)) {
+			cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl;
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
+		PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2);
+		if (m_ti.error) {
+			cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl;
+			typeErrorHandler(method);
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArg2);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		PyTuple_SET_ITEM(pyArgs, 0, pyArg1);
+		Py_INCREF(pyArg1);	
+		PyTuple_SET_ITEM(pyArgs, 1, pyArg2);
+		Py_INCREF(pyArg2);
+
+		// calls the method
+		PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL);
+		if (!pyValue) 
+		{
+			cerr << PLUGIN_ERROR << "Failed to call method." << endl;
+			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArg2);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+			
+		Py_DECREF(pyMethod);
+		Py_DECREF(pyCallable);
+		Py_DECREF(pyArg1);
+		Py_DECREF(pyArg2);
+		Py_DECREF(pyArgs);    
+		
+		/// convert the returned value
+		m_ti.PyValue_To_rValue(pyValue,rValue);
+		if (!m_ti.error) {
+			Py_DECREF(pyValue);
+		} else {
+			Py_CLEAR(pyValue);
+			typeErrorHandler(method);
+		}
+		return rValue;
+	}
+
+	/// trenary call
+	template<typename RET,typename A1,typename A2,typename A3>
+	RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3) const
+	{
+		RET rValue = RET();
+		if (m_debugFlag) cerr << DEBUG_NAME << endl;
+		if (!PyObject_HasAttrString(m_pyInstance,method)) {
+			if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
+			return rValue;
+		}
+		
+		/// prepare arguments for fast method call
+		PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
+		PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod);
+		PyObject* pyArgs = PyTuple_New(3);
+		if (!(pyArgs && pyCallable && pyMethod)) {
+			cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl;
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
+		PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2);
+		PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3);
+		if (m_ti.error) {
+			cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl;
+			typeErrorHandler(method);
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArg2);
+			Py_CLEAR(pyArg3);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+		
+		/// Optimization: Pack args in a tuple to avoid va_list parsing.
+		PyTuple_SET_ITEM(pyArgs, 0, pyArg1);
+		Py_INCREF(pyArg1);	
+		PyTuple_SET_ITEM(pyArgs, 1, pyArg2);
+		Py_INCREF(pyArg2);
+		PyTuple_SET_ITEM(pyArgs, 2, pyArg3);
+		Py_INCREF(pyArg3);
+
+		// PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL);
+		/// fast method call
+		PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL);
+		if (!pyValue) 
+		{
+			cerr << PLUGIN_ERROR << "Failed to call method." << endl;
+			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+			Py_CLEAR(pyMethod);
+			Py_CLEAR(pyCallable);
+			Py_CLEAR(pyArg1);
+			Py_CLEAR(pyArg2);
+			Py_CLEAR(pyArg3);
+			Py_CLEAR(pyArgs);
+			return rValue;
+		}
+			
+		Py_DECREF(pyMethod);
+		Py_DECREF(pyCallable);
+		Py_DECREF(pyArg1);
+		Py_DECREF(pyArg2);
+		Py_DECREF(pyArg3);    
+		Py_DECREF(pyArgs);    
+		
+		/// convert the returned value
+		m_ti.PyValue_To_rValue(pyValue,rValue);
+		if (!m_ti.error) {
+			Py_DECREF(pyValue);
+		} else {
+			Py_CLEAR(pyValue);
+			typeErrorHandler(method);
+		}
+		return rValue;
+	}
+
+};
+
+/// optimised process call
+inline PyPlugin::FeatureSet
+PyPlugin::processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
+{
+	
+	/// Optimizations: 1) we avoid ...ObjArg functions since we know
+	/// the number of arguments, and we don't like va_list parsing 
+	/// in the process. 2) Also: we're supposed to incref args, 
+	/// but instead, we let the arguments tuple steal the references
+	/// and decref them when it is deallocated.
+	/// 3) all conversions are now using the fast sequence protocol
+	/// (indexing the underlying object array).
+	
+	FeatureSet rFeatureSet;
+	PyObject *pyChannelList = NULL;
+
+	if (m_processType == numpy_bufferProcess) {
+		pyChannelList = m_ti.InputBuffers_As_SharedMemoryList(inputBuffers,m_channels,m_blockSize);
+	} 
+
+	if (m_processType == legacyProcess) {
+		pyChannelList = m_ti.InputBuffers_As_PythonLists(inputBuffers,m_channels,m_blockSize,m_inputDomain);
+	}
+
+#ifdef HAVE_NUMPY
+	if (m_processType == numpy_arrayProcess) {
+		pyChannelList = m_ti.InputBuffers_As_NumpyArray(inputBuffers,m_channels,m_blockSize,m_inputDomain);
+	}
+#endif
+
+/// we don't expect these to fail unless out of memory (which is very unlikely on modern systems)
+#ifdef _DEBUG
+	if (!pyChannelList) {
+		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+		std::string method = PyString_AsString(m_pyProcess);
+		cerr << PLUGIN_ERROR << "Failed to create channel list." << endl;
+		return rFeatureSet;
+	}
+#endif		
+
+	PyObject *pyTimeStamp = NULL;
+		
+	if (m_useRealTimeFlag) {
+		//(1) pass TimeStamp as PyRealTime object
+		pyTimeStamp = PyRealTime_FromRealTime(timestamp);
+
+	} else {
+		//(2) pass TimeStamp as frame count (long Sample Count)
+		pyTimeStamp = PyLong_FromLong(Vamp::RealTime::realTime2Frame 
+		(timestamp, (unsigned int) m_inputSampleRate));
+	}
+
+
+#ifdef _DEBUG
+	if (!pyTimeStamp) {
+		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+		std::string method = PyString_AsString(m_pyProcess);
+		cerr << PLUGIN_ERROR << "Failed to create RealTime time stamp." << endl;
+		Py_DECREF(pyChannelList);
+		return rFeatureSet;
+	}
+#endif
+
+	/// Old method: Call python process (returns new reference)
+	/// PyObject *pyValue = PyObject_CallMethodObjArgs
+	/// (m_pyInstance,m_pyProcess,pyChannelList,pyTimeStamp,NULL);
+	
+	PyObject *pyArgs = PyTuple_New(2);
+	PyTuple_SET_ITEM(pyArgs, 0, pyChannelList); 
+	PyTuple_SET_ITEM(pyArgs, 1, pyTimeStamp); 
+
+	/// Call python process (returns new reference) {kwArgs = NULL}
+	PyObject *pyValue = PyObject_Call(m_pyProcessCallable,pyArgs,NULL);
+
+	if (!pyValue) {
+		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
+		std::string method = PyString_AsString(m_pyProcess);
+		cerr << PLUGIN_ERROR << "An error occurred while evaluating Python process." << endl;
+		Py_CLEAR(pyValue);
+		Py_CLEAR(pyArgs);
+		return rFeatureSet;
+	}
+        
+	rFeatureSet = m_ti.PyValue_To_FeatureSet(pyValue);
+	if (!m_ti.error) {
+		Py_DECREF(pyValue);
+		Py_DECREF(pyArgs);
+	} else {
+		typeErrorHandler(PyString_AsString(m_pyProcess));
+		Py_CLEAR(pyValue);
+		Py_CLEAR(pyArgs);
+	}
+	return rFeatureSet;
+}
+
+#endif