Mercurial > hg > vampy
diff PyTypeInterface.cpp @ 37:27bab3a16c9a vampy2final
new branch Vampy2final
author | fazekasgy |
---|---|
date | Mon, 05 Oct 2009 11:28:00 +0000 |
parents | |
children | 94d09b4027aa |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyTypeInterface.cpp Mon Oct 05 11:28:00 2009 +0000 @@ -0,0 +1,1180 @@ +/* + + * 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.) + +*/ + +#include <Python.h> + +#ifdef HAVE_NUMPY +#define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API +#define NO_IMPORT_ARRAY +#include "numpy/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), + error(m_error) // const public reference for easy access +{ +} + +PyTypeInterface::~PyTypeInterface() +{ +} + +/// floating point numbers (TODO: check numpy.float128) +float +PyTypeInterface::PyValue_To_Float(PyObject* pyValue) const +{ + // convert float + if (pyValue && PyFloat_Check(pyValue)) + //TODO: check for limits here (same on most systems) + return (float) PyFloat_AS_DOUBLE(pyValue); + + if (pyValue == NULL) + { + setValueError("Error while converting float object.",m_strict); + return 0.0; + } + + // 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)) + { + PyObject* pyFloat = PyNumber_Float(pyValue); // new ref + if (!pyFloat) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting " + PyValue_Get_TypeName(pyValue) + " object to float.",m_strict); + return 0.0; + } + float rValue = (float) PyFloat_AS_DOUBLE(pyFloat); + Py_DECREF(pyFloat); + return rValue; + } +/* + // 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 > (Py_ssize_t)FLT_MAX || rValue < (Py_ssize_t)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) and 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 to float. "; + 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_Float failed. " << msg << endl; +#endif + 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 + // speed is not critical in the use of this type by Vamp + // 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 ("Error converting string to size_t.",m_strict); + return 0; + } + } + + // convert the first element of iterable sequences + if (PySequence_Check(pyValue) and 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); + setValueError("Could not convert sequence element to size_t. ",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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_Size_t failed. " << msg << endl; +#endif + return 0; +} + +/// long and int +long +PyTypeInterface::PyValue_To_Long(PyObject* pyValue) const +{ + // most common case: convert int (faster) + if (pyValue && PyInt_Check(pyValue)) { + // if the object is not NULL and verified, this macro just extracts the value. + return PyInt_AS_LONG(pyValue); + } + + // long + if (PyLong_Check(pyValue)) { + long rValue = PyLong_AsLong(pyValue); + if (PyErr_Occurred()) { + PyErr_Print(); PyErr_Clear(); + setValueError("Error while converting long object.",m_strict); + return 0; + } + return rValue; + } + + if (m_strict) { + setValueError("Strict conversion error: object is not integer or long integer.",m_strict); + return 0; + } + + // convert all objects supporting the number protocol + if (PyNumber_Check(pyValue)) + { + // 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 (rValue > LONG_MAX || rValue < LONG_MIN) + { + setValueError("Overflow error. Object can not be converted to size_t.",m_strict); + return 0; + } + return (long) rValue; + } + + // 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 long.",m_strict); + return 0; + } + long rValue = this->PyValue_To_Long(pyLong); + if (!m_error) { + Py_DECREF(pyLong); + return rValue; + } else { + Py_CLEAR(pyLong); + setValueError ("Error converting string to long.",m_strict); + return 0; + } + } + + // convert the first element of iterable sequences + if (PySequence_Check(pyValue) and PySequence_Size(pyValue) > 0) + { + PyObject* item = PySequence_GetItem(pyValue,0); + if (item) + { + size_t rValue = this->PyValue_To_Long(item); + if (!m_error) { + Py_DECREF(item); + return rValue; + } else { + Py_CLEAR(item); + setValueError("Could not convert sequence element to long. ",m_strict); + return 0; + } + } + } + + // give up + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to long is not possible."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_Long failed. " << msg << endl; +#endif + 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 in 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_Bool failed. " << msg << endl; +#endif + 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(); + } + + // accept None as empty string + if (pyValue == Py_None) return std::string(); + + // convert list or tuple: empties are turned into empty strings conventionally + 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); + setValueError("Could not convert sequence element to string.",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. "; + 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_String failed. " << msg << endl; +#endif + return std::string(); +} + +/* C Values to Py Values */ + + +PyObject* +PyTypeInterface::PyValue_From_CValue(const char* cValue) const +{ + // returns new reference +#ifdef _DEBUG + if (!cValue) { + std::string msg = "PyTypeInterface::PyValue_From_CValue: Null pointer encountered while converting from const char* ."; + cerr << msg << endl; + setValueError(msg,m_strict); + return NULL; + } +#endif + PyObject *pyValue = PyString_FromString(cValue); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting from char* or string.",m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_From_CValue: Interpreter failed to convert from const char*" << endl; +#endif + 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_From_CValue: Interpreter failed to convert from size_t" << endl; +#endif + 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_From_CValue: Interpreter failed to convert from float or double" << endl; +#endif + 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); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_From_CValue: Interpreter failed to convert from bool" << endl; +#endif + 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 *pyList) const +{ + + std::vector<std::string> Output; + std::string ListElement; + PyObject *pyString = NULL; + + if (PyList_Check(pyList)) { + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + //Get next list item (Borrowed Reference) + pyString = PyList_GET_ITEM(pyList,i); + ListElement = (string) PyString_AsString(PyObject_Str(pyString)); + Output.push_back(ListElement); + } + return Output; + } +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_StringVector: Warning: Value is not list of strings." << endl; +#endif + + /// Assume a single value that can be casted as string + /// this allows to write e.g. Feature.label = 5.2 instead of ['5.2'] + Output.push_back(PyValue_To_String(pyList)); + if (m_error) { + std::string msg = "Value is not list of strings nor can be casted as string. "; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_StringVector failed. " << msg << endl; +#endif + } + 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 +{ + +#ifdef HAVE_NUMPY + // there are four types of values we may receive from a numpy process: + // * a python scalar, + // * an array scalar, (e.g. numpy.float32) + // * an array with nd = 0 (0D array) + // * an array with nd > 0 + + /// check for scalars + if (PyArray_CheckScalar(pyValue) || PyFloat_Check(pyValue)) { + + std::vector<float> Output; + + // we rely on the behaviour the scalars are either floats + // or support the number protocol + // TODO: a potential optimisation is to handle them directly + Output.push_back(PyValue_To_Float(pyValue)); + return Output; + } + + /// numpy array + if (PyArray_CheckExact(pyValue)) + return PyArray_To_FloatVector(pyValue); + +#endif + + /// python list of floats (backward compatible) + if (PyList_Check(pyValue)) { + return PyList_To_FloatVector(pyValue); + } + + std::vector<float> Output; + + /// finally assume a single value supporting the number protocol + /// this allows to write e.g. Feature.values = 5 instead of [5.00] + Output.push_back(PyValue_To_Float(pyValue)); + if (m_error) { + std::string msg = "Value is not list or array of floats nor can be casted as float. "; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_FloatVector failed." << msg << endl; +#endif + } + return Output; +} + +//convert a list of python floats +std::vector<float> +PyTypeInterface::PyList_To_FloatVector (PyObject *inputList) const +{ + std::vector<float> Output; + +#ifdef _DEBUG + // This is a low level function normally called from + // PyValue_To_FloatVector(). Checking for list is not required. + if (!PyList_Check(inputList)) { + std::string msg = "Value is not list."; + setValueError(msg,true); + cerr << "PyTypeInterface::PyList_To_FloatVector failed. " << msg << endl; + return Output; + } +#endif + + float ListElement; + PyObject *pyFloat = NULL; + PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList); + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { + + // pyFloat = PyList_GET_ITEM(inputList,i); + pyFloat = pyObjectArray[i]; + +#ifdef _DEBUG + if (!pyFloat) { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + cerr << "PyTypeInterface::PyList_To_FloatVector: Could not obtain list element: " + << i << " PyList_GetItem returned NULL! Skipping value." << endl; + continue; + } +#endif + + // ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); + ListElement = PyValue_To_Float(pyFloat); + + +#ifdef _DEBUG_VALUES + 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; + +#ifdef _DEBUG + // This is a low level function, normally called from + // PyValue_To_FloatVector(). Checking the array here is not required. + if (!PyArray_Check(pyValue)) { + std::string msg = "Object has no array interface."; + setValueError(msg,true); + cerr << "PyTypeInterface::PyArray_To_FloatVector failed. " << msg << endl; + return Output; + } +#endif + + PyArrayObject* pyArray = (PyArrayObject*) pyValue; + PyArray_Descr* descr = pyArray->descr; + + /// check raw data and descriptor pointers + if (pyArray->data == 0 || descr == 0) { + std::string msg = "NumPy array with NULL data or descriptor pointer encountered."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyArray_To_FloatVector failed. Error: " << msg << endl; +#endif + return Output; + } + + /// check dimensions + if (pyArray->nd != 1) { + std::string msg = "NumPy array must be a one dimensional vector."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyArray_To_FloatVector failed. Error: " << msg << " Dims: " << (int) pyArray->nd << endl; +#endif + return Output; + } + +#ifdef _DEBUG_VALUES + cerr << "PyTypeInterface::PyArray_To_FloatVector: Numpy array verified." << endl; +#endif + + /// check strides (useful if array is not continuous) + size_t strides = *((size_t*) pyArray->strides); + + /// convert the array + switch (descr->type_num) + { + case NPY_FLOAT : // dtype='float32' + return PyArray_Convert<float,float>(pyArray->data,pyArray->dimensions[0],strides); + case NPY_DOUBLE : // dtype='float64' + return PyArray_Convert<float,double>(pyArray->data,pyArray->dimensions[0],strides); + case NPY_INT : // dtype='int' + return PyArray_Convert<float,int>(pyArray->data,pyArray->dimensions[0],strides); + case NPY_LONG : // dtype='long' + return PyArray_Convert<float,long>(pyArray->data,pyArray->dimensions[0],strides); + default : + std::string msg = "Unsupported value type in NumPy array object."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyArray_To_FloatVector failed. Error: " << msg << endl; +#endif + return Output; + } +} +#endif + + +/// FeatureSet (an integer map of OutputLists) +Vamp::Plugin::FeatureSet +PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const +{ + Vamp::Plugin::FeatureSet rFeatureSet; + + /// Convert PyFeatureSet + if (PyFeatureSet_CheckExact(pyValue)) { + + Py_ssize_t pyPos = 0; + PyObject *pyKey, *pyDictValue; // Borrowed References + int key; + // bool it_error = false; + + m_error = false; + while (PyDict_Next(pyValue, &pyPos, &pyKey, &pyDictValue)) + { + key = (int) PyInt_AS_LONG(pyKey); +#ifdef _DEBUG_VALUES + cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl; +#endif + // DictValue -> Vamp::FeatureList + PyValue_To_rValue(pyDictValue,rFeatureSet[key]); + if (m_error) { + // it_error = true; + lastError() << " in output number: " << key; + } + } + // if (it_error) m_error = true; + if (!m_errorQueue.empty()) { + setValueError("Error while converting FeatureSet.",m_strict); + } + return rFeatureSet; + } + + /// Convert Python list (backward compatibility) + if (PyList_Check(pyValue)) { + + PyObject *pyFeatureList; // This will be borrowed reference + + //Parse Output List for each element (FeatureSet) + m_error = false; + 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]); + if (m_error) { + lastError() << " in output number: " << i; + } + } + if (!m_errorQueue.empty()) m_error = true; + return rFeatureSet; + } + + /// accept no return values + if (pyValue == Py_None) return rFeatureSet; + + /// give up + std::string msg = "Unsupported return type. Expected list or vampy.FeatureSet(). "; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_FeatureSet failed. Error: " << msg << endl; +#endif + return rFeatureSet; +} + +Vamp::RealTime +PyTypeInterface::PyValue_To_RealTime(PyObject* pyValue) const +{ +// We accept integer sample counts (for backward compatibility) +// or PyRealTime objects and convert them to Vamp::RealTime + + if (PyRealTime_CheckExact(pyValue)) + { + /// just create a copy of the wrapped object + return Vamp::RealTime(*PyRealTime_AS_REALTIME(pyValue)); + } + + // assume integer sample count + long sampleCount = PyValue_To_Long(pyValue); + if (m_error) { + std::string msg = "Unexpected value passed as RealTime.\nMust be vampy.RealTime type or integer sample count."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_RealTime failed. " << msg << endl; +#endif + return Vamp::RealTime(); + } + +#ifdef _DEBUG_VALUES + 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 + +} + +Vamp::Plugin::OutputDescriptor::SampleType +PyTypeInterface::PyValue_To_SampleType(PyObject* pyValue) const +{ + /// convert simulated enum values + /// { OneSamplePerStep,FixedSampleRate,VariableSampleRate } + if (PyInt_CheckExact(pyValue)) { + long lst = PyInt_AS_LONG(pyValue); + if (lst<0 || lst>2) { + setValueError("Overflow error. SampleType has to be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type.",m_strict); + return Vamp::Plugin::OutputDescriptor::SampleType(); + } + return (Vamp::Plugin::OutputDescriptor::SampleType) lst; + } + + /// convert string (backward compatible) + if (PyString_CheckExact(pyValue)) { + Vamp::Plugin::OutputDescriptor::SampleType st; + st = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[PyValue_To_String(pyValue)]; + if (m_error) { + std::string msg = "Unexpected value passed as SampleType. Must be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type."; + setValueError(msg,m_strict); + return Vamp::Plugin::OutputDescriptor::SampleType(); + } + return st; + } + + /// give up + std::string msg = "Unsupported return type. Expected one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_SampleType failed. Error: " << msg << endl; +#endif + return Vamp::Plugin::OutputDescriptor::SampleType(); +} + +Vamp::Plugin::InputDomain +PyTypeInterface::PyValue_To_InputDomain(PyObject* pyValue) const +{ + /// convert simulated enum values { TimeDomain,FrequencyDomain } + if (PyInt_CheckExact(pyValue)) { + long lst = PyInt_AS_LONG(pyValue); + if (lst!=0 && lst!=1) { + setValueError("Overflow error. InputDomain has to be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type.",m_strict); + return Vamp::Plugin::InputDomain(); + } + return (Vamp::Plugin::InputDomain) lst; + } + + /// convert string (backward compatible) + if (PyString_CheckExact(pyValue)) { + Vamp::Plugin::InputDomain id; + id = (PyValue_To_String(pyValue) == "FrequencyDomain")?Vamp::Plugin::FrequencyDomain:Vamp::Plugin::TimeDomain; + if (m_error) + { + std::string msg = "Unexpected value passed as SampleType. Must be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type."; + setValueError(msg,m_strict); + return Vamp::Plugin::InputDomain(); + } + return id; + } + + /// give up + std::string msg = "Unsupported return type. Expected one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type."; + setValueError(msg,m_strict); +#ifdef _DEBUG + cerr << "PyTypeInterface::PyValue_To_InputDomain failed. Error: " << msg << endl; +#endif + return Vamp::Plugin::InputDomain(); +} + + +/// OutputDescriptor +void +PyTypeInterface::SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const +{ + switch (outKeys[key]) + { + case o::not_found: + setValueError("Unknown key in Vamp OutputDescriptor",m_strict); + 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: + _convert(pyValue,od.sampleType); + break; + case o::sampleRate: + _convert(pyValue,od.sampleRate); + break; + case o::hasDuration: + _convert(pyValue,od.hasDuration); + break; + default: + setValueError("Unknown key in Vamp OutputDescriptor",m_strict); + 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 : + setValueError("Unknown key in Vamp ParameterDescriptor",m_strict); + 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 : + setValueError("Unknown key in Vamp ParameterDescriptor",m_strict); + 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 : + setValueError("Unknown key in Vamp Feature",m_strict); + 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: + setValueError("Unknown key in Vamp Feature",m_strict); + 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(); + } +} + +/// helper function to iterate over the error message queue: +/// pops 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) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + return std::string ("< unknown type >"); + } + PyObject *pyString = PyObject_Str(pyType); + if (!pyString) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_CLEAR(pyType); + return std::string ("< unknown type >"); + } + char *cstr = PyString_AS_STRING(pyString); + if (!cstr) + { + cerr << "Warning: Object type name could not be found." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_DECREF(pyType); + Py_CLEAR(pyString); + 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; // this is the correct one + ffKeys["timeStamp"] = timestamp; // backward compatible + 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; +}