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