view PyTypeInterface.cpp @ 35:2ba482378038 vampy2

* Fix compile error with VC++. I am totally at a loss to explain why this should have compiled with any other compiler! * Update VC project file. This code does now build with VC++ without HAVE_NUMPY -- I haven't installed Numpy yet
author cannam
date Thu, 24 Sep 2009 08:52:04 +0000
parents c905122f79e7
children
line wrap: on
line source
/*
*/

#include <Python.h>
#ifdef HAVE_NUMPY
#include "arrayobject.h"
#endif
#include "PyTypeInterface.h"
#include "PyRealTime.h"
#include "PyExtensionModule.h"
#include <math.h>
#include <float.h>
#include <limits.h>
#ifndef SIZE_T_MAX
#define SIZE_T_MAX ((size_t) -1)
#endif

using std::string;
using std::vector;
using std::cerr;
using std::endl;
using std::map;

static std::map<std::string, o::eOutDescriptors> outKeys;
static std::map<std::string, p::eParmDescriptors> parmKeys;
static std::map<std::string, eSampleTypes> sampleKeys;
static std::map<std::string, eFeatureFields> ffKeys;
static bool isMapInitialised = false;

/*  Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
	(EXCEPT FOR TEMPORARY PYTHON OBJECTS)!  						 */

PyTypeInterface::PyTypeInterface() : 
	m_strict(false),
	m_error(false),
	m_lastError(m_noError),
	error(m_error)
{
}

PyTypeInterface::~PyTypeInterface()
{
}

/// floating point numbers (TODO: check numpy.float128)
float 
PyTypeInterface::PyValue_To_Float(PyObject* pyValue) const
{
	// convert float
	if (PyFloat_Check(pyValue)) 
	{	
		float rValue = (float) PyFloat_AS_DOUBLE(pyValue);
		if (PyErr_Occurred()) 
		{
			PyErr_Print(); PyErr_Clear();
			setValueError("Error while converting float object.",m_strict);
			return 0.0;
		}
		return rValue;
	}

	// in strict mode we will not try harder
	if (m_strict) {
		setValueError("Strict conversion error: object is not float.",m_strict);
		return 0.0;
	}
	
	// convert other objects supporting the number protocol
	if (PyNumber_Check(pyValue)) 
	{	
		// PEP353: Py_ssize_t is size_t but signed !
		// This will work up to numpy.float64
		Py_ssize_t rValue = PyNumber_AsSsize_t(pyValue,NULL);
		if (PyErr_Occurred()) 
		{
			PyErr_Print(); PyErr_Clear();
			setValueError("Error while converting integer object.",m_strict);
			return 0.0;
		}
		if (rValue > FLT_MAX || rValue < FLT_MIN)
		{
			setValueError("Overflow error. Object can not be converted to float.",m_strict);
			return 0.0;
		}
		return (float) rValue;
	}
	
    // convert string
	if (PyString_Check(pyValue))
	{
		PyObject* pyFloat = PyFloat_FromString(pyValue,NULL);
		if (!pyFloat) 
		{
			if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
			setValueError("String value can not be converted to float.",m_strict);
			return 0.0;
		}
		float rValue = (float) PyFloat_AS_DOUBLE(pyFloat);
		if (PyErr_Occurred()) 
		{
			PyErr_Print(); PyErr_Clear(); 
			Py_CLEAR(pyFloat);
			setValueError("Error while converting float object.",m_strict);
			return 0.0;
		}
		Py_DECREF(pyFloat);
		return rValue;
	}
	
	// convert the first element of any iterable sequence (for convenience and backwards compatibility)
	if (PySequence_Check(pyValue) && PySequence_Size(pyValue) > 0) 
	{
		PyObject* item = PySequence_GetItem(pyValue,0);
		if (item)
		{
			float rValue = this->PyValue_To_Float(item);
			if (!m_error) {
				Py_DECREF(item);
				return rValue;
			} else {
				Py_CLEAR(item);
				std::string msg = "Could not convert sequence element. " + lastError().message;
				setValueError(msg,m_strict);
				return 0.0;
			}
		}
	}
	
    // give up
	if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
	std::string msg = "Conversion from " + PyValue_Get_TypeName(pyValue) + " to float is not possible.";
	setValueError(msg,m_strict);
	return 0.0;
}

