Mercurial > hg > vampy
diff PyPlugin.cpp @ 31:4f1894c7591b vampy2
Created Vampy2 branch
author | fazekasgy |
---|---|
date | Sun, 20 Sep 2009 17:31:20 +0000 |
parents | 5139bf30f208 |
children | a8231788216c |
line wrap: on
line diff
--- a/PyPlugin.cpp Tue Aug 25 08:49:22 2009 +0000 +++ b/PyPlugin.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -51,11 +51,16 @@ #include <Python.h> #include "PyPlugin.h" +#include "PyTypeInterface.h" +#include <stdlib.h> +#include "PyExtensionModule.h" +//#include "PyRealTime.h" + #ifdef _WIN32 -#define pathsep ('\\') +#define PATHSEP ('\\') #else -#define pathsep ('/') +#define PATHSEP ('/') #endif //#define _DEBUG @@ -66,30 +71,35 @@ using std::endl; using std::map; -// Maps to associate strings with enum values -static std::map<std::string, o::eOutDescriptors> outKeys; -static std::map<std::string, eSampleTypes> sampleKeys; -static std::map<std::string, eFeatureFields> ffKeys; -static std::map<std::string, p::eParmDescriptors> parmKeys; +Mutex PyPlugin::m_pythonInterpreterMutex; -Mutex PyPlugin::m_pythonInterpreterMutex; -static bool isMapInitialised = false; - -PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass) : +PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount) : Plugin(inputSampleRate), m_pyClass(pyClass), + m_instcount(instcount), m_stepSize(0), m_blockSize(0), m_channels(0), m_plugin(pluginKey), m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), - m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))), + m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))), m_processType(0), m_pyProcess(NULL), - m_inputDomain(TimeDomain) + m_inputDomain(TimeDomain), + m_quitOnErrorFlag(false), + m_debugFlag(false) { + m_ti.setInputSampleRate(inputSampleRate); + MutexLocker locker(&m_pythonInterpreterMutex); + cerr << "Creating instance " << m_instcount << " of " << pluginKey << endl; + + if (m_instcount == 0) initvampy(); + m_instcount++; + + // if (!PyImport_ImportModule("vampy")) + // cerr << "Could not import extension." << endl; + // Create an instance - MutexLocker locker(&m_pythonInterpreterMutex); Py_INCREF(m_pyClass); PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate); PyObject *args = PyTuple_Pack(1, pyInputSampleRate); @@ -100,24 +110,43 @@ Py_DECREF(m_pyClass); Py_CLEAR(args); Py_CLEAR(pyInputSampleRate); - cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \"" << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl; + cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \"" + << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl; throw std::string("Constructor failed"); } Py_INCREF(m_pyInstance); Py_DECREF(args); Py_DECREF(pyInputSampleRate); - + + //query the debug flag + m_debugFlag = getBooleanFlag("vampy_debug_messages",true); + if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl; + else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl; + + //query the quit on error flag + m_quitOnErrorFlag = getBooleanFlag("quit_on_type_error",false); + if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl; + + //query the type conversion mode flag + bool st_flag = getBooleanFlag("use_strict_type_conversion",false); + if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl; + m_ti.setStrictTypingFlag(st_flag); } PyPlugin::~PyPlugin() { + MutexLocker locker(&m_pythonInterpreterMutex); + m_instcount--; + cerr << "Deleting plugin instance. Count: " << m_instcount << endl; + if (m_pyInstance) Py_DECREF(m_pyInstance); - if (m_pyClass) Py_DECREF(m_pyClass); + //we increase the class refcount before creating an instance + if (m_pyClass) Py_DECREF(m_pyClass); if (m_pyProcess) Py_CLEAR(m_pyProcess); + if (m_instcount == 0) cleanModule(); #ifdef _DEBUG - cerr << "PyPlugin::PyPlugin:" << m_class - << " Instance deleted." << endl; + cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl; #endif } @@ -126,647 +155,159 @@ PyPlugin::getIdentifier() const { MutexLocker locker(&m_pythonInterpreterMutex); + string rString="vampy-xxx"; + if (!m_debugFlag) return genericMethodCall("getIdentifier",rString); - char method[]="getIdentifier"; - cerr << "[call] " << method << endl; - string rString="vampy-x"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - return rString; - } - cerr << "Warning: Plugin must return a unique identifier." << endl; + rString = genericMethodCall("getIdentifier",rString); + if (rString == "vampy-xxx") + cerr << "Warning: Plugin must return a unique identifier." << endl; return rString; } - string PyPlugin::getName() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getName"; - cerr << "[call] " << method << endl; string rString="VamPy Plugin (Noname)"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; + return genericMethodCall("getName",rString); } string PyPlugin::getDescription() const { MutexLocker locker(&m_pythonInterpreterMutex); + string rString="Not given. (Hint: Implement getDescription method.)"; + return genericMethodCall("getDescription",rString); +} - char method[]="getDescription"; - cerr << "[call] " << method << endl; - string rString="Not given. (Hint: Implement getDescription method.)"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; -} string PyPlugin::getMaker() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMaker"; - cerr << "[call] " << method << endl; - string rString="Generic VamPy Plugin."; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; + string rString="VamPy Plugin."; + return genericMethodCall("getMaker",rString); } int PyPlugin::getPluginVersion() const { - //!!! implement - - return 2; + MutexLocker locker(&m_pythonInterpreterMutex); + size_t rValue=2; + return genericMethodCall("getPluginVersion",rValue); } string PyPlugin::getCopyright() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getCopyright"; - cerr << "[call] " << method << endl; - string rString="BSD License"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - - return rString; + string rString="Licence information not available."; + return genericMethodCall("getCopyright",rString); } bool PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) { - //useful for debugging Python plugins - char method[]="initialise"; - cerr << "[call] " << method << endl; - - //placing Mutex before these calls causes deadlock + if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false; - + m_inputDomain = getInputDomain(); + //Note: placing Mutex before the calls above causes deadlock !! MutexLocker locker(&m_pythonInterpreterMutex); - initMaps(); - m_stepSize = stepSize; m_blockSize = blockSize; m_channels = channels; - //quering process implementation type - char legacyMethod[]="process"; - char numpyMethod[]="processN"; - - if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && - m_processType == 0) - { - m_processType = legacyProcess; - m_pyProcess = PyString_FromString(legacyMethod); - } - - if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && - m_processType == 0) - { - m_processType = numpyProcess; - m_pyProcess = PyString_FromString(numpyMethod); - } + //query the process implementation type + //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface' + //if they are not provided, we fall back to the original method + setProcessType(); - if (!m_processType) - { - m_processType = not_implemented; - m_pyProcess = NULL; - cerr << "Warning: Python plugin [" << m_class << "::" << method - << "] No process implementation found. Plugin will do nothing." << endl; - } - - //Check if the method is implemented in Python else return false - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels); - PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize); - PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize); - //Call the method - PyObject *pyBool = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,NULL); - - Py_DECREF(pyMethod); - Py_DECREF(pyChannels); - Py_DECREF(pyStepSize); - Py_DECREF(pyBlockSize); - - //Check return value - if (PyErr_Occurred() || !PyBool_Check(pyBool)) { - PyErr_Print(); PyErr_Clear(); - Py_CLEAR(pyBool); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Bool return value." << endl; - return false; - } - - if (pyBool == Py_True) { - Py_CLEAR(pyBool); - return true; - } else { - Py_CLEAR(pyBool); - return false; - } - } - return false; + return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize); } void PyPlugin::reset() { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="reset"; - cerr << "[call] " << method << endl; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - PyObject_CallMethod(m_pyInstance, method, NULL); - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - - } + genericMethodCall("reset"); } -PyPlugin::InputDomain PyPlugin::getInputDomain() const +PyPlugin::InputDomain +PyPlugin::getInputDomain() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getInputDomain"; - cerr << "[call] " << method << endl; - PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - string domain = (string) PyString_AsString(pyString); - if (domain == "FrequencyDomain") rValue = FrequencyDomain; - Py_CLEAR(pyString); - } - return rValue; + // Note: Vamp enum type is mapped to Python string !! + // Is there a better way? (Enums are not native to Python) + string rValue = "TimeDomain"; + genericMethodCall("getInputDomain",rValue); + return (rValue == "FrequencyDomain")?FrequencyDomain:TimeDomain; } - -size_t PyPlugin::getPreferredBlockSize() const +size_t +PyPlugin::getPreferredBlockSize() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getPreferredBlockSize"; - cerr << "[call] " << method << endl; - size_t rValue=0; //not set by default - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Integer return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 0; + return genericMethodCall("getPreferredBlockSize",rValue); } -//size_t PyPlugin::getPreferredStepSize() const { return 0; } -size_t PyPlugin::getPreferredStepSize() const +size_t +PyPlugin::getPreferredStepSize() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getPreferredStepSize"; - cerr << "[call] " << method << endl; - size_t rValue=1024; //not set by default - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Integer return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 0; + return genericMethodCall("getPreferredStepSize",rValue); } -size_t PyPlugin::getMinChannelCount() const +size_t +PyPlugin::getMinChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMinChannelCount"; - cerr << "[call] " << method << endl; - size_t rValue=1; //default value - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 1; + return genericMethodCall("getMinChannelCount",rValue); } -size_t PyPlugin::getMaxChannelCount() const +size_t +PyPlugin::getMaxChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMaxChannelCount"; - cerr << "[call] " << method << endl; - size_t rValue=1; //default value - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; -} - + size_t rValue = 1; + return genericMethodCall("getMaxChannelCount",rValue); +} PyPlugin::OutputList PyPlugin::getOutputDescriptors() const { - MutexLocker locker(&m_pythonInterpreterMutex); - - //PyEval_AcquireThread(newThreadState); OutputList list; - OutputDescriptor od; - char method[]="getOutputDescriptors"; - cerr << "[call] " << method << endl; - - //Check if the method is implemented in Python - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; - - //Call the method: must return list object (new reference) - PyObject *pyList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (! PyList_Check(pyList) ) { - Py_CLEAR(pyList); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - return list; - } - - //These will all be borrowed references (no need to DECREF) - PyObject *pyDict, *pyKey, *pyValue; - - //Parse Output List - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - - //Get i-th Vamp output descriptor (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyList,i); - - //We only care about dictionaries holding output descriptors - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - initMaps(); - - //Python Sequence Iterator - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - switch (outKeys[PyString_AsString(pyKey)]) - { - case o::not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - break; - case o::identifier: - od.identifier = PyString_AsString(pyValue); - break; - case o::name: - od.name = PyString_AsString(pyValue); - break; - case o::description: - od.description = PyString_AsString(pyValue); - break; - case o::unit: - od.unit = PyString_AsString(pyValue); - break; - case o::hasFixedBinCount: - od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue); - break; - case o::binCount: - od.binCount = (size_t) PyInt_AS_LONG(pyValue); - break; - case o::binNames: - od.binNames = PyList_To_StringVector(pyValue); - break; - case o::hasKnownExtents: - od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue); - break; - case o::minValue: - od.minValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::maxValue: - od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::isQuantized: - od.isQuantized = (bool) PyInt_AS_LONG(pyValue); - break; - case o::quantizeStep: - od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::sampleType: - od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; - break; - case o::sampleRate: - od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); -// od.sampleRate = m_inputSampleRate / m_stepSize; - cerr << od.sampleRate << endl; - break; - case o::hasDuration: - od.hasDuration = (bool)PyInt_AS_LONG(pyValue); - break; - default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - } - } // while dict - list.push_back(od); - } // for list - Py_CLEAR(pyList); - return list; + return genericMethodCall("getOutputDescriptors",list); } PyPlugin::ParameterList PyPlugin::getParameterDescriptors() const { MutexLocker locker(&m_pythonInterpreterMutex); - ParameterList list; - ParameterDescriptor pd; - char method[]="getParameterDescriptors"; - cerr << "[call] " << method << endl; - + ///Note: This function is often called first by the host. if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;} - - //Check if the method is implemented in Python - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; - - //Call the method: must return list object (new reference) - PyObject *pyList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (! PyList_Check(pyList) ) { - Py_CLEAR(pyList); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - return list; - } - - - //These will all be borrowed references (no need to DECREF) - PyObject *pyDict, *pyKey, *pyValue; - - //Parse Output List - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - - //Get i-th Vamp output descriptor (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyList,i); - - //We only care about dictionaries holding output descriptors - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - initMaps(); - - //Python Sequence Iterator - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - switch (parmKeys[PyString_AsString(pyKey)]) - { - case p::not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - break; - case p::identifier: - pd.identifier = PyString_AsString(pyValue); - break; - case p::name: - pd.name = PyString_AsString(pyValue); - break; - case p::description: - pd.description = PyString_AsString(pyValue); - break; - case p::unit: - pd.unit = PyString_AsString(pyValue); - break; - case p::minValue: - pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::maxValue: - pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::defaultValue: - pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::isQuantized: - pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); - break; - case p::quantizeStep: - pd.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); - break; - default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - } - } // while dict - list.push_back(pd); - } // for list - Py_CLEAR(pyList); - return list; + return genericMethodCall("getParameterDescriptors",list); } void PyPlugin::setParameter(std::string paramid, float newval) { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="setParameter"; - cerr << "[call] " << method << endl; - - //Check if the method is implemented in Python - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyParamid = PyString_FromString(paramid.c_str()); - PyObject *pyNewval = PyFloat_FromDouble((double)newval); - - //Call the method - PyObject *pyBool = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL); - - //This only happens if there is a syntax error or so - if (pyBool == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Error setting parameter: " << paramid << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } - - Py_DECREF(pyMethod); - Py_DECREF(pyParamid); - Py_DECREF(pyNewval); - } + genericMethodCallArgs<NoneType>("setParameter",paramid,newval); } float PyPlugin::getParameter(std::string paramid) const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getParameter"; - cerr << "[call] " << method << endl; - float rValue = 0.0f; - - //Check if the method is implemented in Python - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyParamid = PyString_FromString(paramid.c_str()); - - //Call the method - PyObject *pyFloat = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL); - - //Check return type - if (! PyFloat_Check(pyFloat) ) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Float return type." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - Py_CLEAR(pyFloat); - return rValue; - } - - rValue = (float) PyFloat_AS_DOUBLE(pyFloat); - - Py_DECREF(pyMethod); - Py_DECREF(pyParamid); - Py_DECREF(pyFloat); - } - - return rValue; + return genericMethodCallArgs<float>("getParameter",paramid); } #ifdef _DEBUG @@ -774,8 +315,7 @@ #endif PyPlugin::FeatureSet -PyPlugin::process(const float *const *inputBuffers, - Vamp::RealTime timestamp) +PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp) { MutexLocker locker(&m_pythonInterpreterMutex); @@ -796,395 +336,172 @@ return FeatureSet(); } - string method=PyString_AsString(m_pyProcess); + // string method=PyString_AsString(m_pyProcess); PyObject *pyOutputList = NULL; - /*new numPy support*/ if (m_processType == numpyProcess) { - - //create a list of buffers - PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); - for (size_t i=0; i < m_channels; ++i) { - - //Expose memory using the Buffer Interface of C/API - //This will virtually pass a pointer which can be - //recasted in Python code as float or complex array - PyObject *pyBuffer = PyBuffer_FromMemory - ((void *) (float *) inputBuffers[i], - (Py_ssize_t) sizeof(float) * m_blockSize); - - PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); - } - - //pass RealTime as frameCount - PyObject *pyLongSample = PyLong_FromLong ( - Vamp::RealTime::realTime2Frame - (timestamp, (unsigned int) m_inputSampleRate)); - - //Call python process (returns new reference) - pyOutputList = PyObject_CallMethodObjArgs - (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); - - Py_DECREF(pyChannelList); - Py_DECREF(pyLongSample); - + pyOutputList = numpyProcessCall(inputBuffers,timestamp); } if (m_processType == legacyProcess) { - - //create a list of lists - PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); - for (size_t i=0; i < m_channels; ++i) { - - //Declare new list object - PyObject *pyFloat, *pyList; - pyList = PyList_New((Py_ssize_t) m_blockSize); - - //Pack samples into a Python List Object - //pyFloat types will always be new references, - //these will be discarded when the list is deallocated - for (size_t j = 0; j < m_blockSize; ++j) { - pyFloat=PyFloat_FromDouble( - (double) inputBuffers[i][j]); - PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); - } - PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); - } - - //pass RealTime as frameCount - PyObject *pyLongSample = PyLong_FromLong ( - Vamp::RealTime::realTime2Frame - (timestamp, (unsigned int) m_inputSampleRate)); - - //Call python process (returns new reference) - pyOutputList = PyObject_CallMethodObjArgs - (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); - - Py_DECREF(pyChannelList); - Py_DECREF(pyLongSample); - + pyOutputList = legacyProcessCall(inputBuffers,timestamp); } - //return nothing - //Py_CLEAR(pyOutputList); - //return FeatureSet(); - - //Check return type - if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { - if (pyOutputList == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Unexpected result." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } else { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - } - Py_CLEAR(pyOutputList); - return FeatureSet(); - } - - // Py_DECREF(pyList); - // This appears to be tracked by the cyclic garbage collector - // hence decrefing produces GC error -#ifdef _DEBUG - cerr << "Process Returned Features" << endl; -#endif - // These will ALL be borrowed references - PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; - - FeatureSet returnFeatures; - - //Parse Output List for each element (FeatureSet) - for (Py_ssize_t i = 0; - i < PyList_GET_SIZE(pyOutputList); ++i) { - //cerr << "output (FeatureSet): " << i << endl; - - //Get i-th FeatureList (Borrowed Reference) - pyFeatureList = PyList_GET_ITEM(pyOutputList,i); - - //Parse FeatureList for each element (Feature) - for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { - //cerr << "element (FeatureList): " << j << endl; - - //Get j-th Feature (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyFeatureList,j); - - //We only care about dictionaries holding a Feature struct - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - bool emptyFeature = true; - Feature feature; - - //process::Python Sequence Iterator for dictionary - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - emptyFeature = false; - switch (ffKeys[PyString_AsString(pyKey)]) - { - case unknown: - cerr << "Unknown key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - break; - case hasTimestamp: - feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); - break; - case timeStamp: - feature.timestamp = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Timestamp: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.timestamp.toString() << endl; -#endif - break; - case hasDuration: - feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); - break; - case duration: - feature.duration = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Duration: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.duration.toString() << endl; -#endif - break; - case values: - feature.values = PyList_As_FloatVector(pyValue); - break; - case label: - feature.label = PyString_AsString(pyValue); - break; - default : - cerr << "Invalid key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - } // switch - - } // while - if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; - else returnFeatures[i].push_back(feature); - - }// for j = FeatureList - - }//for i = FeatureSet + FeatureSet rFeatureset; + rFeatureset = m_ti.PyValue_To_FeatureSet(pyOutputList); Py_CLEAR(pyOutputList); - return returnFeatures; + return rFeatureset; + } +PyObject* +PyPlugin::numpyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp) +{ + PyObject *pyOutputList = NULL; + + //create a list of buffers + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { + //Expose memory using the Buffer Interface of C/API + //This will virtually pass a pointer which can be + //recasted in Python code as float or complex array + PyObject *pyBuffer = PyBuffer_FromMemory + ((void *) (float *) inputBuffers[i], + (Py_ssize_t) sizeof(float) * m_blockSize); + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); + } +/* + //(1) pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + */ + //(2) pass RealTime as PyRealTime + PyObject *pyRealTime = PyRealTime_FromRealTime(timestamp); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyRealTime,NULL); + + Py_DECREF(pyChannelList); + // Py_DECREF(pyLongSample); + Py_DECREF(pyRealTime); + return pyOutputList; +} + +PyObject* +PyPlugin::legacyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp) +{ + PyObject *pyOutputList = NULL; + + //create a list of lists + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { + //New list object + PyObject *pyFloat, *pyList; + pyList = PyList_New((Py_ssize_t) m_blockSize); + + //Pack samples into a Python List Object + //pyFloat types will always be new references, + //these will be discarded when the list is deallocated + for (size_t j = 0; j < m_blockSize; ++j) { + pyFloat=PyFloat_FromDouble( + (double) inputBuffers[i][j]); + PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); + } + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); + } + + //pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + + Py_DECREF(pyChannelList); + Py_DECREF(pyLongSample); + return pyOutputList; +} PyPlugin::FeatureSet PyPlugin::getRemainingFeatures() { MutexLocker locker(&m_pythonInterpreterMutex); - - static char method[]="getRemainingFeatures"; - cerr << "[call] " << method << endl; - - //check if the method is implemented - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) { - return FeatureSet(); - } - - PyObject *pyMethod = PyString_FromString(method); - - PyObject *pyOutputList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { - if (pyOutputList == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Unexpected result." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } else { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - } - Py_CLEAR(pyMethod); - Py_CLEAR(pyOutputList); - return FeatureSet(); - } - Py_DECREF(pyMethod); - - PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; - FeatureSet returnFeatures; - - //iterate through list of outputs - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { - - pyFeatureList = PyList_GET_ITEM(pyOutputList,i); - - //iterate list of Features - for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { -#ifdef _DEBUG - cerr << "feature: " << j << endl; -#endif - pyDict = PyList_GET_ITEM(pyFeatureList,j); - - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - bool emptyFeature = true; - Feature feature; - - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - emptyFeature = false; - switch (ffKeys[PyString_AsString(pyKey)]) - { - case unknown : - cerr << "Unknown key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - break; - case hasTimestamp: - feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); - break; - case timeStamp: - feature.timestamp = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Timestamp: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.timestamp.toString() << endl; -#endif - break; - case hasDuration: - feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); - break; - case duration: - feature.duration = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Duration: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.duration.toString() << endl; -#endif - break; - case values: - feature.values = PyList_As_FloatVector(pyValue); - break; - case label: - feature.label = PyString_AsString(pyValue); - break; - } // switch - } // while - if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; - else returnFeatures[i].push_back(feature); - }// for j - }//for i - Py_CLEAR(pyOutputList); - return returnFeatures; + FeatureSet rValue; + return genericMethodCall("getRemainingFeatures",rValue); } + bool -PyPlugin::initMaps() const +PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const { - - if (isMapInitialised) return true; - - outKeys["identifier"] = o::identifier; - outKeys["name"] = o::name; - outKeys["description"] = o::description; - outKeys["unit"] = o::unit; - outKeys["hasFixedBinCount"] = o::hasFixedBinCount; - outKeys["binCount"] = o::binCount; - outKeys["binNames"] = o::binNames; - outKeys["hasKnownExtents"] = o::hasKnownExtents; - outKeys["minValue"] = o::minValue; - outKeys["maxValue"] = o::maxValue; - outKeys["isQuantized"] = o::isQuantized; - outKeys["quantizeStep"] = o::quantizeStep; - outKeys["sampleType"] = o::sampleType; - outKeys["sampleRate"] = o::sampleRate; - outKeys["hasDuration"] = o::hasDuration; - - sampleKeys["OneSamplePerStep"] = OneSamplePerStep; - sampleKeys["FixedSampleRate"] = FixedSampleRate; - sampleKeys["VariableSampleRate"] = VariableSampleRate; - - ffKeys["hasTimestamp"] = hasTimestamp; - ffKeys["timeStamp"] = timeStamp; - ffKeys["hasDuration"] = hasDuration; - ffKeys["duration"] = duration; - ffKeys["values"] = values; - ffKeys["label"] = label; - - parmKeys["identifier"] = p::identifier; - parmKeys["name"] = p::name; - parmKeys["description"] = p::description; - parmKeys["unit"] = p::unit; - parmKeys["minValue"] = p::minValue; - parmKeys["maxValue"] = p::maxValue; - parmKeys["defaultValue"] = p::defaultValue; - parmKeys["isQuantized"] = p::isQuantized; - parmKeys["quantizeStep"] = p::quantizeStep; - - isMapInitialised = true; - return true; + bool rValue = defValue; + if (PyObject_HasAttrString(m_pyInstance,flagName)) + { + PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + } else { + rValue = m_ti.PyValue_To_Bool(pyValue); + if (m_ti.error) { + cerr << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + rValue = defValue; + } else Py_DECREF(pyValue); + } + } + if (m_debugFlag) cerr << FLAG_VALUE << endl; + return rValue; } +void +PyPlugin::setProcessType() +{ + //quering process implementation type + char legacyMethod[]="process"; + char numpyMethod[]="processN"; -//missing API helper: convert Python list to C++ vector of strings -//TODO: these could be templates if we need more of this kind -std::vector<std::string> -PyPlugin::PyList_To_StringVector (PyObject *inputList) const { + if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && + m_processType == 0) + { + m_processType = legacyProcess; + m_pyProcess = PyString_FromString(legacyMethod); + } + + if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && + m_processType == 0) + { + m_processType = numpyProcess; + m_pyProcess = PyString_FromString(numpyMethod); + } + + // These flags are optional. If provided, they override the + // implementation type making the use of the odd processN() + // function redundant. + // However, the code above provides backwards compatibility. + if (getBooleanFlag("use_numpy_interface",false)) + m_processType = numpyProcess; + if (getBooleanFlag("use_legacy_interface",false)) + m_processType = legacyProcess; + if (m_debugFlag && m_processType) + cerr << "Process type: " << ((m_processType==numpyProcess)?"numpy process":"legacy process") << endl; - std::vector<std::string> Output; - std::string ListElement; - PyObject *pyString = NULL; - - if (!PyList_Check(inputList)) return Output; - - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { - //Get next list item (Borrowed Reference) - pyString = PyList_GET_ITEM(inputList,i); - ListElement = (string) PyString_AsString(PyObject_Str(pyString)); - Output.push_back(ListElement); + if (!m_processType) + { + m_processType = not_implemented; + m_pyProcess = NULL; + char method[]="initialise::setProcessType"; + cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl; } - return Output; } -//missing API helper: convert Python list to C++ vector of floats -std::vector<float> -PyPlugin::PyList_As_FloatVector (PyObject *inputList) const { - - std::vector<float> Output; - float ListElement; - PyObject *pyFloat = NULL; - - if (!PyList_Check(inputList)) return Output; - - for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { - //Get next list item (Borrowed Reference) - pyFloat = PyList_GET_ITEM(inputList,k); - ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); -#ifdef _DEBUG - cerr << "value: " << ListElement << endl; -#endif - Output.push_back(ListElement); - } - - return Output; -} - -/* TODO: find out why this produces error, also - do sg more clever about handling RealTime -Vamp::RealTime -PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) { -Vamp::RealTime result = -Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate); -return result; -} -*/ -