cannam@18: /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */ fazekasgy@0: /* fazekasgy@0: Vamp fazekasgy@0: fazekasgy@0: An API for audio analysis and feature extraction plugins. fazekasgy@0: fazekasgy@0: Centre for Digital Music, Queen Mary, University of London. fazekasgy@0: Copyright 2006 Chris Cannam. fazekasgy@0: fazekasgy@0: Permission is hereby granted, free of charge, to any person fazekasgy@0: obtaining a copy of this software and associated documentation fazekasgy@0: files (the "Software"), to deal in the Software without fazekasgy@0: restriction, including without limitation the rights to use, copy, fazekasgy@0: modify, merge, publish, distribute, sublicense, and/or sell copies fazekasgy@0: of the Software, and to permit persons to whom the Software is fazekasgy@0: furnished to do so, subject to the following conditions: fazekasgy@0: fazekasgy@0: The above copyright notice and this permission notice shall be fazekasgy@0: included in all copies or substantial portions of the Software. fazekasgy@0: fazekasgy@0: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, fazekasgy@0: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF fazekasgy@0: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND fazekasgy@0: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR fazekasgy@0: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF fazekasgy@0: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION fazekasgy@0: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fazekasgy@0: fazekasgy@0: Except as contained in this notice, the names of the Centre for fazekasgy@0: Digital Music; Queen Mary, University of London; and Chris Cannam fazekasgy@0: shall not be used in advertising or otherwise to promote the sale, fazekasgy@0: use or other dealings in this Software without prior written fazekasgy@0: authorization. fazekasgy@0: */ fazekasgy@0: fazekasgy@0: fazekasgy@0: fazekasgy@0: /** fazekasgy@8: * This Vamp plugin is a wrapper for Python Scripts. (VamPy) fazekasgy@0: * Centre for Digital Music, Queen Mary, University of London. fazekasgy@0: * Copyright 2008, George Fazekas. fazekasgy@0: fazekasgy@0: TODO: needs more complete error checking fazekasgy@0: needs correct implementation of Python threading fazekasgy@0: more efficient data conversion using the buffering interface or ctypes cannam@7: Vamp programs not implemented fazekasgy@0: support multiple plugins per script in scanner fazekasgy@0: ensure proper cleanup, host do a good job though fazekasgy@0: fazekasgy@0: */ fazekasgy@0: cannam@3: #include fazekasgy@0: #include "PyPlugin.h" fazekasgy@0: fazekasgy@0: #ifdef _WIN32 fazekasgy@0: #define pathsep ('\\') fazekasgy@0: #else fazekasgy@0: #define pathsep ('/') fazekasgy@0: #endif fazekasgy@0: fazekasgy@6: //#define _DEBUG fazekasgy@0: fazekasgy@0: using std::string; fazekasgy@0: using std::vector; fazekasgy@0: using std::cerr; fazekasgy@0: using std::endl; fazekasgy@0: using std::map; fazekasgy@0: fazekasgy@0: // Maps to associate strings with enum values cannam@18: static std::map outKeys; fazekasgy@0: static std::map sampleKeys; fazekasgy@0: static std::map ffKeys; fazekasgy@0: static std::map parmKeys; fazekasgy@0: cannam@3: Mutex PyPlugin::m_pythonInterpreterMutex; fazekasgy@6: static bool isMapInitialised = false; fazekasgy@0: fazekasgy@0: PyPlugin::PyPlugin(std::string pluginKey,float inputSampleRate, PyObject *pyInstance) : fazekasgy@0: Plugin(inputSampleRate), fazekasgy@0: m_pyInstance(pyInstance), fazekasgy@0: m_stepSize(0), fazekasgy@6: m_blockSize(0), fazekasgy@6: m_channels(0), fazekasgy@0: m_plugin(pluginKey), fazekasgy@0: m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), fazekasgy@6: m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))), fazekasgy@6: m_processType(0), fazekasgy@6: m_pyProcess(NULL), fazekasgy@6: m_inputDomain(TimeDomain) fazekasgy@0: { fazekasgy@0: } fazekasgy@0: fazekasgy@0: PyPlugin::~PyPlugin() fazekasgy@0: { fazekasgy@6: Py_CLEAR(m_pyProcess); fazekasgy@6: #ifdef _DEBUG fazekasgy@0: cerr << "PyPlugin::PyPlugin:" << m_class fazekasgy@0: << " Instance deleted." << endl; fazekasgy@6: #endif fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: string fazekasgy@0: PyPlugin::getIdentifier() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getIdentifier"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@6: string rString="vampy-x"; fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyString = fazekasgy@0: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rString=PyString_AsString(pyString); fazekasgy@0: Py_CLEAR(pyString); fazekasgy@6: return rString; fazekasgy@0: } fazekasgy@0: cerr << "Warning: Plugin must return a unique identifier." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: string fazekasgy@0: PyPlugin::getName() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); fazekasgy@0: fazekasgy@0: char method[]="getName"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: string rString="VamPy Plugin (Noname)"; fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyString = fazekasgy@0: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rString=PyString_AsString(pyString); fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: } fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: string fazekasgy@0: PyPlugin::getDescription() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getDescription"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: string rString="Not given. (Hint: Implement getDescription method.)"; fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyString = fazekasgy@0: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rString=PyString_AsString(pyString); fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: } fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: string fazekasgy@0: PyPlugin::getMaker() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getMaker"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: string rString="Generic VamPy Plugin."; fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyString = fazekasgy@0: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rString=PyString_AsString(pyString); fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: } fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: int fazekasgy@0: PyPlugin::getPluginVersion() const fazekasgy@0: { fazekasgy@0: return 2; fazekasgy@0: } fazekasgy@0: fazekasgy@0: string fazekasgy@0: PyPlugin::getCopyright() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getCopyright"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: string rString="BSD License"; fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyString = fazekasgy@0: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: rString=PyString_AsString(pyString); fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: } fazekasgy@0: return rString; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: bool fazekasgy@0: PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) fazekasgy@0: { fazekasgy@8: //useful for debugging Python plugins fazekasgy@8: char method[]="initialise"; fazekasgy@8: cerr << "[call] " << method << endl; fazekasgy@6: fazekasgy@6: //placing Mutex before these calls causes deadlock fazekasgy@6: if (channels < getMinChannelCount() || fazekasgy@6: channels > getMaxChannelCount()) return false; fazekasgy@6: fazekasgy@6: m_inputDomain = getInputDomain(); fazekasgy@6: cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@6: initMaps(); fazekasgy@6: fazekasgy@6: m_stepSize = stepSize; fazekasgy@6: m_blockSize = blockSize; fazekasgy@6: m_channels = channels; fazekasgy@6: fazekasgy@6: //quering process implementation type fazekasgy@6: char legacyMethod[]="process"; fazekasgy@6: char numpyMethod[]="processN"; fazekasgy@6: cannam@18: if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && cannam@18: m_processType == 0) fazekasgy@6: { fazekasgy@6: m_processType = legacyProcess; fazekasgy@6: m_pyProcess = PyString_FromString(legacyMethod); fazekasgy@6: } fazekasgy@6: cannam@18: if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && cannam@18: m_processType == 0) fazekasgy@6: { fazekasgy@6: m_processType = numpyProcess; fazekasgy@6: m_pyProcess = PyString_FromString(numpyMethod); fazekasgy@6: } fazekasgy@6: fazekasgy@6: if (!m_processType) fazekasgy@6: { fazekasgy@6: m_processType = not_implemented; fazekasgy@6: m_pyProcess = NULL; fazekasgy@6: cerr << "Warning: Python plugin [" << m_class << "::" << method fazekasgy@6: << "] No process implementation found. Plugin will do nothing." << endl; fazekasgy@6: } fazekasgy@6: fazekasgy@6: fazekasgy@0: //Check if the method is implemented in Python else return false fazekasgy@0: if (PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@0: fazekasgy@0: PyObject *pyMethod = PyString_FromString(method); fazekasgy@0: PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels); fazekasgy@0: PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize); fazekasgy@0: PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize); fazekasgy@0: PyObject *pyInputSampleRate = PyFloat_FromDouble((double)m_inputSampleRate); fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyBool = fazekasgy@0: PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL); fazekasgy@8: fazekasgy@0: Py_DECREF(pyMethod); fazekasgy@0: Py_DECREF(pyChannels); fazekasgy@0: Py_DECREF(pyStepSize); fazekasgy@0: Py_DECREF(pyBlockSize); fazekasgy@0: Py_DECREF(pyInputSampleRate); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@8: if (PyErr_Occurred() || !PyBool_Check(pyBool)) { fazekasgy@8: PyErr_Print(); PyErr_Clear(); fazekasgy@0: Py_CLEAR(pyBool); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected Bool return value." << endl; fazekasgy@0: return false; fazekasgy@0: } fazekasgy@0: fazekasgy@0: if (pyBool == Py_True) { fazekasgy@0: Py_CLEAR(pyBool); fazekasgy@0: return true; fazekasgy@0: } else { fazekasgy@0: Py_CLEAR(pyBool); fazekasgy@0: return false;} fazekasgy@0: } fazekasgy@6: return false; fazekasgy@0: } fazekasgy@0: fazekasgy@0: void fazekasgy@0: PyPlugin::reset() fazekasgy@0: { fazekasgy@6: MutexLocker locker(&m_pythonInterpreterMutex); fazekasgy@6: fazekasgy@6: char method[]="reset"; fazekasgy@6: cerr << "[call] " << method << endl; fazekasgy@6: fazekasgy@6: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@6: fazekasgy@6: PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@6: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@6: fazekasgy@6: } fazekasgy@0: } fazekasgy@0: fazekasgy@6: PyPlugin::InputDomain PyPlugin::getInputDomain() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getInputDomain"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain fazekasgy@0: fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: fazekasgy@0: PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyString_Check(pyString)) { fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: string domain = (string) PyString_AsString(pyString); fazekasgy@0: if (domain == "FrequencyDomain") rValue = FrequencyDomain; fazekasgy@0: Py_CLEAR(pyString); fazekasgy@0: } fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@6: fazekasgy@0: size_t PyPlugin::getPreferredBlockSize() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getPreferredBlockSize"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: size_t rValue=0; //not set by default fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyInt_Check(pyInt)) { fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected Integer return value." << endl; fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rValue=(size_t)PyInt_AS_LONG(pyInt); fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: } fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: //size_t PyPlugin::getPreferredStepSize() const { return 0; } fazekasgy@0: size_t PyPlugin::getPreferredStepSize() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getPreferredStepSize"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@8: size_t rValue=1024; //not set by default fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyInt_Check(pyInt)) { fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected Integer return value." << endl; fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rValue=(size_t)PyInt_AS_LONG(pyInt); fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: } fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: size_t PyPlugin::getMinChannelCount() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getMinChannelCount"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: size_t rValue=1; //default value fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyInt_Check(pyInt)) { fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rValue=(size_t)PyInt_AS_LONG(pyInt); fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: } fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: size_t PyPlugin::getMaxChannelCount() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getMaxChannelCount"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: size_t rValue=1; //default value fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@0: fazekasgy@0: //Check return value fazekasgy@0: if (!PyInt_Check(pyInt)) { fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected String return value." << endl; fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rValue=(size_t)PyInt_AS_LONG(pyInt); fazekasgy@0: Py_CLEAR(pyInt); fazekasgy@0: } fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: PyPlugin::OutputList fazekasgy@0: PyPlugin::getOutputDescriptors() const fazekasgy@0: { fazekasgy@6: cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); fazekasgy@6: fazekasgy@0: //PyEval_AcquireThread(newThreadState); fazekasgy@0: OutputList list; fazekasgy@0: OutputDescriptor od; fazekasgy@0: char method[]="getOutputDescriptors"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: fazekasgy@0: //Check if the method is implemented in Python fazekasgy@6: if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; fazekasgy@0: fazekasgy@0: //Call the method: must return list object (new reference) fazekasgy@0: PyObject *pyList = fazekasgy@0: PyObject_CallMethod(m_pyInstance,method, NULL); fazekasgy@0: fazekasgy@0: //Check return type fazekasgy@0: if (! PyList_Check(pyList) ) { fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected List return type." << endl; fazekasgy@0: return list; fazekasgy@0: } fazekasgy@0: fazekasgy@0: //These will all be borrowed references (no need to DECREF) fazekasgy@0: PyObject *pyDict, *pyKey, *pyValue; fazekasgy@0: fazekasgy@0: //Parse Output List fazekasgy@0: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { fazekasgy@0: cannam@18: //Get i-th Vamp output descriptor (Borrowed Reference) fazekasgy@0: pyDict = PyList_GET_ITEM(pyList,i); fazekasgy@0: fazekasgy@0: //We only care about dictionaries holding output descriptors fazekasgy@0: if ( !PyDict_Check(pyDict) ) continue; fazekasgy@0: fazekasgy@0: Py_ssize_t pyPos = NULL; fazekasgy@0: initMaps(); fazekasgy@0: fazekasgy@0: //Python Sequence Iterator fazekasgy@0: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) fazekasgy@0: { fazekasgy@0: switch (outKeys[PyString_AsString(pyKey)]) fazekasgy@0: { cannam@18: case o::not_found : cannam@18: cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@0: break; cannam@18: case o::identifier: fazekasgy@0: od.identifier = PyString_AsString(pyValue); fazekasgy@0: break; cannam@18: case o::name: fazekasgy@0: od.name = PyString_AsString(pyValue); fazekasgy@0: break; cannam@18: case o::description: fazekasgy@0: od.description = PyString_AsString(pyValue); fazekasgy@0: break; cannam@18: case o::unit: fazekasgy@0: od.unit = PyString_AsString(pyValue); fazekasgy@0: break; cannam@18: case o::hasFixedBinCount: fazekasgy@0: od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; cannam@18: case o::binCount: fazekasgy@0: od.binCount = (size_t) PyInt_AS_LONG(pyValue); fazekasgy@0: break; cannam@18: case o::binNames: fazekasgy@0: od.binNames = PyList_To_StringVector(pyValue); fazekasgy@0: break; cannam@18: case o::hasKnownExtents: fazekasgy@0: od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; cannam@18: case o::minValue: fazekasgy@0: od.minValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; cannam@18: case o::maxValue: fazekasgy@0: od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; cannam@18: case o::isQuantized: fazekasgy@0: od.isQuantized = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; cannam@18: case o::quantizeStep: fazekasgy@0: od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; cannam@18: case o::sampleType: fazekasgy@0: od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; fazekasgy@0: break; cannam@18: case o::sampleRate: fazekasgy@0: od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@8: // od.sampleRate = m_inputSampleRate / m_stepSize; fazekasgy@8: cerr << od.sampleRate << endl; fazekasgy@0: break; cannam@18: case o::hasDuration: cannam@18: od.hasDuration = (bool)PyInt_AS_LONG(pyValue); cannam@18: break; fazekasgy@0: default : cannam@18: cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@6: } fazekasgy@6: } // while dict fazekasgy@0: list.push_back(od); fazekasgy@6: } // for list fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: return list; fazekasgy@0: } fazekasgy@0: fazekasgy@0: PyPlugin::ParameterList fazekasgy@0: PyPlugin::getParameterDescriptors() const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: ParameterList list; fazekasgy@0: ParameterDescriptor pd; fazekasgy@0: char method[]="getParameterDescriptors"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: fazekasgy@0: //Check if the method is implemented in Python fazekasgy@6: if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; fazekasgy@0: fazekasgy@0: //Call the method: must return list object (new reference) fazekasgy@0: PyObject *pyList = fazekasgy@0: PyObject_CallMethod(m_pyInstance,method, NULL); fazekasgy@0: fazekasgy@0: //Check return type fazekasgy@0: if (! PyList_Check(pyList) ) { fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected List return type." << endl; fazekasgy@0: return list; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: //These will all be borrowed references (no need to DECREF) fazekasgy@0: PyObject *pyDict, *pyKey, *pyValue; fazekasgy@0: fazekasgy@0: //Parse Output List fazekasgy@0: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { fazekasgy@0: cannam@18: //Get i-th Vamp output descriptor (Borrowed Reference) fazekasgy@0: pyDict = PyList_GET_ITEM(pyList,i); fazekasgy@0: fazekasgy@0: //We only care about dictionaries holding output descriptors fazekasgy@0: if ( !PyDict_Check(pyDict) ) continue; fazekasgy@0: fazekasgy@0: Py_ssize_t pyPos = NULL; fazekasgy@0: initMaps(); fazekasgy@0: fazekasgy@0: //Python Sequence Iterator fazekasgy@0: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) fazekasgy@0: { fazekasgy@0: switch (parmKeys[PyString_AsString(pyKey)]) fazekasgy@0: { cannam@18: case p::not_found : cannam@18: cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@0: break; fazekasgy@0: case p::identifier: fazekasgy@0: pd.identifier = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case p::name: fazekasgy@0: pd.name = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case p::description: fazekasgy@0: pd.description = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case p::unit: fazekasgy@0: pd.unit = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case p::minValue: fazekasgy@0: pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case p::maxValue: fazekasgy@0: pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case p::defaultValue: fazekasgy@0: pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case p::isQuantized: fazekasgy@0: pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: default : cannam@18: cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@6: } fazekasgy@6: } // while dict fazekasgy@0: list.push_back(pd); fazekasgy@6: } // for list fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: return list; fazekasgy@0: } fazekasgy@0: fazekasgy@0: void PyPlugin::setParameter(std::string paramid, float newval) fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="setParameter"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: fazekasgy@0: //Check if the method is implemented in Python fazekasgy@0: if (PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@0: fazekasgy@0: PyObject *pyMethod = PyString_FromString(method); fazekasgy@0: PyObject *pyParamid = PyString_FromString(paramid.c_str()); fazekasgy@0: PyObject *pyNewval = PyFloat_FromDouble((double)newval); fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyBool = fazekasgy@0: PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL); fazekasgy@0: fazekasgy@0: //This only happens if there is a syntax error or so fazekasgy@0: if (pyBool == NULL) { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Error setting parameter: " << paramid << endl; fazekasgy@0: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@0: } fazekasgy@0: fazekasgy@0: Py_DECREF(pyMethod); fazekasgy@0: Py_DECREF(pyParamid); fazekasgy@0: Py_DECREF(pyNewval); fazekasgy@0: } fazekasgy@0: } fazekasgy@0: fazekasgy@0: float PyPlugin::getParameter(std::string paramid) const fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: char method[]="getParameter"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: float rValue = 0.0f; fazekasgy@0: fazekasgy@0: //Check if the method is implemented in Python fazekasgy@0: if (PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@0: fazekasgy@0: PyObject *pyMethod = PyString_FromString(method); fazekasgy@0: PyObject *pyParamid = PyString_FromString(paramid.c_str()); fazekasgy@0: fazekasgy@0: //Call the method fazekasgy@0: PyObject *pyFloat = fazekasgy@0: PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL); fazekasgy@0: fazekasgy@0: //Check return type fazekasgy@0: if (! PyFloat_Check(pyFloat) ) { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected Float return type." << endl; fazekasgy@0: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@0: Py_CLEAR(pyFloat); fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: rValue = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@0: fazekasgy@0: Py_DECREF(pyMethod); fazekasgy@0: Py_DECREF(pyParamid); fazekasgy@0: Py_DECREF(pyFloat); fazekasgy@0: } fazekasgy@0: fazekasgy@0: return rValue; fazekasgy@0: } fazekasgy@0: fazekasgy@0: #ifdef _DEBUG fazekasgy@0: static int proccounter = 0; fazekasgy@0: #endif fazekasgy@0: fazekasgy@0: PyPlugin::FeatureSet fazekasgy@0: PyPlugin::process(const float *const *inputBuffers, fazekasgy@0: Vamp::RealTime timestamp) fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@6: #ifdef _DEBUG fazekasgy@6: cerr << "[call] process, frame:" << proccounter << endl; fazekasgy@6: proccounter++; fazekasgy@6: #endif fazekasgy@6: fazekasgy@8: if (m_blockSize == 0 || m_channels == 0) { fazekasgy@0: cerr << "ERROR: PyPlugin::process: " fazekasgy@0: << "Plugin has not been initialised" << endl; fazekasgy@0: return FeatureSet(); fazekasgy@0: } fazekasgy@0: fazekasgy@6: if (m_processType == not_implemented) { fazekasgy@6: cerr << "ERROR: In Python plugin [" << m_class fazekasgy@6: << "] No process implementation found. Returning empty feature set." << endl; fazekasgy@6: return FeatureSet(); fazekasgy@6: } fazekasgy@0: fazekasgy@6: string method=PyString_AsString(m_pyProcess); fazekasgy@6: fazekasgy@6: PyObject *pyOutputList = NULL; fazekasgy@0: fazekasgy@6: /*new numPy support*/ fazekasgy@6: if (m_processType == numpyProcess) { fazekasgy@8: fazekasgy@8: //create a list of buffers fazekasgy@8: PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); fazekasgy@8: for (size_t i=0; i < m_channels; ++i) { fazekasgy@6: fazekasgy@8: //Expose memory using the Buffer Interface of C/API fazekasgy@8: //This will virtually pass a pointer which can be fazekasgy@8: //recasted in Python code as float or complex array fazekasgy@8: PyObject *pyBuffer = PyBuffer_FromMemory fazekasgy@8: ((void *) (float *) inputBuffers[i], fazekasgy@8: (Py_ssize_t) sizeof(float) * m_blockSize); fazekasgy@6: fazekasgy@8: PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); fazekasgy@8: } fazekasgy@8: fazekasgy@8: //pass RealTime as frameCount fazekasgy@8: PyObject *pyLongSample = PyLong_FromLong ( fazekasgy@8: Vamp::RealTime::realTime2Frame fazekasgy@8: (timestamp, (unsigned int) m_inputSampleRate)); fazekasgy@6: fazekasgy@8: //Call python process (returns new reference) fazekasgy@8: pyOutputList = PyObject_CallMethodObjArgs fazekasgy@8: (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); fazekasgy@8: fazekasgy@8: Py_DECREF(pyChannelList); fazekasgy@8: Py_DECREF(pyLongSample); fazekasgy@6: fazekasgy@6: } fazekasgy@6: fazekasgy@6: if (m_processType == legacyProcess) { fazekasgy@0: fazekasgy@8: //create a list of lists fazekasgy@8: PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); fazekasgy@8: for (size_t i=0; i < m_channels; ++i) { fazekasgy@0: fazekasgy@8: //Declare new list object fazekasgy@8: PyObject *pyFloat, *pyList; fazekasgy@8: pyList = PyList_New((Py_ssize_t) m_blockSize); fazekasgy@8: fazekasgy@8: //Pack samples into a Python List Object fazekasgy@8: //pyFloat types will always be new references, fazekasgy@8: //these will be discarded when the list is deallocated fazekasgy@8: for (size_t j = 0; j < m_blockSize; ++j) { fazekasgy@8: pyFloat=PyFloat_FromDouble( fazekasgy@8: (double) inputBuffers[i][j]); fazekasgy@8: PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); fazekasgy@8: } fazekasgy@8: PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); fazekasgy@8: } fazekasgy@8: fazekasgy@8: //pass RealTime as frameCount fazekasgy@8: PyObject *pyLongSample = PyLong_FromLong ( fazekasgy@8: Vamp::RealTime::realTime2Frame fazekasgy@8: (timestamp, (unsigned int) m_inputSampleRate)); fazekasgy@8: fazekasgy@8: //Call python process (returns new reference) fazekasgy@8: pyOutputList = PyObject_CallMethodObjArgs fazekasgy@8: (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); fazekasgy@8: fazekasgy@8: Py_DECREF(pyChannelList); fazekasgy@8: Py_DECREF(pyLongSample); fazekasgy@8: fazekasgy@0: } fazekasgy@6: fazekasgy@8: //return nothing fazekasgy@8: //Py_CLEAR(pyOutputList); fazekasgy@8: //return FeatureSet(); fazekasgy@0: fazekasgy@0: //Check return type fazekasgy@0: if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { fazekasgy@0: if (pyOutputList == NULL) { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Unexpected result." << endl; fazekasgy@0: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@0: } else { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected List return type." << endl; fazekasgy@0: } fazekasgy@0: Py_CLEAR(pyOutputList); fazekasgy@0: return FeatureSet(); fazekasgy@0: } fazekasgy@0: fazekasgy@0: // Py_DECREF(pyList); fazekasgy@0: // This appears to be tracked by the cyclic garbage collector fazekasgy@0: // hence decrefing produces GC error fazekasgy@0: #ifdef _DEBUG fazekasgy@0: cerr << "Process Returned Features" << endl; fazekasgy@0: #endif fazekasgy@0: // These will ALL be borrowed references fazekasgy@0: PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; fazekasgy@0: fazekasgy@0: FeatureSet returnFeatures; fazekasgy@0: fazekasgy@0: //Parse Output List for each element (FeatureSet) fazekasgy@0: for (Py_ssize_t i = 0; fazekasgy@0: i < PyList_GET_SIZE(pyOutputList); ++i) { fazekasgy@0: //cerr << "output (FeatureSet): " << i << endl; fazekasgy@0: fazekasgy@0: //Get i-th FeatureList (Borrowed Reference) fazekasgy@0: pyFeatureList = PyList_GET_ITEM(pyOutputList,i); fazekasgy@0: fazekasgy@0: //Parse FeatureList for each element (Feature) fazekasgy@0: for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { fazekasgy@0: //cerr << "element (FeatureList): " << j << endl; fazekasgy@0: fazekasgy@0: //Get j-th Feature (Borrowed Reference) fazekasgy@0: pyDict = PyList_GET_ITEM(pyFeatureList,j); fazekasgy@0: fazekasgy@0: //We only care about dictionaries holding a Feature struct fazekasgy@0: if ( !PyDict_Check(pyDict) ) continue; fazekasgy@0: fazekasgy@0: Py_ssize_t pyPos = NULL; fazekasgy@0: bool emptyFeature = true; fazekasgy@0: Feature feature; fazekasgy@0: fazekasgy@0: //process::Python Sequence Iterator for dictionary fazekasgy@0: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) fazekasgy@0: { fazekasgy@0: emptyFeature = false; fazekasgy@0: switch (ffKeys[PyString_AsString(pyKey)]) fazekasgy@0: { cannam@18: case unknown: cannam@18: cerr << "Unknown key in Vamp FeatureSet: " fazekasgy@0: << PyString_AsString(pyKey) << endl; fazekasgy@0: break; fazekasgy@0: case hasTimestamp: fazekasgy@0: feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case timeStamp: fazekasgy@8: feature.timestamp = fazekasgy@8: Vamp::RealTime::frame2RealTime( fazekasgy@8: PyLong_AsLong(pyValue), fazekasgy@8: (unsigned int) m_inputSampleRate ); fazekasgy@8: #ifdef _DEBUG fazekasgy@8: cerr << "Timestamp: " fazekasgy@8: << (long)PyLong_AsLong(pyValue) << ", ->" fazekasgy@8: << feature.timestamp.toString() << endl; fazekasgy@8: #endif fazekasgy@0: break; cannam@18: case hasDuration: cannam@18: feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); cannam@18: break; cannam@18: case duration: cannam@18: feature.duration = cannam@18: Vamp::RealTime::frame2RealTime( cannam@18: PyLong_AsLong(pyValue), cannam@18: (unsigned int) m_inputSampleRate ); cannam@18: #ifdef _DEBUG cannam@18: cerr << "Duration: " cannam@18: << (long)PyLong_AsLong(pyValue) << ", ->" cannam@18: << feature.duration.toString() << endl; cannam@18: #endif cannam@18: break; fazekasgy@0: case values: fazekasgy@0: feature.values = PyList_As_FloatVector(pyValue); fazekasgy@0: break; fazekasgy@0: case label: fazekasgy@0: feature.label = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: default : cannam@18: cerr << "Invalid key in Vamp FeatureSet: " fazekasgy@0: << PyString_AsString(pyKey) << endl; fazekasgy@0: } // switch fazekasgy@0: fazekasgy@0: } // while fazekasgy@0: if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; fazekasgy@0: else returnFeatures[i].push_back(feature); fazekasgy@0: fazekasgy@0: }// for j = FeatureList fazekasgy@0: fazekasgy@0: }//for i = FeatureSet fazekasgy@0: Py_CLEAR(pyOutputList); fazekasgy@0: return returnFeatures; fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: fazekasgy@0: PyPlugin::FeatureSet fazekasgy@0: PyPlugin::getRemainingFeatures() fazekasgy@0: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: static char method[]="getRemainingFeatures"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: fazekasgy@0: //check if the method is implemented fazekasgy@0: if ( ! PyObject_HasAttrString(m_pyInstance,method) ) { fazekasgy@0: return FeatureSet(); fazekasgy@0: } fazekasgy@0: fazekasgy@0: PyObject *pyMethod = PyString_FromString(method); fazekasgy@0: fazekasgy@0: PyObject *pyOutputList = fazekasgy@0: PyObject_CallMethod(m_pyInstance,method, NULL); fazekasgy@0: fazekasgy@0: //Check return type fazekasgy@0: if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { fazekasgy@0: if (pyOutputList == NULL) { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Unexpected result." << endl; fazekasgy@0: if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } fazekasgy@0: } else { fazekasgy@0: cerr << "ERROR: In Python plugin [" << m_class << "::" << method fazekasgy@0: << "] Expected List return type." << endl; fazekasgy@0: } fazekasgy@0: Py_CLEAR(pyMethod); fazekasgy@0: Py_CLEAR(pyOutputList); fazekasgy@0: return FeatureSet(); fazekasgy@0: } fazekasgy@0: Py_DECREF(pyMethod); fazekasgy@0: fazekasgy@0: PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; fazekasgy@0: FeatureSet returnFeatures; fazekasgy@8: fazekasgy@8: //iterate through list of outputs fazekasgy@0: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { fazekasgy@0: fazekasgy@0: pyFeatureList = PyList_GET_ITEM(pyOutputList,i); fazekasgy@0: fazekasgy@8: //iterate list of Features fazekasgy@0: for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { fazekasgy@8: #ifdef _DEBUG fazekasgy@8: cerr << "feature: " << j << endl; fazekasgy@8: #endif fazekasgy@0: pyDict = PyList_GET_ITEM(pyFeatureList,j); fazekasgy@0: fazekasgy@0: if ( !PyDict_Check(pyDict) ) continue; fazekasgy@0: fazekasgy@0: Py_ssize_t pyPos = NULL; fazekasgy@0: bool emptyFeature = true; fazekasgy@0: Feature feature; fazekasgy@0: fazekasgy@0: while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) fazekasgy@0: { fazekasgy@0: emptyFeature = false; fazekasgy@0: switch (ffKeys[PyString_AsString(pyKey)]) fazekasgy@0: { cannam@18: case unknown : cannam@18: cerr << "Unknown key in Vamp FeatureSet: " fazekasgy@0: << PyString_AsString(pyKey) << endl; fazekasgy@0: break; fazekasgy@0: case hasTimestamp: fazekasgy@0: feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case timeStamp: fazekasgy@0: feature.timestamp = fazekasgy@8: Vamp::RealTime::frame2RealTime( fazekasgy@8: PyLong_AsLong(pyValue), fazekasgy@8: (unsigned int) m_inputSampleRate ); fazekasgy@8: #ifdef _DEBUG fazekasgy@8: cerr << "Timestamp: " fazekasgy@8: << (long)PyLong_AsLong(pyValue) << ", ->" fazekasgy@8: << feature.timestamp.toString() << endl; fazekasgy@8: #endif fazekasgy@0: break; cannam@18: case hasDuration: cannam@18: feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); cannam@18: break; cannam@18: case duration: cannam@18: feature.duration = cannam@18: Vamp::RealTime::frame2RealTime( cannam@18: PyLong_AsLong(pyValue), cannam@18: (unsigned int) m_inputSampleRate ); cannam@18: #ifdef _DEBUG cannam@18: cerr << "Duration: " cannam@18: << (long)PyLong_AsLong(pyValue) << ", ->" cannam@18: << feature.duration.toString() << endl; cannam@18: #endif cannam@18: break; fazekasgy@0: case values: fazekasgy@0: feature.values = PyList_As_FloatVector(pyValue); fazekasgy@0: break; fazekasgy@0: case label: fazekasgy@0: feature.label = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: } // switch fazekasgy@0: } // while fazekasgy@0: if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; fazekasgy@0: else returnFeatures[i].push_back(feature); fazekasgy@0: }// for j fazekasgy@0: }//for i fazekasgy@0: Py_CLEAR(pyOutputList); fazekasgy@0: return returnFeatures; fazekasgy@0: } fazekasgy@0: fazekasgy@6: bool fazekasgy@6: PyPlugin::initMaps() const fazekasgy@6: { fazekasgy@6: fazekasgy@6: if (isMapInitialised) return true; fazekasgy@6: cannam@18: outKeys["identifier"] = o::identifier; cannam@18: outKeys["name"] = o::name; cannam@18: outKeys["description"] = o::description; cannam@18: outKeys["unit"] = o::unit; cannam@18: outKeys["hasFixedBinCount"] = o::hasFixedBinCount; cannam@18: outKeys["binCount"] = o::binCount; cannam@18: outKeys["binNames"] = o::binNames; cannam@18: outKeys["hasKnownExtents"] = o::hasKnownExtents; cannam@18: outKeys["minValue"] = o::minValue; cannam@18: outKeys["maxValue"] = o::maxValue; cannam@18: outKeys["isQuantized"] = o::isQuantized; cannam@18: outKeys["quantizeStep"] = o::quantizeStep; cannam@18: outKeys["sampleType"] = o::sampleType; cannam@18: outKeys["sampleRate"] = o::sampleRate; cannam@18: outKeys["hasDuration"] = o::hasDuration; fazekasgy@6: fazekasgy@6: sampleKeys["OneSamplePerStep"] = OneSamplePerStep; fazekasgy@6: sampleKeys["FixedSampleRate"] = FixedSampleRate; fazekasgy@6: sampleKeys["VariableSampleRate"] = VariableSampleRate; fazekasgy@6: fazekasgy@6: ffKeys["hasTimestamp"] = hasTimestamp; fazekasgy@6: ffKeys["timeStamp"] = timeStamp; cannam@18: ffKeys["hasDuration"] = hasDuration; cannam@18: ffKeys["duration"] = duration; fazekasgy@6: ffKeys["values"] = values; fazekasgy@6: ffKeys["label"] = label; fazekasgy@6: fazekasgy@6: parmKeys["identifier"] = p::identifier; fazekasgy@6: parmKeys["name"] = p::name; fazekasgy@6: parmKeys["description"] = p::description; fazekasgy@6: parmKeys["unit"] = p::unit; fazekasgy@6: parmKeys["minValue"] = p::minValue; fazekasgy@6: parmKeys["maxValue"] = p::maxValue; fazekasgy@6: parmKeys["defaultValue"] = p::defaultValue; fazekasgy@6: parmKeys["isQuantized"] = p::isQuantized; fazekasgy@6: fazekasgy@6: isMapInitialised = true; fazekasgy@6: return true; fazekasgy@6: } fazekasgy@6: fazekasgy@6: fazekasgy@6: //missing API helper: convert Python list to C++ vector of strings fazekasgy@6: //TODO: these could be templates if we need more of this kind fazekasgy@6: std::vector fazekasgy@6: PyPlugin::PyList_To_StringVector (PyObject *inputList) const { fazekasgy@6: fazekasgy@6: std::vector Output; fazekasgy@6: std::string ListElement; fazekasgy@6: PyObject *pyString = NULL; fazekasgy@6: fazekasgy@6: if (!PyList_Check(inputList)) return Output; fazekasgy@6: fazekasgy@6: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { fazekasgy@6: //Get next list item (Borrowed Reference) fazekasgy@6: pyString = PyList_GET_ITEM(inputList,i); fazekasgy@6: ListElement = (string) PyString_AsString(PyObject_Str(pyString)); fazekasgy@6: Output.push_back(ListElement); fazekasgy@6: } fazekasgy@6: return Output; fazekasgy@6: } fazekasgy@6: fazekasgy@6: //missing API helper: convert Python list to C++ vector of floats fazekasgy@6: std::vector fazekasgy@6: PyPlugin::PyList_As_FloatVector (PyObject *inputList) const { fazekasgy@6: fazekasgy@6: std::vector Output; fazekasgy@6: float ListElement; fazekasgy@6: PyObject *pyFloat = NULL; fazekasgy@6: fazekasgy@6: if (!PyList_Check(inputList)) return Output; fazekasgy@6: fazekasgy@6: for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { fazekasgy@6: //Get next list item (Borrowed Reference) fazekasgy@6: pyFloat = PyList_GET_ITEM(inputList,k); fazekasgy@6: ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@8: #ifdef _DEBUG fazekasgy@8: cerr << "value: " << ListElement << endl; fazekasgy@8: #endif fazekasgy@6: Output.push_back(ListElement); fazekasgy@6: } fazekasgy@6: fazekasgy@6: return Output; fazekasgy@6: } fazekasgy@6: fazekasgy@6: /* TODO: find out why this produces error, also fazekasgy@6: do sg more clever about handling RealTime fazekasgy@6: Vamp::RealTime fazekasgy@6: PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) { fazekasgy@6: Vamp::RealTime result = fazekasgy@6: Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate); fazekasgy@6: return result; fazekasgy@6: } fazekasgy@6: */ fazekasgy@6: