Mercurial > hg > vampy
diff PyTypeInterface.cpp @ 71:40a01bb24209 vampyhost
Pull apart some type conversion classes for possible use in VamPy Host
author | Chris Cannam |
---|---|
date | Thu, 20 Nov 2014 13:02:50 +0000 |
parents | 6c755f3e1173 |
children | ffaa1fb3d7de |
line wrap: on
line diff
--- a/PyTypeInterface.cpp Mon Nov 17 14:07:00 2014 +0000 +++ b/PyTypeInterface.cpp Thu Nov 20 13:02:50 2014 +0000 @@ -56,707 +56,6 @@ { } -/// 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 object " + PyValue_Get_TypeName(pyValue) + " to float. ",m_strict); - return 0.0; - } - - // in strict mode we will not try harder - if (m_strict) { - setValueError("Strict conversion error: object" + PyValue_Get_TypeName(pyValue) +" 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) && 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; - } - // this test is nonsense -- neither part can occur - // owing to range of data types -- size_t is at least - // as big as long, and unsigned is always non-negative -/* - 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) && 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) && 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 -if (m_numpyInstalled) -{ - // 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; -} - -// if numpy is not installed this will not be called, -// therefor we do not check again -#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(pyArray); - - /// check raw data and descriptor pointers - if (PyArray_DATA(pyArray) == 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_NDIM(pyArray) != 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_NDIM(pyArray) << 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(pyArray)); - - /// convert the array - switch (descr->type_num) - { - case NPY_FLOAT : // dtype='float32' - return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_DOUBLE : // dtype='float64' - return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_INT : // dtype='int' - return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); - case NPY_LONG : // dtype='long' - return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[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 FeatureLists) Vamp::Plugin::FeatureSet PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const @@ -836,8 +135,8 @@ } // assume integer sample count - long sampleCount = PyValue_To_Long(pyValue); - if (m_error) { + long sampleCount = m_conv.PyValue_To_Long(pyValue); + if (m_conv.error) { std::string msg = "Unexpected value passed as RealTime.\nMust be vampy.RealTime type or integer sample count."; setValueError(msg,m_strict); #ifdef _DEBUG @@ -874,8 +173,8 @@ /// 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) { + st = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[m_conv.PyValue_To_String(pyValue)]; + if (m_conv.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(); @@ -908,8 +207,8 @@ /// 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) + id = (m_conv.PyValue_To_String(pyValue) == "FrequencyDomain")?Vamp::Plugin::FrequencyDomain:Vamp::Plugin::TimeDomain; + if (m_conv.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); @@ -927,6 +226,159 @@ return Vamp::Plugin::InputDomain(); } +/* Convert Sample Buffers to Python */ + +/// passing the sample buffers as builtin python lists +/// Optimization: using fast sequence protocol +inline PyObject* +PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) +{ + //create a list of lists (new references) + PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); + + // Pack samples into a Python List Object + // pyFloat/pyComplex types will always be new references, + // they will be freed when the lists are deallocated. + + PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); + for (size_t i=0; i < channels; ++i) { + + size_t arraySize; + if (dtype==Vamp::Plugin::FrequencyDomain) + arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used + else + arraySize = blockSize; + + PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize); + PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList); + + // Note: passing a complex list crashes the C-style plugin + // when it tries to convert it to a numpy array directly. + // This plugin will be obsolete, but we have to find a way + // to prevent such crash: possibly a numpy bug, + // works fine above 1.0.4 + + switch (dtype) //(Vamp::Plugin::TimeDomain) + { + case Vamp::Plugin::TimeDomain : + + for (size_t j = 0; j < arraySize; ++j) { + PyObject *pyFloat=PyFloat_FromDouble( + (double) inputBuffers[i][j]); + pySampleListArray[j] = pyFloat; + } + break; + + case Vamp::Plugin::FrequencyDomain : + + size_t k = 0; + for (size_t j = 0; j < arraySize; ++j) { + PyObject *pyComplex=PyComplex_FromDoubles( + (double) inputBuffers[i][k], + (double) inputBuffers[i][k+1]); + pySampleListArray[j] = pyComplex; + k += 2; + } + break; + + } + pyChannelListArray[i] = pySampleList; + } + return pyChannelList; +} + +/// numpy buffer interface: passing the sample buffers as shared memory buffers +/// Optimization: using sequence protocol for creating the buffer list +inline PyObject* +PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) +{ + //create a list of buffers (returns new references) + PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); + PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); + + // Expose memory using the Buffer Interface. + // This will pass a pointer which can be recasted in Python code + // as complex or float array using Numpy's frombuffer() method + // (this will not copy values just keep the starting adresses + // for each channel in a list) + Py_ssize_t bufferSize; + + if (dtype==Vamp::Plugin::FrequencyDomain) + bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2); + else + bufferSize = (Py_ssize_t) sizeof(float) * blockSize; + + for (size_t i=0; i < channels; ++i) { + PyObject *pyBuffer = PyBuffer_FromMemory + ((void *) (float *) inputBuffers[i],bufferSize); + pyChannelListArray[i] = pyBuffer; + } + return pyChannelList; +} + + +/// numpy array interface: passing the sample buffers as 2D numpy array +/// Optimization: using array API (needs numpy headers) +#ifdef HAVE_NUMPY +inline PyObject* +PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) +{ +/* +NOTE: We create a list of 1D Numpy arrays for each channel instead +of a matrix, because the address space of inputBuffers doesn't seem +to be continuous. Although the array strides could be calculated for +2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure +if this can be trusted, especially for more than 2 channels. + + cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl; + if (channels == 2) + cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl; + +*/ + + // create a list of arrays (returns new references) + PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); + PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); + + // Expose memory using the Numpy Array Interface. + // This will wrap an array objects around the data. + // (will not copy values just steal the starting adresses) + + int arraySize, typenum; + + switch (dtype) + { + case Vamp::Plugin::TimeDomain : + typenum = dtype_float32; //NPY_FLOAT; + arraySize = (int) blockSize; + break; + + case Vamp::Plugin::FrequencyDomain : + typenum = dtype_complex64; //NPY_CFLOAT; + arraySize = (int) (blockSize / 2) + 1; + break; + + default : + cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl; + return pyChannelList; + } + + // size for each dimension + npy_intp ndims[1]={arraySize}; + + for (size_t i=0; i < channels; ++i) { + PyObject *pyChannelArray = + //args: (dimensions, size in each dim, type kind, pointer to continuous array) + PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]); + // make it read-only: set all flags to false except NPY_C_CONTIGUOUS + //!!! what about NPY_ARRAY_OWNDATA? + PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff); + PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS); + pyChannelListArray[i] = pyChannelArray; + } + return pyChannelList; +} +#endif /// OutputDescriptor void @@ -1073,7 +525,7 @@ } -/* Error handling */ +/* Error handling */ void PyTypeInterface::setValueError (std::string message, bool strict) const @@ -1083,7 +535,7 @@ } /// return a reference to the last error or creates a new one. -PyTypeInterface::ValueError& +ValueError& PyTypeInterface::lastError() const { m_error = false; @@ -1096,56 +548,22 @@ /// helper function to iterate over the error message queue: /// pops the oldest item -PyTypeInterface::ValueError +ValueError PyTypeInterface::getError() const { if (!m_errorQueue.empty()) { - PyTypeInterface::ValueError e = m_errorQueue.front(); + ValueError e = m_errorQueue.front(); m_errorQueue.pop(); if (m_errorQueue.empty()) m_error = false; return e; } else { m_error = false; - return PyTypeInterface::ValueError(); + return 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); - -} +/* Utilities */ bool PyTypeInterface::initMaps() const