cannam@18: /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */ 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: #ifndef _PYTHON_WRAPPER_PLUGIN_H_ fazekasgy@0: #define _PYTHON_WRAPPER_PLUGIN_H_ fazekasgy@0: fazekasgy@31: #define _CLASS_METHOD_ m_class << "::" << method fazekasgy@31: #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: " fazekasgy@31: #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " " fazekasgy@31: #define DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented." << endl << "Returning default value: " << rValue fazekasgy@31: #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") fazekasgy@31: fazekasgy@0: #include "vamp-sdk/Plugin.h" cannam@3: #include <Python.h> fazekasgy@31: // #include <typeinfo> fazekasgy@31: // #include <stdarg.h> fazekasgy@31: #include "PyTypeInterface.h" cannam@3: cannam@3: #include "Mutex.h" fazekasgy@0: fazekasgy@31: using std::string; fazekasgy@31: using std::cerr; fazekasgy@31: using std::endl; fazekasgy@0: fazekasgy@6: enum eProcessType { fazekasgy@6: not_implemented, fazekasgy@6: legacyProcess, fazekasgy@6: numpyProcess fazekasgy@6: }; fazekasgy@0: fazekasgy@0: class PyPlugin : public Vamp::Plugin fazekasgy@0: { fazekasgy@0: public: fazekasgy@31: PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount); cannam@18: virtual ~PyPlugin(); fazekasgy@0: cannam@18: bool initialise(size_t channels, size_t stepSize, size_t blockSize); cannam@18: void reset(); fazekasgy@6: fazekasgy@0: InputDomain getInputDomain() const; fazekasgy@0: size_t getPreferredBlockSize() const; fazekasgy@0: size_t getPreferredStepSize() const; fazekasgy@0: size_t getMinChannelCount() const; fazekasgy@0: size_t getMaxChannelCount() const; fazekasgy@0: cannam@18: std::string getIdentifier() const; cannam@18: std::string getName() const; cannam@18: std::string getDescription() const; cannam@18: std::string getMaker() const; cannam@18: int getPluginVersion() const; cannam@18: std::string getCopyright() const; cannam@18: cannam@18: OutputList getOutputDescriptors() const; cannam@18: ParameterList getParameterDescriptors() const; fazekasgy@0: float getParameter(std::string paramid) const; fazekasgy@0: void setParameter(std::string paramid, float newval); fazekasgy@0: cannam@18: FeatureSet process(const float *const *inputBuffers, cannam@18: Vamp::RealTime timestamp); fazekasgy@0: cannam@18: FeatureSet getRemainingFeatures(); fazekasgy@31: fazekasgy@0: protected: fazekasgy@31: static Mutex m_pythonInterpreterMutex; cannam@24: PyObject *m_pyClass; fazekasgy@0: PyObject *m_pyInstance; fazekasgy@31: int &m_instcount; cannam@18: size_t m_stepSize; cannam@18: size_t m_blockSize; cannam@18: size_t m_channels; fazekasgy@0: std::string m_plugin; fazekasgy@0: std::string m_class; fazekasgy@0: std::string m_path; fazekasgy@6: int m_processType; fazekasgy@6: PyObject *m_pyProcess; fazekasgy@6: InputDomain m_inputDomain; fazekasgy@31: PyTypeInterface m_ti; fazekasgy@31: bool m_quitOnErrorFlag; fazekasgy@31: bool m_debugFlag; fazekasgy@31: fazekasgy@31: void setProcessType(); fazekasgy@6: fazekasgy@31: PyObject* numpyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp); fazekasgy@31: PyObject* legacyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp); fazekasgy@31: fazekasgy@31: bool getBooleanFlag(char flagName[],bool) const; fazekasgy@31: /* fazekasgy@31: Flags may be used to control the behaviour of the interface. fazekasgy@31: Flags can be set in any Vampy plugin's __init__() function. fazekasgy@31: Their scope is limited to an instance. fazekasgy@31: Default values for all flags are False. fazekasgy@31: Python Example: fazekasgy@31: def __init__(self,inputSampleRate): fazekasgy@31: self.use_strict_type_conversion = True fazekasgy@31: self.vampy_debug_messages = True fazekasgy@31: self.use_realtime_timestamp = False fazekasgy@31: self.use_numpy_interface = False fazekasgy@31: self.quit_on_type_error = False fazekasgy@31: fazekasgy@31: */ fazekasgy@31: fazekasgy@31: void genericMethodCall(char *method) const fazekasgy@31: { fazekasgy@31: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@31: if ( PyObject_HasAttrString(m_pyInstance,method) ) fazekasgy@31: { fazekasgy@31: PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@31: if (!pyValue) { fazekasgy@31: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@31: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@31: } fazekasgy@31: } fazekasgy@31: } fazekasgy@31: fazekasgy@31: template<typename RET> fazekasgy@31: RET &genericMethodCall(char *method, RET &rValue) const fazekasgy@31: { fazekasgy@31: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@31: if ( PyObject_HasAttrString(m_pyInstance,method) ) fazekasgy@31: { fazekasgy@31: PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); fazekasgy@31: if (pyValue) { fazekasgy@31: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@31: if (!m_ti.error) { fazekasgy@31: Py_DECREF(pyValue); fazekasgy@31: return rValue; fazekasgy@31: } else { fazekasgy@31: cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; fazekasgy@31: Py_CLEAR(pyValue); fazekasgy@31: if (m_quitOnErrorFlag) exit(EXIT_FAILURE); fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: } else { fazekasgy@31: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@31: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: } fazekasgy@31: // TODO: this fails to generalise because the << operator fazekasgy@31: // doesn't accept all types. fazekasgy@31: // if (m_debugFlag) cerr << DEAFULT_RETURN << endl; fazekasgy@31: return rValue; fazekasgy@31: } cannam@3: fazekasgy@31: template<typename RET,typename A1> fazekasgy@31: RET genericMethodCallArgs(char *method, A1 arg1) const fazekasgy@31: { fazekasgy@31: RET rValue = RET(); fazekasgy@31: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@31: if (!PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@31: // if (m_debugFlag) cerr << DEAFULT_RETURN << endl; fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@31: // These functions always return valid PyObjects fazekasgy@31: PyObject *pyMethod = m_ti.PyValue_From_CValue(method); fazekasgy@31: PyObject* pyTuple = PyTuple_New(3); fazekasgy@31: if (!pyTuple) return rValue; fazekasgy@31: fazekasgy@31: PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); fazekasgy@31: fazekasgy@31: PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,NULL); fazekasgy@31: if (!pyValue) fazekasgy@31: { fazekasgy@31: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@31: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@31: } fazekasgy@31: fazekasgy@31: Py_DECREF(pyMethod); fazekasgy@31: Py_DECREF(pyArg1); fazekasgy@31: fazekasgy@31: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@31: if (!m_ti.error) { fazekasgy@31: Py_DECREF(pyValue); fazekasgy@31: } else { fazekasgy@31: cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; fazekasgy@31: Py_CLEAR(pyValue); fazekasgy@31: if (m_quitOnErrorFlag) exit(EXIT_FAILURE); fazekasgy@31: } fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@31: template<typename RET,typename A1,typename A2> fazekasgy@31: RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2) fazekasgy@31: { fazekasgy@31: RET rValue = RET(); fazekasgy@31: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@31: if (!PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@31: // if (m_debugFlag) cerr << DEAFULT_RETURN << endl; fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@31: // These functions always return valid PyObjects fazekasgy@31: PyObject *pyMethod = m_ti.PyValue_From_CValue(method); fazekasgy@31: PyObject* pyTuple = PyTuple_New(3); fazekasgy@31: if (!pyTuple) return rValue; fazekasgy@31: fazekasgy@31: PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); fazekasgy@31: PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); fazekasgy@31: fazekasgy@31: PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,NULL); fazekasgy@31: if (!pyValue) fazekasgy@31: { fazekasgy@31: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@31: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@31: } fazekasgy@31: fazekasgy@31: Py_DECREF(pyMethod); fazekasgy@31: Py_DECREF(pyArg1); fazekasgy@31: Py_DECREF(pyArg2); fazekasgy@31: fazekasgy@31: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@31: if (!m_ti.error) { fazekasgy@31: Py_DECREF(pyValue); fazekasgy@31: } else { fazekasgy@31: cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; fazekasgy@31: Py_CLEAR(pyValue); fazekasgy@31: if (m_quitOnErrorFlag) exit(EXIT_FAILURE); fazekasgy@31: } fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@31: fazekasgy@31: template<typename RET,typename A1,typename A2,typename A3> fazekasgy@31: RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3) fazekasgy@31: { fazekasgy@31: RET rValue = RET(); fazekasgy@31: if (m_debugFlag) cerr << DEBUG_NAME << endl; fazekasgy@31: if (!PyObject_HasAttrString(m_pyInstance,method)) { fazekasgy@31: if (m_debugFlag) cerr << DEAFULT_RETURN << endl; fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@31: // These functions always return valid PyObjects fazekasgy@31: PyObject *pyMethod = m_ti.PyValue_From_CValue(method); fazekasgy@31: PyObject* pyTuple = PyTuple_New(3); fazekasgy@31: if (!pyTuple) return rValue; fazekasgy@31: fazekasgy@31: PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); fazekasgy@31: PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); fazekasgy@31: PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3); fazekasgy@31: fazekasgy@31: // TODO: Pack it in a tuple to avoid va_list parsing! fazekasgy@31: fazekasgy@31: // callable = PyObject_GetAttr(callable, name); fazekasgy@31: // if (callable == NULL) fazekasgy@31: // return NULL; fazekasgy@31: // PyObject* args; // pyTuple of input arguments fazekasgy@31: //tmp = PyObject_Call(callable, args, NULL); fazekasgy@31: fazekasgy@31: fazekasgy@31: PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); fazekasgy@31: if (!pyValue) fazekasgy@31: { fazekasgy@31: cerr << PLUGIN_ERROR << "Failed to call method." << endl; fazekasgy@31: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@31: } fazekasgy@31: fazekasgy@31: Py_DECREF(pyMethod); fazekasgy@31: Py_DECREF(pyArg1); fazekasgy@31: Py_DECREF(pyArg2); fazekasgy@31: Py_DECREF(pyArg3); fazekasgy@31: fazekasgy@31: m_ti.PyValue_To_rValue(pyValue,rValue); fazekasgy@31: if (!m_ti.error) { fazekasgy@31: Py_DECREF(pyValue); fazekasgy@31: } else { fazekasgy@31: cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; fazekasgy@31: Py_CLEAR(pyValue); fazekasgy@31: if (m_quitOnErrorFlag) exit(EXIT_FAILURE); fazekasgy@31: } fazekasgy@31: return rValue; fazekasgy@31: } fazekasgy@31: fazekasgy@0: }; fazekasgy@0: fazekasgy@0: fazekasgy@0: #endif