/// size_t (unsigned integer types)
size_t 
PyTypeInterface::PyValue_To_Size_t(PyObject* pyValue) const
{
	// convert objects supporting the number protocol 
	if (PyNumber_Check(pyValue)) 
	{	
		if (m_strict && !PyInt_Check(pyValue) && !PyLong_Check(pyValue)) 
			setValueError("Strict conversion error: object is not integer type.",m_strict);
		// Note: this function handles Bool,Int,Long,Float
		// PEP353: Py_ssize_t is size_t but signed ! 
		Py_ssize_t rValue = PyInt_AsSsize_t(pyValue);
		if (PyErr_Occurred()) 
		{
			PyErr_Print(); PyErr_Clear();
			setValueError("Error while converting integer object.",m_strict);
			return 0;
		}
		if ((unsigned long)rValue > SIZE_T_MAX || (unsigned long)rValue < 0)
		{
			setValueError("Overflow error. Object can not be converted to size_t.",m_strict);
			return 0;
		}
		return (size_t) rValue;
	}
	
	// in strict mode we will not try harder and throw an exception
	// then the caller should decide what to do with it
	if (m_strict) {
		setValueError("Strict conversion error: object is not integer.",m_strict);
		return 0;
	}
	
	// convert string
	if (PyString_Check(pyValue))
	{
		PyObject* pyLong = PyNumber_Long(pyValue);
		if (!pyLong) 
		{
			if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
			setValueError("String object can not be converted to size_t.",m_strict);
			return 0;
		}
		size_t rValue = this->PyValue_To_Size_t(pyLong);
		if (!m_error) {
			Py_DECREF(pyLong);
			return rValue;
		} else {
			Py_CLEAR(pyLong);
			setValueError (lastError().message,m_strict);
			return 0;
		}
	}
	
	// convert the first element of iterable sequences
	if (PySequence_Check(pyValue) && PySequence_Size(pyValue) > 0) 
	{
		PyObject* item = PySequence_GetItem(pyValue,0);
		if (item)
		{
			size_t rValue = this->PyValue_To_Size_t(item);
			if (!m_error) {
				Py_DECREF(item);
				return rValue;
			} else {
				Py_CLEAR(item);
				std::string msg = "Could not convert sequence element. " + lastError().message;
				setValueError(msg,m_strict);
				return 0;
			}
		}
	}
	
    // give up
	if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
	std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to size_t is not possible.";
	setValueError(msg,m_strict);
	return 0;
}

bool 
PyTypeInterface::PyValue_To_Bool(PyObject* pyValue) const
{
	// convert objects supporting the number protocol
	// Note: PyBool is a subclass of PyInt
	if (PyNumber_Check(pyValue)) 
	{	
		if (m_strict && !PyBool_Check(pyValue)) 
			setValueError
			("Strict conversion error: object is not boolean type.",m_strict);

		// Note: this function handles Bool,Int,Long,Float
		Py_ssize_t rValue = PyInt_AsSsize_t(pyValue);
		if (PyErr_Occurred()) 
		{
			PyErr_Print(); PyErr_Clear();
			setValueError ("Error while converting boolean object.",m_strict);
		}
		if (rValue != 1 && rValue != 0)
		{
			setValueError ("Overflow error. Object can not be converted to boolean.",m_strict);
		}
		return (bool) rValue;
	}
	
	if (m_strict) {
		setValueError ("Strict conversion error: object is not numerical type.",m_strict);
		return false;
	}
	
	// convert iterables: the rule is the same as of the interpreter:
	// empty sequence evaluates to False, anything else is True
	if (PySequence_Check(pyValue)) 
	{
		return PySequence_Size(pyValue)?true:false;
	}
	
    // give up
	if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
	std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to boolean is not possible.";
	setValueError(msg,m_strict);
	return false;
}

/// string and objects that support .__str__() (TODO: check unicode objects)
std::string 
PyTypeInterface::PyValue_To_String(PyObject* pyValue) const
{
	// convert string
	if (PyString_Check(pyValue)) 
	{	
		char *cstr = PyString_AS_STRING(pyValue);
		if (!cstr) 
		{
			if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
			setValueError("Error while converting string object.",m_strict);
			return std::string();
		}
		return std::string(cstr);
	}
	// TODO: deal with unicode here (argh!)

	// in strict mode we will not try harder
	if (m_strict) {
		setValueError("Strict conversion error: object is not string.",m_strict);
		return std::string();
	}
		
	// convert list or tuple: empty lists are turned into empty strings
	if (PyList_Check(pyValue) || PyTuple_Check(pyValue)) 
	{
		if (!PySequence_Size(pyValue)) return std::string();
		PyObject* item = PySequence_GetItem(pyValue,0);
		if (item)
		{
			std::string rValue = this->PyValue_To_String(item);
			if (m_error) {
				Py_DECREF(item);
				return rValue;
			} else {
				Py_CLEAR(item);
				std::string msg = "Could not convert sequence element. " + lastError().message;
				setValueError(msg,m_strict);
				return std::string();
			}
		}
	}

	// convert any other object that has .__str__() or .__repr__()
	PyObject* pyString = PyObject_Str(pyValue);
	if (pyString && !PyErr_Occurred())
	{
		std::string rValue = this->PyValue_To_String(pyString);
		if (m_error) {
			Py_DECREF(pyString);
			return rValue;
		} else {
			Py_CLEAR(pyString);
			std::string msg = "Object " + this->PyValue_Get_TypeName(pyValue) +" can not be represented as string. " + lastError().message;
			setValueError (msg,m_strict);
			return std::string();
		}
	}

	// give up
	PyErr_Print(); PyErr_Clear();
	std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to string is not possible.";
	setValueError(msg,m_strict);
	return std::string();
}

/*			 			C Values to Py Values				  		*/


PyObject*
PyTypeInterface::PyValue_From_CValue(const char* cValue) const
{
	// returns new reference
	if (!cValue) 
		setValueError("Invalid pointer encountered while converting from char* .",m_strict);
	PyObject *pyValue = PyString_FromString(cValue);
	if (!pyValue)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		setValueError("Error while converting from char* or string.",m_strict);
		return NULL;
	}
	return pyValue;
}

PyObject*
PyTypeInterface::PyValue_From_CValue(size_t cValue) const
{
	// returns new reference
	PyObject *pyValue = PyInt_FromSsize_t((Py_ssize_t)cValue);
	if (!pyValue)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		setValueError("Error while converting from size_t.",m_strict);
		return NULL;
	}
	return pyValue;
}

PyObject*
PyTypeInterface::PyValue_From_CValue(double cValue) const
{
	// returns new reference
	PyObject *pyValue = PyFloat_FromDouble(cValue);
	if (!pyValue)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		setValueError("Error while converting from float or double.",m_strict);
		return NULL;
	}
	return pyValue;
}

PyObject*
PyTypeInterface::PyValue_From_CValue(bool cValue) const
{
	// returns new reference
	PyObject *pyValue = PyBool_FromLong((long)cValue);
	if (!pyValue)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		setValueError("Error while converting from bool.",m_strict);
		return NULL;
	}
	return pyValue;
}


/*			 			Sequence Types to C++ Types	    		  	*/

//convert Python list to C++ vector of strings
std::vector<std::string> 
PyTypeInterface::PyValue_To_StringVector (PyObject *inputList) const 
{
	
	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;
}

//convert PyFeature.value (typically a list or numpy array) to C++ vector of floats
std::vector<float> 
PyTypeInterface::PyValue_To_FloatVector (PyObject *pyValue) const 
{
	std::vector<float> Output;

#ifdef HAVE_NUMPY
	/// Check for NumPy Array: this requires linking with numpy
	/// but, we don't really need this macro
	// if (PyArray_CheckExact(inputList)) cerr << "PyPyArray_CheckExact OK" << endl;
	
	/// numpy array
	if (PyObject_HasAttrString(pyValue,"__array_struct__")) {
		return PyArray_To_FloatVector(pyValue);
	}
#endif

	/// python list
	if (PyList_Check(pyValue)) {
		return PyList_To_FloatVector(pyValue);
	}
	
	/// assume a single number 
	/// this allows to write e.g. Feature.values = 5 instead of [5.00]
	Output.push_back(PyValue_To_Float(pyValue));
	return Output;
	
	/// TODO : set error
	
}

//convert a list of python floats
std::vector<float> 
PyTypeInterface::PyList_To_FloatVector (PyObject *inputList) const 
{
	std::vector<float> Output;
	
	float ListElement;
	PyObject *pyFloat = 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)
		pyFloat = PyList_GET_ITEM(inputList,i);
		ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
#ifdef _DEBUG
		cerr << "value: " << ListElement << endl;
#endif
		Output.push_back(ListElement);
	}

	return Output;
}

#ifdef HAVE_NUMPY
std::vector<float> 
PyTypeInterface::PyArray_To_FloatVector (PyObject *pyValue) const 
{
	std::vector<float> Output;
	 
	/// we don't verify the array here as it'd be duplicated mostly
	// if (!PyObject_HasAttrString(pyValue,"__array_struct__")) {
	// 	return Output;
	// }

	PyArrayObject* pyArray = (PyArrayObject*) pyValue;
	PyArray_Descr* descr = pyArray->descr;
	
	/// check raw data pointer
	if (pyArray->data == 0) return Output;

	/// check dimensions
	if (pyArray->nd != 1) {
		cerr << "Error: array must be 1D" << endl;
		return Output;
	}

#ifdef _DEBUG
	cerr << "Numpy array verified." << endl;
#endif	

	switch (descr->type_num)
	{
		case NPY_FLOAT :
			return PyArray_Convert<float,float>(pyArray->data,pyArray->dimensions[0]);
		case NPY_DOUBLE :
			return PyArray_Convert<float,double>(pyArray->data,pyArray->dimensions[0]);
		case NPY_INT :
			return PyArray_Convert<float,int>(pyArray->data,pyArray->dimensions[0]);
		case NPY_LONG :
			return PyArray_Convert<float,long>(pyArray->data,pyArray->dimensions[0]);
		
		default :
		cerr << "Error. Unsupported element type in NumPy array object." << endl;
			return Output;
	}
}
#endif

/*			 			Vamp API Specific Types				  		

Vamp::Plugin::OutputList 
PyTypeInterface::PyValue_To_OutputList(PyObject* pyList) const
{
	Vamp::Plugin::OutputList list;
	Vamp::Plugin::OutputDescriptor od;
	
	// Type checking
	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;
		
	//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);
		od = PyValue_To_OutputDescriptor(pyDict);
		list.push_back(od);
	}
	return list;
}

Vamp::Plugin::ParameterList 
PyTypeInterface::PyValue_To_ParameterList(PyObject* pyList) const
{
	Vamp::Plugin::ParameterList list;
	Vamp::Plugin::ParameterDescriptor pd;
	
	// Type checking
	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;
		
	//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);
		pd = PyValue_To_ParameterDescriptor(pyDict);
		list.push_back(pd);
	}
	return list;
}

Vamp::Plugin::OutputDescriptor
PyTypeInterface::PyValue_To_OutputDescriptor(PyObject* pyDict) const
{
	//We only care about dictionaries holding output descriptors
	if (!PyDict_Check(pyDict)) 
		return Vamp::Plugin::OutputDescriptor();
                       
	Py_ssize_t pyPos = 0;
	PyObject *pyKey, *pyValue;
	initMaps();
	Vamp::Plugin::OutputDescriptor od;

	//Python Dictionary Iterator:
	while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
	{
		std::string key = PyValue_To_String(pyKey);
		SetValue(od,key,pyValue);
		if (m_error) {
			_lastError().location += "parameter: '" 
			+ key +"' descriptor: '" + od.identifier + "'";
		}
	}
	if (!m_errorQueue.empty()) m_error = true;
	return od;
}

Vamp::Plugin::ParameterDescriptor
PyTypeInterface::PyValue_To_ParameterDescriptor(PyObject* pyDict) const
{
	//We only care about dictionaries holding output descriptors
	if (!PyDict_Check(pyDict)) 
		return Vamp::Plugin::ParameterDescriptor();
                       
	Py_ssize_t pyPos = 0;
	PyObject *pyKey, *pyValue;
	initMaps();
	Vamp::Plugin::ParameterDescriptor pd;

	//Python Dictionary Iterator:
	while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
	{
		std::string key = PyValue_To_String(pyKey);
		SetValue(pd,key,pyValue);
		if (m_error) {
			_lastError().location += "parameter: '" 
			+ key +"' descriptor: '" + pd.identifier + "'";
		}
	}
	if (!m_errorQueue.empty()) m_error = true;
	return pd;
}
 
Vamp::Plugin::Feature
PyTypeInterface::PyValue_To_Feature(PyObject* pyDict) const
{
	//We only care about dictionaries holding output descriptors
	if (!PyDict_Check(pyDict)) 
		return Vamp::Plugin::Feature();
                       
	Py_ssize_t pyPos = 0;
	PyObject *pyKey, *pyValue;
	initMaps();
	Vamp::Plugin::Feature feature;

	//Python Dictionary Iterator:
	while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
	{
		std::string key = PyValue_To_String(pyKey);
		float isr = 22050.0;
		Feature_SetValue(feature,key,pyValue,isr);
		if (m_error) {
			_lastError().location += "key: '" + key + "'";
			// _lastError().location += "parameter: '" 
			// + key +"' descriptor: '" + pd.identifier + "'";
		}
	}
	if (!m_errorQueue.empty()) m_error = true;
	return feature;
}
*/

