fazekasgy@37: /* fazekasgy@37: fazekasgy@37: * Vampy : This plugin is a wrapper around the Vamp plugin API. fazekasgy@37: * It allows for writing Vamp plugins in Python. fazekasgy@37: fazekasgy@37: * Centre for Digital Music, Queen Mary University of London. fazekasgy@37: * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources fazekasgy@37: * for licence information.) fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: /* fazekasgy@37: PyTypeInterface: Type safe conversion utilities between Python types fazekasgy@37: and basic C/C++ types and Vamp API types. fazekasgy@37: */ fazekasgy@37: fazekasgy@37: #ifndef _PY_TYPE_INTERFACE_H_ fazekasgy@37: #define _PY_TYPE_INTERFACE_H_ fazekasgy@37: #include fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API fazekasgy@37: #define NO_IMPORT_ARRAY fazekasgy@37: #include "numpy/arrayobject.h" fazekasgy@37: #endif fazekasgy@37: #include "PyExtensionModule.h" fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #include "vamp-sdk/Plugin.h" fazekasgy@37: fazekasgy@37: using std::cerr; fazekasgy@37: using std::endl; fazekasgy@37: fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: enum eArrayDataType { fazekasgy@37: dtype_float32 = (int) NPY_FLOAT, fazekasgy@37: dtype_complex64 = (int) NPY_CFLOAT fazekasgy@37: }; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: namespace o { fazekasgy@37: enum eOutDescriptors { fazekasgy@37: not_found, fazekasgy@37: identifier, fazekasgy@37: name, fazekasgy@37: description, fazekasgy@37: unit, fazekasgy@37: hasFixedBinCount, fazekasgy@37: binCount, fazekasgy@37: binNames, fazekasgy@37: hasKnownExtents, fazekasgy@37: minValue, fazekasgy@37: maxValue, fazekasgy@37: isQuantized, fazekasgy@37: quantizeStep, fazekasgy@37: sampleType, fazekasgy@37: sampleRate, fazekasgy@37: hasDuration, fazekasgy@37: endNode fazekasgy@37: }; fazekasgy@37: } fazekasgy@37: fazekasgy@37: namespace p { fazekasgy@37: enum eParmDescriptors { fazekasgy@37: not_found, fazekasgy@37: identifier, fazekasgy@37: name, fazekasgy@37: description, fazekasgy@37: unit, fazekasgy@37: minValue, fazekasgy@37: maxValue, fazekasgy@37: defaultValue, fazekasgy@37: isQuantized, fazekasgy@37: quantizeStep fazekasgy@37: }; fazekasgy@37: } fazekasgy@37: fazekasgy@37: enum eSampleTypes { fazekasgy@37: OneSamplePerStep, fazekasgy@37: FixedSampleRate, fazekasgy@37: VariableSampleRate fazekasgy@37: }; fazekasgy@37: fazekasgy@37: enum eFeatureFields { fazekasgy@37: unknown, fazekasgy@37: hasTimestamp, fazekasgy@37: timestamp, fazekasgy@37: hasDuration, fazekasgy@37: duration, fazekasgy@37: values, fazekasgy@37: label fazekasgy@37: }; fazekasgy@37: fazekasgy@37: /* C++ mapping of PyNone Type */ fazekasgy@37: struct NoneType {}; fazekasgy@37: fazekasgy@37: class PyTypeInterface fazekasgy@37: { fazekasgy@37: public: fazekasgy@37: PyTypeInterface(); fazekasgy@37: ~PyTypeInterface(); fazekasgy@37: fazekasgy@37: // Data fazekasgy@37: class ValueError fazekasgy@37: { fazekasgy@37: public: fazekasgy@37: ValueError() {} fazekasgy@37: ValueError(std::string m, bool s) : message(m),strict(s) {} fazekasgy@37: std::string location; fazekasgy@37: std::string message; fazekasgy@37: bool strict; fazekasgy@37: std::string str() const { fazekasgy@37: return (location.empty()) ? message : message + "\nLocation: " + location;} fazekasgy@37: void print() const { cerr << str() << endl; } fazekasgy@37: template ValueError &operator<< (const V& v) fazekasgy@37: { fazekasgy@37: std::ostringstream ss; fazekasgy@37: ss << v; fazekasgy@37: location += ss.str(); fazekasgy@37: return *this; fazekasgy@37: } fazekasgy@37: }; fazekasgy@37: fazekasgy@37: // Utilities fazekasgy@37: void setStrictTypingFlag(bool b) {m_strict = b;} fazekasgy@37: ValueError getError() const; fazekasgy@37: std::string PyValue_Get_TypeName(PyObject*) const; fazekasgy@37: bool initMaps() const; fazekasgy@37: fazekasgy@37: // Basic type conversion: Python to C++ fazekasgy@37: float PyValue_To_Float(PyObject*) const; fazekasgy@37: size_t PyValue_To_Size_t(PyObject*) const; fazekasgy@37: bool PyValue_To_Bool(PyObject*) const; fazekasgy@37: std::string PyValue_To_String(PyObject*) const; fazekasgy@37: long PyValue_To_Long(PyObject*) const; fazekasgy@37: // int PyValue_To_Int(PyObject* pyValue) const; fazekasgy@37: fazekasgy@37: fazekasgy@37: // C++ to Python fazekasgy@37: PyObject *PyValue_From_CValue(const char*) const; fazekasgy@37: PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); } fazekasgy@37: PyObject *PyValue_From_CValue(size_t) const; fazekasgy@37: PyObject *PyValue_From_CValue(double) const; fazekasgy@37: PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); } fazekasgy@37: PyObject *PyValue_From_CValue(bool) const; fazekasgy@37: fazekasgy@37: // Sequence types fazekasgy@37: std::vector PyValue_To_StringVector (PyObject*) const; fazekasgy@37: std::vector PyValue_To_FloatVector (PyObject*) const; fazekasgy@37: std::vector PyList_To_FloatVector (PyObject*) const; fazekasgy@37: fazekasgy@37: // Input buffers to Python fazekasgy@37: PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype); fazekasgy@37: PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize); fazekasgy@37: fazekasgy@37: // Numpy types fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: std::vector PyArray_To_FloatVector (PyObject *pyValue) const; fazekasgy@37: PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype); fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Template functions */ fazekasgy@37: fazekasgy@37: fazekasgy@37: /// Common wrappers to set values in Vamp API structs. (to be used in template functions) fazekasgy@37: void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const; fazekasgy@37: void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const; fazekasgy@37: bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const; fazekasgy@37: PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue); fazekasgy@37: if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue); fazekasgy@37: if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue); fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: fazekasgy@37: //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature fazekasgy@37: template fazekasgy@37: RET PyValue_To_VampDescriptor(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: PyObject* pyDict; fazekasgy@37: fazekasgy@37: // Descriptors encoded as dicts fazekasgy@37: pyDict = GetDescriptor_As_Dict(pyValue); fazekasgy@37: if (!pyDict) pyDict = pyValue; fazekasgy@37: fazekasgy@37: // TODO: support full mapping protocol as fallback. fazekasgy@37: if (!PyDict_Check(pyDict)) { fazekasgy@37: setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_VampDescriptor failed. Error: Unexpected return type." << endl; fazekasgy@37: #endif fazekasgy@37: return RET(); fazekasgy@37: } fazekasgy@37: fazekasgy@37: Py_ssize_t pyPos = 0; fazekasgy@37: PyObject *pyKey, *pyDictValue; fazekasgy@37: initMaps(); fazekasgy@37: int errors = 0; fazekasgy@37: m_error = false; fazekasgy@37: RET rd; fazekasgy@37: fazekasgy@37: //Python Dictionary Iterator: fazekasgy@37: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue)) fazekasgy@37: { fazekasgy@37: std::string key = PyValue_To_String(pyKey); fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl; fazekasgy@37: #endif fazekasgy@37: SetValue(rd,key,pyDictValue); fazekasgy@37: if (m_error) { fazekasgy@37: errors++; fazekasgy@37: lastError() << "attribute '" << key << "'";// << " of " << getDescriptorId(rd); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: if (errors) { fazekasgy@37: lastError() << " of " << getDescriptorId(rd); fazekasgy@37: m_error = true; fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_VampDescriptor: Warning: Value error in descriptor." << endl; fazekasgy@37: #endif fazekasgy@37: } fazekasgy@37: return rd; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// Convert a sequence (tipically list) of PySomething to fazekasgy@37: /// OutputList,ParameterList or FeatureList fazekasgy@37: /// fazekasgy@37: template fazekasgy@37: RET PyValue_To_VampList(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: RET list; // e.g. Vamp::Plugin::OutputList fazekasgy@37: ELEM element; // e.g. Vamp::Plugin::OutputDescriptor fazekasgy@37: fazekasgy@37: /// convert lists (ParameterList, OutputList, FeatureList) fazekasgy@37: if (PyList_Check(pyValue)) { fazekasgy@37: PyObject *pyDict; //This reference will be borrowed fazekasgy@37: m_error = false; int errors = 0; fazekasgy@37: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) { fazekasgy@37: //Get i-th Vamp output descriptor (Borrowed Reference) fazekasgy@37: pyDict = PyList_GET_ITEM(pyValue,i); fazekasgy@37: element = PyValue_To_VampDescriptor(pyDict); fazekasgy@37: if (m_error) errors++; fazekasgy@37: // Check for empty Feature/Descriptor as before? fazekasgy@37: list.push_back(element); fazekasgy@37: } fazekasgy@37: if (errors) m_error=true; fazekasgy@37: return list; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// convert other types implementing the sequence protocol fazekasgy@37: if (PySequence_Check(pyValue)) { fazekasgy@37: PyObject *pySequence = PySequence_Fast(pyValue,"Returned value can not be converted to list or tuple."); fazekasgy@37: PyObject **pyElements = PySequence_Fast_ITEMS(pySequence); fazekasgy@37: m_error = false; int errors = 0; fazekasgy@37: for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(pySequence); ++i) fazekasgy@37: { fazekasgy@37: element = PyValue_To_VampDescriptor(pyElements[i]); fazekasgy@37: if (m_error) errors++; fazekasgy@37: list.push_back(element); fazekasgy@37: } fazekasgy@37: if (errors) m_error=true; fazekasgy@37: Py_XDECREF(pySequence); fazekasgy@37: return list; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // accept None as an empty list fazekasgy@37: if (pyValue == Py_None) return list; fazekasgy@37: fazekasgy@37: // in strict mode, returning a single value is not allowed fazekasgy@37: if (m_strict) { fazekasgy@37: setValueError("Strict conversion error: object is not list or iterable sequence.",m_strict); fazekasgy@37: return list; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// try to insert single, non-iterable values. i.e. feature <- [feature] fazekasgy@37: element = PyValue_To_VampDescriptor(pyValue); fazekasgy@37: if (m_error) { fazekasgy@37: setValueError("Could not insert returned value to Vamp List.",m_strict); fazekasgy@37: return list; fazekasgy@37: } fazekasgy@37: list.push_back(element); fazekasgy@37: return list; fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_VampList failed. Expected iterable return type." << endl; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// Convert DTYPE type 1D NumpyArray to std::vector fazekasgy@37: template fazekasgy@37: std::vector PyArray_Convert(char* raw_data_ptr, long length, size_t strides) const fazekasgy@37: { fazekasgy@37: std::vector rValue; fazekasgy@37: fazekasgy@37: /// check if the array is continuous, if not use strides info fazekasgy@37: if (sizeof(DTYPE)!=strides) { fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl; fazekasgy@37: #endif fazekasgy@37: char* data = (char*) raw_data_ptr; fazekasgy@37: for (long i = 0; i PyArray0D_Convert(PyArrayInterface *ai) const fazekasgy@37: { fazekasgy@37: std::vector rValue; fazekasgy@37: if ((ai->typekind) == *"f") fazekasgy@37: rValue.push_back((float)*(double*)(ai->data)); fazekasgy@37: else { fazekasgy@37: setValueError("Unsupported NumPy data type.",m_strict); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: cerr << "value: " << rValue[0] << endl; fazekasgy@37: #endif fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: //Vamp specific types fazekasgy@37: Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const; fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const fazekasgy@37: { r = this->PyValue_To_FeatureSet(pyValue); } fazekasgy@37: fazekasgy@37: Vamp::RealTime PyValue_To_RealTime(PyObject*) const; fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const fazekasgy@37: { r = this->PyValue_To_RealTime(pyValue); } fazekasgy@37: fazekasgy@37: Vamp::Plugin::OutputDescriptor::SampleType PyValue_To_SampleType(PyObject*) const; fazekasgy@37: fazekasgy@37: Vamp::Plugin::InputDomain PyValue_To_InputDomain(PyObject*) const; fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const fazekasgy@37: { r = this->PyValue_To_InputDomain(pyValue); } fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Overloaded PyValue_To_rValue() to support generic functions */ fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const fazekasgy@37: { float tmp = this->PyValue_To_Float(pyValue); fazekasgy@37: if(!m_error) defValue = tmp; } fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const fazekasgy@37: { size_t tmp = this->PyValue_To_Size_t(pyValue); fazekasgy@37: if(!m_error) defValue = tmp; } fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const fazekasgy@37: { bool tmp = this->PyValue_To_Bool(pyValue); fazekasgy@37: if(!m_error) defValue = tmp; } fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const fazekasgy@37: { std::string tmp = this->PyValue_To_String(pyValue); fazekasgy@37: if(!m_error) defValue = tmp; } fazekasgy@37: /*used by templates where we expect no return value, if there is one it will be ignored*/ fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const fazekasgy@37: { if (m_strict && pyValue != Py_None) fazekasgy@37: setValueError("Strict conversion error: Expected 'None' type.",m_strict); fazekasgy@37: } fazekasgy@37: fazekasgy@37: /* convert sequence types to Vamp List types */ fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const fazekasgy@37: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const fazekasgy@37: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@37: inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const fazekasgy@37: { r = this->PyValue_To_VampList(pyValue); } fazekasgy@37: fazekasgy@37: /// this is only needed for RealTime->Frame conversion fazekasgy@37: void setInputSampleRate(float inputSampleRate) fazekasgy@37: { m_inputSampleRate = (unsigned int) inputSampleRate; } fazekasgy@37: fazekasgy@37: private: fazekasgy@37: bool m_strict; fazekasgy@37: mutable bool m_error; fazekasgy@37: mutable std::queue m_errorQueue; fazekasgy@37: unsigned int m_inputSampleRate; fazekasgy@37: fazekasgy@37: void setValueError(std::string,bool) const; fazekasgy@37: ValueError& lastError() const; fazekasgy@37: fazekasgy@37: /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */ fazekasgy@37: inline void _convert(PyObject *pyValue,float &r) const fazekasgy@37: { r = PyValue_To_Float(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,size_t &r) const fazekasgy@37: { r = PyValue_To_Size_t(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,bool &r) const fazekasgy@37: { r = PyValue_To_Bool(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,std::string &r) const fazekasgy@37: { r = PyValue_To_String(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,std::vector &r) const fazekasgy@37: { r = PyValue_To_StringVector(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,std::vector &r) const fazekasgy@37: { r = PyValue_To_FloatVector(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const fazekasgy@37: { r = PyValue_To_RealTime(pyValue); } fazekasgy@37: inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const fazekasgy@37: { r = PyValue_To_SampleType(pyValue); } fazekasgy@37: // inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const fazekasgy@37: // { r = PyValue_To_InputDomain(pyValue); } fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Identify descriptors for error reporting */ fazekasgy@37: inline std::string getDescriptorId(Vamp::Plugin::OutputDescriptor d) const fazekasgy@37: {return std::string("Output Descriptor '") + d.identifier +"' ";} fazekasgy@37: inline std::string getDescriptorId(Vamp::Plugin::ParameterDescriptor d) const fazekasgy@37: {return std::string("Parameter Descriptor '") + d.identifier +"' ";} fazekasgy@37: inline std::string getDescriptorId(Vamp::Plugin::Feature f) const fazekasgy@37: {return std::string("Feature (") + f.label + ")"; } fazekasgy@37: fazekasgy@37: public: fazekasgy@37: const bool& error; fazekasgy@37: fazekasgy@37: }; fazekasgy@37: fazekasgy@37: /* Convert Sample Buffers to Python */ fazekasgy@37: fazekasgy@37: /// passing the sample buffers as buitin python lists fazekasgy@37: /// Optimization: using fast sequence protocol fazekasgy@37: inline PyObject* fazekasgy@37: PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) fazekasgy@37: { fazekasgy@37: //create a list of lists (new references) fazekasgy@37: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); fazekasgy@37: fazekasgy@37: // Pack samples into a Python List Object fazekasgy@37: // pyFloat/pyComplex types will always be new references, fazekasgy@37: // they will be freed when the lists are deallocated. fazekasgy@37: fazekasgy@37: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); fazekasgy@37: for (size_t i=0; i < channels; ++i) { fazekasgy@37: fazekasgy@37: PyObject *pySampleList = PyList_New((Py_ssize_t) blockSize); fazekasgy@37: PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList); fazekasgy@37: fazekasgy@37: // Note: passing a complex list crashes the C-style plugin fazekasgy@37: // when it tries to convert it to a numpy array directly. fazekasgy@37: // This plugin will be obsolete, but we have to find a way fazekasgy@37: // to prevent such crash. fazekasgy@37: fazekasgy@37: switch (Vamp::Plugin::TimeDomain) //(dtype) fazekasgy@37: { fazekasgy@37: case Vamp::Plugin::TimeDomain : fazekasgy@37: fazekasgy@37: for (size_t j = 0; j < blockSize; ++j) { fazekasgy@37: PyObject *pyFloat=PyFloat_FromDouble( fazekasgy@37: (double) inputBuffers[i][j]); fazekasgy@37: pySampleListArray[j] = pyFloat; fazekasgy@37: } fazekasgy@37: break; fazekasgy@37: fazekasgy@37: case Vamp::Plugin::FrequencyDomain : fazekasgy@37: fazekasgy@37: size_t k = 0; fazekasgy@37: for (size_t j = 0; j < blockSize/2; ++j) { fazekasgy@37: PyObject *pyComplex=PyComplex_FromDoubles( fazekasgy@37: (double) inputBuffers[i][k], fazekasgy@37: (double) inputBuffers[i][k+1]); fazekasgy@37: pySampleListArray[j] = pyComplex; fazekasgy@37: k += 2; fazekasgy@37: } fazekasgy@37: break; fazekasgy@37: fazekasgy@37: } fazekasgy@37: pyChannelListArray[i] = pySampleList; fazekasgy@37: } fazekasgy@37: return pyChannelList; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// numpy buffer interface: passing the sample buffers as shared memory buffers fazekasgy@37: /// Optimization: using sequence protocol for creating the buffer list fazekasgy@37: inline PyObject* fazekasgy@37: PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize) fazekasgy@37: { fazekasgy@37: //create a list of buffers (returns new references) fazekasgy@37: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); fazekasgy@37: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); fazekasgy@37: fazekasgy@37: // Expose memory using the Buffer Interface. fazekasgy@37: // This will pass a pointer which can be recasted in Python code fazekasgy@37: // as complex or float array using Numpy's frombuffer() method fazekasgy@37: // (this will not copy values just keep the starting adresses fazekasgy@37: // for each channel in a list) fazekasgy@37: Py_ssize_t bufferSize = (Py_ssize_t) sizeof(float) * blockSize; fazekasgy@37: fazekasgy@37: for (size_t i=0; i < channels; ++i) { fazekasgy@37: PyObject *pyBuffer = PyBuffer_FromMemory fazekasgy@37: ((void *) (float *) inputBuffers[i],bufferSize); fazekasgy@37: pyChannelListArray[i] = pyBuffer; fazekasgy@37: } fazekasgy@37: return pyChannelList; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: /// numpy array interface: passing the sample buffers as 2D numpy array fazekasgy@37: /// Optimization: using array API (needs numpy headers) fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: inline PyObject* fazekasgy@37: PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) fazekasgy@37: { fazekasgy@37: /* fazekasgy@37: NOTE: We create a list of 1D Numpy arrays for each channel instead fazekasgy@37: of a matrix, because the address space of inputBuffers doesn't seem fazekasgy@37: to be continuous. Although the array strides could be calculated for fazekasgy@37: 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure fazekasgy@37: if this can be trusted, especially for more than 2 channels. fazekasgy@37: fazekasgy@37: cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl; fazekasgy@37: if (channels == 2) fazekasgy@37: cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl; fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: // create a list of arrays (returns new references) fazekasgy@37: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); fazekasgy@37: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); fazekasgy@37: fazekasgy@37: // Expose memory using the Numpy Array Interface. fazekasgy@37: // This will wrap an array objects around the data. fazekasgy@37: // (will not copy values just steal the starting adresses) fazekasgy@37: fazekasgy@37: int arraySize, typenum; fazekasgy@37: fazekasgy@37: switch (dtype) fazekasgy@37: { fazekasgy@37: case Vamp::Plugin::TimeDomain : fazekasgy@37: typenum = dtype_float32; //NPY_FLOAT; fazekasgy@37: arraySize = (int) blockSize; fazekasgy@37: break; fazekasgy@37: fazekasgy@37: case Vamp::Plugin::FrequencyDomain : fazekasgy@37: typenum = dtype_complex64; //NPY_CFLOAT; fazekasgy@37: arraySize = (int) blockSize / 2; fazekasgy@37: break; fazekasgy@37: fazekasgy@37: default : fazekasgy@37: cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl; fazekasgy@37: return pyChannelList; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // size for each dimension fazekasgy@37: npy_intp ndims[1]={arraySize}; fazekasgy@37: fazekasgy@37: for (size_t i=0; i < channels; ++i) { fazekasgy@37: PyObject *pyChannelArray = fazekasgy@37: //args: (dimensions, size in each dim, type kind, pointer to continuous array) fazekasgy@37: PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]); fazekasgy@37: // make it read-only: set all flags to false except NPY_C_CONTIGUOUS fazekasgy@37: ((PyArrayObject*)pyChannelArray)->flags = NPY_C_CONTIGUOUS; fazekasgy@37: pyChannelListArray[i] = pyChannelArray; fazekasgy@37: } fazekasgy@37: return pyChannelList; fazekasgy@37: } fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: fazekasgy@37: fazekasgy@37: #ifdef NUMPY_REFERENCE fazekasgy@37: /// This should be all we need to compile without direct dependency, fazekasgy@37: /// but we don't do that. (it may not work on some platforms) fazekasgy@37: typedef struct { fazekasgy@37: int two; /* contains the integer 2 -- simple sanity check */ fazekasgy@37: int nd; /* number of dimensions */ fazekasgy@37: char typekind; /* kind in array --- character code of typestr */ fazekasgy@37: int itemsize; /* size of each element */ fazekasgy@37: int flags; /* flags indicating how the data should be interpreted */ fazekasgy@37: /* must set ARR_HAS_DESCR bit to validate descr */ fazekasgy@37: Py_intptr_t *shape; /* A length-nd array of shape information */ fazekasgy@37: Py_intptr_t *strides; /* A length-nd array of stride information */ fazekasgy@37: void *data; /* A pointer to the first element of the array */ fazekasgy@37: PyObject *descr; /* NULL or data-description (same as descr key */ fazekasgy@37: /* of __array_interface__) -- must set ARR_HAS_DESCR */ fazekasgy@37: /* flag or this will be ignored. */ fazekasgy@37: } PyArrayInterface; fazekasgy@37: fazekasgy@37: typedef struct PyArrayObject { fazekasgy@37: PyObject_HEAD fazekasgy@37: char *data; /* pointer to raw data buffer */ fazekasgy@37: int nd; /* number of dimensions, also called ndim */ fazekasgy@37: npy_intp *dimensions; /* size in each dimension */ fazekasgy@37: npy_intp *strides; /* bytes to jump to get to the fazekasgy@37: next element in each dimension */ fazekasgy@37: PyObject *base; /* This object should be decref'd fazekasgy@37: upon deletion of array */ fazekasgy@37: /* For views it points to the original array */ fazekasgy@37: /* For creation from buffer object it points fazekasgy@37: to an object that shold be decref'd on fazekasgy@37: deletion */ fazekasgy@37: /* For UPDATEIFCOPY flag this is an array fazekasgy@37: to-be-updated upon deletion of this one */ fazekasgy@37: PyArray_Descr *descr; /* Pointer to type structure */ fazekasgy@37: int flags; /* Flags describing array -- see below*/ fazekasgy@37: PyObject *weakreflist; /* For weakreferences */ fazekasgy@37: } PyArrayObject; fazekasgy@37: fazekasgy@37: typedef struct _PyArray_Descr { fazekasgy@37: PyObject_HEAD fazekasgy@37: PyTypeObject *typeobj; /* the type object representing an fazekasgy@37: instance of this type -- should not fazekasgy@37: be two type_numbers with the same type fazekasgy@37: object. */ fazekasgy@37: char kind; /* kind for this type */ fazekasgy@37: char type; /* unique-character representing this type */ fazekasgy@37: char byteorder; /* '>' (big), '<' (little), '|' fazekasgy@37: (not-applicable), or '=' (native). */ fazekasgy@37: char hasobject; /* non-zero if it has object arrays fazekasgy@37: in fields */ fazekasgy@37: int type_num; /* number representing this type */ fazekasgy@37: int elsize; /* element size for this type */ fazekasgy@37: int alignment; /* alignment needed for this type */ fazekasgy@37: struct _arr_descr \ fazekasgy@37: *subarray; /* Non-NULL if this type is fazekasgy@37: is an array (C-contiguous) fazekasgy@37: of some other type fazekasgy@37: */ fazekasgy@37: PyObject *fields; /* The fields dictionary for this type */ fazekasgy@37: /* For statically defined descr this fazekasgy@37: is always Py_None */ fazekasgy@37: fazekasgy@37: PyObject *names; /* An ordered tuple of field names or NULL fazekasgy@37: if no fields are defined */ fazekasgy@37: fazekasgy@37: PyArray_ArrFuncs *f; /* a table of functions specific for each fazekasgy@37: basic data descriptor */ fazekasgy@37: } PyArray_Descr; fazekasgy@37: fazekasgy@37: enum NPY_TYPES { NPY_BOOL=0, fazekasgy@37: NPY_BYTE, NPY_UBYTE, fazekasgy@37: NPY_SHORT, NPY_USHORT, fazekasgy@37: NPY_INT, NPY_UINT, fazekasgy@37: NPY_LONG, NPY_ULONG, fazekasgy@37: NPY_LONGLONG, NPY_ULONGLONG, fazekasgy@37: NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, fazekasgy@37: NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, fazekasgy@37: NPY_OBJECT=17, fazekasgy@37: NPY_STRING, NPY_UNICODE, fazekasgy@37: NPY_VOID, fazekasgy@37: NPY_NTYPES, fazekasgy@37: NPY_NOTYPE, fazekasgy@37: NPY_CHAR, /* special flag */ fazekasgy@37: NPY_USERDEF=256 /* leave room for characters */ fazekasgy@37: }; fazekasgy@37: #endif /*NUMPY_REFERENCE*/ fazekasgy@37: #endif