Mercurial > hg > vampy
view PyPlugin.cpp @ 46:af9c4cee95a8
VC++ fixes.
Much of this is changing "and" to "&&". I had never realised
until today that "and" is in fact a keyword in C++, albeit not
one that has been there since the start, so this should compile
(I eventually looked this up having been puzzled by how this
code could ever build with any other compiler). However,
despite its keywordness, "and" still doesn't seem to be acceptable
to VC++. Possibly there's an option to change this, or one could
use a macro -- but why not just stick with the token that compilers
are known to like?
author | cannam |
---|---|
date | Mon, 05 Oct 2009 16:14:25 +0000 |
parents | 27bab3a16c9a |
children | c1e4f706ca9a |
line wrap: on
line source
/* * Vampy : This plugin is a wrapper around the Vamp plugin API. * It allows for writing Vamp plugins in Python. * Centre for Digital Music, Queen Mary University of London. * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources * for licence information.) */ #include <Python.h> #include "PyPlugin.h" #include "PyTypeInterface.h" #include <stdlib.h> #include "PyExtensionModule.h" #ifdef _WIN32 #define PATHSEP ('\\') #else #define PATHSEP ('/') #endif using std::string; using std::vector; using std::cerr; using std::endl; using std::map; Mutex PyPlugin::m_pythonInterpreterMutex; 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_processType(not_implemented), m_pyProcess(NULL), 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; // Create an instance Py_INCREF(m_pyClass); PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate); PyObject *args = PyTuple_Pack(1, pyInputSampleRate); m_pyInstance = PyObject_Call(m_pyClass, args, NULL); if (!m_pyInstance || PyErr_Occurred()) { if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } 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; throw std::string("Constructor failed"); } Py_INCREF(m_pyInstance); Py_DECREF(args); Py_DECREF(pyInputSampleRate); m_instcount++; // query and decode vampy flags m_vampyFlags = getBinaryFlags("vampy_flags",vf_NULL); m_debugFlag = (bool) (m_vampyFlags & vf_DEBUG); m_quitOnErrorFlag = (bool) (m_vampyFlags & vf_QUIT); bool st_flag = (bool) (m_vampyFlags & vf_STRICT); m_useRealTimeFlag = (bool) (m_vampyFlags & vf_REALTIME); if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl; else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl; if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl; 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); //we increase the class refcount before creating an instance if (m_pyClass) Py_DECREF(m_pyClass); if (m_pyProcess) Py_CLEAR(m_pyProcess); #ifdef _DEBUG cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl; #endif } string PyPlugin::getIdentifier() const { MutexLocker locker(&m_pythonInterpreterMutex); string rString="vampy-xxx"; if (!m_debugFlag) return genericMethodCall("getIdentifier",rString); 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); string rString="VamPy Plugin (Noname)"; return genericMethodCall("getName",rString); } string PyPlugin::getDescription() const { MutexLocker locker(&m_pythonInterpreterMutex); string rString="Not given. (Hint: Implement getDescription method.)"; return genericMethodCall("getDescription",rString); } string PyPlugin::getMaker() const { MutexLocker locker(&m_pythonInterpreterMutex); string rString="VamPy Plugin."; return genericMethodCall("getMaker",rString); } int PyPlugin::getPluginVersion() const { MutexLocker locker(&m_pythonInterpreterMutex); size_t rValue=2; return genericMethodCall("getPluginVersion",rValue); } string PyPlugin::getCopyright() const { MutexLocker locker(&m_pythonInterpreterMutex); string rString="Licence information not available."; return genericMethodCall("getCopyright",rString); } bool PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) { if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false; m_inputDomain = getInputDomain(); //Note: placing Mutex before the calls above causes deadlock !! MutexLocker locker(&m_pythonInterpreterMutex); m_stepSize = stepSize; m_blockSize = blockSize; m_channels = channels; //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(); return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize); } void PyPlugin::reset() { MutexLocker locker(&m_pythonInterpreterMutex); genericMethodCall("reset"); } PyPlugin::InputDomain PyPlugin::getInputDomain() const { MutexLocker locker(&m_pythonInterpreterMutex); return genericMethodCall("getInputDomain",m_inputDomain); } size_t PyPlugin::getPreferredBlockSize() const { MutexLocker locker(&m_pythonInterpreterMutex); size_t rValue = 0; return genericMethodCall("getPreferredBlockSize",rValue); } size_t PyPlugin::getPreferredStepSize() const { MutexLocker locker(&m_pythonInterpreterMutex); size_t rValue = 0; return genericMethodCall("getPreferredStepSize",rValue); } size_t PyPlugin::getMinChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); size_t rValue = 1; return genericMethodCall("getMinChannelCount",rValue); } size_t PyPlugin::getMaxChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); size_t rValue = 1; return genericMethodCall("getMaxChannelCount",rValue); } PyPlugin::OutputList PyPlugin::getOutputDescriptors() const { MutexLocker locker(&m_pythonInterpreterMutex); OutputList list; return genericMethodCall("getOutputDescriptors",list); } PyPlugin::ParameterList PyPlugin::getParameterDescriptors() const { MutexLocker locker(&m_pythonInterpreterMutex); ParameterList list; #ifdef _DEBUG ///Note: This function is often called first by the host. if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;} #endif return genericMethodCall("getParameterDescriptors",list); } void PyPlugin::setParameter(std::string paramid, float newval) { MutexLocker locker(&m_pythonInterpreterMutex); genericMethodCallArgs<NoneType>("setParameter",paramid,newval); } float PyPlugin::getParameter(std::string paramid) const { MutexLocker locker(&m_pythonInterpreterMutex); return genericMethodCallArgs<float>("getParameter",paramid); } #ifdef _DEBUG_VALUES static int proccounter = 0; #endif PyPlugin::FeatureSet PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp) { MutexLocker locker(&m_pythonInterpreterMutex); #ifdef _DEBUG_VALUES /// we only need this if we'd like to see what frame a set of values belong to cerr << "[Vampy::call] process, frame:" << proccounter << endl; proccounter++; #endif if (m_blockSize == 0 || m_channels == 0) { cerr << "ERROR: PyPlugin::process: " << "Plugin has not been initialised" << endl; return FeatureSet(); } if (m_processType == not_implemented) { cerr << "ERROR: In Python plugin [" << m_class << "] No process implementation found. Returning empty feature set." << endl; return FeatureSet(); } return processMethodCall(inputBuffers,timestamp); } PyPlugin::FeatureSet PyPlugin::getRemainingFeatures() { MutexLocker locker(&m_pythonInterpreterMutex); FeatureSet rValue; return genericMethodCall("getRemainingFeatures",rValue); } bool PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const { 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) { Py_CLEAR(pyValue); typeErrorHandler(flagName); rValue = defValue; } else Py_DECREF(pyValue); } } if (m_debugFlag) cerr << FLAG_VALUE << endl; return rValue; } int PyPlugin::getBinaryFlags(char flagName[], eVampyFlags defValue = vf_NULL) const { int 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 |= (int) m_ti.PyValue_To_Size_t(pyValue); if (m_ti.error) { Py_CLEAR(pyValue); typeErrorHandler(flagName); 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"; if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && m_processType == 0) { m_processType = legacyProcess; m_pyProcess = PyString_FromString(legacyMethod); m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess); } if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && m_processType == 0) { m_processType = numpy_bufferProcess; m_pyProcess = PyString_FromString(numpyMethod); m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess); } // 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 backward compatibility. if (m_vampyFlags & vf_BUFFER) { m_processType = numpy_bufferProcess; if (m_debugFlag) cerr << "Process using (numpy) buffer interface." << endl; } if (m_vampyFlags & vf_ARRAY) { #ifdef HAVE_NUMPY m_processType = numpy_arrayProcess; if (m_debugFlag) cerr << "Process using numpy array interface." << endl; #else cerr << "Error: This version of vampy was compiled without numpy support, " << "however the vf_ARRAY flag is set for plugin: " << m_class << endl << "The default behaviour is: passing a python list of samples for each channel in process() " << "or a list of memory buffers in processN(). " << endl << "This can be used create numpy arrays using the numpy.frombuffer() command." << endl; #endif } if (!m_processType) { m_processType = not_implemented; m_pyProcess = NULL; m_pyProcessCallable = NULL; char method[]="initialise::setProcessType"; cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl; } } void PyPlugin::typeErrorHandler(char *method) const { bool strict = false; while (m_ti.error) { PyTypeInterface::ValueError e = m_ti.getError(); cerr << PLUGIN_ERROR << e.str() << endl; if (e.strict) strict = true; // e.print(); } /// quit on hard errors like accessing NULL pointers or strict type conversion /// errors IF the user sets the quitOnErrorFlag in the plugin. /// Otherwise most errors will go unnoticed apart from /// a messages in the terminal. /// It would be best if hosts could catch an exception instead /// and display something meaningful to the user. if (strict && m_quitOnErrorFlag) exit(EXIT_FAILURE); }