/// FeatureSet (an int map of OutputLists)
Vamp::Plugin::FeatureSet
PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const
{
	Vamp::Plugin::FeatureSet rFeatureSet; /// map<int>
	if (pyValue == NULL) {
		cerr << "NULL FeatureSet" << endl;
		return rFeatureSet;
	}

	cerr << "PyValue_To_FeatureSet" << endl;
	//Convert PyFeatureSet 
	if (PyFeatureSet_CheckExact(pyValue)) { 
		cerr << "FeatureSet Return type" << endl;
		Py_ssize_t pyPos = 0;
		//Borrowed References
		PyObject *pyKey, *pyDictValue;
		int key;

			//Python Dictionary Iterator:
			while (PyDict_Next(pyValue, &pyPos, &pyKey, &pyDictValue))
			{
				/// DictValue -> Vamp::FeatureList
				key = (int) PyInt_AS_LONG(pyKey);
				cerr << "FeatureSet key = " << key << endl;
				/// Error checking is done at value assignment
				PyValue_To_rValue(pyDictValue,rFeatureSet[key]);
			}
			if (!m_errorQueue.empty()) m_error = true;
			return rFeatureSet;
	}
	cerr << "not FeatureSet Return type" << endl;
	
		
	//Check return type
	if (pyValue == NULL || !PyList_Check(pyValue) ) {
		if (pyValue == 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(pyValue);
		return Vamp::Plugin::FeatureSet();
	}
		
	// This will be borrowed reference
	PyObject *pyFeatureList;

	//Parse Output List for each element (FeatureSet)
	for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
		//Get i-th FeatureList (Borrowed Reference)
		pyFeatureList = PyList_GET_ITEM(pyValue,i);
		PyValue_To_rValue(pyFeatureList,rFeatureSet[i]);
	}
	// Py_CLEAR(pyOutputList);
	return rFeatureSet;
}

Vamp::RealTime
PyTypeInterface::PyValue_To_RealTime(PyObject* pyValue) const
{
// We accept integer sample counts (for backwards compatibility)
// or PyRealTime objects and convert them to Vamp::RealTime
	
	if (PyRealTime_CheckExact(pyValue))
	{
#ifdef _DEBUG
		cerr << "Converting from PyRealTime" << endl;
#endif
		/// just create a copy of the wrapped object
		return Vamp::RealTime(
			*PyRealTime_AS_REALTIME(pyValue));
	}
	// assume integer sample count
	long sampleCount = PyLong_AsLong(pyValue);
	if (PyErr_Occurred()) 
	{
		PyErr_Print(); PyErr_Clear();
		setValueError("Error while converting integer to RealTime.",m_strict);
		return Vamp::RealTime();
	}
#ifdef _DEBUG
	Vamp::RealTime rt = 
		Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate );
	cerr << "RealTime: " << (long)sampleCount << ", ->" << rt.toString() << endl;
	return rt;
