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: fazekasgy@31: #include "vamp-sdk/Plugin.h" fazekasgy@31: #include fazekasgy@31: #include "PyExtensionModule.h" fazekasgy@31: #include fazekasgy@31: #include fazekasgy@31: #include 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: fazekasgy@31: /// sutructure of NumPy array interface: fazekasgy@31: /// this is all we need to support numpy without direct dependency fazekasgy@31: typedef struct { fazekasgy@31: int two; /* contains the integer 2 -- simple sanity check */ fazekasgy@31: int nd; /* number of dimensions */ fazekasgy@31: char typekind; /* kind in array --- character code of typestr */ fazekasgy@31: int itemsize; /* size of each element */ fazekasgy@31: int flags; /* flags indicating how the data should be interpreted */ fazekasgy@31: /* must set ARR_HAS_DESCR bit to validate descr */ fazekasgy@31: Py_intptr_t *shape; /* A length-nd array of shape information */ fazekasgy@31: Py_intptr_t *strides; /* A length-nd array of stride information */ fazekasgy@31: void *data; /* A pointer to the first element of the array */ fazekasgy@31: PyObject *descr; /* NULL or data-description (same as descr key */ fazekasgy@31: /* of __array_interface__) -- must set ARR_HAS_DESCR */ fazekasgy@31: /* flag or this will be ignored. */ fazekasgy@31: } PyArrayInterface; fazekasgy@31: fazekasgy@31: /* C++ mapping of PyNone Type*/ fazekasgy@31: typedef 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@31: fazekasgy@31: // Numpy types fazekasgy@31: float* getNumPyObjectData(PyObject *object, int &length) const; fazekasgy@31: fazekasgy@31: fazekasgy@31: /* Template functions */ fazekasgy@31: fazekasgy@31: fazekasgy@31: /// Common wrappers to set a value in one of these 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 fazekasgy@31: RET PyTypeInterface::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 // fazekasgy@31: RET PyTypeInterface::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@31: fazekasgy@31: //Vamp specific types fazekasgy@31: fazekasgy@31: Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const; fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const fazekasgy@31: { r = this->PyValue_To_FeatureSet(pyValue); } fazekasgy@31: fazekasgy@31: Vamp::RealTime::RealTime PyValue_To_RealTime(PyObject*) const; fazekasgy@31: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime::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@31: 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); } fazekasgy@31: inline void _convert(PyObject *pyValue,Vamp::RealTime::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@31: #endif