fazekasgy@37: /* fazekasgy@37: fazekasgy@37: * Vampy : This plugin is a wrapper around the Vamp plugin API. fazekasgy@37: * It allows for writing Vamp plugins in Python. fazekasgy@37: fazekasgy@37: * Centre for Digital Music, Queen Mary University of London. fazekasgy@37: * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources fazekasgy@37: * for licence information.) fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: #ifndef _PYTHON_WRAPPER_PLUGIN_H_ fazekasgy@37: #define _PYTHON_WRAPPER_PLUGIN_H_ fazekasgy@37: fazekasgy@37: #define _CLASS_METHOD_ m_class << "::" << method fazekasgy@37: #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: " fazekasgy@37: #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " " Chris@67: #define DEFAULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented. Returning default value." fazekasgy@37: #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") fazekasgy@37: fazekasgy@37: #include fazekasgy@37: #include "PyExtensionModule.h" fazekasgy@37: #include "PyTypeInterface.h" Chris@71: #include "PyTypeConversions.h" fazekasgy@37: #include "vamp-sdk/Plugin.h" fazekasgy@37: #include "Mutex.h" fazekasgy@37: fazekasgy@37: using std::string; fazekasgy@37: using std::cerr; fazekasgy@37: using std::endl; fazekasgy@37: fazekasgy@37: enum eProcessType { fazekasgy@37: not_implemented, fazekasgy@37: legacyProcess, fazekasgy@37: numpyProcess, fazekasgy@37: numpy_bufferProcess, fazekasgy@37: numpy_arrayProcess fazekasgy@37: }; fazekasgy@37: fazekasgy@37: class PyPlugin : public Vamp::Plugin fazekasgy@37: { fazekasgy@37: public: fazekasgy@51: PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount, bool &numpyInstalled); fazekasgy@37: virtual ~PyPlugin(); fazekasgy@37: fazekasgy@37: bool initialise(size_t channels, size_t stepSize, size_t blockSize); fazekasgy@37: void reset(); fazekasgy@37: fazekasgy@37: InputDomain getInputDomain() const; fazekasgy@37: size_t getPreferredBlockSize() const; fazekasgy@37: size_t getPreferredStepSize() const; fazekasgy@37: size_t getMinChannelCount() const; fazekasgy@37: size_t getMaxChannelCount() const; fazekasgy@37: fazekasgy@37: std::string getIdentifier() const; fazekasgy@37: std::string getName() const; fazekasgy@37: std::string getDescription() const; fazekasgy@37: std::string getMaker() const; fazekasgy@37: int getPluginVersion() const; fazekasgy@37: std::string getCopyright() const; fazekasgy@37: fazekasgy@37: OutputList getOutputDescriptors() const; fazekasgy@37: ParameterList getParameterDescriptors() const; fazekasgy@37: float getParameter(std::string paramid) const; fazekasgy@37: void setParameter(std::string paramid, float newval); fazekasgy@37: fazekasgy@37: FeatureSet process(const float *const *inputBuffers, fazekasgy@37: Vamp::RealTime timestamp); fazekasgy@37: fazekasgy@37: FeatureSet getRemainingFeatures(); fazekasgy@37: fazekasgy@37: protected: fazekasgy@37: static Mutex m_pythonInterpreterMutex; fazekasgy@37: PyObject *m_pyClass; fazekasgy@37: PyObject *m_pyInstance; fazekasgy@37: int &m_instcount; fazekasgy@37: size_t m_stepSize; fazekasgy@37: size_t m_blockSize; fazekasgy@37: size_t m_channels; fazekasgy@37: std::string m_plugin; fazekasgy@37: std::string m_class; fazekasgy@37: std::string m_path; fazekasgy@37: eProcessType m_processType; fazekasgy@37: PyObject *m_pyProcess; fazekasgy@37: PyObject *m_pyProcessCallable; fazekasgy@37: mutable InputDomain m_inputDomain; Chris@71: PyTypeConversions m_tc; fazekasgy@37: PyTypeInterface m_ti; fazekasgy@37: int m_vampyFlags; fazekasgy@37: bool m_quitOnErrorFlag; fazekasgy@37: bool m_debugFlag; fazekasgy@37: bool m_useRealTimeFlag; fazekasgy@51: bool m_numpyInstalled; fazekasgy@51: mutable bool m_processFailure; fazekasgy@37: fazekasgy@37: void setProcessType(); fazekasgy@37: fazekasgy@37: FeatureSet processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp); fazekasgy@37: Chris@66: bool getBooleanFlag(const char flagName[],bool) const; Chris@66: int getBinaryFlags(const char flagName[], eVampyFlags) const; Chris@66: void typeErrorHandler(const char *method, bool process = false) const; fazekasgy@37: fazekasgy@37: /// simple 'void return' call with no args Chris@66: void genericMethodCall(const char *method) const fazekasgy@37: { fazekasgy@37: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@37: if ( PyObject_HasAttrString(m_pyInstance,method) ) fazekasgy@37: { Chris@66: PyObject *pyValue = PyObject_CallMethod(m_pyInstance, (char *)method, NULL); fazekasgy@37: if (!pyValue) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: } fazekasgy@37: } fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// 'no arg with default return value' call fazekasgy@37: template Chris@66: RET &genericMethodCall(const char *method, RET &rValue) const fazekasgy@37: { fazekasgy@37: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@37: if ( PyObject_HasAttrString(m_pyInstance,method) ) fazekasgy@37: { Chris@66: PyObject *pyValue = PyObject_CallMethod(m_pyInstance, (char *)method, NULL); fazekasgy@37: if (!pyValue) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// convert the returned value fazekasgy@37: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@37: if (!m_ti.error) { fazekasgy@37: Py_DECREF(pyValue); fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyValue); fazekasgy@37: typeErrorHandler(method); fazekasgy@37: } fazekasgy@37: return rValue; fazekasgy@37: } Chris@67: if (m_debugFlag) cerr << DEFAULT_RETURN << endl; fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// unary call fazekasgy@37: template Chris@66: RET genericMethodCallArgs(const char *method, A1 arg1) const fazekasgy@37: { fazekasgy@37: RET rValue = RET(); fazekasgy@37: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@37: if (!PyObject_HasAttrString(m_pyInstance,method)) { Chris@67: if (m_debugFlag) cerr << DEFAULT_RETURN << endl; fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// prepare arguments for fast method call Chris@71: PyObject *pyMethod = m_tc.PyValue_From_CValue(method); fazekasgy@37: PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); fazekasgy@37: PyObject* pyArgs = PyTuple_New(1); fazekasgy@37: if (!(pyArgs && pyCallable && pyMethod)) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to prepare argument for calling method." << endl; fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: Chris@71: PyObject *pyArg1 = m_tc.PyValue_From_CValue(arg1); Chris@71: if (m_tc.error) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to convert argument for calling method." << endl; fazekasgy@37: typeErrorHandler(method); fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 0, pyArg1); fazekasgy@37: Py_INCREF(pyArg1); fazekasgy@37: fazekasgy@37: /// call the method fazekasgy@37: PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: Py_DECREF(pyMethod); fazekasgy@37: Py_DECREF(pyCallable); fazekasgy@37: Py_DECREF(pyArg1); fazekasgy@37: Py_DECREF(pyArgs); fazekasgy@37: fazekasgy@37: /// convert the returned value fazekasgy@37: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@37: if (!m_ti.error) { fazekasgy@37: Py_DECREF(pyValue); fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyValue); fazekasgy@37: typeErrorHandler(method); fazekasgy@37: } fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// binary call fazekasgy@37: template Chris@66: RET genericMethodCallArgs(const char *method, A1 arg1, A2 arg2) const fazekasgy@37: { fazekasgy@37: RET rValue = RET(); fazekasgy@37: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@37: if (!PyObject_HasAttrString(m_pyInstance,method)) { Chris@67: if (m_debugFlag) cerr << DEFAULT_RETURN << endl; fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// prepare arguments for fast method call Chris@71: PyObject *pyMethod = m_tc.PyValue_From_CValue(method); fazekasgy@37: PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); fazekasgy@37: PyObject* pyArgs = PyTuple_New(2); fazekasgy@37: if (!(pyArgs && pyCallable && pyMethod)) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: Chris@71: PyObject *pyArg1 = m_tc.PyValue_From_CValue(arg1); Chris@71: PyObject *pyArg2 = m_tc.PyValue_From_CValue(arg2); Chris@71: if (m_tc.error) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; fazekasgy@37: typeErrorHandler(method); fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArg2); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 0, pyArg1); fazekasgy@37: Py_INCREF(pyArg1); fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 1, pyArg2); fazekasgy@37: Py_INCREF(pyArg2); fazekasgy@37: fazekasgy@37: // calls the method fazekasgy@37: PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArg2); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: Py_DECREF(pyMethod); fazekasgy@37: Py_DECREF(pyCallable); fazekasgy@37: Py_DECREF(pyArg1); fazekasgy@37: Py_DECREF(pyArg2); fazekasgy@37: Py_DECREF(pyArgs); fazekasgy@37: fazekasgy@37: /// convert the returned value fazekasgy@37: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@37: if (!m_ti.error) { fazekasgy@37: Py_DECREF(pyValue); fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyValue); fazekasgy@37: typeErrorHandler(method); fazekasgy@37: } fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// trenary call fazekasgy@37: template Chris@66: RET genericMethodCallArgs(const char *method, A1 arg1, A2 arg2, A3 arg3) const fazekasgy@37: { fazekasgy@37: RET rValue = RET(); fazekasgy@37: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@37: if (!PyObject_HasAttrString(m_pyInstance,method)) { Chris@67: if (m_debugFlag) cerr << DEFAULT_RETURN << endl; fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// prepare arguments for fast method call Chris@71: PyObject *pyMethod = m_tc.PyValue_From_CValue(method); fazekasgy@37: PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); fazekasgy@37: PyObject* pyArgs = PyTuple_New(3); fazekasgy@37: if (!(pyArgs && pyCallable && pyMethod)) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: Chris@71: PyObject *pyArg1 = m_tc.PyValue_From_CValue(arg1); Chris@71: PyObject *pyArg2 = m_tc.PyValue_From_CValue(arg2); Chris@71: PyObject *pyArg3 = m_tc.PyValue_From_CValue(arg3); Chris@71: if (m_tc.error) { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; fazekasgy@37: typeErrorHandler(method); fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArg2); fazekasgy@37: Py_CLEAR(pyArg3); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// Optimization: Pack args in a tuple to avoid va_list parsing. fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 0, pyArg1); fazekasgy@37: Py_INCREF(pyArg1); fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 1, pyArg2); fazekasgy@37: Py_INCREF(pyArg2); fazekasgy@37: PyTuple_SET_ITEM(pyArgs, 2, pyArg3); fazekasgy@37: Py_INCREF(pyArg3); fazekasgy@37: fazekasgy@37: // PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); fazekasgy@37: /// fast method call fazekasgy@37: PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); fazekasgy@37: if (!pyValue) fazekasgy@37: { fazekasgy@37: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: Py_CLEAR(pyMethod); fazekasgy@37: Py_CLEAR(pyCallable); fazekasgy@37: Py_CLEAR(pyArg1); fazekasgy@37: Py_CLEAR(pyArg2); fazekasgy@37: Py_CLEAR(pyArg3); fazekasgy@37: Py_CLEAR(pyArgs); fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: Py_DECREF(pyMethod); fazekasgy@37: Py_DECREF(pyCallable); fazekasgy@37: Py_DECREF(pyArg1); fazekasgy@37: Py_DECREF(pyArg2); fazekasgy@37: Py_DECREF(pyArg3); fazekasgy@37: Py_DECREF(pyArgs); fazekasgy@37: fazekasgy@37: /// convert the returned value fazekasgy@37: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@37: if (!m_ti.error) { fazekasgy@37: Py_DECREF(pyValue); fazekasgy@37: } else { fazekasgy@37: Py_CLEAR(pyValue); fazekasgy@37: typeErrorHandler(method); fazekasgy@37: } fazekasgy@37: return rValue; fazekasgy@37: } fazekasgy@37: fazekasgy@37: }; fazekasgy@37: fazekasgy@37: #endif