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: #include fazekasgy@37: #include "PyExtensionModule.h" fazekasgy@37: #include "PyRealTime.h" fazekasgy@37: #include "PyFeature.h" fazekasgy@37: #include "PyFeatureSet.h" fazekasgy@37: #include "PyParameterDescriptor.h" fazekasgy@37: #include "PyOutputDescriptor.h" fazekasgy@37: #include "vamp/vamp.h" fazekasgy@37: #include "vamp-sdk/Plugin.h" fazekasgy@37: fazekasgy@37: using namespace std; fazekasgy@37: using namespace Vamp; fazekasgy@37: using Vamp::Plugin; fazekasgy@37: using Vamp::RealTime; fazekasgy@37: fazekasgy@37: /* Functions Exposed by Vampy */ fazekasgy@37: fazekasgy@37: /* Creating PyRealTime Objects from frame count */ fazekasgy@37: fazekasgy@37: /* New RealTime object from Frame (with given samplerate) */ fazekasgy@37: static PyObject * fazekasgy@37: RealTime_frame2RealTime(PyObject *ignored, PyObject *args) fazekasgy@37: { fazekasgy@37: long frame; fazekasgy@37: unsigned int sampleRate; fazekasgy@37: fazekasgy@37: if (!(args && PyTuple_GET_SIZE(args) == 2)) { fazekasgy@37: PyErr_SetString(PyExc_ValueError,"frame2RealTime requires two arguments: frame and sample rate."); fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: fazekasgy@37: PyObject* pyFrame = PyTuple_GET_ITEM(args,0); fazekasgy@37: PyObject* pySampleRate = PyTuple_GET_ITEM(args,1); fazekasgy@37: fazekasgy@37: /// frame fazekasgy@37: if (PyInt_Check(pyFrame)) frame = PyInt_AS_LONG(pyFrame); fazekasgy@37: else if (PyLong_Check(pyFrame)) frame = PyLong_AsLong(pyFrame); fazekasgy@37: else { fazekasgy@37: PyErr_SetString(PyExc_ValueError,"frame2RealTime 'frame' argument must be long integer."); fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /// sample rate fazekasgy@37: if (PyInt_Check(pySampleRate)) fazekasgy@37: sampleRate = _long2uint(PyInt_AS_LONG(pySampleRate)); fazekasgy@37: else if (PyFloat_Check(pySampleRate)) fazekasgy@37: sampleRate = _dbl2uint(PyFloat_AS_DOUBLE(pySampleRate)); fazekasgy@37: else if (PyLong_Check(pySampleRate)) fazekasgy@37: sampleRate = _long2uint(PyLong_AsLong(pySampleRate)); fazekasgy@37: else { fazekasgy@37: PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument must be int, long or float."); fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: fazekasgy@37: if (!sampleRate) { fazekasgy@37: PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument overflow error. Argument must be 0 < arg < UINT_MAX."); fazekasgy@37: cerr << "Value: " << sampleRate << endl; fazekasgy@37: return NULL; fazekasgy@37: } fazekasgy@37: fazekasgy@37: // simpler but slower: fazekasgy@37: // if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ", fazekasgy@37: // &frame,&sampleRate)) fazekasgy@37: // return NULL; fazekasgy@37: fazekasgy@37: RealTimeObject *self; fazekasgy@37: self = PyObject_New(RealTimeObject, &RealTime_Type); fazekasgy@37: if (self == NULL) return NULL; fazekasgy@37: fazekasgy@37: self->rt = new RealTime( fazekasgy@37: RealTime::frame2RealTime(frame,sampleRate)); fazekasgy@37: fazekasgy@37: return (PyObject *) self; fazekasgy@37: } fazekasgy@37: fazekasgy@37: /* fazekasgy@37: fazekasgy@37: Note: these functions are not very interesting on their own, but fazekasgy@37: they can be used to make the semantics of the plugin clearer. fazekasgy@37: They return ordinary Python list objects. All type checking fazekasgy@37: is performed in the type interface. fazekasgy@37: fazekasgy@37: */ fazekasgy@37: fazekasgy@37: /* New PyOutputList Objects */ fazekasgy@37: static PyObject * fazekasgy@37: OutputList_new(PyObject *ignored, PyObject *args) fazekasgy@37: { fazekasgy@37: if (args and PyTuple_Check(args)) fazekasgy@37: return PySequence_List(args); fazekasgy@37: else return (PyObject *) PyList_New(0); fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: /* New PyParameterList Objects */ fazekasgy@37: static PyObject * fazekasgy@37: ParameterList_new(PyObject *ignored, PyObject *args) fazekasgy@37: { fazekasgy@37: if (args and PyTuple_Check(args)) fazekasgy@37: return PySequence_List(args); fazekasgy@37: else return (PyObject *) PyList_New(0); fazekasgy@37: } fazekasgy@37: fazekasgy@37: /* New PyFeatureList Objects */ fazekasgy@37: static PyObject * fazekasgy@37: FeatureList_new(PyObject *ignored, PyObject *args) fazekasgy@37: { fazekasgy@37: if (args and PyTuple_Check(args)) fazekasgy@37: return PySequence_List(args); fazekasgy@37: else return (PyObject *) PyList_New(0); fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: /* Declare the methods exposed by the vampy module */ fazekasgy@37: fazekasgy@37: fazekasgy@37: PyMethodDef VampyMethods[] = { fazekasgy@37: /*NOTE: This is conventionally static, but limiting the scope fazekasgy@37: here will cause seg fault if the declared functions are fazekasgy@37: called back from a Python function wrapped in a C++ class.*/ fazekasgy@37: fazekasgy@37: {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS, fazekasgy@37: PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")}, fazekasgy@37: fazekasgy@37: {"OutputList", OutputList_new, METH_VARARGS, fazekasgy@37: PyDoc_STR("OutputList() -> returns new OutputList object")}, fazekasgy@37: fazekasgy@37: {"ParameterList", ParameterList_new, METH_VARARGS, fazekasgy@37: PyDoc_STR("ParameterList() -> returns new ParameterList object")}, fazekasgy@37: fazekasgy@37: {"FeatureList", FeatureList_new, METH_VARARGS, fazekasgy@37: PyDoc_STR("FeatureList() -> returns new FeatureList object")}, fazekasgy@37: fazekasgy@37: {NULL, NULL, 0, NULL} fazekasgy@37: }; fazekasgy@37: fazekasgy@37: /* Module Documentation */ fazekasgy@37: // PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers."); fazekasgy@37: fazekasgy@37: static int fazekasgy@37: setint(PyObject *d, char *name, int value) fazekasgy@37: { fazekasgy@37: PyObject *v; fazekasgy@37: int err; fazekasgy@37: v = PyInt_FromLong((long)value); fazekasgy@37: err = PyDict_SetItemString(d, name, v); fazekasgy@37: Py_XDECREF(v); fazekasgy@37: return err; fazekasgy@37: } fazekasgy@37: fazekasgy@37: static int fazekasgy@37: setdbl(PyObject *d, char *name, double value) fazekasgy@37: { fazekasgy@37: PyObject *v; fazekasgy@37: int err; fazekasgy@37: v = PyFloat_FromDouble(value); fazekasgy@37: err = PyDict_SetItemString(d, name, v); fazekasgy@37: Py_XDECREF(v); fazekasgy@37: return err; fazekasgy@37: } fazekasgy@37: fazekasgy@37: static int fazekasgy@37: setstr(PyObject *d, char *name, char *value) fazekasgy@37: { fazekasgy@37: PyObject *v; fazekasgy@37: int err; fazekasgy@37: v = PyString_FromString(value); fazekasgy@37: err = PyDict_SetItemString(d, name, v); fazekasgy@37: Py_XDECREF(v); fazekasgy@37: return err; fazekasgy@37: } fazekasgy@37: fazekasgy@37: fazekasgy@37: PyMODINIT_FUNC fazekasgy@37: initvampy(void) fazekasgy@37: { fazekasgy@37: PyObject *module, *mdict; fazekasgy@37: fazekasgy@37: /* if (PyType_Ready(&Feature_Type) < 0) return; fazekasgy@37: Note: Why do we get a segfault if this is initialised here? fazekasgy@37: PyType_Ready adds these object to the GC. fazekasgy@37: This is OK for an extension module, but it is a mistake here, fazekasgy@37: because the adresses become invalid when the shared library fazekasgy@37: is unloaded. When the GC tries to visit a these objects, fazekasgy@37: it will fail.*/ fazekasgy@37: fazekasgy@37: RealTime_Type.ob_type = &PyType_Type; fazekasgy@37: Feature_Type.ob_type = &PyType_Type; fazekasgy@37: OutputDescriptor_Type.ob_type = &PyType_Type; fazekasgy@37: ParameterDescriptor_Type.ob_type = &PyType_Type; fazekasgy@37: initFeatureSetType(); // this is derived from the builtin dict fazekasgy@37: fazekasgy@37: PyImport_AddModule("vampy"); fazekasgy@37: module = Py_InitModule("vampy", VampyMethods); fazekasgy@37: if (!module) goto failure; fazekasgy@37: mdict = PyModule_GetDict(module); fazekasgy@37: if (!mdict) goto failure; fazekasgy@37: fazekasgy@37: /// vampy plugin wrapper flags fazekasgy@37: if (setint(mdict, "vf_NULL", vf_NULL) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_DEBUG", vf_DEBUG) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_STRICT", vf_STRICT) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_QUIT", vf_QUIT) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_REALTIME", vf_REALTIME) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_BUFFER", vf_BUFFER) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_ARRAY", vf_ARRAY) < 0) goto failure; fazekasgy@37: if (setint(mdict, "vf_DEFAULT_V2", vf_DEFAULT_V2) < 0) goto failure; fazekasgy@37: fazekasgy@37: /// Vamp enum types simulation fazekasgy@37: if (setint(mdict, "OneSamplePerStep", Vamp::Plugin::OutputDescriptor::OneSamplePerStep) < 0) goto failure; fazekasgy@37: if (setint(mdict, "FixedSampleRate", Vamp::Plugin::OutputDescriptor::FixedSampleRate) < 0) goto failure; fazekasgy@37: if (setint(mdict, "VariableSampleRate", Vamp::Plugin::OutputDescriptor::VariableSampleRate) < 0) goto failure; fazekasgy@37: if (setint(mdict, "TimeDomain", Vamp::Plugin::TimeDomain) < 0) goto failure; fazekasgy@37: if (setint(mdict, "FrequencyDomain", Vamp::Plugin::FrequencyDomain) < 0) goto failure; fazekasgy@37: fazekasgy@37: /// module attributes fazekasgy@37: if (setstr(mdict, "__name__", "vampy") < 0) goto failure; fazekasgy@37: if (setdbl(mdict, "__version__", 2.0) < 0) goto failure; fazekasgy@37: if (setdbl(mdict, "__VAMP_API_VERSION__", (double) VAMP_API_VERSION) < 0) goto failure; fazekasgy@37: #ifdef HAVE_NUMPY fazekasgy@37: if (setint(mdict, "__numpy__", 1) < 0) goto failure; fazekasgy@37: #else fazekasgy@37: if (setint(mdict, "__numpy__", 0) < 0) goto failure; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: /// type objects fazekasgy@37: Py_INCREF(&RealTime_Type); fazekasgy@37: if (PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type) !=0) goto failure; fazekasgy@37: fazekasgy@37: Py_INCREF((PyObject*)&Feature_Type); fazekasgy@37: if (PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type) !=0) goto failure; fazekasgy@37: fazekasgy@37: Py_INCREF((PyObject*)&FeatureSet_Type); fazekasgy@37: if (PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type) !=0) goto failure; fazekasgy@37: fazekasgy@37: Py_INCREF((PyObject*)&OutputDescriptor_Type); fazekasgy@37: if (PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type) !=0) goto failure; fazekasgy@37: fazekasgy@37: Py_INCREF((PyObject*)&ParameterDescriptor_Type); fazekasgy@37: if (PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type) !=0) goto failure; fazekasgy@37: fazekasgy@37: #ifdef _DEBUG fazekasgy@37: cerr << "Vampy: extension module initialised." << endl; fazekasgy@37: #endif fazekasgy@37: fazekasgy@37: return; fazekasgy@37: fazekasgy@37: failure : fazekasgy@37: if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} fazekasgy@37: cerr << "Vampy::PyExtensionModule::initvampy: Failed to initialise extension module." << endl; fazekasgy@37: return; fazekasgy@37: } fazekasgy@37: fazekasgy@37: