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@0: * 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 fazekasgy@0: 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@0: #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 fazekasgy@0: 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; cannam@3: fazekasgy@0: void initMaps() fazekasgy@0: { fazekasgy@0: outKeys["identifier"] = identifier; fazekasgy@0: outKeys["name"] = name; fazekasgy@0: outKeys["description"] = description; fazekasgy@0: outKeys["unit"] = unit; fazekasgy@0: outKeys["hasFixedBinCount"] = hasFixedBinCount; fazekasgy@0: outKeys["binCount"] = binCount; fazekasgy@0: outKeys["binNames"] = binNames; fazekasgy@0: outKeys["hasKnownExtents"] = hasKnownExtents; fazekasgy@0: outKeys["minValue"] = minValue; fazekasgy@0: outKeys["maxValue"] = maxValue; fazekasgy@0: outKeys["isQuantized"] = isQuantized; fazekasgy@0: outKeys["quantizeStep"] = quantizeStep; fazekasgy@0: outKeys["sampleType"] = sampleType; fazekasgy@0: outKeys["sampleRate"] = sampleRate; fazekasgy@0: fazekasgy@0: sampleKeys["OneSamplePerStep"] = OneSamplePerStep; fazekasgy@0: sampleKeys["FixedSampleRate"] = FixedSampleRate; fazekasgy@0: sampleKeys["VariableSampleRate"] = VariableSampleRate; fazekasgy@0: fazekasgy@0: ffKeys["hasTimestamp"] = hasTimestamp; fazekasgy@0: ffKeys["timeStamp"] = timeStamp; fazekasgy@0: ffKeys["values"] = values; fazekasgy@0: ffKeys["label"] = label; fazekasgy@0: fazekasgy@0: parmKeys["identifier"] = p::identifier; fazekasgy@0: parmKeys["name"] = p::name; fazekasgy@0: parmKeys["description"] = p::description; fazekasgy@0: parmKeys["unit"] = p::unit; fazekasgy@0: parmKeys["minValue"] = p::minValue; fazekasgy@0: parmKeys["maxValue"] = p::maxValue; fazekasgy@0: parmKeys["defaultValue"] = p::defaultValue; fazekasgy@0: parmKeys["isQuantized"] = p::isQuantized; fazekasgy@0: fazekasgy@0: } fazekasgy@0: fazekasgy@0: fazekasgy@0: //missing API helper: convert Python list to C++ vector of strings fazekasgy@0: //TODO: these could be templates if we need more of this kind fazekasgy@0: std::vector PyList_To_StringVector (PyObject *inputList) { fazekasgy@0: fazekasgy@0: std::vector Output; fazekasgy@0: std::string ListElement; fazekasgy@0: PyObject *pyString = NULL; fazekasgy@0: fazekasgy@0: if (!PyList_Check(inputList)) return Output; fazekasgy@0: fazekasgy@0: for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { fazekasgy@0: //Get next list item (Borrowed Reference) fazekasgy@0: pyString = PyList_GET_ITEM(inputList,i); fazekasgy@0: ListElement = (string) PyString_AsString(PyObject_Str(pyString)); fazekasgy@0: Output.push_back(ListElement); fazekasgy@0: } fazekasgy@0: return Output; fazekasgy@0: } fazekasgy@0: fazekasgy@0: //missing API helper: convert Python list to C++ vector of floats fazekasgy@0: std::vector PyList_As_FloatVector (PyObject *inputList) { fazekasgy@0: fazekasgy@0: std::vector Output; fazekasgy@0: float ListElement; fazekasgy@0: PyObject *pyFloat = NULL; fazekasgy@0: fazekasgy@0: if (!PyList_Check(inputList)) return Output; fazekasgy@0: fazekasgy@0: for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { fazekasgy@0: //Get next list item (Borrowed Reference) fazekasgy@0: pyFloat = PyList_GET_ITEM(inputList,k); fazekasgy@0: ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); fazekasgy@0: Output.push_back(ListElement); fazekasgy@0: } fazekasgy@0: fazekasgy@0: return Output; fazekasgy@0: } fazekasgy@0: fazekasgy@0: /* TODO: find out why this produces error, also fazekasgy@0: do sg more clever about handling RealTime fazekasgy@0: Vamp::RealTime fazekasgy@0: PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) { fazekasgy@0: Vamp::RealTime result = fazekasgy@0: Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate); fazekasgy@0: return result; fazekasgy@0: } fazekasgy@0: */ fazekasgy@0: 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@0: m_previousSample(0.0f), fazekasgy@0: m_plugin(pluginKey), fazekasgy@0: m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), fazekasgy@0: m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))) fazekasgy@0: { fazekasgy@0: } fazekasgy@0: fazekasgy@0: PyPlugin::~PyPlugin() fazekasgy@0: { fazekasgy@0: cerr << "PyPlugin::PyPlugin:" << m_class fazekasgy@0: << " Instance deleted." << endl; 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@0: string rString="VampPy-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@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: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: fazekasgy@0: if (channels < getMinChannelCount() || fazekasgy@0: channels > getMaxChannelCount()) return false; fazekasgy@0: fazekasgy@0: m_stepSize = std::min(stepSize, blockSize); fazekasgy@0: char method[]="initialise"; fazekasgy@0: cerr << "[call] " << method << endl; fazekasgy@0: 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@0: 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@0: if (!PyBool_Check(pyBool)) { 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@0: return true; fazekasgy@0: } fazekasgy@0: fazekasgy@0: void fazekasgy@0: PyPlugin::reset() fazekasgy@0: { cannam@3: //!!! implement this! fazekasgy@0: m_previousSample = 0.0f; fazekasgy@0: } fazekasgy@0: fazekasgy@0: 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@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@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::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: { cannam@3: MutexLocker locker(&m_pythonInterpreterMutex); cannam@3: 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@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { 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: fazekasgy@0: //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: { fazekasgy@0: case not_found : fazekasgy@0: cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@0: break; fazekasgy@0: case identifier: fazekasgy@0: od.identifier = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case name: fazekasgy@0: od.name = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case description: fazekasgy@0: od.description = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case unit: fazekasgy@0: od.unit = PyString_AsString(pyValue); fazekasgy@0: break; fazekasgy@0: case hasFixedBinCount: fazekasgy@0: od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case binCount: fazekasgy@0: od.binCount = (size_t) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case binNames: fazekasgy@0: od.binNames = PyList_To_StringVector(pyValue); fazekasgy@0: break; fazekasgy@0: case hasKnownExtents: fazekasgy@0: od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case minValue: fazekasgy@0: od.minValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case maxValue: fazekasgy@0: od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case isQuantized: fazekasgy@0: od.isQuantized = (bool) PyInt_AS_LONG(pyValue); fazekasgy@0: break; fazekasgy@0: case quantizeStep: fazekasgy@0: od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: case sampleType: fazekasgy@0: od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; fazekasgy@0: break; fazekasgy@0: case sampleRate: fazekasgy@0: od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); fazekasgy@0: break; fazekasgy@0: default : fazekasgy@0: cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@0: } // switch fazekasgy@0: } // while dict keys fazekasgy@0: list.push_back(od); fazekasgy@0: } // for each memeber in outputlist fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: } // if method is implemented fazekasgy@0: //PyEval_ReleaseThread(newThreadState); 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@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) { 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: fazekasgy@0: //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: { fazekasgy@0: case not_found : fazekasgy@0: 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 : fazekasgy@0: cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl; fazekasgy@0: } // switch fazekasgy@0: } // while dict keys fazekasgy@0: list.push_back(pd); fazekasgy@0: } // for each memeber in outputlist fazekasgy@0: Py_CLEAR(pyList); fazekasgy@0: } // if 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@0: if (m_stepSize == 0) { fazekasgy@0: cerr << "ERROR: PyPlugin::process: " fazekasgy@0: << "Plugin has not been initialised" << endl; fazekasgy@0: return FeatureSet(); fazekasgy@0: } fazekasgy@0: static char method[]="process"; fazekasgy@0: fazekasgy@0: #ifdef _DEBUG fazekasgy@0: cerr << "[call] " << method << " frame:" << proccounter << endl; fazekasgy@0: proccounter++; fazekasgy@0: //cerr << "C: proc..." << proccounter << " " << endl; fazekasgy@0: #endif fazekasgy@0: fazekasgy@0: //proces::Check if the method is implemented in Python fazekasgy@0: if ( PyObject_HasAttrString(m_pyInstance,method) ) fazekasgy@0: { fazekasgy@0: fazekasgy@0: //Pack method name into Python Object fazekasgy@0: PyObject *pyMethod = PyString_FromString(method); fazekasgy@0: fazekasgy@0: //Declare new list object fazekasgy@0: PyObject *pyFloat, *pyList; fazekasgy@0: pyList = PyList_New((Py_ssize_t) m_stepSize); fazekasgy@0: fazekasgy@0: //Pack samples into a Python List Object fazekasgy@0: //pyFloat will always be new references, fazekasgy@0: //these will be discarded when the list is deallocated fazekasgy@0: for (size_t i = 0; i < m_stepSize; ++i) { fazekasgy@0: pyFloat=PyFloat_FromDouble((double) inputBuffers[0][i]); fazekasgy@0: PyList_SET_ITEM(pyList, (Py_ssize_t) i, pyFloat); fazekasgy@0: } fazekasgy@0: fazekasgy@0: //Call python process (returns new reference) fazekasgy@0: PyObject *pyOutputList = fazekasgy@0: PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyList,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: fazekasgy@0: Py_DECREF(pyMethod); 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: { fazekasgy@0: case not_found : fazekasgy@0: 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 = timestamp + fazekasgy@0: Vamp::RealTime::frame2RealTime fazekasgy@0: ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); fazekasgy@0: 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 : fazekasgy@0: 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: }//if (has_attribute) fazekasgy@0: return FeatureSet(); 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@0: 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@0: for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { fazekasgy@0: 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: { fazekasgy@0: case not_found : fazekasgy@0: 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: // TODO: clarify what to do here fazekasgy@0: case timeStamp: fazekasgy@0: feature.timestamp = fazekasgy@0: Vamp::RealTime::frame2RealTime fazekasgy@0: ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); fazekasgy@0: 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: