Chris@66: /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
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: PyTypeInterface: Type safe conversion utilities between Python types 
fazekasgy@37: and basic C/C++ types and Vamp API types.
fazekasgy@37: */
fazekasgy@37: 
fazekasgy@37: #ifndef _PY_TYPE_INTERFACE_H_
fazekasgy@37: #define _PY_TYPE_INTERFACE_H_
fazekasgy@37: #include <Python.h>
fazekasgy@37: #ifdef HAVE_NUMPY
fazekasgy@37: #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
fazekasgy@37: #define NO_IMPORT_ARRAY
Chris@66: #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
fazekasgy@37: #include "numpy/arrayobject.h"
fazekasgy@37: #endif
fazekasgy@37: #include "PyExtensionModule.h"
fazekasgy@37: #include <vector>
fazekasgy@37: #include <queue>
fazekasgy@37: #include <string>
fazekasgy@37: #include <sstream>
fazekasgy@37: #include "vamp-sdk/Plugin.h"
fazekasgy@37: 
fazekasgy@37: using std::cerr;
fazekasgy@37: using std::endl;
fazekasgy@37: 
fazekasgy@37: #ifdef HAVE_NUMPY
fazekasgy@37: enum eArrayDataType {
fazekasgy@37: 	dtype_float32 = (int) NPY_FLOAT,
fazekasgy@37: 	dtype_complex64 = (int) NPY_CFLOAT 
fazekasgy@37: 	};
fazekasgy@37: #endif 
fazekasgy@37: 
fazekasgy@37: namespace o {
fazekasgy@37: enum eOutDescriptors {
fazekasgy@37: 	not_found,
fazekasgy@37: 	identifier,
fazekasgy@37: 	name,
fazekasgy@37: 	description,
fazekasgy@37: 	unit, 
fazekasgy@37: 	hasFixedBinCount,
fazekasgy@37: 	binCount,
fazekasgy@37: 	binNames,
fazekasgy@37: 	hasKnownExtents,
fazekasgy@37: 	minValue,
fazekasgy@37: 	maxValue,
fazekasgy@37: 	isQuantized,
fazekasgy@37: 	quantizeStep,
fazekasgy@37: 	sampleType,	
fazekasgy@37: 	sampleRate,
fazekasgy@37: 	hasDuration,
fazekasgy@37: 	endNode
fazekasgy@37: 	}; 
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: namespace p {
fazekasgy@37: enum eParmDescriptors {
fazekasgy@37: 	not_found,
fazekasgy@37: 	identifier,
fazekasgy@37: 	name,
fazekasgy@37: 	description,
fazekasgy@37: 	unit, 
fazekasgy@37: 	minValue,
fazekasgy@37: 	maxValue,
fazekasgy@37: 	defaultValue,
fazekasgy@37: 	isQuantized,
gyorgyf@62: 	quantizeStep,
gyorgyf@62: 	valueNames
fazekasgy@37: 	};
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: enum eSampleTypes {
fazekasgy@37: 	OneSamplePerStep,
fazekasgy@37: 	FixedSampleRate,
fazekasgy@37: 	VariableSampleRate
fazekasgy@37: 	};
fazekasgy@37: 
fazekasgy@37: enum eFeatureFields {
fazekasgy@37: 	unknown,
fazekasgy@37: 	hasTimestamp,
fazekasgy@37: 	timestamp,
fazekasgy@37: 	hasDuration,
fazekasgy@37: 	duration,
fazekasgy@37: 	values,
fazekasgy@37: 	label
fazekasgy@37: 	};
fazekasgy@37: 
fazekasgy@37: /* C++ mapping of PyNone Type */
fazekasgy@37: struct NoneType {};
fazekasgy@37: 
fazekasgy@37: class PyTypeInterface
fazekasgy@37: {
fazekasgy@37: public:
fazekasgy@37: 	PyTypeInterface();
fazekasgy@37: 	~PyTypeInterface();
fazekasgy@37: 	
fazekasgy@37: 	// Data
fazekasgy@37:  	class ValueError
fazekasgy@37: 	{
fazekasgy@37: 	public:
fazekasgy@37: 		ValueError() {}
fazekasgy@37: 		ValueError(std::string m, bool s) : message(m),strict(s) {}
fazekasgy@37: 		std::string location;
fazekasgy@37: 		std::string message;
fazekasgy@37: 		bool strict;
fazekasgy@37: 		std::string str() const { 
fazekasgy@37: 			return (location.empty()) ? message : message + "\nLocation: " + location;}
fazekasgy@37: 		void print() const { cerr << str() << endl; }
fazekasgy@37: 		template<typename V> ValueError &operator<< (const V& v)
fazekasgy@37: 		{
fazekasgy@37: 			std::ostringstream ss;
fazekasgy@37: 			ss << v;
fazekasgy@37: 			location += ss.str();
fazekasgy@37: 			return *this;
fazekasgy@37: 		}
fazekasgy@37: 	};
fazekasgy@37: 	
fazekasgy@37: 	// Utilities
fazekasgy@37: 	void setStrictTypingFlag(bool b) {m_strict = b;}
fazekasgy@51: 	void setNumpyInstalled(bool b) {m_numpyInstalled = b;}
fazekasgy@37: 	ValueError getError() const;
fazekasgy@37: 	std::string PyValue_Get_TypeName(PyObject*) const;
fazekasgy@37: 	bool initMaps() const;
fazekasgy@37: 
fazekasgy@37: 	// Basic type conversion: Python to C++ 
fazekasgy@37: 	float 	PyValue_To_Float(PyObject*) const;
fazekasgy@37: 	size_t 	PyValue_To_Size_t(PyObject*) const;
fazekasgy@37: 	bool 	PyValue_To_Bool(PyObject*) const;
fazekasgy@37: 	std::string PyValue_To_String(PyObject*) const;
fazekasgy@37: 	long 	PyValue_To_Long(PyObject*) const;
fazekasgy@37: 	// int 	PyValue_To_Int(PyObject* pyValue) const;
fazekasgy@37: 	
fazekasgy@37: 	
fazekasgy@37: 	// C++ to Python
fazekasgy@37: 	PyObject *PyValue_From_CValue(const char*) const;
fazekasgy@37: 	PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
fazekasgy@37: 	PyObject *PyValue_From_CValue(size_t) const;
fazekasgy@37: 	PyObject *PyValue_From_CValue(double) const;
fazekasgy@37: 	PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
fazekasgy@37: 	PyObject *PyValue_From_CValue(bool) const;
fazekasgy@37: 	
fazekasgy@37: 	// Sequence types
fazekasgy@37: 	std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
fazekasgy@37: 	std::vector<float> PyValue_To_FloatVector (PyObject*) const;
fazekasgy@37: 	std::vector<float> PyList_To_FloatVector (PyObject*) const;
fazekasgy@37: 
fazekasgy@37: 	// Input buffers to Python
fazekasgy@37: 	PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@47: 	PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37: 
fazekasgy@37: 	// Numpy types
fazekasgy@37: #ifdef HAVE_NUMPY
fazekasgy@37: 	std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const;
fazekasgy@37: 	PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype);
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37: 	
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: /* 						Template functions 							*/
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: 	/// Common wrappers to set values in Vamp API structs. (to be used in template functions)
fazekasgy@37: 	void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37: 	void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37: 	bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
fazekasgy@37:     PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const 
fazekasgy@37: 	{
fazekasgy@37: 		if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
fazekasgy@37: 		if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
fazekasgy@37: 		if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
fazekasgy@37: 		return NULL;
fazekasgy@37: 	}
fazekasgy@37: 	
fazekasgy@37: 	//returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
fazekasgy@37: 	template<typename RET> 
fazekasgy@37: 	RET PyValue_To_VampDescriptor(PyObject* pyValue) const
fazekasgy@37: 	{
fazekasgy@37: 		PyObject* pyDict;
fazekasgy@37: 
fazekasgy@37: 		// Descriptors encoded as dicts
fazekasgy@37: 		pyDict = GetDescriptor_As_Dict(pyValue);
fazekasgy@37: 		if (!pyDict) pyDict = pyValue;
fazekasgy@37: 	
fazekasgy@37: 		// TODO: support full mapping protocol as fallback.
fazekasgy@37: 		if (!PyDict_Check(pyDict)) {
fazekasgy@37: 			setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
fazekasgy@37: #ifdef _DEBUG
fazekasgy@37: 			cerr << "PyTypeInterface::PyValue_To_VampDescriptor failed. Error: Unexpected return type." << endl;
fazekasgy@37: #endif			
fazekasgy@37: 			return RET();
fazekasgy@37: 		}
fazekasgy@37: 
fazekasgy@37: 		Py_ssize_t pyPos = 0;
fazekasgy@37: 		PyObject *pyKey, *pyDictValue;
fazekasgy@37: 		initMaps();
fazekasgy@37: 		int errors = 0;
fazekasgy@37: 		m_error = false;
fazekasgy@37: 		RET rd;
fazekasgy@37: 
fazekasgy@37: 		//Python Dictionary Iterator:
fazekasgy@37: 		while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
fazekasgy@37: 		{
fazekasgy@37: 			std::string key = PyValue_To_String(pyKey);
fazekasgy@37: #ifdef _DEBUG_VALUES			
fazekasgy@37: 			cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl;
fazekasgy@37: #endif			
fazekasgy@37: 			SetValue(rd,key,pyDictValue);
fazekasgy@37: 			if (m_error) {
fazekasgy@37: 				errors++;
fazekasgy@37: 				lastError() << "attribute '" << key << "'";// << " of " << getDescriptorId(rd);
fazekasgy@37: 			}
fazekasgy@37: 		}
fazekasgy@37: 		if (errors) {
fazekasgy@37: 			lastError() << " of " << getDescriptorId(rd);
fazekasgy@37: 			m_error = true;
fazekasgy@37: #ifdef _DEBUG
fazekasgy@37: 			cerr << "PyTypeInterface::PyValue_To_VampDescriptor: Warning: Value error in descriptor." << endl;
fazekasgy@37: #endif				
fazekasgy@37: 		}
fazekasgy@37: 		return rd;
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37: 	/// Convert a sequence (tipically list) of PySomething to 
fazekasgy@37: 	/// OutputList,ParameterList or FeatureList
fazekasgy@37: 	/// <OutputList> <OutputDescriptor>
fazekasgy@37: 	template<typename RET,typename ELEM> 
fazekasgy@37: 	RET PyValue_To_VampList(PyObject* pyValue) const
fazekasgy@37: 	{
fazekasgy@37: 		RET list; // e.g. Vamp::Plugin::OutputList
fazekasgy@37: 		ELEM element; // e.g. Vamp::Plugin::OutputDescriptor
fazekasgy@37: 
fazekasgy@37: 		/// convert lists (ParameterList, OutputList, FeatureList)
fazekasgy@37: 		if (PyList_Check(pyValue)) {
fazekasgy@37: 			PyObject *pyDict; //This reference will be borrowed
fazekasgy@37: 			m_error = false; int errors = 0;
fazekasgy@37: 			for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
fazekasgy@37: 				//Get i-th Vamp output descriptor (Borrowed Reference)
fazekasgy@37: 				pyDict = PyList_GET_ITEM(pyValue,i);
fazekasgy@37: 				element = PyValue_To_VampDescriptor<ELEM>(pyDict);
fazekasgy@37: 				if (m_error) errors++;
fazekasgy@37: 				// Check for empty Feature/Descriptor as before?
fazekasgy@37: 				list.push_back(element);
fazekasgy@37: 			}
fazekasgy@37: 			if (errors) m_error=true;
fazekasgy@37: 			return list;
fazekasgy@37: 		}
fazekasgy@37: 		
fazekasgy@37: 		/// convert other types implementing the sequence protocol
fazekasgy@37: 		if (PySequence_Check(pyValue)) {
fazekasgy@37: 			PyObject *pySequence = PySequence_Fast(pyValue,"Returned value can not be converted to list or tuple.");
fazekasgy@37: 			PyObject **pyElements =  PySequence_Fast_ITEMS(pySequence);
fazekasgy@37: 			m_error = false; int errors = 0;
fazekasgy@37: 			for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(pySequence); ++i) 
fazekasgy@37: 			{
fazekasgy@37: 				element = PyValue_To_VampDescriptor<ELEM>(pyElements[i]);
fazekasgy@37: 				if (m_error) errors++;
fazekasgy@37: 				list.push_back(element);
fazekasgy@37: 			}
fazekasgy@37: 			if (errors) m_error=true;
fazekasgy@37: 			Py_XDECREF(pySequence);
fazekasgy@37: 			return list;
fazekasgy@37: 		}
fazekasgy@37: 
fazekasgy@37: 		// accept None as an empty list
fazekasgy@37: 		if (pyValue == Py_None) return list;
fazekasgy@37: 		
fazekasgy@37: 		// in strict mode, returning a single value is not allowed 
fazekasgy@37: 		if (m_strict) {
fazekasgy@37: 			setValueError("Strict conversion error: object is not list or iterable sequence.",m_strict);
fazekasgy@37: 			return list;
fazekasgy@37: 		}
fazekasgy@37: 		
fazekasgy@37: 		/// try to insert single, non-iterable values. i.e. feature <- [feature]
fazekasgy@37: 		element = PyValue_To_VampDescriptor<ELEM>(pyValue);
fazekasgy@37: 		if (m_error) {
fazekasgy@37: 			setValueError("Could not insert returned value to Vamp List.",m_strict);
fazekasgy@37: 			return list; 
fazekasgy@37: 		}
fazekasgy@37: 		list.push_back(element);
fazekasgy@37: 		return list;
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37: 	/// Convert DTYPE type 1D NumpyArray to std::vector<RET>
fazekasgy@37: 	template<typename RET, typename DTYPE>
Chris@66: 	std::vector<RET> PyArray_Convert(void* raw_data_ptr, long length, size_t strides) const
fazekasgy@37: 	{
fazekasgy@37: 		std::vector<RET> rValue;
fazekasgy@37: 		
fazekasgy@37: 		/// check if the array is continuous, if not use strides info
fazekasgy@37: 		if (sizeof(DTYPE)!=strides) {
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: 			cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl;
fazekasgy@37: #endif
fazekasgy@37: 			char* data = (char*) raw_data_ptr;
fazekasgy@37: 			for (long i = 0; i<length; ++i){
fazekasgy@37: 				rValue.push_back((RET)(*((DTYPE*)data)));
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: 				cerr << "value: " << (RET)(*((DTYPE*)data)) << endl;
fazekasgy@37: #endif				
fazekasgy@37: 				data+=strides;
fazekasgy@37: 			}
fazekasgy@37: 			return rValue;
fazekasgy@37: 		}
fazekasgy@37: 
fazekasgy@37: 		DTYPE* data = (DTYPE*) raw_data_ptr;
fazekasgy@37: 		for (long i = 0; i<length; ++i){
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: 			cerr << "value: " << (RET)data[i] << endl;
fazekasgy@37: #endif
fazekasgy@37: 			rValue.push_back((RET)data[i]);
fazekasgy@37: 		}
fazekasgy@37: 		return rValue;
fazekasgy@37: 	}
fazekasgy@37: 
Chris@77: #ifdef HAVE_NUMPY
fazekasgy@37: 	/// this is a special case. numpy.float64 has an array interface but no array descriptor
fazekasgy@37: 	inline std::vector<float> PyArray0D_Convert(PyArrayInterface *ai) const
fazekasgy@37: 	{
fazekasgy@37: 		std::vector<float> rValue;
fazekasgy@37: 		if ((ai->typekind) == *"f") 
fazekasgy@37: 			rValue.push_back((float)*(double*)(ai->data));
fazekasgy@37: 		else { 
fazekasgy@37: 			setValueError("Unsupported NumPy data type.",m_strict); 
fazekasgy@37: 			return rValue;
fazekasgy@37: 		}
fazekasgy@37: #ifdef _DEBUG_VALUES
fazekasgy@37: 		cerr << "value: " << rValue[0] << endl;
fazekasgy@37: #endif
fazekasgy@37: 		return rValue;
fazekasgy@37: 	}
Chris@77: #endif
Chris@77: 	
fazekasgy@37: 	//Vamp specific types
fazekasgy@37: 	Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
fazekasgy@37: 		{ r = this->PyValue_To_FeatureSet(pyValue); }
fazekasgy@37: 
fazekasgy@37: 	Vamp::RealTime PyValue_To_RealTime(PyObject*) const;
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const
fazekasgy@37: 		{ r = this->PyValue_To_RealTime(pyValue); }
fazekasgy@37: 	
fazekasgy@37: 	Vamp::Plugin::OutputDescriptor::SampleType PyValue_To_SampleType(PyObject*) const;
fazekasgy@37: 
fazekasgy@37: 	Vamp::Plugin::InputDomain PyValue_To_InputDomain(PyObject*) const;
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const
fazekasgy@37: 		{ r = this->PyValue_To_InputDomain(pyValue); }
fazekasgy@37: 	
fazekasgy@37: 	
fazekasgy@37: 	/* Overloaded PyValue_To_rValue() to support generic functions */
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const 
fazekasgy@37: 		{ float tmp = this->PyValue_To_Float(pyValue);                                              
fazekasgy@37: 			if(!m_error) defValue = tmp; }
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
fazekasgy@37: 		{ size_t tmp = this->PyValue_To_Size_t(pyValue); 
fazekasgy@37: 			if(!m_error) defValue = tmp; }
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
fazekasgy@37: 		{ bool tmp = this->PyValue_To_Bool(pyValue); 
fazekasgy@37: 			if(!m_error) defValue = tmp; }
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
fazekasgy@37: 		{ std::string tmp = this->PyValue_To_String(pyValue); 
fazekasgy@37: 			if(!m_error) defValue = tmp; }
fazekasgy@37: 	/*used by templates where we expect no return value, if there is one it will be ignored*/			
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
fazekasgy@37: 		{ if (m_strict && pyValue != Py_None) 
fazekasgy@37: 				setValueError("Strict conversion error: Expected 'None' type.",m_strict); 
fazekasgy@37: 		}
fazekasgy@37: 
fazekasgy@37: 	/* convert sequence types to Vamp List types */			
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
fazekasgy@37: 		{ r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
fazekasgy@37: 		{ r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
fazekasgy@37: 	inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
fazekasgy@37: 		{ r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
fazekasgy@37: 	
fazekasgy@37: 	/// this is only needed for RealTime->Frame conversion
fazekasgy@37: 	void setInputSampleRate(float inputSampleRate)
fazekasgy@37: 		{ m_inputSampleRate = (unsigned int) inputSampleRate; }
fazekasgy@37: 	
fazekasgy@37: private:
fazekasgy@37: 	bool m_strict;
fazekasgy@37: 	mutable bool m_error;
fazekasgy@37: 	mutable std::queue<ValueError> m_errorQueue;
fazekasgy@37: 	unsigned int m_inputSampleRate; 
fazekasgy@51: 	bool m_numpyInstalled;
fazekasgy@37: 	
fazekasgy@37: 	void setValueError(std::string,bool) const;
fazekasgy@37: 	ValueError& lastError() const;
fazekasgy@37: 
fazekasgy@37: 	/* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
fazekasgy@37: 	inline void _convert(PyObject *pyValue,float &r) const 
fazekasgy@37: 		{ r = PyValue_To_Float(pyValue); }
fazekasgy@37: 	inline void _convert(PyObject *pyValue,size_t &r) const 
fazekasgy@37: 		{ r = PyValue_To_Size_t(pyValue); }
fazekasgy@37:     inline void _convert(PyObject *pyValue,bool &r) const 
fazekasgy@37: 		{ r = PyValue_To_Bool(pyValue); }
fazekasgy@37: 	inline void _convert(PyObject *pyValue,std::string &r) const
fazekasgy@37: 		{ r = PyValue_To_String(pyValue); }
fazekasgy@37: 	inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
fazekasgy@37: 		{ r = PyValue_To_StringVector(pyValue); }
fazekasgy@37: 	inline void _convert(PyObject *pyValue,std::vector<float> &r) const
fazekasgy@37: 		{ r = PyValue_To_FloatVector(pyValue); }
fazekasgy@37:     inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const 
fazekasgy@37: 		{ r = PyValue_To_RealTime(pyValue); }
fazekasgy@37: 	inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const 
fazekasgy@37: 		{ r = PyValue_To_SampleType(pyValue); }
fazekasgy@37: 	// inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const 
fazekasgy@37: 	// 	{ r = PyValue_To_InputDomain(pyValue); }
fazekasgy@37: 	    
fazekasgy@37: 
fazekasgy@37: 	/* Identify descriptors for error reporting */
fazekasgy@37: 	inline std::string getDescriptorId(Vamp::Plugin::OutputDescriptor d) const
fazekasgy@37: 		{return std::string("Output Descriptor '") + d.identifier +"' ";}
fazekasgy@37: 	inline std::string getDescriptorId(Vamp::Plugin::ParameterDescriptor d) const
fazekasgy@37: 		{return std::string("Parameter Descriptor '") + d.identifier +"' ";}
fazekasgy@37: 	inline std::string getDescriptorId(Vamp::Plugin::Feature f) const
fazekasgy@37: 		{return std::string("Feature (") + f.label + ")"; }
fazekasgy@37: 	
fazekasgy@37: public:
fazekasgy@37: 	const bool& error;
fazekasgy@37: 
fazekasgy@37: };
fazekasgy@37: 
fazekasgy@37: /* 		   		  Convert Sample Buffers to Python 	         		*/
fazekasgy@37: 
fazekasgy@51: /// passing the sample buffers as builtin python lists
fazekasgy@37: /// Optimization: using fast sequence protocol
fazekasgy@37: inline PyObject*
fazekasgy@37: PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37: {
fazekasgy@37: 	//create a list of lists (new references)
fazekasgy@37: 	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37: 	
fazekasgy@37: 	// Pack samples into a Python List Object
fazekasgy@37: 	// pyFloat/pyComplex types will always be new references, 
fazekasgy@37: 	// they will be freed when the lists are deallocated.
fazekasgy@37: 	
fazekasgy@37: 	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37: 	for (size_t i=0; i < channels; ++i) {
fazekasgy@51: 		
fazekasgy@51:         size_t arraySize;
fazekasgy@47: 		if (dtype==Vamp::Plugin::FrequencyDomain) 
fazekasgy@51: 			arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used
fazekasgy@47: 		else 
fazekasgy@47: 			arraySize = blockSize;
fazekasgy@51: 
fazekasgy@51: 		PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize);
fazekasgy@51: 		PyObject **pySampleListArray =  PySequence_Fast_ITEMS(pySampleList);
fazekasgy@37: 		
fazekasgy@37: 		// Note: passing a complex list crashes the C-style plugin
fazekasgy@37: 		// when it tries to convert it to a numpy array directly.
fazekasgy@37: 		// This plugin will be obsolete, but we have to find a way
fazekasgy@51: 		// to prevent such crash: possibly a numpy bug, 
fazekasgy@51: 		// works fine above 1.0.4
fazekasgy@37: 		
fazekasgy@51: 		switch (dtype) //(Vamp::Plugin::TimeDomain)
fazekasgy@37: 		{
fazekasgy@37: 			case Vamp::Plugin::TimeDomain :
fazekasgy@37: 
fazekasgy@47: 			for (size_t j = 0; j < arraySize; ++j) {
fazekasgy@37: 				PyObject *pyFloat=PyFloat_FromDouble(
fazekasgy@37: 					(double) inputBuffers[i][j]);
fazekasgy@37: 				pySampleListArray[j] = pyFloat;
fazekasgy@37: 			}
fazekasgy@37: 			break;
fazekasgy@37: 
fazekasgy@37: 			case Vamp::Plugin::FrequencyDomain :
fazekasgy@37: 
fazekasgy@37: 			size_t k = 0;
fazekasgy@51: 			for (size_t j = 0; j < arraySize; ++j) {
fazekasgy@37: 				PyObject *pyComplex=PyComplex_FromDoubles(
fazekasgy@37: 					(double) inputBuffers[i][k], 
fazekasgy@37: 					(double) inputBuffers[i][k+1]);
fazekasgy@37: 				pySampleListArray[j] = pyComplex;
fazekasgy@37: 				k += 2;
fazekasgy@37: 			}
fazekasgy@37: 			break;
fazekasgy@37: 			
fazekasgy@37: 		}
fazekasgy@37: 		pyChannelListArray[i] = pySampleList;
fazekasgy@37: 	}
fazekasgy@37: 	return pyChannelList;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: /// numpy buffer interface: passing the sample buffers as shared memory buffers
fazekasgy@37: /// Optimization: using sequence protocol for creating the buffer list
fazekasgy@37: inline PyObject*
fazekasgy@47: PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37: {	
fazekasgy@37: 	//create a list of buffers (returns new references)
fazekasgy@37: 	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37: 	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37: 
fazekasgy@37: 	// Expose memory using the Buffer Interface.		
fazekasgy@37: 	// This will pass a pointer which can be recasted in Python code 
fazekasgy@37: 	// as complex or float array using Numpy's frombuffer() method
fazekasgy@37: 	// (this will not copy values just keep the starting adresses 
fazekasgy@37: 	// for each channel in a list)
fazekasgy@47: 	Py_ssize_t bufferSize;
fazekasgy@47: 	
fazekasgy@47: 	if (dtype==Vamp::Plugin::FrequencyDomain) 
fazekasgy@47: 		bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2);
fazekasgy@47: 	else 
fazekasgy@47: 		bufferSize = (Py_ssize_t) sizeof(float) * blockSize;
fazekasgy@47: 	
fazekasgy@37: 	for (size_t i=0; i < channels; ++i) {
fazekasgy@37: 		PyObject *pyBuffer = PyBuffer_FromMemory
fazekasgy@37: 		((void *) (float *) inputBuffers[i],bufferSize);
fazekasgy@37: 		pyChannelListArray[i] = pyBuffer;
fazekasgy@37: 	}
fazekasgy@37: 	return pyChannelList;
fazekasgy@37: }
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: /// numpy array interface: passing the sample buffers as 2D numpy array
fazekasgy@37: /// Optimization: using array API (needs numpy headers)
fazekasgy@37: #ifdef HAVE_NUMPY
fazekasgy@37: inline PyObject*
fazekasgy@37: PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
fazekasgy@37: {	
fazekasgy@37: /*
fazekasgy@37: NOTE: We create a list of 1D Numpy arrays for each channel instead
fazekasgy@37: of a matrix, because the address space of inputBuffers doesn't seem
fazekasgy@37: to be continuous. Although the array strides could be calculated for
fazekasgy@37: 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure
fazekasgy@37: if this can be trusted, especially for more than 2 channels.
fazekasgy@37: 
fazekasgy@37: 	cerr << "First channel: " << inputBuffers[0][0] << " address: " <<  inputBuffers[0] << endl;
fazekasgy@37: 	if (channels == 2)
fazekasgy@37: 		cerr << "Second channel: " << inputBuffers[1][0] << " address: " <<  inputBuffers[1] << endl;
fazekasgy@37: 
fazekasgy@37: */	
fazekasgy@37: 	
fazekasgy@37: 	// create a list of arrays (returns new references)
fazekasgy@37: 	PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
fazekasgy@37: 	PyObject **pyChannelListArray =  PySequence_Fast_ITEMS(pyChannelList);
fazekasgy@37: 	
fazekasgy@37: 	// Expose memory using the Numpy Array Interface.		
fazekasgy@37: 	// This will wrap an array objects around the data.
fazekasgy@37: 	// (will not copy values just steal the starting adresses)
fazekasgy@37: 
fazekasgy@37: 	int arraySize, typenum;
fazekasgy@37: 	
fazekasgy@37: 	switch (dtype)
fazekasgy@37: 	{
fazekasgy@37: 		case Vamp::Plugin::TimeDomain :
fazekasgy@37: 		typenum = dtype_float32; //NPY_FLOAT; 
fazekasgy@37: 		arraySize = (int) blockSize;
fazekasgy@37: 		break;
fazekasgy@37: 
fazekasgy@37: 		case Vamp::Plugin::FrequencyDomain :
fazekasgy@37: 		typenum = dtype_complex64; //NPY_CFLOAT;
fazekasgy@47: 		arraySize = (int) (blockSize / 2) + 1;
fazekasgy@37: 		break;
fazekasgy@37: 		
fazekasgy@37: 		default :
fazekasgy@37: 		cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl;
fazekasgy@37: 		return pyChannelList;
fazekasgy@37: 	}
fazekasgy@37: 
fazekasgy@37: 	// size for each dimension
fazekasgy@37: 	npy_intp ndims[1]={arraySize}; 
fazekasgy@37: 	
fazekasgy@37: 	for (size_t i=0; i < channels; ++i) {
fazekasgy@37: 		PyObject *pyChannelArray = 
fazekasgy@37: 			//args: (dimensions, size in each dim, type kind, pointer to continuous array)
fazekasgy@37: 			PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]);
Chris@66: 		// make it read-only: set all flags to false except NPY_C_CONTIGUOUS
Chris@66: 		//!!! what about NPY_ARRAY_OWNDATA?
Chris@66: 		PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff);
Chris@66: 		PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS);
fazekasgy@37: 		pyChannelListArray[i] = pyChannelArray;
fazekasgy@37: 	}
fazekasgy@37: 	return pyChannelList;
fazekasgy@37: }
fazekasgy@37: #endif
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: 
fazekasgy@37: #ifdef NUMPY_REFERENCE
fazekasgy@37: /// This should be all we need to compile without direct dependency,
fazekasgy@37: /// but we don't do that. (it may not work on some platforms)
fazekasgy@37: typedef struct {
fazekasgy@37:     int two;              /* contains the integer 2 -- simple sanity check */
fazekasgy@37:     int nd;               /* number of dimensions */
fazekasgy@37:     char typekind;        /* kind in array --- character code of typestr */
fazekasgy@37:     int itemsize;         /* size of each element */
fazekasgy@37:     int flags;            /* flags indicating how the data should be interpreted */
fazekasgy@37:                           /*   must set ARR_HAS_DESCR bit to validate descr */
fazekasgy@37:     Py_intptr_t *shape;   /* A length-nd array of shape information */
fazekasgy@37:     Py_intptr_t *strides; /* A length-nd array of stride information */
fazekasgy@37:     void *data;           /* A pointer to the first element of the array */
fazekasgy@37:     PyObject *descr;      /* NULL or data-description (same as descr key */
fazekasgy@37:                           /*        of __array_interface__) -- must set ARR_HAS_DESCR */
fazekasgy@37:                           /*        flag or this will be ignored. */
fazekasgy@37: } PyArrayInterface;
fazekasgy@37: 
fazekasgy@37: typedef struct PyArrayObject {
fazekasgy@37:         PyObject_HEAD
fazekasgy@37:         char *data;             /* pointer to raw data buffer */
fazekasgy@37:         int nd;                 /* number of dimensions, also called ndim */
fazekasgy@37:         npy_intp *dimensions;       /* size in each dimension */
fazekasgy@37:         npy_intp *strides;          /* bytes to jump to get to the
fazekasgy@37:                                    next element in each dimension */
fazekasgy@37:         PyObject *base;         /* This object should be decref'd
fazekasgy@37:                                    upon deletion of array */
fazekasgy@37:                                 /* For views it points to the original array */
fazekasgy@37:                                 /* For creation from buffer object it points
fazekasgy@37:                                    to an object that shold be decref'd on
fazekasgy@37:                                    deletion */
fazekasgy@37:                                 /* For UPDATEIFCOPY flag this is an array
fazekasgy@37:                                    to-be-updated upon deletion of this one */
fazekasgy@37:         PyArray_Descr *descr;   /* Pointer to type structure */
fazekasgy@37:         int flags;              /* Flags describing array -- see below*/
fazekasgy@37:         PyObject *weakreflist;  /* For weakreferences */
fazekasgy@37: } PyArrayObject;
fazekasgy@37: 
fazekasgy@37: typedef struct _PyArray_Descr {
fazekasgy@37:         PyObject_HEAD
fazekasgy@37:         PyTypeObject *typeobj;  /* the type object representing an
fazekasgy@37:                                    instance of this type -- should not
fazekasgy@37:                                    be two type_numbers with the same type
fazekasgy@37:                                    object. */
fazekasgy@37:         char kind;              /* kind for this type */
fazekasgy@37:         char type;              /* unique-character representing this type */
fazekasgy@37:         char byteorder;         /* '>' (big), '<' (little), '|'
fazekasgy@37:                                    (not-applicable), or '=' (native). */
fazekasgy@37:         char hasobject;         /* non-zero if it has object arrays
fazekasgy@37:                                    in fields */
fazekasgy@37:         int type_num;          /* number representing this type */
fazekasgy@37:         int elsize;             /* element size for this type */
fazekasgy@37:         int alignment;          /* alignment needed for this type */
fazekasgy@37:         struct _arr_descr                                       \
fazekasgy@37:         *subarray;              /* Non-NULL if this type is
fazekasgy@37:                                    is an array (C-contiguous)
fazekasgy@37:                                    of some other type
fazekasgy@37:                                 */
fazekasgy@37:         PyObject *fields;       /* The fields dictionary for this type */
fazekasgy@37:                                 /* For statically defined descr this
fazekasgy@37:                                    is always Py_None */
fazekasgy@37: 
fazekasgy@37:         PyObject *names;        /* An ordered tuple of field names or NULL
fazekasgy@37:                                    if no fields are defined */
fazekasgy@37: 
fazekasgy@37:         PyArray_ArrFuncs *f;     /* a table of functions specific for each
fazekasgy@37:                                     basic data descriptor */
fazekasgy@37: } PyArray_Descr;
fazekasgy@37: 
fazekasgy@37: enum NPY_TYPES {    NPY_BOOL=0,
fazekasgy@37:                     NPY_BYTE, NPY_UBYTE,
fazekasgy@37:                     NPY_SHORT, NPY_USHORT,
fazekasgy@37:                     NPY_INT, NPY_UINT,
fazekasgy@37:                     NPY_LONG, NPY_ULONG,
fazekasgy@37:                     NPY_LONGLONG, NPY_ULONGLONG,
fazekasgy@37:                     NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE,
fazekasgy@37:                     NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE,
fazekasgy@37:                     NPY_OBJECT=17,
fazekasgy@37:                     NPY_STRING, NPY_UNICODE,
fazekasgy@37:                     NPY_VOID,
fazekasgy@37:                     NPY_NTYPES,
fazekasgy@37:                     NPY_NOTYPE,
fazekasgy@37:                     NPY_CHAR,      /* special flag */
fazekasgy@37:                     NPY_USERDEF=256  /* leave room for characters */
fazekasgy@37: };
fazekasgy@37: #endif /*NUMPY_REFERENCE*/
fazekasgy@37: #endif