Chris@66: /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */ 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: #include fazekasgy@37: fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API fazekasgy@37: #define NO_IMPORT_ARRAY Chris@66: #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION fazekasgy@37: #include "numpy/arrayobject.h" fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: #include "PyTypeInterface.h" fazekasgy@37: #include "PyRealTime.h" fazekasgy@37: #include "PyExtensionModule.h" fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #include fazekasgy@37: #ifndef SIZE_T_MAX fazekasgy@37: #define SIZE_T_MAX ((size_t) -1) fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: using std::string; fazekasgy@37: using std::vector; fazekasgy@37: using std::cerr; fazekasgy@37: using std::endl; fazekasgy@37: using std::map; fazekasgy@37: fazekasgy@37: static std::map outKeys; fazekasgy@37: static std::map parmKeys; fazekasgy@37: static std::map sampleKeys; fazekasgy@37: static std::map ffKeys; fazekasgy@37: static bool isMapInitialised = false; fazekasgy@37: fazekasgy@37: /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS Chris@66: (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */ fazekasgy@37: fazekasgy@37: PyTypeInterface::PyTypeInterface() : fazekasgy@37: m_strict(false), fazekasgy@37: m_error(false), fazekasgy@51: m_numpyInstalled(false), fazekasgy@37: error(m_error) // const public reference for easy access fazekasgy@37: { fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyTypeInterface::~PyTypeInterface() fazekasgy@37: { fazekasgy@37: } fazekasgy@37: fazekasgy@51: /// FeatureSet (an integer map of FeatureLists) fazekasgy@37: Vamp::Plugin::FeatureSet fazekasgy@37: PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: Vamp::Plugin::FeatureSet rFeatureSet; fazekasgy@37: fazekasgy@37: /// Convert PyFeatureSet fazekasgy@37: if (PyFeatureSet_CheckExact(pyValue)) { fazekasgy@37: fazekasgy@37: Py_ssize_t pyPos = 0; fazekasgy@37: PyObject *pyKey, *pyDictValue; // Borrowed References fazekasgy@37: int key; fazekasgy@37: // bool it_error = false; fazekasgy@37: fazekasgy@37: m_error = false; fazekasgy@37: while (PyDict_Next(pyValue, &pyPos, &pyKey, &pyDictValue)) fazekasgy@37: { fazekasgy@37: key = (int) PyInt_AS_LONG(pyKey); fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl; fazekasgy@37: #endif fazekasgy@37: // DictValue -> Vamp::FeatureList fazekasgy@37: PyValue_To_rValue(pyDictValue,rFeatureSet[key]); fazekasgy@37: if (m_error) { fazekasgy@37: // it_error = true; fazekasgy@37: lastError() << " in output number: " << key; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: // if (it_error) m_error = true; fazekasgy@37: if (!m_errorQueue.empty()) { fazekasgy@37: setValueError("Error while converting FeatureSet.",m_strict); fazekasgy@37: } fazekasgy@37: return rFeatureSet; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// Convert Python list (backward compatibility) fazekasgy@37: if (PyList_Check(pyValue)) { fazekasgy@37: fazekasgy@37: PyObject *pyFeatureList; // This will be borrowed reference fazekasgy@37: fazekasgy@37: //Parse Output List for each element (FeatureSet) fazekasgy@37: m_error = false; fazekasgy@37: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) { fazekasgy@37: //Get i-th FeatureList (Borrowed Reference) fazekasgy@37: pyFeatureList = PyList_GET_ITEM(pyValue,i); fazekasgy@37: PyValue_To_rValue(pyFeatureList,rFeatureSet[i]); fazekasgy@37: if (m_error) { fazekasgy@37: lastError() << " in output number: " << i; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: if (!m_errorQueue.empty()) m_error = true; fazekasgy@37: return rFeatureSet; fazekasgy@37: } fazekasgy@37: fazekasgy@51: /// accept None return values fazekasgy@37: if (pyValue == Py_None) return rFeatureSet; fazekasgy@37: fazekasgy@37: /// give up fazekasgy@37: std::string msg = "Unsupported return type. Expected list or vampy.FeatureSet(). "; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_FeatureSet failed. Error: " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return rFeatureSet; fazekasgy@37: } fazekasgy@37: fazekasgy@37: Vamp::RealTime fazekasgy@37: PyTypeInterface::PyValue_To_RealTime(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: // We accept integer sample counts (for backward compatibility) fazekasgy@37: // or PyRealTime objects and convert them to Vamp::RealTime fazekasgy@37: fazekasgy@37: if (PyRealTime_CheckExact(pyValue)) fazekasgy@37: { fazekasgy@37: /// just create a copy of the wrapped object fazekasgy@37: return Vamp::RealTime(*PyRealTime_AS_REALTIME(pyValue)); fazekasgy@37: } fazekasgy@37: fazekasgy@37: // assume integer sample count Chris@71: long sampleCount = m_conv.PyValue_To_Long(pyValue); Chris@71: if (m_conv.error) { fazekasgy@37: std::string msg = "Unexpected value passed as RealTime.\nMust be vampy.RealTime type or integer sample count."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_RealTime failed. " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return Vamp::RealTime(); fazekasgy@37: } fazekasgy@37: fazekasgy@37: #ifdef _DEBUG_VALUES fazekasgy@37: Vamp::RealTime rt = fazekasgy@37: Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate ); fazekasgy@37: cerr << "RealTime: " << (long)sampleCount << ", ->" << rt.toString() << endl; fazekasgy@37: return rt; fazekasgy@37: #else fazekasgy@37: return Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate ); fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: } fazekasgy@37: fazekasgy@37: Vamp::Plugin::OutputDescriptor::SampleType fazekasgy@37: PyTypeInterface::PyValue_To_SampleType(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: /// convert simulated enum values fazekasgy@37: /// { OneSamplePerStep,FixedSampleRate,VariableSampleRate } fazekasgy@37: if (PyInt_CheckExact(pyValue)) { fazekasgy@37: long lst = PyInt_AS_LONG(pyValue); fazekasgy@37: if (lst<0 || lst>2) { fazekasgy@37: setValueError("Overflow error. SampleType has to be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type.",m_strict); fazekasgy@37: return Vamp::Plugin::OutputDescriptor::SampleType(); fazekasgy@37: } fazekasgy@37: return (Vamp::Plugin::OutputDescriptor::SampleType) lst; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// convert string (backward compatible) fazekasgy@37: if (PyString_CheckExact(pyValue)) { fazekasgy@37: Vamp::Plugin::OutputDescriptor::SampleType st; Chris@71: st = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[m_conv.PyValue_To_String(pyValue)]; Chris@71: if (m_conv.error) { fazekasgy@37: std::string msg = "Unexpected value passed as SampleType. Must be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: return Vamp::Plugin::OutputDescriptor::SampleType(); fazekasgy@37: } fazekasgy@37: return st; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// give up fazekasgy@37: std::string msg = "Unsupported return type. Expected one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_SampleType failed. Error: " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return Vamp::Plugin::OutputDescriptor::SampleType(); fazekasgy@37: } fazekasgy@37: fazekasgy@37: Vamp::Plugin::InputDomain fazekasgy@37: PyTypeInterface::PyValue_To_InputDomain(PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: /// convert simulated enum values { TimeDomain,FrequencyDomain } fazekasgy@37: if (PyInt_CheckExact(pyValue)) { fazekasgy@37: long lst = PyInt_AS_LONG(pyValue); fazekasgy@37: if (lst!=0 && lst!=1) { fazekasgy@37: setValueError("Overflow error. InputDomain has to be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type.",m_strict); fazekasgy@37: return Vamp::Plugin::InputDomain(); fazekasgy@37: } fazekasgy@37: return (Vamp::Plugin::InputDomain) lst; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// convert string (backward compatible) fazekasgy@37: if (PyString_CheckExact(pyValue)) { fazekasgy@37: Vamp::Plugin::InputDomain id; Chris@71: id = (m_conv.PyValue_To_String(pyValue) == "FrequencyDomain")?Vamp::Plugin::FrequencyDomain:Vamp::Plugin::TimeDomain; Chris@71: if (m_conv.error) fazekasgy@37: { fazekasgy@37: std::string msg = "Unexpected value passed as SampleType. Must be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: return Vamp::Plugin::InputDomain(); fazekasgy@37: } fazekasgy@37: return id; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// give up fazekasgy@37: std::string msg = "Unsupported return type. Expected one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type."; fazekasgy@37: setValueError(msg,m_strict); fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "PyTypeInterface::PyValue_To_InputDomain failed. Error: " << msg << endl; fazekasgy@37: #endif fazekasgy@37: return Vamp::Plugin::InputDomain(); fazekasgy@37: } fazekasgy@37: Chris@71: /* Convert Sample Buffers to Python */ Chris@71: Chris@71: /// passing the sample buffers as builtin python lists Chris@71: /// Optimization: using fast sequence protocol Chris@72: PyObject* Chris@71: PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) Chris@71: { Chris@71: //create a list of lists (new references) Chris@71: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); Chris@71: Chris@71: // Pack samples into a Python List Object Chris@71: // pyFloat/pyComplex types will always be new references, Chris@71: // they will be freed when the lists are deallocated. Chris@71: Chris@71: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); Chris@71: for (size_t i=0; i < channels; ++i) { Chris@71: Chris@71: size_t arraySize; Chris@71: if (dtype==Vamp::Plugin::FrequencyDomain) Chris@71: arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used Chris@71: else Chris@71: arraySize = blockSize; Chris@71: Chris@71: PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize); Chris@71: PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList); Chris@71: Chris@71: // Note: passing a complex list crashes the C-style plugin Chris@71: // when it tries to convert it to a numpy array directly. Chris@71: // This plugin will be obsolete, but we have to find a way Chris@71: // to prevent such crash: possibly a numpy bug, Chris@71: // works fine above 1.0.4 Chris@71: Chris@71: switch (dtype) //(Vamp::Plugin::TimeDomain) Chris@71: { Chris@71: case Vamp::Plugin::TimeDomain : Chris@71: Chris@71: for (size_t j = 0; j < arraySize; ++j) { Chris@71: PyObject *pyFloat=PyFloat_FromDouble( Chris@71: (double) inputBuffers[i][j]); Chris@71: pySampleListArray[j] = pyFloat; Chris@71: } Chris@71: break; Chris@71: Chris@71: case Vamp::Plugin::FrequencyDomain : Chris@71: Chris@71: size_t k = 0; Chris@71: for (size_t j = 0; j < arraySize; ++j) { Chris@71: PyObject *pyComplex=PyComplex_FromDoubles( Chris@71: (double) inputBuffers[i][k], Chris@71: (double) inputBuffers[i][k+1]); Chris@71: pySampleListArray[j] = pyComplex; Chris@71: k += 2; Chris@71: } Chris@71: break; Chris@71: Chris@71: } Chris@71: pyChannelListArray[i] = pySampleList; Chris@71: } Chris@71: return pyChannelList; Chris@71: } Chris@71: Chris@71: /// numpy buffer interface: passing the sample buffers as shared memory buffers Chris@71: /// Optimization: using sequence protocol for creating the buffer list Chris@72: PyObject* Chris@71: PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) Chris@71: { Chris@71: //create a list of buffers (returns new references) Chris@71: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); Chris@71: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); Chris@71: Chris@71: // Expose memory using the Buffer Interface. Chris@71: // This will pass a pointer which can be recasted in Python code Chris@71: // as complex or float array using Numpy's frombuffer() method Chris@71: // (this will not copy values just keep the starting adresses Chris@71: // for each channel in a list) Chris@71: Py_ssize_t bufferSize; Chris@71: Chris@71: if (dtype==Vamp::Plugin::FrequencyDomain) Chris@71: bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2); Chris@71: else Chris@71: bufferSize = (Py_ssize_t) sizeof(float) * blockSize; Chris@71: Chris@71: for (size_t i=0; i < channels; ++i) { Chris@71: PyObject *pyBuffer = PyBuffer_FromMemory Chris@71: ((void *) (float *) inputBuffers[i],bufferSize); Chris@71: pyChannelListArray[i] = pyBuffer; Chris@71: } Chris@71: return pyChannelList; Chris@71: } Chris@71: Chris@71: Chris@71: /// numpy array interface: passing the sample buffers as 2D numpy array Chris@71: /// Optimization: using array API (needs numpy headers) Chris@71: #ifdef HAVE_NUMPY Chris@72: PyObject* Chris@71: PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype) Chris@71: { Chris@71: /* Chris@71: NOTE: We create a list of 1D Numpy arrays for each channel instead Chris@71: of a matrix, because the address space of inputBuffers doesn't seem Chris@71: to be continuous. Although the array strides could be calculated for Chris@71: 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure Chris@71: if this can be trusted, especially for more than 2 channels. Chris@71: Chris@71: cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl; Chris@71: if (channels == 2) Chris@71: cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl; Chris@71: Chris@71: */ Chris@71: Chris@71: // create a list of arrays (returns new references) Chris@71: PyObject *pyChannelList = PyList_New((Py_ssize_t) channels); Chris@71: PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList); Chris@71: Chris@71: // Expose memory using the Numpy Array Interface. Chris@71: // This will wrap an array objects around the data. Chris@71: // (will not copy values just steal the starting adresses) Chris@71: Chris@71: int arraySize, typenum; Chris@71: Chris@71: switch (dtype) Chris@71: { Chris@71: case Vamp::Plugin::TimeDomain : Chris@71: typenum = dtype_float32; //NPY_FLOAT; Chris@71: arraySize = (int) blockSize; Chris@71: break; Chris@71: Chris@71: case Vamp::Plugin::FrequencyDomain : Chris@71: typenum = dtype_complex64; //NPY_CFLOAT; Chris@71: arraySize = (int) (blockSize / 2) + 1; Chris@71: break; Chris@71: Chris@71: default : Chris@71: cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl; Chris@71: return pyChannelList; Chris@71: } Chris@71: Chris@71: // size for each dimension Chris@71: npy_intp ndims[1]={arraySize}; Chris@71: Chris@71: for (size_t i=0; i < channels; ++i) { Chris@71: PyObject *pyChannelArray = Chris@71: //args: (dimensions, size in each dim, type kind, pointer to continuous array) Chris@71: PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]); Chris@71: // make it read-only: set all flags to false except NPY_C_CONTIGUOUS Chris@71: //!!! what about NPY_ARRAY_OWNDATA? Chris@71: PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff); Chris@71: PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS); Chris@71: pyChannelListArray[i] = pyChannelArray; Chris@71: } Chris@71: return pyChannelList; Chris@71: } Chris@71: #endif fazekasgy@37: fazekasgy@37: /// OutputDescriptor fazekasgy@37: void fazekasgy@37: PyTypeInterface::SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: switch (outKeys[key]) fazekasgy@37: { fazekasgy@37: case o::not_found: fazekasgy@37: setValueError("Unknown key in Vamp OutputDescriptor",m_strict); fazekasgy@37: cerr << "Unknown key in Vamp OutputDescriptor: " << key << endl; fazekasgy@37: break; fazekasgy@37: case o::identifier: fazekasgy@37: _convert(pyValue,od.identifier); fazekasgy@37: break; fazekasgy@37: case o::name: fazekasgy@37: _convert(pyValue,od.name); fazekasgy@37: break; fazekasgy@37: case o::description: fazekasgy@37: _convert(pyValue,od.description); fazekasgy@37: break; fazekasgy@37: case o::unit: fazekasgy@37: _convert(pyValue,od.unit); fazekasgy@37: break; fazekasgy@37: case o::hasFixedBinCount: fazekasgy@37: _convert(pyValue,od.hasFixedBinCount); fazekasgy@37: break; fazekasgy@37: case o::binCount: fazekasgy@37: _convert(pyValue,od.binCount); fazekasgy@37: break; fazekasgy@37: case o::binNames: fazekasgy@37: _convert(pyValue,od.binNames); fazekasgy@37: break; fazekasgy@37: case o::hasKnownExtents: fazekasgy@37: _convert(pyValue,od.hasKnownExtents); fazekasgy@37: break; fazekasgy@37: case o::minValue: fazekasgy@37: _convert(pyValue,od.minValue); fazekasgy@37: break; fazekasgy@37: case o::maxValue: fazekasgy@37: _convert(pyValue,od.maxValue); fazekasgy@37: break; fazekasgy@37: case o::isQuantized: fazekasgy@37: _convert(pyValue,od.isQuantized); fazekasgy@37: break; fazekasgy@37: case o::quantizeStep: fazekasgy@37: _convert(pyValue,od.quantizeStep); fazekasgy@37: break; fazekasgy@37: case o::sampleType: fazekasgy@37: _convert(pyValue,od.sampleType); fazekasgy@37: break; fazekasgy@37: case o::sampleRate: fazekasgy@37: _convert(pyValue,od.sampleRate); fazekasgy@37: break; fazekasgy@37: case o::hasDuration: fazekasgy@37: _convert(pyValue,od.hasDuration); fazekasgy@37: break; fazekasgy@37: default: fazekasgy@37: setValueError("Unknown key in Vamp OutputDescriptor",m_strict); fazekasgy@37: cerr << "Invalid key in Vamp OutputDescriptor: " << key << endl; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// ParameterDescriptor fazekasgy@37: void fazekasgy@37: PyTypeInterface::SetValue(Vamp::Plugin::ParameterDescriptor& pd, std::string& key, PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: switch (parmKeys[key]) fazekasgy@37: { fazekasgy@37: case p::not_found : fazekasgy@37: setValueError("Unknown key in Vamp ParameterDescriptor",m_strict); fazekasgy@37: cerr << "Unknown key in Vamp ParameterDescriptor: " << key << endl; fazekasgy@37: break; fazekasgy@37: case p::identifier: fazekasgy@37: _convert(pyValue,pd.identifier); fazekasgy@37: break; fazekasgy@37: case p::name: fazekasgy@37: _convert(pyValue,pd.name); fazekasgy@37: break; fazekasgy@37: case p::description: fazekasgy@37: _convert(pyValue,pd.description); fazekasgy@37: break; fazekasgy@37: case p::unit: fazekasgy@37: _convert(pyValue,pd.unit); fazekasgy@37: break; fazekasgy@37: case p::minValue: fazekasgy@37: _convert(pyValue,pd.minValue); fazekasgy@37: break; fazekasgy@37: case p::maxValue: fazekasgy@37: _convert(pyValue,pd.maxValue); fazekasgy@37: break; fazekasgy@37: case p::defaultValue: fazekasgy@37: _convert(pyValue,pd.defaultValue); fazekasgy@37: break; fazekasgy@37: case p::isQuantized: fazekasgy@37: _convert(pyValue,pd.isQuantized); fazekasgy@37: break; fazekasgy@37: case p::quantizeStep: fazekasgy@37: _convert(pyValue,pd.quantizeStep); fazekasgy@37: break; gyorgyf@62: case p::valueNames: gyorgyf@62: _convert(pyValue,pd.valueNames); gyorgyf@62: break; fazekasgy@37: default : fazekasgy@37: setValueError("Unknown key in Vamp ParameterDescriptor",m_strict); fazekasgy@37: cerr << "Invalid key in Vamp ParameterDescriptor: " << key << endl; fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// Feature (it's like a Descriptor) fazekasgy@37: bool fazekasgy@37: PyTypeInterface::SetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const fazekasgy@37: { fazekasgy@37: bool found = true; fazekasgy@37: switch (ffKeys[key]) fazekasgy@37: { fazekasgy@37: case unknown : fazekasgy@37: setValueError("Unknown key in Vamp Feature",m_strict); fazekasgy@37: cerr << "Unknown key in Vamp Feature: " << key << endl; fazekasgy@37: found = false; fazekasgy@37: break; fazekasgy@37: case hasTimestamp: fazekasgy@37: _convert(pyValue,feature.hasTimestamp); fazekasgy@37: break; fazekasgy@37: case timestamp: fazekasgy@37: _convert(pyValue,feature.timestamp); fazekasgy@37: break; fazekasgy@37: case hasDuration: fazekasgy@37: _convert(pyValue,feature.hasDuration); fazekasgy@37: break; fazekasgy@37: case duration: fazekasgy@37: _convert(pyValue,feature.duration); fazekasgy@37: break; fazekasgy@37: case values: fazekasgy@37: _convert(pyValue,feature.values); fazekasgy@37: break; fazekasgy@37: case label: fazekasgy@37: _convert(pyValue,feature.label); fazekasgy@37: break; fazekasgy@37: default: fazekasgy@37: setValueError("Unknown key in Vamp Feature",m_strict); fazekasgy@37: found = false; fazekasgy@37: } fazekasgy@37: return found; fazekasgy@37: } fazekasgy@37: fazekasgy@37: Chris@71: /* Error handling */ fazekasgy@37: fazekasgy@37: void fazekasgy@37: PyTypeInterface::setValueError (std::string message, bool strict) const fazekasgy@37: { fazekasgy@37: m_error = true; fazekasgy@37: m_errorQueue.push(ValueError(message,strict)); fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// return a reference to the last error or creates a new one. Chris@71: ValueError& fazekasgy@37: PyTypeInterface::lastError() const fazekasgy@37: { fazekasgy@37: m_error = false; fazekasgy@37: if (!m_errorQueue.empty()) return m_errorQueue.back(); fazekasgy@37: else { fazekasgy@37: m_errorQueue.push(ValueError("Type conversion error.",m_strict)); fazekasgy@37: return m_errorQueue.back(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// helper function to iterate over the error message queue: fazekasgy@37: /// pops the oldest item Chris@71: ValueError fazekasgy@37: PyTypeInterface::getError() const fazekasgy@37: { fazekasgy@37: if (!m_errorQueue.empty()) { Chris@71: ValueError e = m_errorQueue.front(); fazekasgy@37: m_errorQueue.pop(); fazekasgy@37: if (m_errorQueue.empty()) m_error = false; fazekasgy@37: return e; fazekasgy@37: } fazekasgy@37: else { fazekasgy@37: m_error = false; Chris@71: return ValueError(); fazekasgy@37: } fazekasgy@37: } fazekasgy@37: Chris@71: /* Utilities */ fazekasgy@37: fazekasgy@37: bool fazekasgy@37: PyTypeInterface::initMaps() const fazekasgy@37: { fazekasgy@37: fazekasgy@37: if (isMapInitialised) return true; fazekasgy@37: fazekasgy@37: outKeys["identifier"] = o::identifier; fazekasgy@37: outKeys["name"] = o::name; fazekasgy@37: outKeys["description"] = o::description; fazekasgy@37: outKeys["unit"] = o::unit; fazekasgy@37: outKeys["hasFixedBinCount"] = o::hasFixedBinCount; fazekasgy@37: outKeys["binCount"] = o::binCount; fazekasgy@37: outKeys["binNames"] = o::binNames; fazekasgy@37: outKeys["hasKnownExtents"] = o::hasKnownExtents; fazekasgy@37: outKeys["minValue"] = o::minValue; fazekasgy@37: outKeys["maxValue"] = o::maxValue; fazekasgy@37: outKeys["isQuantized"] = o::isQuantized; fazekasgy@37: outKeys["quantizeStep"] = o::quantizeStep; fazekasgy@37: outKeys["sampleType"] = o::sampleType; fazekasgy@37: outKeys["sampleRate"] = o::sampleRate; fazekasgy@37: outKeys["hasDuration"] = o::hasDuration; fazekasgy@37: fazekasgy@37: sampleKeys["OneSamplePerStep"] = OneSamplePerStep; fazekasgy@37: sampleKeys["FixedSampleRate"] = FixedSampleRate; fazekasgy@37: sampleKeys["VariableSampleRate"] = VariableSampleRate; fazekasgy@37: fazekasgy@37: ffKeys["hasTimestamp"] = hasTimestamp; fazekasgy@37: ffKeys["timestamp"] = timestamp; // this is the correct one fazekasgy@37: ffKeys["timeStamp"] = timestamp; // backward compatible fazekasgy@37: ffKeys["hasDuration"] = hasDuration; fazekasgy@37: ffKeys["duration"] = duration; fazekasgy@37: ffKeys["values"] = values; fazekasgy@37: ffKeys["label"] = label; fazekasgy@37: fazekasgy@37: parmKeys["identifier"] = p::identifier; fazekasgy@37: parmKeys["name"] = p::name; fazekasgy@37: parmKeys["description"] = p::description; fazekasgy@37: parmKeys["unit"] = p::unit; fazekasgy@37: parmKeys["minValue"] = p::minValue; fazekasgy@37: parmKeys["maxValue"] = p::maxValue; fazekasgy@37: parmKeys["defaultValue"] = p::defaultValue; fazekasgy@37: parmKeys["isQuantized"] = p::isQuantized; fazekasgy@37: parmKeys["quantizeStep"] = p::quantizeStep; gyorgyf@62: parmKeys["valueNames"] = p::valueNames; fazekasgy@37: fazekasgy@37: isMapInitialised = true; fazekasgy@37: return true; fazekasgy@37: }