fazekasgy@31: /* fazekasgy@31: fazekasgy@31: Type safe conversion utilities from Python types to C/C++ types, fazekasgy@31: mainly using Py/C API macros. fazekasgy@31: fazekasgy@31: */ fazekasgy@31: fazekasgy@31: #ifndef _PY_TYPE_INTERFACE_H_ fazekasgy@31: #define _PY_TYPE_INTERFACE_H_ fazekasgy@31: #include fazekasgy@32: #ifdef HAVE_NUMPY fazekasgy@32: #include "arrayobject.h" fazekasgy@32: #endif fazekasgy@31: #include "PyExtensionModule.h" fazekasgy@31: #include fazekasgy@31: #include fazekasgy@31: #include fazekasgy@32: #include "vamp-sdk/Plugin.h" fazekasgy@31: //#include fazekasgy@31: fazekasgy@31: fazekasgy@31: using std::cerr; fazekasgy@31: using std::endl; fazekasgy@31: fazekasgy@31: namespace o { fazekasgy@31: enum eOutDescriptors { fazekasgy@31: not_found, fazekasgy@31: identifier, fazekasgy@31: name, fazekasgy@31: description, fazekasgy@31: unit, fazekasgy@31: hasFixedBinCount, fazekasgy@31: binCount, fazekasgy@31: binNames, fazekasgy@31: hasKnownExtents, fazekasgy@31: minValue, fazekasgy@31: maxValue, fazekasgy@31: isQuantized, fazekasgy@31: quantizeStep, fazekasgy@31: sampleType, fazekasgy@31: sampleRate, fazekasgy@31: hasDuration, fazekasgy@31: endNode fazekasgy@31: }; fazekasgy@31: } fazekasgy@31: fazekasgy@31: namespace p { fazekasgy@31: enum eParmDescriptors { fazekasgy@31: not_found, fazekasgy@31: identifier, fazekasgy@31: name, fazekasgy@31: description, fazekasgy@31: unit, fazekasgy@31: minValue, fazekasgy@31: maxValue, fazekasgy@31: defaultValue, fazekasgy@31: isQuantized, fazekasgy@31: quantizeStep fazekasgy@31: }; fazekasgy@31: } fazekasgy@31: fazekasgy@31: enum eSampleTypes { fazekasgy@31: OneSamplePerStep, fazekasgy@31: FixedSampleRate, fazekasgy@31: VariableSampleRate fazekasgy@31: }; fazekasgy@31: fazekasgy@31: enum eFeatureFields { fazekasgy@31: unknown, fazekasgy@31: hasTimestamp, fazekasgy@31: timeStamp, fazekasgy@31: hasDuration, fazekasgy@31: duration, fazekasgy@31: values, fazekasgy@31: label fazekasgy@31: }; fazekasgy@31: fazekasgy@31: /* C++ mapping of PyNone Type*/ cannam@33: struct NoneType {}; fazekasgy@31: fazekasgy@31: class PyTypeInterface fazekasgy@31: { fazekasgy@31: public: fazekasgy@31: PyTypeInterface(); fazekasgy@31: ~PyTypeInterface(); fazekasgy@31: fazekasgy@31: // Data fazekasgy@31: class ValueError fazekasgy@31: { fazekasgy@31: public: fazekasgy@31: ValueError() {} fazekasgy@31: ValueError(std::string m, bool s) : message(m),strict(s) {} fazekasgy@31: std::string location; fazekasgy@31: std::string message; fazekasgy@31: bool strict; fazekasgy@31: std::string get() const { return message + "\nLocation: " + location + "\n";} fazekasgy@31: void print() const { cerr << get(); } fazekasgy@31: }; fazekasgy@31: fazekasgy@31: // Utilities fazekasgy@31: void setStrictTypingFlag(bool b) {m_strict = b;} fazekasgy@31: const ValueError &lastError() const; fazekasgy@31: ValueError getError() const; fazekasgy@31: std::string PyValue_Get_TypeName(PyObject*) const; fazekasgy@31: bool initMaps() const; fazekasgy@31: fazekasgy@31: // Basic type conversion: Python to C++ fazekasgy@31: float PyValue_To_Float(PyObject*) const; fazekasgy@31: size_t PyValue_To_Size_t(PyObject*) const; fazekasgy@31: bool PyValue_To_Bool(PyObject*) const; fazekasgy@31: std::string PyValue_To_String(PyObject*) const; fazekasgy@31: // int PyValue_To_Int(PyObject*) const; fazekasgy@31: fazekasgy@31: // C++ to Python fazekasgy@31: PyObject *PyValue_From_CValue(const char*) const; fazekasgy@31: PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); } fazekasgy@31: PyObject *PyValue_From_CValue(size_t) const; fazekasgy@31: PyObject *PyValue_From_CValue(double) const; fazekasgy@31: PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); } fazekasgy@31: PyObject *PyValue_From_CValue(bool) const; fazekasgy@31: fazekasgy@31: // Sequence types fazekasgy@31: std::vector PyValue_To_StringVector (PyObject*) const; fazekasgy@31: std::vector PyValue_To_FloatVector (PyObject*) const; fazekasgy@32: std::vector PyList_To_FloatVector (PyObject*) const; fazekasgy@31: fazekasgy@31: // Numpy types fazekasgy@32: #ifdef HAVE_NUMPY fazekasgy@32: std::vector PyArray_To_FloatVector (PyObject *pyValue) const; fazekasgy@32: #endif fazekasgy@31: fazekasgy@31: fazekasgy@31: /* Template functions */ fazekasgy@31: fazekasgy@31: fazekasgy@32: /// Common wrappers to set values in Vamp API structs. (to be used in template functions) fazekasgy@31: void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const; fazekasgy@31: void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const; fazekasgy@31: bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const; fazekasgy@31: PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const fazekasgy@31: { fazekasgy@31: if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue); fazekasgy@31: if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue); fazekasgy@31: if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue); fazekasgy@31: return NULL; fazekasgy@31: } fazekasgy@31: fazekasgy@31: fazekasgy@31: template cannam@33: RET PyValue_To_VampDescriptor(PyObject* pyValue) const fazekasgy@31: //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature fazekasgy@31: { fazekasgy@31: PyObject* pyDict; fazekasgy@31: fazekasgy@31: // Descriptors encoded as dicts fazekasgy@31: pyDict = GetDescriptor_As_Dict(pyValue); fazekasgy@31: if (!pyDict) pyDict = pyValue; fazekasgy@31: fazekasgy@31: // TODO: support full mapping protocol as fallback. fazekasgy@31: if (!PyDict_Check(pyDict)) { fazekasgy@31: setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict); fazekasgy@31: return RET(); fazekasgy@31: } fazekasgy@31: fazekasgy@31: Py_ssize_t pyPos = 0; fazekasgy@31: PyObject *pyKey, *pyDictValue; fazekasgy@31: initMaps(); fazekasgy@31: RET rd; fazekasgy@31: fazekasgy@31: //Python Dictionary Iterator: fazekasgy@31: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue)) fazekasgy@31: { fazekasgy@31: std::string key = PyValue_To_String(pyKey); fazekasgy@31: SetValue(rd,key,pyDictValue); fazekasgy@31: if (m_error) { fazekasgy@31: _lastError().location += "parameter: '" + key + "'";//"' descriptor: '" + rd.identifier + "'"; fazekasgy@31: } fazekasgy@31: } fazekasgy@31: if (!m_errorQueue.empty()) m_error = true; fazekasgy@31: return rd; fazekasgy@31: } fazekasgy@31: fazekasgy@31: /// Convert a sequence (tipically list) of PySomething to fazekasgy@31: /// OutputList,ParameterList or FeatureList fazekasgy@31: template // cannam@33: cannam@33: RET PyValue_To_VampList(PyObject* pyList) const fazekasgy@31: { fazekasgy@31: // Vamp::Plugin::OutputList list; fazekasgy@31: // Vamp::Plugin::OutputDescriptor od; fazekasgy@31: RET list; fazekasgy@31: ELEM element; fazekasgy@31: fazekasgy@31: // Type checking fazekasgy@31: if (! PyList_Check(pyList) ) { fazekasgy@31: Py_CLEAR(pyList); fazekasgy@31: // cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@31: // << "] Expected List return type." << endl; fazekasgy@31: return list; fazekasgy@31: } fazekasgy@31: fazekasgy@31: //This reference will be borrowed fazekasgy@31: PyObject *pyDict; fazekasgy@31: fazekasgy@31: //Parse Output List fazekasgy@31: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { fazekasgy@31: //Get i-th Vamp output descriptor (Borrowed Reference) fazekasgy@31: pyDict = PyList_GET_ITEM(pyList,i); fazekasgy@31: element = PyValue_To_VampDescriptor(pyDict); fazekasgy@31: // Check for empty Feature/Descriptor as before? fazekasgy@31: list.push_back(element); fazekasgy@31: } fazekasgy@31: return list; fazekasgy@31: } fazekasgy@31: fazekasgy@32: /// Convert DTYPE type 1D NumpyArray to std::vector fazekasgy@32: template cannam@33: std::vector PyArray_Convert(char* raw_data_ptr, long length) const fazekasgy@32: { fazekasgy@32: std::vector rValue; fazekasgy@32: DTYPE* data = (DTYPE*) raw_data_ptr; fazekasgy@32: for (long i = 0; iPyValue_To_FeatureSet(pyValue); } fazekasgy@31: cannam@34: Vamp::RealTime PyValue_To_RealTime(PyObject*) const; cannam@34: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const fazekasgy@31: { r = this->PyValue_To_RealTime(pyValue); } fazekasgy@31: fazekasgy@31: fazekasgy@31: /* Overloaded PyValue_To_rValue() to support generic functions */ fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const fazekasgy@31: { float tmp = this->PyValue_To_Float(pyValue); fazekasgy@31: if(!m_error) defValue = tmp; } fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const fazekasgy@31: { size_t tmp = this->PyValue_To_Size_t(pyValue); fazekasgy@31: if(!m_error) defValue = tmp; } fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const fazekasgy@31: { bool tmp = this->PyValue_To_Bool(pyValue); fazekasgy@31: if(!m_error) defValue = tmp; } fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const fazekasgy@31: { std::string tmp = this->PyValue_To_String(pyValue); fazekasgy@31: if(!m_error) defValue = tmp; } fazekasgy@31: /*used by templates where we expect no return value, if there is one it will be ignored*/ fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const fazekasgy@31: { if (m_strict && pyValue != Py_None) fazekasgy@32: setValueError("Strict conversion error: Expected 'None' type.",m_strict); fazekasgy@31: } fazekasgy@31: fazekasgy@31: /* convert sequence types to Vamp List types */ fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const fazekasgy@31: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const fazekasgy@31: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const fazekasgy@31: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@31: fazekasgy@31: /// this is only needed for RealTime->Frame conversion fazekasgy@31: void setInputSampleRate(float inputSampleRate) fazekasgy@31: { m_inputSampleRate = (unsigned int) inputSampleRate; } fazekasgy@31: fazekasgy@31: private: fazekasgy@31: bool m_strict; fazekasgy@31: ValueError m_noError; fazekasgy@31: mutable bool m_error; fazekasgy@31: mutable ValueError& m_lastError; fazekasgy@31: mutable std::queue m_errorQueue; fazekasgy@31: // we only use it for RealTime conversion which requires unsigned int fazekasgy@31: unsigned int m_inputSampleRate; fazekasgy@31: fazekasgy@31: void setValueError(std::string,bool) const; fazekasgy@31: ValueError& _lastError() const; fazekasgy@31: fazekasgy@31: /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */ fazekasgy@31: inline void _convert(PyObject *pyValue,float &r) const fazekasgy@31: { r = PyValue_To_Float(pyValue); } fazekasgy@31: inline void _convert(PyObject *pyValue,size_t &r) const fazekasgy@31: { r = PyValue_To_Size_t(pyValue); } fazekasgy@31: inline void _convert(PyObject *pyValue,bool &r) const fazekasgy@31: { r = PyValue_To_Bool(pyValue); } fazekasgy@31: inline void _convert(PyObject *pyValue,std::string &r) const fazekasgy@31: { r = PyValue_To_String(pyValue); } fazekasgy@31: inline void _convert(PyObject *pyValue,std::vector &r) const fazekasgy@31: { r = PyValue_To_StringVector(pyValue); } fazekasgy@31: inline void _convert(PyObject *pyValue,std::vector &r) const fazekasgy@31: { r = PyValue_To_FloatVector(pyValue); } cannam@34: inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const fazekasgy@31: { r = PyValue_To_RealTime(pyValue); } fazekasgy@31: fazekasgy@31: public: fazekasgy@31: const bool& error; fazekasgy@31: fazekasgy@31: }; fazekasgy@31: fazekasgy@32: #ifdef NUMPY_REFERENCE fazekasgy@32: /// This should be all we need to compile without direct dependency, fazekasgy@32: /// but we don't do that. (it may not work on some platforms) fazekasgy@32: typedef struct { fazekasgy@32: int two; /* contains the integer 2 -- simple sanity check */ fazekasgy@32: int nd; /* number of dimensions */ fazekasgy@32: char typekind; /* kind in array --- character code of typestr */ fazekasgy@32: int itemsize; /* size of each element */ fazekasgy@32: int flags; /* flags indicating how the data should be interpreted */ fazekasgy@32: /* must set ARR_HAS_DESCR bit to validate descr */ fazekasgy@32: Py_intptr_t *shape; /* A length-nd array of shape information */ fazekasgy@32: Py_intptr_t *strides; /* A length-nd array of stride information */ fazekasgy@32: void *data; /* A pointer to the first element of the array */ fazekasgy@32: PyObject *descr; /* NULL or data-description (same as descr key */ fazekasgy@32: /* of __array_interface__) -- must set ARR_HAS_DESCR */ fazekasgy@32: /* flag or this will be ignored. */ fazekasgy@32: } PyArrayInterface; fazekasgy@32: fazekasgy@32: typedef struct PyArrayObject { fazekasgy@32: PyObject_HEAD fazekasgy@32: char *data; /* pointer to raw data buffer */ fazekasgy@32: int nd; /* number of dimensions, also called ndim */ fazekasgy@32: npy_intp *dimensions; /* size in each dimension */ fazekasgy@32: npy_intp *strides; /* bytes to jump to get to the fazekasgy@32: next element in each dimension */ fazekasgy@32: PyObject *base; /* This object should be decref'd fazekasgy@32: upon deletion of array */ fazekasgy@32: /* For views it points to the original array */ fazekasgy@32: /* For creation from buffer object it points fazekasgy@32: to an object that shold be decref'd on fazekasgy@32: deletion */ fazekasgy@32: /* For UPDATEIFCOPY flag this is an array fazekasgy@32: to-be-updated upon deletion of this one */ fazekasgy@32: PyArray_Descr *descr; /* Pointer to type structure */ fazekasgy@32: int flags; /* Flags describing array -- see below*/ fazekasgy@32: PyObject *weakreflist; /* For weakreferences */ fazekasgy@32: } PyArrayObject; fazekasgy@32: fazekasgy@32: typedef struct _PyArray_Descr { fazekasgy@32: PyObject_HEAD fazekasgy@32: PyTypeObject *typeobj; /* the type object representing an fazekasgy@32: instance of this type -- should not fazekasgy@32: be two type_numbers with the same type fazekasgy@32: object. */ fazekasgy@32: char kind; /* kind for this type */ fazekasgy@32: char type; /* unique-character representing this type */ fazekasgy@32: char byteorder; /* '>' (big), '<' (little), '|' fazekasgy@32: (not-applicable), or '=' (native). */ fazekasgy@32: char hasobject; /* non-zero if it has object arrays fazekasgy@32: in fields */ fazekasgy@32: int type_num; /* number representing this type */ fazekasgy@32: int elsize; /* element size for this type */ fazekasgy@32: int alignment; /* alignment needed for this type */ fazekasgy@32: struct _arr_descr \ fazekasgy@32: *subarray; /* Non-NULL if this type is fazekasgy@32: is an array (C-contiguous) fazekasgy@32: of some other type fazekasgy@32: */ fazekasgy@32: PyObject *fields; /* The fields dictionary for this type */ fazekasgy@32: /* For statically defined descr this fazekasgy@32: is always Py_None */ fazekasgy@32: fazekasgy@32: PyObject *names; /* An ordered tuple of field names or NULL fazekasgy@32: if no fields are defined */ fazekasgy@32: fazekasgy@32: PyArray_ArrFuncs *f; /* a table of functions specific for each fazekasgy@32: basic data descriptor */ fazekasgy@32: } PyArray_Descr; fazekasgy@32: fazekasgy@32: enum NPY_TYPES { NPY_BOOL=0, fazekasgy@32: NPY_BYTE, NPY_UBYTE, fazekasgy@32: NPY_SHORT, NPY_USHORT, fazekasgy@32: NPY_INT, NPY_UINT, fazekasgy@32: NPY_LONG, NPY_ULONG, fazekasgy@32: NPY_LONGLONG, NPY_ULONGLONG, fazekasgy@32: NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, fazekasgy@32: NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, fazekasgy@32: NPY_OBJECT=17, fazekasgy@32: NPY_STRING, NPY_UNICODE, fazekasgy@32: NPY_VOID, fazekasgy@32: NPY_NTYPES, fazekasgy@32: NPY_NOTYPE, fazekasgy@32: NPY_CHAR, /* special flag */ fazekasgy@32: NPY_USERDEF=256 /* leave room for characters */ fazekasgy@32: }; fazekasgy@32: #endif /*NUMPY_REFERENCE*/ cannam@33: #endif