Mercurial > hg > vampy
view PyPlugin.h @ 101:28d22109991c
Text tweaks
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Fri, 01 Feb 2019 18:07:02 +0000 |
parents | f5c028376bf9 |
children |
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.) */ #ifndef _PYTHON_WRAPPER_PLUGIN_H_ #define _PYTHON_WRAPPER_PLUGIN_H_ #define _CLASS_METHOD_ m_class << "::" << method #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: " #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " " #define DEFAULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented. Returning default value." #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") #include <Python.h> #include "PyExtensionModule.h" #include "PyTypeInterface.h" #include "vamp-sdk/Plugin.h" #include "Mutex.h" using std::string; using std::cerr; using std::endl; enum eProcessType { not_implemented, legacyProcess, numpyProcess, numpy_bufferProcess, numpy_arrayProcess }; class PyPlugin : public Vamp::Plugin { public: PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount, bool &numpyInstalled); virtual ~PyPlugin(); bool initialise(size_t channels, size_t stepSize, size_t blockSize); void reset(); InputDomain getInputDomain() const; size_t getPreferredBlockSize() const; size_t getPreferredStepSize() const; size_t getMinChannelCount() const; size_t getMaxChannelCount() const; std::string getIdentifier() const; std::string getName() const; std::string getDescription() const; std::string getMaker() const; int getPluginVersion() const; std::string getCopyright() const; OutputList getOutputDescriptors() const; ParameterList getParameterDescriptors() const; float getParameter(std::string paramid) const; void setParameter(std::string paramid, float newval); FeatureSet process(const float *const *inputBuffers, Vamp::RealTime timestamp); FeatureSet getRemainingFeatures(); protected: static Mutex m_pythonInterpreterMutex; PyObject *m_pyClass; PyObject *m_pyInstance; int &m_instcount; size_t m_stepSize; size_t m_blockSize; size_t m_channels; std::string m_plugin; std::string m_class; std::string m_path; eProcessType m_processType; PyObject *m_pyProcess; PyObject *m_pyProcessCallable; mutable InputDomain m_inputDomain; PyTypeInterface m_ti; int m_vampyFlags; bool m_quitOnErrorFlag; bool m_debugFlag; bool m_useRealTimeFlag; bool m_numpyInstalled; mutable bool m_processFailure; void setProcessType(); FeatureSet processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp); bool getBooleanFlag(const char flagName[],bool) const; int getBinaryFlags(const char flagName[], eVampyFlags) const; void typeErrorHandler(const char *method, bool process = false) const; /// simple 'void return' call with no args void genericMethodCall(const char *method) const { if (m_debugFlag) cerr << DEBUG_NAME << endl; if ( PyObject_HasAttrString(m_pyInstance,method) ) { PyObject *pyValue = PyObject_CallMethod(m_pyInstance, (char *)method, NULL); if (!pyValue) { cerr << PLUGIN_ERROR << "Failed to call method." << endl; if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} } } } /// 'no arg with default return value' call template<typename RET> RET &genericMethodCall(const char *method, RET &rValue) const { if (m_debugFlag) cerr << DEBUG_NAME << endl; if ( PyObject_HasAttrString(m_pyInstance,method) ) { PyObject *pyValue = PyObject_CallMethod(m_pyInstance, (char *)method, NULL); if (!pyValue) { cerr << PLUGIN_ERROR << "Failed to call method." << endl; if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} return rValue; } /// convert the returned value m_ti.PyValue_To_rValue(pyValue,rValue); if (!m_ti.error) { Py_DECREF(pyValue); } else { Py_CLEAR(pyValue); typeErrorHandler(method); } return rValue; } if (m_debugFlag) cerr << DEFAULT_RETURN << endl; return rValue; } /// unary call template<typename RET,typename A1> RET genericMethodCallArgs(const char *method, A1 arg1) const { RET rValue = RET(); if (m_debugFlag) cerr << DEBUG_NAME << endl; if (!PyObject_HasAttrString(m_pyInstance,method)) { if (m_debugFlag) cerr << DEFAULT_RETURN << endl; return rValue; } /// prepare arguments for fast method call PyObject *pyMethod = m_ti.PyValue_From_CValue(method); PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); PyObject* pyArgs = PyTuple_New(1); if (!(pyArgs && pyCallable && pyMethod)) { cerr << PLUGIN_ERROR << "Failed to prepare argument for calling method." << endl; Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArgs); return rValue; } PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); if (m_ti.error) { cerr << PLUGIN_ERROR << "Failed to convert argument for calling method." << endl; typeErrorHandler(method); Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArgs); return rValue; } PyTuple_SET_ITEM(pyArgs, 0, pyArg1); Py_INCREF(pyArg1); /// call the method PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); if (!pyValue) { cerr << PLUGIN_ERROR << "Failed to call method." << endl; if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArgs); return rValue; } Py_DECREF(pyMethod); Py_DECREF(pyCallable); Py_DECREF(pyArg1); Py_DECREF(pyArgs); /// convert the returned value m_ti.PyValue_To_rValue(pyValue,rValue); if (!m_ti.error) { Py_DECREF(pyValue); } else { Py_CLEAR(pyValue); typeErrorHandler(method); } return rValue; } /// binary call template<typename RET,typename A1,typename A2> RET genericMethodCallArgs(const char *method, A1 arg1, A2 arg2) const { RET rValue = RET(); if (m_debugFlag) cerr << DEBUG_NAME << endl; if (!PyObject_HasAttrString(m_pyInstance,method)) { if (m_debugFlag) cerr << DEFAULT_RETURN << endl; return rValue; } /// prepare arguments for fast method call PyObject *pyMethod = m_ti.PyValue_From_CValue(method); PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); PyObject* pyArgs = PyTuple_New(2); if (!(pyArgs && pyCallable && pyMethod)) { cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArgs); return rValue; } PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); if (m_ti.error) { cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; typeErrorHandler(method); Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArg2); Py_CLEAR(pyArgs); return rValue; } PyTuple_SET_ITEM(pyArgs, 0, pyArg1); Py_INCREF(pyArg1); PyTuple_SET_ITEM(pyArgs, 1, pyArg2); Py_INCREF(pyArg2); // calls the method PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); if (!pyValue) { cerr << PLUGIN_ERROR << "Failed to call method." << endl; if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArg2); Py_CLEAR(pyArgs); return rValue; } Py_DECREF(pyMethod); Py_DECREF(pyCallable); Py_DECREF(pyArg1); Py_DECREF(pyArg2); Py_DECREF(pyArgs); /// convert the returned value m_ti.PyValue_To_rValue(pyValue,rValue); if (!m_ti.error) { Py_DECREF(pyValue); } else { Py_CLEAR(pyValue); typeErrorHandler(method); } return rValue; } /// trenary call template<typename RET,typename A1,typename A2,typename A3> RET genericMethodCallArgs(const char *method, A1 arg1, A2 arg2, A3 arg3) const { RET rValue = RET(); if (m_debugFlag) cerr << DEBUG_NAME << endl; if (!PyObject_HasAttrString(m_pyInstance,method)) { if (m_debugFlag) cerr << DEFAULT_RETURN << endl; return rValue; } /// prepare arguments for fast method call PyObject *pyMethod = m_ti.PyValue_From_CValue(method); PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); PyObject* pyArgs = PyTuple_New(3); if (!(pyArgs && pyCallable && pyMethod)) { cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArgs); return rValue; } PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3); if (m_ti.error) { cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; typeErrorHandler(method); Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArg2); Py_CLEAR(pyArg3); Py_CLEAR(pyArgs); return rValue; } /// Optimization: Pack args in a tuple to avoid va_list parsing. PyTuple_SET_ITEM(pyArgs, 0, pyArg1); Py_INCREF(pyArg1); PyTuple_SET_ITEM(pyArgs, 1, pyArg2); Py_INCREF(pyArg2); PyTuple_SET_ITEM(pyArgs, 2, pyArg3); Py_INCREF(pyArg3); // PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); /// fast method call PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); if (!pyValue) { cerr << PLUGIN_ERROR << "Failed to call method." << endl; if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} Py_CLEAR(pyMethod); Py_CLEAR(pyCallable); Py_CLEAR(pyArg1); Py_CLEAR(pyArg2); Py_CLEAR(pyArg3); Py_CLEAR(pyArgs); return rValue; } Py_DECREF(pyMethod); Py_DECREF(pyCallable); Py_DECREF(pyArg1); Py_DECREF(pyArg2); Py_DECREF(pyArg3); Py_DECREF(pyArgs); /// convert the returned value m_ti.PyValue_To_rValue(pyValue,rValue); if (!m_ti.error) { Py_DECREF(pyValue); } else { Py_CLEAR(pyValue); typeErrorHandler(method); } return rValue; } }; /// optimised process call inline PyPlugin::FeatureSet PyPlugin::processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp) { /// Optimizations: 1) we avoid ...ObjArg functions since we know /// the number of arguments, and we don't like va_list parsing /// in the process. 2) Also: we're supposed to incref args, /// but instead, we let the arguments tuple steal the references /// and decref them when it is deallocated. /// 3) all conversions are now using the fast sequence protocol /// (indexing the underlying object array). FeatureSet rFeatureSet; PyObject *pyChannelList = NULL; if (m_processType == numpy_bufferProcess) { pyChannelList = m_ti.InputBuffers_As_SharedMemoryList( inputBuffers,m_channels,m_blockSize,m_inputDomain); } if (m_processType == legacyProcess) { pyChannelList = m_ti.InputBuffers_As_PythonLists( inputBuffers,m_channels,m_blockSize,m_inputDomain); } #ifdef HAVE_NUMPY if (m_processType == numpy_arrayProcess) { pyChannelList = m_ti.InputBuffers_As_NumpyArray( inputBuffers,m_channels,m_blockSize,m_inputDomain); } #endif /// we don't expect these to fail unless out of memory (which is very unlikely on modern systems) #ifdef _DEBUG if (!pyChannelList) { if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} std::string method = PyString_AsString(m_pyProcess); cerr << PLUGIN_ERROR << "Failed to create channel list." << endl; return rFeatureSet; } #endif PyObject *pyTimeStamp = NULL; if (m_useRealTimeFlag) { //(1) pass TimeStamp as PyRealTime object pyTimeStamp = PyRealTime_FromRealTime(timestamp); } else { //(2) pass TimeStamp as frame count (long Sample Count) pyTimeStamp = PyLong_FromLong(Vamp::RealTime::realTime2Frame (timestamp, (unsigned int) m_inputSampleRate)); } #ifdef _DEBUG if (!pyTimeStamp) { if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} std::string method = PyString_AsString(m_pyProcess); cerr << PLUGIN_ERROR << "Failed to create RealTime time stamp." << endl; Py_DECREF(pyChannelList); return rFeatureSet; } #endif /// Old method: Call python process (returns new reference) /// PyObject *pyValue = PyObject_CallMethodObjArgs /// (m_pyInstance,m_pyProcess,pyChannelList,pyTimeStamp,NULL); PyObject *pyArgs = PyTuple_New(2); PyTuple_SET_ITEM(pyArgs, 0, pyChannelList); PyTuple_SET_ITEM(pyArgs, 1, pyTimeStamp); /// Call python process (returns new reference) {kwArgs = NULL} PyObject *pyValue = PyObject_Call(m_pyProcessCallable,pyArgs,NULL); if (!pyValue) { if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} std::string method = PyString_AsString(m_pyProcess); cerr << PLUGIN_ERROR << "An error occurred while evaluating Python process." << endl; Py_CLEAR(pyValue); Py_CLEAR(pyArgs); return rFeatureSet; } rFeatureSet = m_ti.PyValue_To_FeatureSet(pyValue); if (!m_ti.error) { Py_DECREF(pyValue); Py_DECREF(pyArgs); } else { typeErrorHandler(PyString_AsString(m_pyProcess),true); Py_CLEAR(pyValue); Py_CLEAR(pyArgs); } return rFeatureSet; } #endif