Chris@26: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@26: Chris@26: /* Chris@28: VampyHost Chris@26: Chris@28: Use Vamp audio analysis plugins in Python Chris@26: Chris@28: Gyorgy Fazekas and Chris Cannam Chris@28: Centre for Digital Music, Queen Mary, University of London Chris@117: Copyright 2008-2015 Queen Mary, University of London Chris@26: Chris@28: Permission is hereby granted, free of charge, to any person Chris@28: obtaining a copy of this software and associated documentation Chris@28: files (the "Software"), to deal in the Software without Chris@28: restriction, including without limitation the rights to use, copy, Chris@28: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@28: of the Software, and to permit persons to whom the Software is Chris@28: furnished to do so, subject to the following conditions: Chris@26: Chris@28: The above copyright notice and this permission notice shall be Chris@28: included in all copies or substantial portions of the Software. Chris@26: Chris@28: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@28: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@28: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@28: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@28: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@28: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@28: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@26: Chris@28: Except as contained in this notice, the names of the Centre for Chris@28: Digital Music; Queen Mary, University of London; and the authors Chris@28: shall not be used in advertising or otherwise to promote the sale, Chris@28: use or other dealings in this Software without prior written Chris@28: authorization. Chris@26: */ Chris@26: Chris@26: #include Chris@26: Chris@112: #include "VectorConversion.h" Chris@48: #include "FloatConversion.h" Chris@112: #include "StringConversion.h" Chris@26: Chris@26: #include Chris@26: #include Chris@26: Chris@28: using namespace std; Chris@26: Chris@26: /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS Chris@28: (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */ Chris@26: Chris@29: VectorConversion::VectorConversion() : Chris@28: m_error(false), Chris@28: error(m_error) // const public reference for easy access Chris@26: { Chris@26: } Chris@26: Chris@29: VectorConversion::~VectorConversion() Chris@26: { Chris@26: } Chris@26: Chris@26: /// floating point numbers (TODO: check numpy.float128) Chris@26: float Chris@29: VectorConversion::PyValue_To_Float(PyObject* pyValue) const Chris@26: { Chris@48: if (FloatConversion::check(pyValue)) { Chris@48: return FloatConversion::convert(pyValue); Chris@43: } Chris@43: Chris@43: setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float, int, or long."); Chris@28: return 0.0; Chris@26: } Chris@26: Chris@28: vector Chris@29: VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const Chris@26: { Chris@28: /// numpy array Chris@28: if (PyArray_CheckExact(pyValue)) Chris@28: return PyArray_To_FloatVector(pyValue); Chris@26: Chris@28: /// python list of floats (backward compatible) Chris@28: if (PyList_Check(pyValue)) { Chris@28: return PyList_To_FloatVector(pyValue); Chris@28: } Chris@26: Chris@28: string msg = "Value is not list or array of floats"; Chris@28: setValueError(msg); Chris@26: #ifdef _DEBUG Chris@29: cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl; Chris@26: #endif Chris@28: return vector(); Chris@26: } Chris@26: Chris@28: vector Chris@29: VectorConversion::PyList_To_FloatVector (PyObject *inputList) const Chris@26: { Chris@28: vector v; Chris@26: Chris@28: if (!PyList_Check(inputList)) { Chris@28: setValueError("Value is not a list"); Chris@28: return v; Chris@28: } Chris@26: Chris@28: PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList); Chris@28: int n = PyList_GET_SIZE(inputList); Chris@26: Chris@28: for (int i = 0; i < n; ++i) { Chris@28: v.push_back(PyValue_To_Float(pyObjectArray[i])); Chris@28: } Chris@28: Chris@28: return v; Chris@26: } Chris@26: Chris@28: vector Chris@29: VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const Chris@26: { Chris@28: vector v; Chris@26: Chris@28: if (!PyArray_Check(pyValue)) { Chris@28: setValueError("Value is not an array"); Chris@28: return v; Chris@28: } Chris@28: Chris@28: PyArrayObject* pyArray = (PyArrayObject*) pyValue; Chris@28: PyArray_Descr* descr = PyArray_DESCR(pyArray); Chris@28: Chris@28: if (PyArray_DATA(pyArray) == 0 || descr == 0) { Chris@28: string msg = "NumPy array with NULL data or descriptor pointer encountered."; Chris@28: setValueError(msg); Chris@28: return v; Chris@28: } Chris@28: Chris@28: if (PyArray_NDIM(pyArray) != 1) { Chris@40: string msg = "NumPy array must be a one-dimensional vector."; Chris@28: setValueError(msg); Chris@28: return v; Chris@28: } Chris@28: Chris@28: /// check strides (useful if array is not continuous) Chris@28: size_t strides = *((size_t*) PyArray_STRIDES(pyArray)); Chris@28: Chris@28: /// convert the array Chris@28: switch (descr->type_num) { Chris@28: Chris@28: case NPY_FLOAT : // dtype='float32' Chris@28: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); Chris@28: case NPY_DOUBLE : // dtype='float64' Chris@28: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); Chris@28: case NPY_INT : // dtype='int' Chris@28: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); Chris@28: case NPY_LONG : // dtype='long' Chris@28: return PyArray_Convert(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides); Chris@28: default : Chris@28: string msg = "Unsupported value type in NumPy array object."; Chris@28: setValueError(msg); Chris@26: #ifdef _DEBUG Chris@29: cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl; Chris@26: #endif Chris@28: return v; Chris@28: } Chris@26: } Chris@26: Chris@40: vector > Chris@40: VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const Chris@40: { Chris@40: vector > v; Chris@40: Chris@40: if (!PyArray_Check(pyValue)) { Chris@40: setValueError("Value is not an array"); Chris@40: return v; Chris@40: } Chris@40: Chris@40: PyArrayObject* pyArray = (PyArrayObject*) pyValue; Chris@40: PyArray_Descr* descr = PyArray_DESCR(pyArray); Chris@40: Chris@40: if (PyArray_DATA(pyArray) == 0 || descr == 0) { Chris@40: string msg = "NumPy array with NULL data or descriptor pointer encountered."; Chris@40: setValueError(msg); Chris@40: return v; Chris@40: } Chris@40: Chris@40: if (PyArray_NDIM(pyArray) != 2) { Chris@40: string msg = "NumPy array must be a two-dimensional matrix."; Chris@40: setValueError(msg); Chris@40: return v; Chris@40: } Chris@40: Chris@40: /// check strides (useful if array is not continuous) Chris@41: size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray); Chris@40: Chris@40: /// convert the array Chris@40: for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) { Chris@40: Chris@40: vector vv; Chris@40: Chris@40: switch (descr->type_num) { Chris@40: Chris@40: case NPY_FLOAT : // dtype='float32' Chris@41: vv = PyArray_Convert(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); Chris@40: break; Chris@40: case NPY_DOUBLE : // dtype='float64' Chris@41: vv = PyArray_Convert(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); Chris@40: break; Chris@40: case NPY_INT : // dtype='int' Chris@41: vv = PyArray_Convert(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); Chris@40: break; Chris@40: case NPY_LONG : // dtype='long' Chris@41: vv = PyArray_Convert(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]); Chris@40: break; Chris@40: default : Chris@40: string msg = "Unsupported value type in NumPy array object."; Chris@40: cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl; Chris@40: setValueError(msg); Chris@40: return v; Chris@40: } Chris@40: Chris@40: v.push_back(vv); Chris@40: } Chris@40: Chris@40: return v; Chris@40: } Chris@40: Chris@26: PyObject * Chris@29: VectorConversion::PyArray_From_FloatVector(const vector &v) const Chris@26: { Chris@28: npy_intp ndims[1]; Chris@28: ndims[0] = (int)v.size(); Chris@28: PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT); Chris@28: float *data = (float *)PyArray_DATA((PyArrayObject *)arr); Chris@28: for (int i = 0; i < ndims[0]; ++i) { Chris@28: data[i] = v[i]; Chris@28: } Chris@28: return arr; Chris@26: } Chris@26: Chris@26: PyObject * Chris@29: VectorConversion::PyValue_From_StringVector(const vector &v) const Chris@26: { Chris@28: PyObject *pyList = PyList_New(v.size()); Chris@28: for (size_t i = 0; i < v.size(); ++i) { Chris@112: PyObject *pyStr = StringConversion().string2py(v[i].c_str()); Chris@28: PyList_SET_ITEM(pyList, i, pyStr); Chris@28: } Chris@28: return pyList; Chris@26: } Chris@26: Chris@26: Chris@28: /* Error handling */ Chris@26: Chris@26: void Chris@29: VectorConversion::setValueError (string message) const Chris@26: { Chris@28: m_error = true; Chris@28: m_errorQueue.push(ValueError(message)); Chris@26: } Chris@26: Chris@26: /// return a reference to the last error or creates a new one. Chris@26: ValueError& Chris@29: VectorConversion::lastError() const Chris@26: { Chris@28: m_error = false; Chris@28: if (!m_errorQueue.empty()) return m_errorQueue.back(); Chris@28: else { Chris@28: m_errorQueue.push(ValueError("Type conversion error.")); Chris@28: return m_errorQueue.back(); Chris@28: } Chris@26: } Chris@26: Chris@26: /// helper function to iterate over the error message queue: Chris@26: /// pops the oldest item Chris@26: ValueError Chris@29: VectorConversion::getError() const Chris@26: { Chris@28: if (!m_errorQueue.empty()) { Chris@28: ValueError e = m_errorQueue.front(); Chris@28: m_errorQueue.pop(); Chris@28: if (m_errorQueue.empty()) m_error = false; Chris@28: return e; Chris@28: } Chris@28: else { Chris@28: m_error = false; Chris@28: return ValueError(); Chris@28: } Chris@26: } Chris@26: Chris@28: /* Utilities */ Chris@26: Chris@26: /// get the type name of an object Chris@28: string Chris@29: VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const Chris@26: { Chris@28: PyObject *pyType = PyObject_Type(pyValue); Chris@112: if (!pyType) { Chris@28: cerr << "Warning: Object type name could not be found." << endl; Chris@28: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Chris@28: return string ("< unknown type >"); Chris@28: } Chris@28: PyObject *pyString = PyObject_Str(pyType); Chris@112: if (!pyString) { Chris@28: cerr << "Warning: Object type name could not be found." << endl; Chris@28: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Chris@28: Py_CLEAR(pyType); Chris@28: return string ("< unknown type >"); Chris@28: } Chris@112: string str = StringConversion().py2string(pyString); Chris@112: if (str == "") { Chris@28: cerr << "Warning: Object type name could not be found." << endl; Chris@28: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Chris@28: Py_DECREF(pyType); Chris@28: Py_CLEAR(pyString); Chris@28: return string("< unknown type >"); Chris@28: } Chris@28: Py_DECREF(pyType); Chris@28: Py_DECREF(pyString); Chris@112: return str; Chris@26: }