Mercurial > hg > vampy
diff PyTypeInterface.h @ 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 | 5664fe298af2 |
children | ffaa1fb3d7de |
line wrap: on
line diff
--- a/PyTypeInterface.h Mon Nov 17 14:07:00 2014 +0000 +++ b/PyTypeInterface.h Thu Nov 20 13:02:50 2014 +0000 @@ -12,7 +12,7 @@ /* PyTypeInterface: Type safe conversion utilities between Python types -and basic C/C++ types and Vamp API types. +and Vamp API types. See PyTypeConversions for basic C/C++ types. */ #ifndef _PY_TYPE_INTERFACE_H_ @@ -25,6 +25,7 @@ #include "numpy/arrayobject.h" #endif #include "PyExtensionModule.h" +#include "PyTypeConversions.h" #include <vector> #include <queue> #include <string> @@ -34,13 +35,6 @@ using std::cerr; using std::endl; -#ifdef HAVE_NUMPY -enum eArrayDataType { - dtype_float32 = (int) NPY_FLOAT, - dtype_complex64 = (int) NPY_CFLOAT - }; -#endif - namespace o { enum eOutDescriptors { not_found, @@ -95,79 +89,31 @@ label }; -/* C++ mapping of PyNone Type */ -struct NoneType {}; - class PyTypeInterface { + PyTypeConversions m_conv; + public: PyTypeInterface(); ~PyTypeInterface(); - // Data - class ValueError - { - public: - ValueError() {} - ValueError(std::string m, bool s) : message(m),strict(s) {} - std::string location; - std::string message; - bool strict; - std::string str() const { - return (location.empty()) ? message : message + "\nLocation: " + location;} - void print() const { cerr << str() << endl; } - template<typename V> ValueError &operator<< (const V& v) - { - std::ostringstream ss; - ss << v; - location += ss.str(); - return *this; - } - }; - // Utilities - void setStrictTypingFlag(bool b) {m_strict = b;} - void setNumpyInstalled(bool b) {m_numpyInstalled = b;} + void setStrictTypingFlag(bool b) {m_strict = b; m_conv.setStrictTypingFlag(b);} + void setNumpyInstalled(bool b) {m_numpyInstalled = b; m_conv.setNumpyInstalled(b); } ValueError getError() const; std::string PyValue_Get_TypeName(PyObject*) const; bool initMaps() const; - // Basic type conversion: Python to C++ - float PyValue_To_Float(PyObject*) const; - size_t PyValue_To_Size_t(PyObject*) const; - bool PyValue_To_Bool(PyObject*) const; - std::string PyValue_To_String(PyObject*) const; - long PyValue_To_Long(PyObject*) const; - // int PyValue_To_Int(PyObject* pyValue) const; - - - // C++ to Python - PyObject *PyValue_From_CValue(const char*) const; - PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); } - PyObject *PyValue_From_CValue(size_t) const; - PyObject *PyValue_From_CValue(double) const; - PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); } - PyObject *PyValue_From_CValue(bool) const; - - // Sequence types - std::vector<std::string> PyValue_To_StringVector (PyObject*) const; - std::vector<float> PyValue_To_FloatVector (PyObject*) const; - std::vector<float> PyList_To_FloatVector (PyObject*) const; - // Input buffers to Python PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype); PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype); // Numpy types #ifdef HAVE_NUMPY - std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const; PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype); #endif - - - -/* Template functions */ +/* Template functions */ /// Common wrappers to set values in Vamp API structs. (to be used in template functions) @@ -211,9 +157,9 @@ //Python Dictionary Iterator: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue)) { - std::string key = PyValue_To_String(pyKey); + std::string key = m_conv.PyValue_To_String(pyKey); #ifdef _DEBUG_VALUES - cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl; + cerr << "key: '" << key << "' value: '" << m_conv.PyValue_To_String(pyDictValue) << "' " << endl; #endif SetValue(rd,key,pyDictValue); if (m_error) { @@ -296,54 +242,6 @@ } - /// Convert DTYPE type 1D NumpyArray to std::vector<RET> - template<typename RET, typename DTYPE> - std::vector<RET> PyArray_Convert(void* raw_data_ptr, long length, size_t strides) const - { - std::vector<RET> rValue; - - /// check if the array is continuous, if not use strides info - if (sizeof(DTYPE)!=strides) { -#ifdef _DEBUG_VALUES - cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl; -#endif - char* data = (char*) raw_data_ptr; - for (long i = 0; i<length; ++i){ - rValue.push_back((RET)(*((DTYPE*)data))); -#ifdef _DEBUG_VALUES - cerr << "value: " << (RET)(*((DTYPE*)data)) << endl; -#endif - data+=strides; - } - return rValue; - } - - DTYPE* data = (DTYPE*) raw_data_ptr; - for (long i = 0; i<length; ++i){ -#ifdef _DEBUG_VALUES - cerr << "value: " << (RET)data[i] << endl; -#endif - rValue.push_back((RET)data[i]); - } - return rValue; - } - - /// this is a special case. numpy.float64 has an array interface but no array descriptor - inline std::vector<float> PyArray0D_Convert(PyArrayInterface *ai) const - { - std::vector<float> rValue; - if ((ai->typekind) == *"f") - rValue.push_back((float)*(double*)(ai->data)); - else { - setValueError("Unsupported NumPy data type.",m_strict); - return rValue; - } -#ifdef _DEBUG_VALUES - cerr << "value: " << rValue[0] << endl; -#endif - return rValue; - } - //Vamp specific types Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const; inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const @@ -359,26 +257,25 @@ inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const { r = this->PyValue_To_InputDomain(pyValue); } - /* Overloaded PyValue_To_rValue() to support generic functions */ inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const - { float tmp = this->PyValue_To_Float(pyValue); + { float tmp = m_conv.PyValue_To_Float(pyValue); if(!m_error) defValue = tmp; } inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const - { size_t tmp = this->PyValue_To_Size_t(pyValue); + { size_t tmp = m_conv.PyValue_To_Size_t(pyValue); if(!m_error) defValue = tmp; } inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const - { bool tmp = this->PyValue_To_Bool(pyValue); + { bool tmp = m_conv.PyValue_To_Bool(pyValue); if(!m_error) defValue = tmp; } inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const - { std::string tmp = this->PyValue_To_String(pyValue); + { std::string tmp = m_conv.PyValue_To_String(pyValue); if(!m_error) defValue = tmp; } /*used by templates where we expect no return value, if there is one it will be ignored*/ inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const { if (m_strict && pyValue != Py_None) setValueError("Strict conversion error: Expected 'None' type.",m_strict); } - + /* convert sequence types to Vamp List types */ inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); } @@ -403,23 +300,23 @@ /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */ inline void _convert(PyObject *pyValue,float &r) const - { r = PyValue_To_Float(pyValue); } + { r = m_conv.PyValue_To_Float(pyValue); } inline void _convert(PyObject *pyValue,size_t &r) const - { r = PyValue_To_Size_t(pyValue); } - inline void _convert(PyObject *pyValue,bool &r) const - { r = PyValue_To_Bool(pyValue); } + { r = m_conv.PyValue_To_Size_t(pyValue); } + inline void _convert(PyObject *pyValue,bool &r) const + { r = m_conv.PyValue_To_Bool(pyValue); } inline void _convert(PyObject *pyValue,std::string &r) const - { r = PyValue_To_String(pyValue); } + { r = m_conv.PyValue_To_String(pyValue); } inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const - { r = PyValue_To_StringVector(pyValue); } + { r = m_conv.PyValue_To_StringVector(pyValue); } inline void _convert(PyObject *pyValue,std::vector<float> &r) const - { r = PyValue_To_FloatVector(pyValue); } - inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const + { r = m_conv.PyValue_To_FloatVector(pyValue); } + inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const { r = PyValue_To_RealTime(pyValue); } inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const { r = PyValue_To_SampleType(pyValue); } // inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const - // { r = PyValue_To_InputDomain(pyValue); } + // { r = m_conv.PyValue_To_InputDomain(pyValue); } /* Identify descriptors for error reporting */ @@ -435,246 +332,4 @@ }; -/* 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 - - - -#ifdef NUMPY_REFERENCE -/// This should be all we need to compile without direct dependency, -/// but we don't do that. (it may not work on some platforms) -typedef struct { - int two; /* contains the integer 2 -- simple sanity check */ - int nd; /* number of dimensions */ - char typekind; /* kind in array --- character code of typestr */ - int itemsize; /* size of each element */ - int flags; /* flags indicating how the data should be interpreted */ - /* must set ARR_HAS_DESCR bit to validate descr */ - Py_intptr_t *shape; /* A length-nd array of shape information */ - Py_intptr_t *strides; /* A length-nd array of stride information */ - void *data; /* A pointer to the first element of the array */ - PyObject *descr; /* NULL or data-description (same as descr key */ - /* of __array_interface__) -- must set ARR_HAS_DESCR */ - /* flag or this will be ignored. */ -} PyArrayInterface; - -typedef struct PyArrayObject { - PyObject_HEAD - char *data; /* pointer to raw data buffer */ - int nd; /* number of dimensions, also called ndim */ - npy_intp *dimensions; /* size in each dimension */ - npy_intp *strides; /* bytes to jump to get to the - next element in each dimension */ - PyObject *base; /* This object should be decref'd - upon deletion of array */ - /* For views it points to the original array */ - /* For creation from buffer object it points - to an object that shold be decref'd on - deletion */ - /* For UPDATEIFCOPY flag this is an array - to-be-updated upon deletion of this one */ - PyArray_Descr *descr; /* Pointer to type structure */ - int flags; /* Flags describing array -- see below*/ - PyObject *weakreflist; /* For weakreferences */ -} PyArrayObject; - -typedef struct _PyArray_Descr { - PyObject_HEAD - PyTypeObject *typeobj; /* the type object representing an - instance of this type -- should not - be two type_numbers with the same type - object. */ - char kind; /* kind for this type */ - char type; /* unique-character representing this type */ - char byteorder; /* '>' (big), '<' (little), '|' - (not-applicable), or '=' (native). */ - char hasobject; /* non-zero if it has object arrays - in fields */ - int type_num; /* number representing this type */ - int elsize; /* element size for this type */ - int alignment; /* alignment needed for this type */ - struct _arr_descr \ - *subarray; /* Non-NULL if this type is - is an array (C-contiguous) - of some other type - */ - PyObject *fields; /* The fields dictionary for this type */ - /* For statically defined descr this - is always Py_None */ - - PyObject *names; /* An ordered tuple of field names or NULL - if no fields are defined */ - - PyArray_ArrFuncs *f; /* a table of functions specific for each - basic data descriptor */ -} PyArray_Descr; - -enum NPY_TYPES { NPY_BOOL=0, - NPY_BYTE, NPY_UBYTE, - NPY_SHORT, NPY_USHORT, - NPY_INT, NPY_UINT, - NPY_LONG, NPY_ULONG, - NPY_LONGLONG, NPY_ULONGLONG, - NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, - NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, - NPY_OBJECT=17, - NPY_STRING, NPY_UNICODE, - NPY_VOID, - NPY_NTYPES, - NPY_NOTYPE, - NPY_CHAR, /* special flag */ - NPY_USERDEF=256 /* leave room for characters */ -}; -#endif /*NUMPY_REFERENCE*/ -#endif