#else
	return Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate );	
#endif

}


/// OutputDescriptor
void
PyTypeInterface::SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const
{
	switch (outKeys[key])
	{
		case o::not_found:
			cerr << "Unknown key in Vamp OutputDescriptor: " << key << endl;
			break;
			case o::identifier: 
			_convert(pyValue,od.identifier);
			break;				
		case o::name: 			
			_convert(pyValue,od.name);
			break;
		case o::description:
			_convert(pyValue,od.description);
			break;
		case o::unit:
			_convert(pyValue,od.unit);
			break;
		case o::hasFixedBinCount:
			_convert(pyValue,od.hasFixedBinCount);
			break;
		case o::binCount:
			_convert(pyValue,od.binCount);
			break;
		case o::binNames:
			_convert(pyValue,od.binNames);
			break;
		case o::hasKnownExtents:
			_convert(pyValue,od.hasKnownExtents);
			break;
		case o::minValue:
			_convert(pyValue,od.minValue);
			break;
		case o::maxValue:
			_convert(pyValue,od.maxValue);
			break;
		case o::isQuantized:
			_convert(pyValue,od.isQuantized);
			break;					
		case o::quantizeStep:
			_convert(pyValue,od.quantizeStep);
			break;
		case o::sampleType:
			// implements specific conversion!
			od.sampleType = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[PyValue_To_String(pyValue)];
			break;
		case o::sampleRate:
			_convert(pyValue,od.sampleRate);
			break;
		case o::hasDuration:
			_convert(pyValue,od.hasDuration);
			break;
		default:
			cerr << "Invalid key in Vamp OutputDescriptor: " << key << endl; 
	}
}

/// ParameterDescriptor
void
PyTypeInterface::SetValue(Vamp::Plugin::ParameterDescriptor& pd, std::string& key, PyObject* pyValue) const
{
	switch (parmKeys[key]) 
	{
		case p::not_found : 	
			cerr << "Unknown key in Vamp ParameterDescriptor: " << key << endl; 
			break;
		case p::identifier:
			_convert(pyValue,pd.identifier);
			break;				
		case p::name:
			_convert(pyValue,pd.name);
			break;
		case p::description: 	
			_convert(pyValue,pd.description);
			break; 								
		case p::unit:
			_convert(pyValue,pd.unit);
			break; 																		
		case p::minValue:	
			_convert(pyValue,pd.minValue);
			break;
		case p::maxValue:
			_convert(pyValue,pd.maxValue);
			break;
		case p::defaultValue:
			_convert(pyValue,pd.defaultValue);
			break;
		case p::isQuantized:
			_convert(pyValue,pd.isQuantized);
			break;									
		case p::quantizeStep:
			_convert(pyValue,pd.quantizeStep);
			break;
		default : 	
			cerr << "Invalid key in Vamp ParameterDescriptor: " << key << endl; 
	}
}

/// Feature (it's like a Descriptor)
bool
PyTypeInterface::SetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const
{
	bool found = true;
	switch (ffKeys[key])
	{
		case unknown :
			cerr << "Unknown key in Vamp Feature: " << key << endl; 
			found = false;
			break;
		case hasTimestamp:
			_convert(pyValue,feature.hasTimestamp);
			break;				
		case timeStamp:
			_convert(pyValue,feature.timestamp);
			break;
		case hasDuration: 	
			_convert(pyValue,feature.hasDuration);
			break;
		case duration:
			_convert(pyValue,feature.duration);
			break;
		case values:
			_convert(pyValue,feature.values);
			break; 								
		case label:
			_convert(pyValue,feature.label);
			break;
		default:
			found = false;
	}
	return found;
}

/*
/// Feature (it's like a Descriptor)
bool
PyTypeInterface::GetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const
{
	bool found = true;
	switch (ffKeys[key])
	{
		case unknown :
			cerr << "Unknown key in Vamp Feature: " << key << endl; 
			found = false;
			break;
		case hasTimestamp:
			_convert(pyValue,feature.hasTimestamp);
			// pyValue = PyValue_From_CValue(feature.hasTimestamp)
			break;				
		case timeStamp:
			_convert(pyValue,feature.timestamp);
			break;
		case hasDuration: 	
			_convert(pyValue,feature.hasDuration);
			break;
		case duration:
			_convert(pyValue,feature.duration);
			break;
		case values:
			_convert(pyValue,feature.values);
			break; 								
		case label:
			_convert(pyValue,feature.label); //vector<string>
			break;
		default:
			found = false;
	}
	return found;
}
*/

/*			   			  	Error handling		   			  		*/

void
PyTypeInterface::setValueError (std::string message, bool strict) const
{
	m_error = true;
	m_errorQueue.push(ValueError(message,strict));
}

/// return a reference to the last error or creates a new one.
PyTypeInterface::ValueError&
PyTypeInterface::_lastError() const 
{
	m_error = false;
	if (!m_errorQueue.empty()) return m_errorQueue.back();
	else {
		m_errorQueue.push(ValueError("Type conversion error.",m_strict));
		return m_errorQueue.back();
	}
}

/// return the last error message and clear the error flag
const PyTypeInterface::ValueError&
PyTypeInterface::lastError() const 
{
	// PyTypeInterface *self = const_cast<PyTypeInterface*> (this);
	m_error = false;
	if (!m_errorQueue.empty()) return m_errorQueue.back();
	else return m_noError;
}

/// iterate over the error message queue and pop the oldest item
PyTypeInterface::ValueError 
PyTypeInterface::getError() const
{
	if (!m_errorQueue.empty()) {
		PyTypeInterface::ValueError e = m_errorQueue.front();
		m_errorQueue.pop();
		if (m_errorQueue.empty()) m_error = false;
		return e;
	}
	else {
		m_error = false;
		return PyTypeInterface::ValueError();
	}
}

/*			   			  	Utilities						  		*/

/// get the type name of an object
std::string
PyTypeInterface::PyValue_Get_TypeName(PyObject* pyValue) const
{
	PyObject *pyType = PyObject_Type(pyValue);
	if (!pyType) return std::string ("< unknown type >");
	PyObject *pyString = PyObject_Str(pyType);
	if (!pyString)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		Py_CLEAR(pyType);
		return std::string ("< unknown type >");
	}
	char *cstr = PyString_AS_STRING(pyString);
	if (!cstr)
	{
		if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
		Py_DECREF(pyType);
		Py_CLEAR(pyString);
		cerr << "Warning: Object type name could not be found." << endl;
		return std::string("< unknown type >");
	}
	Py_DECREF(pyType);
	Py_DECREF(pyString);
	return std::string(cstr);
}

bool
PyTypeInterface::initMaps() const
{

	if (isMapInitialised) return true;

	outKeys["identifier"] = o::identifier;
	outKeys["name"] = o::name;
	outKeys["description"] = o::description;
	outKeys["unit"] = o::unit;
	outKeys["hasFixedBinCount"] = o::hasFixedBinCount; 
	outKeys["binCount"] = o::binCount;
	outKeys["binNames"] = o::binNames;
	outKeys["hasKnownExtents"] = o::hasKnownExtents;
	outKeys["minValue"] = o::minValue;
	outKeys["maxValue"] = o::maxValue;
	outKeys["isQuantized"] = o::isQuantized;
	outKeys["quantizeStep"] = o::quantizeStep;
	outKeys["sampleType"] = o::sampleType;
	outKeys["sampleRate"] = o::sampleRate;
	outKeys["hasDuration"] = o::hasDuration;

	sampleKeys["OneSamplePerStep"] = OneSamplePerStep;
	sampleKeys["FixedSampleRate"] = FixedSampleRate;
	sampleKeys["VariableSampleRate"] = VariableSampleRate;

	ffKeys["hasTimestamp"] = hasTimestamp;
	ffKeys["timeStamp"] = timeStamp;
	ffKeys["hasDuration"] = hasDuration;
	ffKeys["duration"] = duration;
	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;
	parmKeys["quantizeStep"] = p::quantizeStep;

	isMapInitialised = true;
	return true;
}