Mercurial > hg > vampy
changeset 31:4f1894c7591b vampy2
Created Vampy2 branch
author | fazekasgy |
---|---|
date | Sun, 20 Sep 2009 17:31:20 +0000 |
parents | 5139bf30f208 |
children | a8231788216c |
files | Makefile PyExtensionModule.cpp PyExtensionModule.h PyFeature.cpp PyFeature.h PyFeatureSet.cpp PyFeatureSet.h PyOutputDescriptor.cpp PyOutputDescriptor.h PyParameterDescriptor.cpp PyParameterDescriptor.h PyPlugin.cpp PyPlugin.h PyRealTime.cpp PyRealTime.h PyTypeInterface.cpp PyTypeInterface.h pyvamp-main.cpp vampy-main.cpp |
diffstat | 19 files changed, 3411 insertions(+), 1217 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Tue Aug 25 08:49:22 2009 +0000 +++ b/Makefile Sun Sep 20 17:31:20 2009 +0000 @@ -1,9 +1,18 @@ -CXXFLAGS := -I../vamp-plugin-sdk -O2 -Wall -I/usr/include/python2.5 -#-fvisibility=hidden +CXXFLAGS := -I../vamp-plugin-sdk -O2 -Wall -I/usr/include/python2.5 #-I../host/pyRealTime.h #-fvisibility=hidden +LDFLAGS := -L../vamp-plugin-sdk/vamp-sdk -lvamp-sdk -dynamiclib -lpython2.5 -lpthread -vampy.dylib: PyPlugin.o PyPlugScanner.o pyvamp-main.o Mutex.o - g++ -shared $^ -o $@ -L../vamp-plugin-sdk/vamp-sdk -lvamp-sdk -dynamiclib -lpython2.5 -lpthread +all: vampy.dylib + +PyExtensionModule.a: PyExtensionModule.o PyRealTime.o PyFeature.o PyParameterDescriptor.o PyOutputDescriptor.o PyFeatureSet.o + libtool -static $^ -o $@ + +# The standard python extension is .so (even on the Mac) +PyExtensionModule.so: PyExtensionModule.o PyRealTime.o PyFeature.o PyParameterDescriptor.o PyOutputDescriptor.o PyFeatureSet.o + g++ -shared $^ -o $@ $(LDFLAGS) + +vampy.dylib: PyPlugin.o PyPlugScanner.o vampy-main.o Mutex.o PyTypeInterface.o PyExtensionModule.a + g++ -shared $^ -o $@ $(LDFLAGS) # Install plugin # @@ -24,5 +33,6 @@ clean: rm *.o + rm *.a rm *$(PLUGIN_EXT)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyExtensionModule.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,292 @@ +#include <Python.h> +#include "PyExtensionModule.h" +#include "PyRealTime.h" +#include "PyFeature.h" +#include "PyFeatureSet.h" +#include "PyParameterDescriptor.h" +#include "PyOutputDescriptor.h" +#include "vamp-sdk/Plugin.h" + +using namespace std; +using namespace Vamp; +using Vamp::Plugin; +using Vamp::RealTime; + +/* Simple Example Function */ + +static int five=5; + +PyObject* +get_five(PyObject *self, PyObject *args) +{ + if(!PyArg_ParseTuple(args, ":five")) return NULL; + return Py_BuildValue("i", five); +} + + +/* Functions Exposed by Vampy */ + +/* Creating PyRealTime Objects */ + + +/* New RealTime object from Frame (with given samplerate) */ +static PyObject * +RealTime_frame2RealTime(PyObject *ignored, PyObject *args) +{ + long frame; + unsigned int sampleRate; + + if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ", + &frame,&sampleRate)) + return NULL; + + RealTimeObject *self; + self = PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = new RealTime::RealTime( + RealTime::frame2RealTime(frame,sampleRate)); + + return (PyObject *) self; +} + +/* Creating PyParameterDescriptor Objects */ + +/* New ParameterDescriptor object +static PyObject * +ParameterDescriptor_new(PyObject *ignored, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, ":ParameterDescriptor")) { + PyErr_SetString(PyExc_TypeError, + "Error: ParameterDescriptor initialised with arguments."); + return NULL; + } + + ParameterDescriptorObject *self = + PyObject_New(ParameterDescriptorObject, &ParameterDescriptor_Type); + if (self == NULL) return NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) return NULL; + return (PyObject *) self; +} +*/ + +/* Creating PyOutputDescriptor Objects */ + +/* New OutputDescriptor object +static PyObject * +OutputDescriptor_new(PyObject *ignored, PyObject *args) +{ + + if (!PyArg_ParseTuple(args, ":OutputDescriptor")) { + PyErr_SetString(PyExc_TypeError, + "Error: OutputDescriptor initialised with arguments."); + return NULL; + } + + OutputDescriptorObject *self = + PyObject_New(OutputDescriptorObject, &OutputDescriptor_Type); + if (self == NULL) return NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) return NULL; + return (PyObject *) self; +} +*/ + +/* Creating PyOutputList Objects */ + +/* New OutputList object */ +static PyObject * +OutputList_new(PyObject *ignored, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":OutputList")) { + PyErr_SetString(PyExc_TypeError, + "Error: OutputList initialised with arguments."); + return NULL; + } + + return (PyObject *) PyList_New(0); +} + + +/* Creating PyParameterList Objects */ + +/* New ParameterList object */ +static PyObject * +ParameterList_new(PyObject *ignored, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":ParameterList")) { + PyErr_SetString(PyExc_TypeError, + "Error: ParameterList initialised with arguments."); + return NULL; + } + return (PyObject *) PyList_New(0); +} + + +/* Creating PyFeatureList Objects */ + +/* New FeatureList object +static PyObject * +FeatureList_new(PyObject *ignored, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":FeatureList")) { + PyErr_SetString(PyExc_TypeError, + "Error: FeatureList initialised with arguments."); + return NULL; + } + return (PyObject *) PyList_New(0); +} +*/ + +/* Creating PyFeatureSet Objects */ + +/* New FeatureSet object +static PyObject * +FeatureSet_new(PyObject *ignored, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":FeatureSet")) { + PyErr_SetString(PyExc_TypeError, + "Error: FeatureSet initialised with arguments."); + return NULL; + } + return (PyObject *) PyDict_New(); +} +*/ + + +/* Declare the methods exposed by the vampy module */ + + +PyMethodDef VampyMethods[] = { +/*NOTE: This is conventionally static, but limiting the scope + here will cause seg fault if the declared functions are + called back from a Python function wrapped in a C++ class.*/ + {"five", get_five, METH_VARARGS, "Return a number."}, + + {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS, + PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")}, + + /*{"RealTime", RealTime_new, METH_VARARGS, + PyDoc_STR("RealTime() -> returns new RealTime object")},*/ + + /*{"Feature", Feature_new, METH_VARARGS, + PyDoc_STR("Feature() -> returns new Feature object")},*/ + + /*{"ParameterDescriptor", ParameterDescriptor_new, METH_VARARGS, + PyDoc_STR("ParameterDescriptor() -> returns new ParameterDescriptor object")}, + + {"OutputDescriptor", OutputDescriptor_new, METH_VARARGS, + PyDoc_STR("OutputDescriptor() -> returns new OutputDescriptor object")}, + + {"FeatureList", FeatureList_new, METH_VARARGS, + PyDoc_STR("FeatureList() -> returns new FeatureList object")},*/ + + {"OutputList", OutputList_new, METH_VARARGS, + PyDoc_STR("OutputList() -> returns new OutputList object")}, + + {"ParameterList", ParameterList_new, METH_VARARGS, + PyDoc_STR("ParameterList() -> returns new ParameterList object")}, + + {NULL, NULL, 0, NULL} +}; + +/* Module Documentation */ +// PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers."); + +PyMODINIT_FUNC +initvampy(void) +{ + PyObject* module; + + // if (PyType_Ready(&Feature_Type) < 0) return; + /// Why do we get a segfault if this is initialised here? + /*PyType_Ready adds these object to the GC. + This is OK for an extension module, but it is a mistake here, + because the reference count will be decremented in the Vamp + wrapper plugin outside the interpreter. + When the GC tries to visit a deallocated object, it will throw up.*/ + + RealTime_Type.ob_type = &PyType_Type; + Feature_Type.ob_type = &PyType_Type; + OutputDescriptor_Type.ob_type = &PyType_Type; + ParameterDescriptor_Type.ob_type = &PyType_Type; + initFeatureSetType(); /// this is derived from the builtin dict + + PyImport_AddModule("vampy"); + module = Py_InitModule("vampy", VampyMethods); + if (!module) return; + + Py_INCREF(&RealTime_Type); + PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type); + // Py_INCREF(&RealTime_Type); + + Py_INCREF((PyObject*)&Feature_Type); + PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type); + // Py_INCREF((PyObject*)&Feature_Type); + + Py_INCREF((PyObject*)&FeatureSet_Type); + PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type); + // Py_INCREF((PyObject*)&FeatureSet_Type); + + Py_INCREF((PyObject*)&OutputDescriptor_Type); + PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type); + + Py_INCREF((PyObject*)&ParameterDescriptor_Type); + PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type); + + cerr << "Vampy: extension module initialised." << endl; +} + +/* +NOTE: Why do we need to clean up the module? + +The module exposed by Vampy to the embedded interpreter +contains callback functions. These functions are accessed +via function pointers stored in the extension module dictionary. + +Unfortunately, when the Vampy shared library is unloaded and +reloaded again during a host session, these addresses might +change. Therefore, we reinitialise the module dict before +each use. However, this will cause garbage collection errors +or segmentation faults, when elements of the dict of the +previous session are attempted to free. Therefore, we hold +a global reference count to all initialised Vampy plugins, +and when this reaches zero, we clean up the module dict. + +This is an attempt to catch the moment when the shared lib +is finally unloaded and the references are still point to valid +memory addresses. + +Why doesn't the GC clean this up correctly? + +In a normal Python session the GC would deallocate the module +dict at the end. In embedded python, although the GC appears +to be called when the shared lib is unloaded, the interpreter +is reused. Since there is no C/API call to unload modules, +and at the time of unloading vampy the wrapped function pointers +are still valid, the GC doesn't collect them, nor are they freed +by the interpreter. When vampy is reloaded however, the module +dict will contain invalid addresses. The above procedure solves +this problem. + + +*/ + +void cleanModule(void) +{ + PyObject *m = PyImport_AddModule("vampy"); + if (!m) cerr << "Destr: PyImport_AddModule returned NULL!" << endl; + else { + // cerr << "Destr: Add module found existing." << endl; + PyObject *dict = PyModule_GetDict(m); + Py_ssize_t ln = PyDict_Size(dict); + cerr << "Destr: Size of module dict = " << (int) ln << endl; + /// Clean the module dictionary. + PyDict_Clear(dict); + ln = PyDict_Size(dict); + cerr << "Destr: Cleaned size of module dict = " << (int) ln << endl; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyExtensionModule.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,19 @@ +#ifndef _PYEXTENSIONMODULE_H_ +#define _PYEXTENSIONMODULE_H_ + +#include <Python.h> +#include "PyRealTime.h" +#include "PyFeature.h" +#include "PyFeatureSet.h" +#include "PyParameterDescriptor.h" +#include "PyOutputDescriptor.h" + +#define PyDescriptor_Check(v) ((v)->ob_type == &Feature_Type) || ((v)->ob_type == &OutputDescriptor_Type) || ((v)->ob_type == &ParameterDescriptor_Type) + +#ifndef PyMODINIT_FUNC +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC initvampy(); +void cleanModule(); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyFeature.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,189 @@ +#include <Python.h> +#include "PyExtensionModule.h" +#include "PyFeature.h" +#include "vamp-sdk/Plugin.h" +#include <string> +/*#include "PyTypeInterface.h"*/ + +using namespace std; +using namespace Vamp; +using Vamp::Plugin; + +/* Feature Object's Methods */ +//Feature objects have no callable methods + +/* PyFeature methods implementing protocols */ +// these functions are called by the interpreter automatically + + +/* Function to set basic attributes +static int +Feature_setattr(FeatureObject *self, char *name, PyObject *value) +{ + std::string key = std::string(name); + if (self->ti.SetValue(*(self->feature),key,value)) return 0; + else return -1; +}*/ + +/* Function to get basic attributes +static PyObject * +Feature_getattr(FeatureObject *self, char *name) +{ + std::string key = std::string(name); + PyObject* pyValue; + if (self->ti.GetValue(*(self->feature),key,pyValue)) + return pyValue; + else return NULL; +}*/ + +/* Set attributes */ +static int +Feature_setattr(FeatureObject *self, char *name, PyObject *v) +{ + if (v == NULL) { + int rv = PyDict_DelItemString(self->dict, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError,"non-existing Feature attribute"); + return rv; + } + else + return PyDict_SetItemString(self->dict, name, v); +} + + +/* Get attributes */ +static PyObject * +Feature_getattr(FeatureObject *self, char *name) +{ + if (self->dict != NULL) { + PyObject *v = PyDict_GetItemString(self->dict, name); + if (v != NULL) + { + Py_INCREF(v); + return v; + } + } + return NULL; +} + +/* New Feature object */ +static PyObject * +Feature_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + /// TODO support kwargs e.g. Feature(values = val, timestamp = ts) + cerr << "FeatureObject new method called" << endl; + if (!PyArg_ParseTuple(args, ":Feature")) { + PyErr_SetString(PyExc_TypeError, + "Error: Feature initialised with arguments."); + return NULL; + } + FeatureObject *self = (FeatureObject*)type->tp_alloc(type, 0); + // FeatureObject *self = PyObject_New(FeatureObject, &Feature_Type); + if (self == NULL) return NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) return NULL; + return (PyObject *) self; +} + +/* DESTRUCTOR: delete type object */ +static void +FeatureObject_dealloc(FeatureObject *self) +{ + Py_XDECREF(self->dict); + // delete self->feature; //delete the C object + // PyObject_Del(self); //delete the Python object + self->ob_type->tp_free((PyObject*)self); + cerr << "Feature object deallocated." << endl; +} + + +static int +Feature_init(FeatureObject *self, PyObject *args, PyObject *kwds) +{ + cerr << "FeatureObject Init called" << endl; + return 0; +} + +PyObject* +Feature_test(PyObject *self, PyObject *args, PyObject *kwds) +{ + cerr << "FeatureObject TEST called" << endl; + return self; +} + + +/* String representation */ +static PyObject * +Feature_repr(PyObject *self) +{ + // if (PyFeature_CheckExact(self)) {} + // PyObject* intdict = self + return Py_BuildValue("s", + "not yet implemented"); + // ((RealTimeObject*)self)->rt->toString().c_str()); +} + +#define Feature_alloc PyType_GenericAlloc +#define Feature_free PyObject_Del + + +/* FEATURE TYPE OBJECT */ + +PyTypeObject Feature_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampy.Feature", /*tp_name*/ + sizeof(FeatureObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)FeatureObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Feature_getattr, /*tp_getattr*/ + (setattrfunc)Feature_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + Feature_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + Feature_test, /*tp_call*/ // call on an instance + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ //TypeObject Methods + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0,//(initproc)Feature_init, /*tp_init*/ + Feature_alloc, /*tp_alloc*/ + Feature_new, /*tp_new*/ + Feature_free, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* PyRealTime C++ API */ + +/*Feature* from PyFeature +const Vamp::Plugin::Feature* +PyFeature_AsFeature (PyObject *self) { + + FeatureObject *s = (FeatureObject*) self; + + if (!PyFeature_Check(s)) { + PyErr_SetString(PyExc_TypeError, "Feature Object Expected."); + cerr << "in call PyFeature_AsPointer(): Feature Object Expected. " << endl; + return NULL; } + return s->feature; +};*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyFeature.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,36 @@ +#ifndef _PYFEATURE_H_ +#define _PYFEATURE_H_ + +#include "vamp-sdk/Plugin.h" +// #include "PyTypeInterface.h" + +typedef struct { + PyObject_HEAD + PyObject *dict; /* Attributes dictionary */ + // Vamp::Plugin::Feature *feature; + /// pointer to type interface required + // PyTypeInterface ti; +} FeatureObject; + +PyAPI_DATA(PyTypeObject) Feature_Type; + +#define PyFeature_CheckExact(v) ((v)->ob_type == &Feature_Type) +#define PyFeature_Check(v) PyObject_TypeCheck(v, &Feature_Type) +///fast macro version as per API convention +#define PyFeature_AS_DICT(v) ((const FeatureObject* const) (v))->dict + + +/* PyFeature C++ API */ + +/// Not required: we will never have to pass a feature back from the wrapper +// PyAPI_FUNC(PyObject *) +// PyFeature_FromFeature(Vamp::RealTime&); + +// PyAPI_FUNC(const Vamp::Plugin::Feature*) +// PyFeature_AsFeature (PyObject *self); + +///fast macro version as per API convention +// #define PyFeature_AS_FEATURE(v) ((const FeatureObject* const) (v))->feature + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyFeatureSet.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,99 @@ +#include <Python.h> +#include "PyFeatureSet.h" +#include "vamp-sdk/Plugin.h" + +using namespace std; + +static int +FeatureSet_init(FeatureSetObject *self, PyObject *args, PyObject *kwds) +{ + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + return -1; + self->state = 0; + cerr << "FeatureSet initialised" << endl; + return 0; +} + +static int +FeatureSetObject_ass_sub(FeatureSetObject *mp, PyObject *v, PyObject *w) +{ + // cerr << "called FeatureSetObject_ass_sub" << endl; + if (!PyInt_CheckExact(v)) { + /// TODO: Set ValueError here. + cerr << "Output index must be positive integer" << endl; + return 0; + } + if (w == NULL) + return PyDict_DelItem((PyObject *)mp, v); + else + return PyDict_SetItem((PyObject *)mp, v, w); +} + +#define FeatureSet_alloc PyType_GenericAlloc +#define FeatureSet_free PyObject_Del +//#define FeatureSet_as_mapping PyDict_Type.tp_as_mapping + +static PyMappingMethods FeatureSet_as_mapping = *(PyDict_Type.tp_as_mapping); + +PyTypeObject FeatureSet_Type = PyDict_Type; +// PyTypeObject FeatureSet_Type = { +// PyObject_HEAD_INIT(NULL) +// 0, /*ob_size*/ +// "vampy.FeatureSet", /*tp_name*/ +// sizeof(FeatureSetObject), /*tp_basicsize*/ +// 0, /*tp_itemsize*/ +// (destructor)FeatureSetObject_dealloc, /*tp_dealloc*/ +// 0,//PyDict_Type.tp_print, /*tp_print*/ +// 0,//PyDict_Type.tp_getattr, /*tp_getattr*/ +// 0,//PyDict_Type.tp_setattr, /*tp_setattr*/ +// 0, /*tp_compare*/ +// 0,//PyDict_Type.tp_repr, /*tp_repr*/ +// 0, /*tp_as_number*/ +// 0, /*tp_as_sequence*/ +// FeatureSet_as_mapping, /*tp_as_mapping*/ +// 0, /*tp_hash*/ +// 0,//Feature_test, /*tp_call*/ // call on an instance +// 0, /*tp_str*/ +// PyDict_Type.tp_getattro,/*tp_getattro*/ +// 0,//PyDict_Type.tp_setattro,/*tp_setattro*/ +// 0, /*tp_as_buffer*/ +// Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ +// 0, /*tp_doc*/ +// PyDict_Type.tp_traverse, /*tp_traverse*/ +// PyDict_Type.tp_clear, /*tp_clear*/ +// 0, /*tp_richcompare*/ +// 0, /*tp_weaklistoffset*/ +// 0, /*tp_iter*/ +// 0, /*tp_iternext*/ +// PyDict_Type.tp_methods, /*tp_methods*/ //TypeObject Methods +// PyDict_Type.tp_members, /*tp_members*/ +// PyDict_Type.tp_getset, /*tp_getset*/ +// 0, /*tp_base*/ +// PyDict_Type.tp_dict, /*tp_dict*/ +// 0, /*tp_descr_get*/ +// 0, /*tp_descr_set*/ +// PyDict_Type.tp_dictoffset, /*tp_dictoffset*/ +// (initproc)FeatureSet_init, /*tp_init*/ +// FeatureSet_alloc, /*tp_alloc*/ +// FeatureSet_new, /*tp_new*/ +// FeatureSet_free, /*tp_free*/ +// 0, /*tp_is_gc*/ +// }; + + +void +initFeatureSetType(void) +{ + /*This type is derived from PyDict. We just override some slots here.*/ + /*The typical use case is index based assignment as opposed to object memeber access.*/ + FeatureSet_Type.ob_type = &PyType_Type; + FeatureSet_Type.tp_base = &PyDict_Type; + FeatureSet_Type.tp_bases = PyTuple_Pack(1, FeatureSet_Type.tp_base); + FeatureSet_Type.tp_name = "vampy.FeatureSet"; + // FeatureSet_Type.tp_new = FeatureSet_new; + FeatureSet_Type.tp_init = (initproc)FeatureSet_init; + FeatureSet_Type.tp_basicsize = sizeof(FeatureSetObject); + FeatureSet_as_mapping.mp_ass_subscript = (objobjargproc)FeatureSetObject_ass_sub; + FeatureSet_Type.tp_as_mapping = &FeatureSet_as_mapping; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyFeatureSet.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,22 @@ +#ifndef _PYFEATURESET_H_ +#define _PYFEATURESET_H_ + +#include <Python.h> + +typedef struct { + PyDictObject dict; + int state; +} FeatureSetObject; + +PyAPI_DATA(PyTypeObject) FeatureSet_Type; + +#define PyFeatureSet_CheckExact(v) ((v)->ob_type == &FeatureSet_Type) +#define PyFeatureSet_Check(v) PyObject_TypeCheck(v, &FeatureSet_Type) +// #define PyFeatureSet_CheckExact(v) ((v)->ob_type == &PyDict_Type) +// #define PyFeatureSet_Check(v) PyObject_TypeCheck(v, &PyDict_Type) + +// #define PyFeature_AS_DICT(v) ((const FeatureObject* const) (v))->dict + +void initFeatureSetType(void); + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyOutputDescriptor.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,140 @@ +#include <Python.h> +#include "PyOutputDescriptor.h" +#include "vamp-sdk/Plugin.h" +#include <string> +#include "PyTypeInterface.h" + +using namespace std; +using namespace Vamp; +using Vamp::Plugin; + +/* OutputDescriptor Object's Methods */ +//Feature objects have no callable methods + +/* PyOutputDescriptor methods implementing protocols */ +// these functions are called by the interpreter automatically + +/* New OutputDescriptor object */ +static PyObject * +OutputDescriptor_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + + if (!PyArg_ParseTuple(args, ":OutputDescriptor")) { + PyErr_SetString(PyExc_TypeError, + "Error: OutputDescriptor initialised with arguments."); + return NULL; + } + + OutputDescriptorObject *self = + (OutputDescriptorObject*)type->tp_alloc(type, 0); + + if (self == NULL) return NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) return NULL; + return (PyObject *) self; +} + + +/* DESTRUCTOR: delete type object */ +static void +OutputDescriptorObject_dealloc(OutputDescriptorObject *self) +{ + Py_XDECREF(self->dict); + PyObject_Del(self); +} + + +/* Set attributes */ +static int +OutputDescriptor_setattr(OutputDescriptorObject *self, char *name, PyObject *v) +{ + if (v == NULL) { + int rv = PyDict_DelItemString(self->dict, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError,"non-existing OutputDescriptor attribute"); + return rv; + } + else + return PyDict_SetItemString(self->dict, name, v); +} + + +/* Get attributes */ +static PyObject * +OutputDescriptor_getattr(OutputDescriptorObject *self, char *name) +{ + if (self->dict != NULL) { + PyObject *v = PyDict_GetItemString(self->dict, name); + if (v != NULL) + { + Py_INCREF(v); + return v; + } + } + return NULL; +} + + +/* String representation */ +static PyObject * +OutputDescriptor_repr(PyObject *self) +{ + // if (PyFeature_CheckExact(self)) {} + // PyObject* intdict = self + return Py_BuildValue("s", + "not yet implemented"); + // ((RealTimeObject*)self)->rt->toString().c_str()); +} + +#define OutputDescriptor_alloc PyType_GenericAlloc +#define OutputDescriptor_free PyObject_Del + + +/* REAL-TIME TYPE OBJECT */ + +PyTypeObject OutputDescriptor_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampy.OutputDescriptor",/*tp_name*/ + sizeof(OutputDescriptorObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)OutputDescriptorObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)OutputDescriptor_getattr, /*tp_getattr*/ + (setattrfunc)OutputDescriptor_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + OutputDescriptor_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ //TypeObject Methods + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + OutputDescriptor_alloc, /*tp_alloc*/ + OutputDescriptor_new, /*tp_new*/ + OutputDescriptor_free, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* PyOutputDescriptor C++ API */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyOutputDescriptor.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,22 @@ +#ifndef _PYOUTPUTDESCRIPTOR_H_ +#define _PYOUTPUTDESCRIPTOR_H_ + +#include "vamp-sdk/Plugin.h" + +typedef struct { + PyObject_HEAD + PyObject *dict; +} OutputDescriptorObject; + +PyAPI_DATA(PyTypeObject) OutputDescriptor_Type; + +#define PyOutputDescriptor_CheckExact(v) ((v)->ob_type == &OutputDescriptor_Type) +#define PyOutputDescriptor_Check(v) PyObject_TypeCheck(v, &OutputDescriptor_Type) + +/* PyOutputDescriptor C++ API */ + + +///fast macro version as per API convention +#define PyOutputDescriptor_AS_DICT(v) ((const OutputDescriptorObject* const) (v))->dict + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyParameterDescriptor.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,137 @@ +#include <Python.h> +#include "PyParameterDescriptor.h" +#include "vamp-sdk/Plugin.h" +#include <string> +#include "PyTypeInterface.h" + +using namespace std; +using namespace Vamp; +using Vamp::Plugin; + +/* ParameterDescriptor Object's Methods */ +//Feature objects have no callable methods + +/* PyParameterDescriptor methods implementing protocols */ +// these functions are called by the interpreter automatically + +/* New ParameterDescriptor object */ +static PyObject * +ParameterDescriptor_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + + if (!PyArg_ParseTuple(args, ":ParameterDescriptor")) { + PyErr_SetString(PyExc_TypeError, + "Error: ParameterDescriptor initialised with arguments."); + return NULL; + } + + ParameterDescriptorObject *self = + (ParameterDescriptorObject*)type->tp_alloc(type, 0); + + if (self == NULL) return NULL; + self->dict = PyDict_New(); + if (self->dict == NULL) return NULL; + return (PyObject *) self; +} + + +/* DESTRUCTOR: delete type object */ +static void +ParameterDescriptorObject_dealloc(ParameterDescriptorObject *self) +{ + Py_XDECREF(self->dict); + PyObject_Del(self); +} + + +/* Set attributes */ +static int +ParameterDescriptor_setattr(ParameterDescriptorObject *self, char *name, PyObject *v) +{ + if (v == NULL) { + int rv = PyDict_DelItemString(self->dict, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError,"non-existing ParameterDescriptor attribute"); + return rv; + } + else + return PyDict_SetItemString(self->dict, name, v); +} + + +/* Get attributes */ +static PyObject * +ParameterDescriptor_getattr(ParameterDescriptorObject *self, char *name) +{ + if (self->dict != NULL) { + PyObject *v = PyDict_GetItemString(self->dict, name); + if (v != NULL) + { + Py_INCREF(v); + return v; + } + } + return NULL; +} + + +/* String representation */ +static PyObject * +ParameterDescriptor_repr(PyObject *self) +{ + // if (PyFeature_CheckExact(self)) {} + // PyObject* intdict = self + return Py_BuildValue("s", + "not yet implemented"); + // ((RealTimeObject*)self)->rt->toString().c_str()); +} + +#define ParameterDescriptor_alloc PyType_GenericAlloc +#define ParameterDescriptor_free PyObject_Del + +PyTypeObject ParameterDescriptor_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampy.ParameterDescriptor",/*tp_name*/ + sizeof(ParameterDescriptorObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)ParameterDescriptorObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)ParameterDescriptor_getattr, /*tp_getattr*/ + (setattrfunc)ParameterDescriptor_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + ParameterDescriptor_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ //TypeObject Methods + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + ParameterDescriptor_alloc,/*tp_alloc*/ + ParameterDescriptor_new,/*tp_new*/ + ParameterDescriptor_free,/*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* PyParameterDescriptor C++ API */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyParameterDescriptor.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,22 @@ +#ifndef _PYPARAMETERDESCRIPTOR_H_ +#define _PYPARAMETERDESCRIPTOR_H_ + +#include "vamp-sdk/Plugin.h" + +typedef struct { + PyObject_HEAD + PyObject *dict; +} ParameterDescriptorObject; + +PyAPI_DATA(PyTypeObject) ParameterDescriptor_Type; + +#define PyParameterDescriptor_CheckExact(v) ((v)->ob_type == &ParameterDescriptor_Type) +#define PyParameterDescriptor_Check(v) PyObject_TypeCheck(v, &ParameterDescriptor_Type) + +/* PyParameterDescriptor C++ API */ + + +///fast macro version as per API convention +#define PyParameterDescriptor_AS_DICT(v) ((const ParameterDescriptorObject* const) (v))->dict + +#endif \ No newline at end of file
--- a/PyPlugin.cpp Tue Aug 25 08:49:22 2009 +0000 +++ b/PyPlugin.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -51,11 +51,16 @@ #include <Python.h> #include "PyPlugin.h" +#include "PyTypeInterface.h" +#include <stdlib.h> +#include "PyExtensionModule.h" +//#include "PyRealTime.h" + #ifdef _WIN32 -#define pathsep ('\\') +#define PATHSEP ('\\') #else -#define pathsep ('/') +#define PATHSEP ('/') #endif //#define _DEBUG @@ -66,30 +71,35 @@ using std::endl; using std::map; -// Maps to associate strings with enum values -static std::map<std::string, o::eOutDescriptors> outKeys; -static std::map<std::string, eSampleTypes> sampleKeys; -static std::map<std::string, eFeatureFields> ffKeys; -static std::map<std::string, p::eParmDescriptors> parmKeys; +Mutex PyPlugin::m_pythonInterpreterMutex; -Mutex PyPlugin::m_pythonInterpreterMutex; -static bool isMapInitialised = false; - -PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass) : +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_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))), m_processType(0), m_pyProcess(NULL), - m_inputDomain(TimeDomain) + 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; + + if (m_instcount == 0) initvampy(); + m_instcount++; + + // if (!PyImport_ImportModule("vampy")) + // cerr << "Could not import extension." << endl; + // Create an instance - MutexLocker locker(&m_pythonInterpreterMutex); Py_INCREF(m_pyClass); PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate); PyObject *args = PyTuple_Pack(1, pyInputSampleRate); @@ -100,24 +110,43 @@ 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; + 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); - + + //query the debug flag + m_debugFlag = getBooleanFlag("vampy_debug_messages",true); + if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl; + else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl; + + //query the quit on error flag + m_quitOnErrorFlag = getBooleanFlag("quit_on_type_error",false); + if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl; + + //query the type conversion mode flag + bool st_flag = getBooleanFlag("use_strict_type_conversion",false); + 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); - if (m_pyClass) Py_DECREF(m_pyClass); + //we increase the class refcount before creating an instance + if (m_pyClass) Py_DECREF(m_pyClass); if (m_pyProcess) Py_CLEAR(m_pyProcess); + if (m_instcount == 0) cleanModule(); #ifdef _DEBUG - cerr << "PyPlugin::PyPlugin:" << m_class - << " Instance deleted." << endl; + cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl; #endif } @@ -126,647 +155,159 @@ PyPlugin::getIdentifier() const { MutexLocker locker(&m_pythonInterpreterMutex); + string rString="vampy-xxx"; + if (!m_debugFlag) return genericMethodCall("getIdentifier",rString); - char method[]="getIdentifier"; - cerr << "[call] " << method << endl; - string rString="vampy-x"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - return rString; - } - cerr << "Warning: Plugin must return a unique identifier." << endl; + 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); - - char method[]="getName"; - cerr << "[call] " << method << endl; string rString="VamPy Plugin (Noname)"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; + return genericMethodCall("getName",rString); } string PyPlugin::getDescription() const { MutexLocker locker(&m_pythonInterpreterMutex); + string rString="Not given. (Hint: Implement getDescription method.)"; + return genericMethodCall("getDescription",rString); +} - char method[]="getDescription"; - cerr << "[call] " << method << endl; - string rString="Not given. (Hint: Implement getDescription method.)"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; -} string PyPlugin::getMaker() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMaker"; - cerr << "[call] " << method << endl; - string rString="Generic VamPy Plugin."; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - return rString; + string rString="VamPy Plugin."; + return genericMethodCall("getMaker",rString); } int PyPlugin::getPluginVersion() const { - //!!! implement - - return 2; + MutexLocker locker(&m_pythonInterpreterMutex); + size_t rValue=2; + return genericMethodCall("getPluginVersion",rValue); } string PyPlugin::getCopyright() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getCopyright"; - cerr << "[call] " << method << endl; - string rString="BSD License"; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - //Call the method - PyObject *pyString = - PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rString; - } - - - rString=PyString_AsString(pyString); - Py_CLEAR(pyString); - } - - return rString; + string rString="Licence information not available."; + return genericMethodCall("getCopyright",rString); } bool PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) { - //useful for debugging Python plugins - char method[]="initialise"; - cerr << "[call] " << method << endl; - - //placing Mutex before these calls causes deadlock + if (channels < getMinChannelCount() || channels > getMaxChannelCount()) return false; - + m_inputDomain = getInputDomain(); + //Note: placing Mutex before the calls above causes deadlock !! MutexLocker locker(&m_pythonInterpreterMutex); - initMaps(); - m_stepSize = stepSize; m_blockSize = blockSize; m_channels = channels; - //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); - } - - if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && - m_processType == 0) - { - m_processType = numpyProcess; - m_pyProcess = PyString_FromString(numpyMethod); - } + //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(); - if (!m_processType) - { - m_processType = not_implemented; - m_pyProcess = NULL; - cerr << "Warning: Python plugin [" << m_class << "::" << method - << "] No process implementation found. Plugin will do nothing." << endl; - } - - //Check if the method is implemented in Python else return false - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels); - PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize); - PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize); - //Call the method - PyObject *pyBool = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,NULL); - - Py_DECREF(pyMethod); - Py_DECREF(pyChannels); - Py_DECREF(pyStepSize); - Py_DECREF(pyBlockSize); - - //Check return value - if (PyErr_Occurred() || !PyBool_Check(pyBool)) { - PyErr_Print(); PyErr_Clear(); - Py_CLEAR(pyBool); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Bool return value." << endl; - return false; - } - - if (pyBool == Py_True) { - Py_CLEAR(pyBool); - return true; - } else { - Py_CLEAR(pyBool); - return false; - } - } - return false; + return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize); } void PyPlugin::reset() { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="reset"; - cerr << "[call] " << method << endl; - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - PyObject_CallMethod(m_pyInstance, method, NULL); - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - - } + genericMethodCall("reset"); } -PyPlugin::InputDomain PyPlugin::getInputDomain() const +PyPlugin::InputDomain +PyPlugin::getInputDomain() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getInputDomain"; - cerr << "[call] " << method << endl; - PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain - - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - - PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyString_Check(pyString)) { - Py_CLEAR(pyString); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - string domain = (string) PyString_AsString(pyString); - if (domain == "FrequencyDomain") rValue = FrequencyDomain; - Py_CLEAR(pyString); - } - return rValue; + // Note: Vamp enum type is mapped to Python string !! + // Is there a better way? (Enums are not native to Python) + string rValue = "TimeDomain"; + genericMethodCall("getInputDomain",rValue); + return (rValue == "FrequencyDomain")?FrequencyDomain:TimeDomain; } - -size_t PyPlugin::getPreferredBlockSize() const +size_t +PyPlugin::getPreferredBlockSize() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getPreferredBlockSize"; - cerr << "[call] " << method << endl; - size_t rValue=0; //not set by default - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Integer return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 0; + return genericMethodCall("getPreferredBlockSize",rValue); } -//size_t PyPlugin::getPreferredStepSize() const { return 0; } -size_t PyPlugin::getPreferredStepSize() const +size_t +PyPlugin::getPreferredStepSize() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getPreferredStepSize"; - cerr << "[call] " << method << endl; - size_t rValue=1024; //not set by default - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Integer return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 0; + return genericMethodCall("getPreferredStepSize",rValue); } -size_t PyPlugin::getMinChannelCount() const +size_t +PyPlugin::getMinChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMinChannelCount"; - cerr << "[call] " << method << endl; - size_t rValue=1; //default value - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; + size_t rValue = 1; + return genericMethodCall("getMinChannelCount",rValue); } -size_t PyPlugin::getMaxChannelCount() const +size_t +PyPlugin::getMaxChannelCount() const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getMaxChannelCount"; - cerr << "[call] " << method << endl; - size_t rValue=1; //default value - if ( PyObject_HasAttrString(m_pyInstance,method) ) { - PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); - - //Check return value - if (!PyInt_Check(pyInt)) { - Py_CLEAR(pyInt); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected String return value." << endl; - return rValue; - } - - rValue=(size_t)PyInt_AS_LONG(pyInt); - Py_CLEAR(pyInt); - } - return rValue; -} - + size_t rValue = 1; + return genericMethodCall("getMaxChannelCount",rValue); +} PyPlugin::OutputList PyPlugin::getOutputDescriptors() const { - MutexLocker locker(&m_pythonInterpreterMutex); - - //PyEval_AcquireThread(newThreadState); OutputList list; - OutputDescriptor od; - char method[]="getOutputDescriptors"; - cerr << "[call] " << method << endl; - - //Check if the method is implemented in Python - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; - - //Call the method: must return list object (new reference) - PyObject *pyList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (! PyList_Check(pyList) ) { - Py_CLEAR(pyList); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - return list; - } - - //These will all be borrowed references (no need to DECREF) - PyObject *pyDict, *pyKey, *pyValue; - - //Parse Output List - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - - //Get i-th Vamp output descriptor (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyList,i); - - //We only care about dictionaries holding output descriptors - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - initMaps(); - - //Python Sequence Iterator - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - switch (outKeys[PyString_AsString(pyKey)]) - { - case o::not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - break; - case o::identifier: - od.identifier = PyString_AsString(pyValue); - break; - case o::name: - od.name = PyString_AsString(pyValue); - break; - case o::description: - od.description = PyString_AsString(pyValue); - break; - case o::unit: - od.unit = PyString_AsString(pyValue); - break; - case o::hasFixedBinCount: - od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue); - break; - case o::binCount: - od.binCount = (size_t) PyInt_AS_LONG(pyValue); - break; - case o::binNames: - od.binNames = PyList_To_StringVector(pyValue); - break; - case o::hasKnownExtents: - od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue); - break; - case o::minValue: - od.minValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::maxValue: - od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::isQuantized: - od.isQuantized = (bool) PyInt_AS_LONG(pyValue); - break; - case o::quantizeStep: - od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case o::sampleType: - od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; - break; - case o::sampleRate: - od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); -// od.sampleRate = m_inputSampleRate / m_stepSize; - cerr << od.sampleRate << endl; - break; - case o::hasDuration: - od.hasDuration = (bool)PyInt_AS_LONG(pyValue); - break; - default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - } - } // while dict - list.push_back(od); - } // for list - Py_CLEAR(pyList); - return list; + return genericMethodCall("getOutputDescriptors",list); } PyPlugin::ParameterList PyPlugin::getParameterDescriptors() const { MutexLocker locker(&m_pythonInterpreterMutex); - ParameterList list; - ParameterDescriptor pd; - char method[]="getParameterDescriptors"; - cerr << "[call] " << method << endl; - + ///Note: This function is often called first by the host. if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;} - - //Check if the method is implemented in Python - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list; - - //Call the method: must return list object (new reference) - PyObject *pyList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (! PyList_Check(pyList) ) { - Py_CLEAR(pyList); - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - return list; - } - - - //These will all be borrowed references (no need to DECREF) - PyObject *pyDict, *pyKey, *pyValue; - - //Parse Output List - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { - - //Get i-th Vamp output descriptor (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyList,i); - - //We only care about dictionaries holding output descriptors - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - initMaps(); - - //Python Sequence Iterator - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - switch (parmKeys[PyString_AsString(pyKey)]) - { - case p::not_found : - cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - break; - case p::identifier: - pd.identifier = PyString_AsString(pyValue); - break; - case p::name: - pd.name = PyString_AsString(pyValue); - break; - case p::description: - pd.description = PyString_AsString(pyValue); - break; - case p::unit: - pd.unit = PyString_AsString(pyValue); - break; - case p::minValue: - pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::maxValue: - pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::defaultValue: - pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue); - break; - case p::isQuantized: - pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); - break; - case p::quantizeStep: - pd.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue); - break; - default : - cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; - } - } // while dict - list.push_back(pd); - } // for list - Py_CLEAR(pyList); - return list; + return genericMethodCall("getParameterDescriptors",list); } void PyPlugin::setParameter(std::string paramid, float newval) { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="setParameter"; - cerr << "[call] " << method << endl; - - //Check if the method is implemented in Python - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyParamid = PyString_FromString(paramid.c_str()); - PyObject *pyNewval = PyFloat_FromDouble((double)newval); - - //Call the method - PyObject *pyBool = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL); - - //This only happens if there is a syntax error or so - if (pyBool == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Error setting parameter: " << paramid << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } - - Py_DECREF(pyMethod); - Py_DECREF(pyParamid); - Py_DECREF(pyNewval); - } + genericMethodCallArgs<NoneType>("setParameter",paramid,newval); } float PyPlugin::getParameter(std::string paramid) const { MutexLocker locker(&m_pythonInterpreterMutex); - - char method[]="getParameter"; - cerr << "[call] " << method << endl; - float rValue = 0.0f; - - //Check if the method is implemented in Python - if (PyObject_HasAttrString(m_pyInstance,method)) { - - PyObject *pyMethod = PyString_FromString(method); - PyObject *pyParamid = PyString_FromString(paramid.c_str()); - - //Call the method - PyObject *pyFloat = - PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL); - - //Check return type - if (! PyFloat_Check(pyFloat) ) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected Float return type." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - Py_CLEAR(pyFloat); - return rValue; - } - - rValue = (float) PyFloat_AS_DOUBLE(pyFloat); - - Py_DECREF(pyMethod); - Py_DECREF(pyParamid); - Py_DECREF(pyFloat); - } - - return rValue; + return genericMethodCallArgs<float>("getParameter",paramid); } #ifdef _DEBUG @@ -774,8 +315,7 @@ #endif PyPlugin::FeatureSet -PyPlugin::process(const float *const *inputBuffers, - Vamp::RealTime timestamp) +PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp) { MutexLocker locker(&m_pythonInterpreterMutex); @@ -796,395 +336,172 @@ return FeatureSet(); } - string method=PyString_AsString(m_pyProcess); + // string method=PyString_AsString(m_pyProcess); PyObject *pyOutputList = NULL; - /*new numPy support*/ if (m_processType == numpyProcess) { - - //create a list of buffers - PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); - for (size_t i=0; i < m_channels; ++i) { - - //Expose memory using the Buffer Interface of C/API - //This will virtually pass a pointer which can be - //recasted in Python code as float or complex array - PyObject *pyBuffer = PyBuffer_FromMemory - ((void *) (float *) inputBuffers[i], - (Py_ssize_t) sizeof(float) * m_blockSize); - - PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); - } - - //pass RealTime as frameCount - PyObject *pyLongSample = PyLong_FromLong ( - Vamp::RealTime::realTime2Frame - (timestamp, (unsigned int) m_inputSampleRate)); - - //Call python process (returns new reference) - pyOutputList = PyObject_CallMethodObjArgs - (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); - - Py_DECREF(pyChannelList); - Py_DECREF(pyLongSample); - + pyOutputList = numpyProcessCall(inputBuffers,timestamp); } if (m_processType == legacyProcess) { - - //create a list of lists - PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); - for (size_t i=0; i < m_channels; ++i) { - - //Declare new list object - PyObject *pyFloat, *pyList; - pyList = PyList_New((Py_ssize_t) m_blockSize); - - //Pack samples into a Python List Object - //pyFloat types will always be new references, - //these will be discarded when the list is deallocated - for (size_t j = 0; j < m_blockSize; ++j) { - pyFloat=PyFloat_FromDouble( - (double) inputBuffers[i][j]); - PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); - } - PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); - } - - //pass RealTime as frameCount - PyObject *pyLongSample = PyLong_FromLong ( - Vamp::RealTime::realTime2Frame - (timestamp, (unsigned int) m_inputSampleRate)); - - //Call python process (returns new reference) - pyOutputList = PyObject_CallMethodObjArgs - (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); - - Py_DECREF(pyChannelList); - Py_DECREF(pyLongSample); - + pyOutputList = legacyProcessCall(inputBuffers,timestamp); } - //return nothing - //Py_CLEAR(pyOutputList); - //return FeatureSet(); - - //Check return type - if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { - if (pyOutputList == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Unexpected result." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } else { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - } - Py_CLEAR(pyOutputList); - return FeatureSet(); - } - - // Py_DECREF(pyList); - // This appears to be tracked by the cyclic garbage collector - // hence decrefing produces GC error -#ifdef _DEBUG - cerr << "Process Returned Features" << endl; -#endif - // These will ALL be borrowed references - PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; - - FeatureSet returnFeatures; - - //Parse Output List for each element (FeatureSet) - for (Py_ssize_t i = 0; - i < PyList_GET_SIZE(pyOutputList); ++i) { - //cerr << "output (FeatureSet): " << i << endl; - - //Get i-th FeatureList (Borrowed Reference) - pyFeatureList = PyList_GET_ITEM(pyOutputList,i); - - //Parse FeatureList for each element (Feature) - for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { - //cerr << "element (FeatureList): " << j << endl; - - //Get j-th Feature (Borrowed Reference) - pyDict = PyList_GET_ITEM(pyFeatureList,j); - - //We only care about dictionaries holding a Feature struct - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - bool emptyFeature = true; - Feature feature; - - //process::Python Sequence Iterator for dictionary - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - emptyFeature = false; - switch (ffKeys[PyString_AsString(pyKey)]) - { - case unknown: - cerr << "Unknown key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - break; - case hasTimestamp: - feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); - break; - case timeStamp: - feature.timestamp = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Timestamp: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.timestamp.toString() << endl; -#endif - break; - case hasDuration: - feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); - break; - case duration: - feature.duration = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Duration: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.duration.toString() << endl; -#endif - break; - case values: - feature.values = PyList_As_FloatVector(pyValue); - break; - case label: - feature.label = PyString_AsString(pyValue); - break; - default : - cerr << "Invalid key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - } // switch - - } // while - if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; - else returnFeatures[i].push_back(feature); - - }// for j = FeatureList - - }//for i = FeatureSet + FeatureSet rFeatureset; + rFeatureset = m_ti.PyValue_To_FeatureSet(pyOutputList); Py_CLEAR(pyOutputList); - return returnFeatures; + return rFeatureset; + } +PyObject* +PyPlugin::numpyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp) +{ + PyObject *pyOutputList = NULL; + + //create a list of buffers + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { + //Expose memory using the Buffer Interface of C/API + //This will virtually pass a pointer which can be + //recasted in Python code as float or complex array + PyObject *pyBuffer = PyBuffer_FromMemory + ((void *) (float *) inputBuffers[i], + (Py_ssize_t) sizeof(float) * m_blockSize); + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer); + } +/* + //(1) pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + */ + //(2) pass RealTime as PyRealTime + PyObject *pyRealTime = PyRealTime_FromRealTime(timestamp); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyRealTime,NULL); + + Py_DECREF(pyChannelList); + // Py_DECREF(pyLongSample); + Py_DECREF(pyRealTime); + return pyOutputList; +} + +PyObject* +PyPlugin::legacyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp) +{ + PyObject *pyOutputList = NULL; + + //create a list of lists + PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels); + for (size_t i=0; i < m_channels; ++i) { + //New list object + PyObject *pyFloat, *pyList; + pyList = PyList_New((Py_ssize_t) m_blockSize); + + //Pack samples into a Python List Object + //pyFloat types will always be new references, + //these will be discarded when the list is deallocated + for (size_t j = 0; j < m_blockSize; ++j) { + pyFloat=PyFloat_FromDouble( + (double) inputBuffers[i][j]); + PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat); + } + PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList); + } + + //pass RealTime as frameCount + PyObject *pyLongSample = PyLong_FromLong ( + Vamp::RealTime::realTime2Frame + (timestamp, (unsigned int) m_inputSampleRate)); + + //Call python process (returns new reference) + pyOutputList = PyObject_CallMethodObjArgs + (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL); + + Py_DECREF(pyChannelList); + Py_DECREF(pyLongSample); + return pyOutputList; +} PyPlugin::FeatureSet PyPlugin::getRemainingFeatures() { MutexLocker locker(&m_pythonInterpreterMutex); - - static char method[]="getRemainingFeatures"; - cerr << "[call] " << method << endl; - - //check if the method is implemented - if ( ! PyObject_HasAttrString(m_pyInstance,method) ) { - return FeatureSet(); - } - - PyObject *pyMethod = PyString_FromString(method); - - PyObject *pyOutputList = - PyObject_CallMethod(m_pyInstance,method, NULL); - - //Check return type - if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { - if (pyOutputList == NULL) { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Unexpected result." << endl; - if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } - } else { - cerr << "ERROR: In Python plugin [" << m_class << "::" << method - << "] Expected List return type." << endl; - } - Py_CLEAR(pyMethod); - Py_CLEAR(pyOutputList); - return FeatureSet(); - } - Py_DECREF(pyMethod); - - PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; - FeatureSet returnFeatures; - - //iterate through list of outputs - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { - - pyFeatureList = PyList_GET_ITEM(pyOutputList,i); - - //iterate list of Features - for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { -#ifdef _DEBUG - cerr << "feature: " << j << endl; -#endif - pyDict = PyList_GET_ITEM(pyFeatureList,j); - - if ( !PyDict_Check(pyDict) ) continue; - - Py_ssize_t pyPos = NULL; - bool emptyFeature = true; - Feature feature; - - while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) - { - emptyFeature = false; - switch (ffKeys[PyString_AsString(pyKey)]) - { - case unknown : - cerr << "Unknown key in Vamp FeatureSet: " - << PyString_AsString(pyKey) << endl; - break; - case hasTimestamp: - feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); - break; - case timeStamp: - feature.timestamp = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Timestamp: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.timestamp.toString() << endl; -#endif - break; - case hasDuration: - feature.hasDuration = (bool) PyInt_AS_LONG(pyValue); - break; - case duration: - feature.duration = - Vamp::RealTime::frame2RealTime( - PyLong_AsLong(pyValue), - (unsigned int) m_inputSampleRate ); -#ifdef _DEBUG - cerr << "Duration: " - << (long)PyLong_AsLong(pyValue) << ", ->" - << feature.duration.toString() << endl; -#endif - break; - case values: - feature.values = PyList_As_FloatVector(pyValue); - break; - case label: - feature.label = PyString_AsString(pyValue); - break; - } // switch - } // while - if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; - else returnFeatures[i].push_back(feature); - }// for j - }//for i - Py_CLEAR(pyOutputList); - return returnFeatures; + FeatureSet rValue; + return genericMethodCall("getRemainingFeatures",rValue); } + bool -PyPlugin::initMaps() const +PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const { - - if (isMapInitialised) return true; - - outKeys["identifier"] = o::identifier; - outKeys["name"] = o::name; - outKeys["description"] = o::description; - outKeys["unit"] = o::unit; - outKeys["hasFixedBinCount"] = o::hasFixedBinCount; - outKeys["binCount"] = o::binCount; - outKeys["binNames"] = o::binNames; - outKeys["hasKnownExtents"] = o::hasKnownExtents; - outKeys["minValue"] = o::minValue; - outKeys["maxValue"] = o::maxValue; - outKeys["isQuantized"] = o::isQuantized; - outKeys["quantizeStep"] = o::quantizeStep; - outKeys["sampleType"] = o::sampleType; - outKeys["sampleRate"] = o::sampleRate; - outKeys["hasDuration"] = o::hasDuration; - - sampleKeys["OneSamplePerStep"] = OneSamplePerStep; - sampleKeys["FixedSampleRate"] = FixedSampleRate; - sampleKeys["VariableSampleRate"] = VariableSampleRate; - - ffKeys["hasTimestamp"] = hasTimestamp; - ffKeys["timeStamp"] = timeStamp; - ffKeys["hasDuration"] = hasDuration; - ffKeys["duration"] = duration; - ffKeys["values"] = values; - ffKeys["label"] = label; - - parmKeys["identifier"] = p::identifier; - parmKeys["name"] = p::name; - parmKeys["description"] = p::description; - parmKeys["unit"] = p::unit; - parmKeys["minValue"] = p::minValue; - parmKeys["maxValue"] = p::maxValue; - parmKeys["defaultValue"] = p::defaultValue; - parmKeys["isQuantized"] = p::isQuantized; - parmKeys["quantizeStep"] = p::quantizeStep; - - isMapInitialised = true; - return true; + 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) { + cerr << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + 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"; -//missing API helper: convert Python list to C++ vector of strings -//TODO: these could be templates if we need more of this kind -std::vector<std::string> -PyPlugin::PyList_To_StringVector (PyObject *inputList) const { + if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && + m_processType == 0) + { + m_processType = legacyProcess; + m_pyProcess = PyString_FromString(legacyMethod); + } + + if (PyObject_HasAttrString(m_pyInstance,numpyMethod) && + m_processType == 0) + { + m_processType = numpyProcess; + m_pyProcess = PyString_FromString(numpyMethod); + } + + // 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 backwards compatibility. + if (getBooleanFlag("use_numpy_interface",false)) + m_processType = numpyProcess; + if (getBooleanFlag("use_legacy_interface",false)) + m_processType = legacyProcess; + if (m_debugFlag && m_processType) + cerr << "Process type: " << ((m_processType==numpyProcess)?"numpy process":"legacy process") << endl; - std::vector<std::string> Output; - std::string ListElement; - PyObject *pyString = NULL; - - if (!PyList_Check(inputList)) return Output; - - for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { - //Get next list item (Borrowed Reference) - pyString = PyList_GET_ITEM(inputList,i); - ListElement = (string) PyString_AsString(PyObject_Str(pyString)); - Output.push_back(ListElement); + if (!m_processType) + { + m_processType = not_implemented; + m_pyProcess = NULL; + char method[]="initialise::setProcessType"; + cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl; } - return Output; } -//missing API helper: convert Python list to C++ vector of floats -std::vector<float> -PyPlugin::PyList_As_FloatVector (PyObject *inputList) const { - - std::vector<float> Output; - float ListElement; - PyObject *pyFloat = NULL; - - if (!PyList_Check(inputList)) return Output; - - for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { - //Get next list item (Borrowed Reference) - pyFloat = PyList_GET_ITEM(inputList,k); - ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); -#ifdef _DEBUG - cerr << "value: " << ListElement << endl; -#endif - Output.push_back(ListElement); - } - - return Output; -} - -/* TODO: find out why this produces error, also - do sg more clever about handling RealTime -Vamp::RealTime -PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) { -Vamp::RealTime result = -Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate); -return result; -} -*/ -
--- a/PyPlugin.h Tue Aug 25 08:49:22 2009 +0000 +++ b/PyPlugin.h Sun Sep 20 17:31:20 2009 +0000 @@ -33,71 +33,26 @@ authorization. */ - /** - * This plugin abstracts appropriate Python Scripts as a Vamp plugin. - */ - #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 DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented." << endl << "Returning default value: " << rValue +#define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") + #include "vamp-sdk/Plugin.h" #include <Python.h> +// #include <typeinfo> +// #include <stdarg.h> +#include "PyTypeInterface.h" #include "Mutex.h" -//fields in OutputDescriptor -namespace o { -enum eOutDescriptors { - not_found, - identifier, - name, - description, - unit, - hasFixedBinCount, - binCount, - binNames, - hasKnownExtents, - minValue, - maxValue, - isQuantized, - quantizeStep, - sampleType, - sampleRate, - hasDuration, - endNode - }; -} - -namespace p { -enum eParmDescriptors { - not_found, - identifier, - name, - description, - unit, - minValue, - maxValue, - defaultValue, - isQuantized, - quantizeStep - }; -} - -enum eSampleTypes { - OneSamplePerStep, - FixedSampleRate, - VariableSampleRate - }; - -enum eFeatureFields { - unknown, - hasTimestamp, - timeStamp, - hasDuration, - duration, - values, - label - }; +using std::string; +using std::cerr; +using std::endl; enum eProcessType { not_implemented, @@ -108,7 +63,7 @@ class PyPlugin : public Vamp::Plugin { public: - PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass); + PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount); virtual ~PyPlugin(); bool initialise(size_t channels, size_t stepSize, size_t blockSize); @@ -136,10 +91,12 @@ 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; @@ -149,12 +106,204 @@ int m_processType; PyObject *m_pyProcess; InputDomain m_inputDomain; + PyTypeInterface m_ti; + bool m_quitOnErrorFlag; + bool m_debugFlag; + + void setProcessType(); - bool initMaps() const; - std::vector<std::string> PyList_To_StringVector (PyObject *inputList) const; - std::vector<float> PyList_As_FloatVector (PyObject *inputList) const; + PyObject* numpyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp); + PyObject* legacyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp); + + bool getBooleanFlag(char flagName[],bool) const; +/* + Flags may be used to control the behaviour of the interface. + Flags can be set in any Vampy plugin's __init__() function. + Their scope is limited to an instance. + Default values for all flags are False. + Python Example: + def __init__(self,inputSampleRate): + self.use_strict_type_conversion = True + self.vampy_debug_messages = True + self.use_realtime_timestamp = False + self.use_numpy_interface = False + self.quit_on_type_error = False + +*/ + + void genericMethodCall(char *method) const + { + if (m_debugFlag) cerr << DEBUG_NAME << endl; + if ( PyObject_HasAttrString(m_pyInstance,method) ) + { + PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); + if (!pyValue) { + cerr << PLUGIN_ERROR << "Failed to call method." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + } + } + } + + template<typename RET> + RET &genericMethodCall(char *method, RET &rValue) const + { + if (m_debugFlag) cerr << DEBUG_NAME << endl; + if ( PyObject_HasAttrString(m_pyInstance,method) ) + { + PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); + if (pyValue) { + m_ti.PyValue_To_rValue(pyValue,rValue); + if (!m_ti.error) { + Py_DECREF(pyValue); + return rValue; + } else { + cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + if (m_quitOnErrorFlag) exit(EXIT_FAILURE); + return rValue; + } + } else { + cerr << PLUGIN_ERROR << "Failed to call method." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + return rValue; + } + } + // TODO: this fails to generalise because the << operator + // doesn't accept all types. + // if (m_debugFlag) cerr << DEAFULT_RETURN << endl; + return rValue; + } - static Mutex m_pythonInterpreterMutex; + template<typename RET,typename A1> + RET genericMethodCallArgs(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 << DEAFULT_RETURN << endl; + return rValue; + } + + // These functions always return valid PyObjects + PyObject *pyMethod = m_ti.PyValue_From_CValue(method); + PyObject* pyTuple = PyTuple_New(3); + if (!pyTuple) return rValue; + + PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); + + PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,NULL); + if (!pyValue) + { + cerr << PLUGIN_ERROR << "Failed to call method." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + } + + Py_DECREF(pyMethod); + Py_DECREF(pyArg1); + + m_ti.PyValue_To_rValue(pyValue,rValue); + if (!m_ti.error) { + Py_DECREF(pyValue); + } else { + cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + if (m_quitOnErrorFlag) exit(EXIT_FAILURE); + } + return rValue; + } + + template<typename RET,typename A1,typename A2> + RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2) + { + RET rValue = RET(); + if (m_debugFlag) cerr << DEBUG_NAME << endl; + if (!PyObject_HasAttrString(m_pyInstance,method)) { + // if (m_debugFlag) cerr << DEAFULT_RETURN << endl; + return rValue; + } + + // These functions always return valid PyObjects + PyObject *pyMethod = m_ti.PyValue_From_CValue(method); + PyObject* pyTuple = PyTuple_New(3); + if (!pyTuple) return rValue; + + PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); + PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); + + PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,NULL); + if (!pyValue) + { + cerr << PLUGIN_ERROR << "Failed to call method." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + } + + Py_DECREF(pyMethod); + Py_DECREF(pyArg1); + Py_DECREF(pyArg2); + + m_ti.PyValue_To_rValue(pyValue,rValue); + if (!m_ti.error) { + Py_DECREF(pyValue); + } else { + cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + if (m_quitOnErrorFlag) exit(EXIT_FAILURE); + } + return rValue; + } + + + template<typename RET,typename A1,typename A2,typename A3> + RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3) + { + RET rValue = RET(); + if (m_debugFlag) cerr << DEBUG_NAME << endl; + if (!PyObject_HasAttrString(m_pyInstance,method)) { + if (m_debugFlag) cerr << DEAFULT_RETURN << endl; + return rValue; + } + + // These functions always return valid PyObjects + PyObject *pyMethod = m_ti.PyValue_From_CValue(method); + PyObject* pyTuple = PyTuple_New(3); + if (!pyTuple) 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); + + // TODO: Pack it in a tuple to avoid va_list parsing! + + // callable = PyObject_GetAttr(callable, name); + // if (callable == NULL) + // return NULL; + // PyObject* args; // pyTuple of input arguments + //tmp = PyObject_Call(callable, args, NULL); + + + PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); + if (!pyValue) + { + cerr << PLUGIN_ERROR << "Failed to call method." << endl; + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + } + + Py_DECREF(pyMethod); + Py_DECREF(pyArg1); + Py_DECREF(pyArg2); + Py_DECREF(pyArg3); + + m_ti.PyValue_To_rValue(pyValue,rValue); + if (!m_ti.error) { + Py_DECREF(pyValue); + } else { + cerr << PLUGIN_ERROR << m_ti.lastError().message << endl; + Py_CLEAR(pyValue); + if (m_quitOnErrorFlag) exit(EXIT_FAILURE); + } + return rValue; + } + };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyRealTime.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,345 @@ +#include <Python.h> +#include "PyRealTime.h" +#include "vamp-sdk/Plugin.h" +#include <string> + +using namespace std; +using namespace Vamp; +using Vamp::Plugin; +using Vamp::RealTime; + + +/* CONSTRUCTOR: New RealTime object from sec and nsec */ +static PyObject* +RealTime_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + unsigned int sec = 0; + unsigned int nsec = 0; + double unary = 0; + const char *fmt = NULL; + + if ( + /// new RealTime from ('format',float) e.g. ('seconds',2.34123) + !PyArg_ParseTuple(args, "|sd:RealTime.new ", + (const char *) &fmt, + (double *) &unary) && + + /// new RealTime from (sec{int},nsec{int}) e.g. (2,34) + !PyArg_ParseTuple(args, "|II:RealTime.new ", + (unsigned int*) &sec, + (unsigned int*) &nsec) + + ) { + PyErr_SetString(PyExc_TypeError, + "RealTime initialised with wrong arguments."); + return NULL; + } + + // PyErr_Clear(); + + // RealTimeObject *self = PyObject_New(RealTimeObject, &RealTime_Type); + RealTimeObject *self = (RealTimeObject*)type->tp_alloc(type, 0); + + if (self == NULL) return NULL; + + self->rt = NULL; + + if (sec == 0 && nsec == 0 && fmt == 0) + self->rt = new RealTime::RealTime(); + else if (fmt == 0) + self->rt = new RealTime::RealTime(sec,nsec); + else { + /// new RealTime from seconds or milliseconds: i.e. >>>RealTime('seconds',12.3) + if (!string(fmt).compare("float") || + !string(fmt).compare("seconds")) + self->rt = new RealTime::RealTime( + RealTime::fromSeconds((double) unary)); + + if (!string(fmt).compare("milliseconds")) { + self->rt = new RealTime::RealTime( + RealTime::fromSeconds((double) unary / 1000.0)); } + } + + if (!self->rt) { + PyErr_SetString(PyExc_TypeError, + "RealTime initialised with wrong arguments."); + return NULL; + } + + return (PyObject *) self; +} + +/* DESTRUCTOR: delete type object */ +static void +RealTimeObject_dealloc(RealTimeObject *self) +{ + if (self->rt) delete self->rt; //delete the C object + PyObject_Del(self); //delete the Python object (original) + /// this requires PyType_Ready() which fills ob_type + // self->ob_type->tp_free((PyObject*)self); +} + +/* RealTime Object's Methods */ +//these are internals not exposed by the module but the object + +/* Returns a Tuple containing sec and nsec values */ +static PyObject * +RealTime_values(RealTimeObject *self) +{ + return Py_BuildValue("(ii)",self->rt->sec,self->rt->nsec); +} + +/* Returns a Text representation */ +static PyObject * +RealTime_toString(RealTimeObject *self, PyObject *args) +{ + return Py_BuildValue("s",self->rt->toText().c_str()); +} + +/* Frame representation */ +static PyObject * +RealTime_toFrame(PyObject *self, PyObject *args) +{ + unsigned int samplerate; + + if ( !PyArg_ParseTuple(args, "I:realtime.toFrame object ", + (unsigned int *) &samplerate )) { + PyErr_SetString(PyExc_ValueError,"Integer Sample Rate Required."); + return NULL; + } + + return Py_BuildValue("k", + RealTime::realTime2Frame( + *(const RealTime*) ((RealTimeObject*)self)->rt, + (unsigned int) samplerate)); +} + +/* Conversion of realtime to a double precision floating point value */ +/* ...in Python called by e.g. float(realtime) */ +static PyObject * +RealTime_float(PyObject *s) +{ + double drt = ((double) ((RealTimeObject*)s)->rt->sec + + (double)((double) ((RealTimeObject*)s)->rt->nsec)/1000000000); + return PyFloat_FromDouble(drt); +} + + +/* Type object's (RealTime) methods table */ +static PyMethodDef RealTime_methods[] = +{ + {"values", (PyCFunction)RealTime_values, METH_NOARGS, + PyDoc_STR("values() -> Tuple of sec,nsec representation.")}, + + {"toString", (PyCFunction)RealTime_toString, METH_NOARGS, + PyDoc_STR("toString() -> Return a user-readable string to the nearest millisecond in a form like HH:MM:SS.mmm")}, + + {"toFrame", (PyCFunction)RealTime_toFrame, METH_VARARGS, + PyDoc_STR("toFrame(samplerate) -> Sample count for given sample rate.")}, + + {"toFloat", (PyCFunction)RealTime_float, METH_NOARGS, + PyDoc_STR("toFloat() -> Floating point representation.")}, + + {NULL, NULL} /* sentinel */ +}; + + +/* Methods implementing protocols */ +// these functions are called by the interpreter + +/* Object Protocol */ + +static int +RealTime_setattr(RealTimeObject *self, char *name, PyObject *value) +{ + + if ( !string(name).compare("sec")) { + self->rt->sec= (int) PyInt_AS_LONG(value); + return 0; + } + + if ( !string(name).compare("nsec")) { + self->rt->nsec= (int) PyInt_AS_LONG(value); + return 0; + } + + return -1; +} + +static PyObject * +RealTime_getattr(RealTimeObject *self, char *name) +{ + + if ( !string(name).compare("sec") ) { + return PyInt_FromSsize_t( + (Py_ssize_t) self->rt->sec); + } + + if ( !string(name).compare("nsec") ) { + return PyInt_FromSsize_t( + (Py_ssize_t) self->rt->nsec); + } + + return Py_FindMethod(RealTime_methods, + (PyObject *)self, name); +} + +/* String representation called by e.g. str(realtime), print realtime*/ +static PyObject * +RealTime_repr(PyObject *self) +{ + return Py_BuildValue("s", + ((RealTimeObject*)self)->rt->toString().c_str()); +} + + +/* Number Protocol */ +/// Only add and substract make sense, or what about the +/// square root of Monday morning? +/// Divide by integer maybe for durations? + +static PyObject * +RealTime_add(PyObject *s, PyObject *w) +{ + RealTimeObject *result = + PyObject_New(RealTimeObject, &RealTime_Type); + if (result == NULL) return NULL; + + result->rt = new RealTime::RealTime( + *((RealTimeObject*)s)->rt + *((RealTimeObject*)w)->rt); + return (PyObject*)result; +} + +static PyObject * +RealTime_subtract(PyObject *s, PyObject *w) +{ + RealTimeObject *result = + PyObject_New(RealTimeObject, &RealTime_Type); + if (result == NULL) return NULL; + + result->rt = new RealTime::RealTime( + *((RealTimeObject*)s)->rt - *((RealTimeObject*)w)->rt); + return (PyObject*)result; +} + +static PyNumberMethods realtime_as_number = +{ + RealTime_add, /*nb_add*/ + RealTime_subtract, /*nb_subtract*/ + 0, /*nb_multiply*/ + 0, /*nb_divide*/ + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_neg*/ + 0, /*nb_pos*/ + 0, /*(unaryfunc)array_abs,*/ + 0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ + 0, /*nb_coerce*/ + 0, /*nb_int*/ + 0, /*nb_long*/ + (unaryfunc)RealTime_float,/*nb_float*/ + 0, /*nb_oct*/ + 0, /*nb_hex*/ +}; + +/* REAL-TIME TYPE OBJECT */ + +#define RealTime_alloc PyType_GenericAlloc +#define RealTime_free PyObject_Del + +/* Doc:: 10.3 Type Objects */ /* static */ +PyTypeObject RealTime_Type = +{ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "vampy.RealTime", /*tp_name*/ + sizeof(RealTimeObject), /*tp_basicsize*/ + 0,//sizeof(RealTime), /*tp_itemsize*/ + /* methods */ + (destructor)RealTimeObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)RealTime_getattr, /*tp_getattr*/ + (setattrfunc)RealTime_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + RealTime_repr, /*tp_repr*/ + &realtime_as_number, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0,//(ternaryfunc)RealTime_new, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "RealTime Object", /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + RealTime_methods, /*tp_methods*/ //TypeObject Methods + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + RealTime_alloc, /*tp_alloc*/ + RealTime_new, /*tp_new*/ + RealTime_free, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + + +/* PyRealTime C++ API */ + +/*PyRealTime from RealTime pointer +PyObject* +PyRealTime_FromRealTime(Vamp::RealTime *rt) { + + RealTimeObject *self = + PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = new RealTime::RealTime(*rt); + return (PyObject*) self; +}*/ + + +/*PyRealTime from RealTime*/ +PyObject* +PyRealTime_FromRealTime(Vamp::RealTime& rt) { + + RealTimeObject *self = + PyObject_New(RealTimeObject, &RealTime_Type); + if (self == NULL) return NULL; + + self->rt = new RealTime::RealTime(rt); + return (PyObject*) self; +} + +/*RealTime* from PyRealTime*/ +const Vamp::RealTime::RealTime* +PyRealTime_AsRealTime (PyObject *self) { + + RealTimeObject *s = (RealTimeObject*) self; + + if (!PyRealTime_Check(s)) { + PyErr_SetString(PyExc_TypeError, "RealTime Object Expected."); + cerr << "in call PyRealTime_AsPointer(): RealTime Object Expected. " << endl; + return NULL; } + return s->rt; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyRealTime.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,28 @@ +#ifndef _PYREALTIME_H_ +#define _PYREALTIME_H_ + +#include "vamp-sdk/Plugin.h" + +typedef struct { + PyObject_HEAD + Vamp::RealTime::RealTime *rt; +} RealTimeObject; + +PyAPI_DATA(PyTypeObject) RealTime_Type; + +#define PyRealTime_CheckExact(v) ((v)->ob_type == &RealTime_Type) +#define PyRealTime_Check(v) PyObject_TypeCheck(v, &RealTime_Type) +///fast macro version as per API convention +#define PyRealTime_AS_REALTIME(v) ((const RealTimeObject* const) (v))->rt + +/* PyRealTime C++ API */ + + +PyAPI_FUNC(PyObject *) +PyRealTime_FromRealTime(Vamp::RealTime&); + +PyAPI_FUNC(const Vamp::RealTime::RealTime*) +PyRealTime_AsRealTime (PyObject *self); + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyTypeInterface.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,1039 @@ +/* +*/ + +#include <Python.h> +#include "PyTypeInterface.h" +#include "PyRealTime.h" +#include "PyExtensionModule.h" +#include <math.h> +#include <float.h> +#include <limits.h> +#ifndef SIZE_T_MAX +#define (SIZE_T_MAX (size_t) -1) +#endif + +using std::string; +using std::vector; +using std::cerr; +using std::endl; +using std::map; + +static std::map<std::string, o::eOutDescriptors> outKeys; +static std::map<std::string, p::eParmDescriptors> parmKeys; +static std::map<std::string, eSampleTypes> sampleKeys; +static std::map<std::string, eFeatureFields> ffKeys; +static bool isMapInitialised = false; + +/* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS + (EXCEPT FOR TEMPORARY PYOBJECTS)! */ + +PyTypeInterface::PyTypeInterface() : + m_strict(false), + m_error(false), + m_lastError(m_noError), + error(m_error) +{ +} + +PyTypeInterface::~PyTypeInterface() +{ +} + +/// floating point numbers (TODO: check numpy.float128) +float +PyTypeInterface::PyValue_To_Float(PyObject* pyValue) const +{ + // convert float + if (PyFloat_Check(pyValue)) + { + float rValue = (float) PyFloat_AS_DOUBLE(pyValue); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + setValueError("Error while converting float object.",m_strict); + return 0.0; + } + return rValue; + } + + // in strict mode we will not try harder + if (m_strict) { + setValueError("Strict conversion error: object is not float.",m_strict); + return 0.0; + } + + // convert other objects supporting the number protocol + if (PyNumber_Check(pyValue)) + { + // PEP353: Py_ssize_t is size_t but signed ! + // This will work up to numpy.float64 + Py_ssize_t rValue = PyNumber_AsSsize_t(pyValue,NULL); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + setValueError("Error while converting integer object.",m_strict); + return 0.0; + } + if (rValue > FLT_MAX || rValue < FLT_MIN) + { + setValueError("Overflow error. Object can not be converted to float.",m_strict); + return 0.0; + } + return (float) rValue; + } + + // convert string + if (PyString_Check(pyValue)) + { + PyObject* pyFloat = PyFloat_FromString(pyValue,NULL); + if (!pyFloat) + { + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + setValueError("String value can not be converted to float.",m_strict); + return 0.0; + } + float rValue = (float) PyFloat_AS_DOUBLE(pyFloat); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + Py_CLEAR(pyFloat); + setValueError("Error while converting float object.",m_strict); + return 0.0; + } + Py_DECREF(pyFloat); + return rValue; + } + + // convert the first element of any iterable sequence (for convenience and backwards compatibility) + if (PySequence_Check(pyValue) and PySequence_Size(pyValue) > 0) + { + PyObject* item = PySequence_GetItem(pyValue,0); + if (item) + { + float rValue = this->PyValue_To_Float(item); + if (!m_error) { + Py_DECREF(item); + return rValue; + } else { + Py_CLEAR(item); + std::string msg = "Could not convert sequence element. " + lastError().message; + setValueError(msg,m_strict); + return 0.0; + } + } + } + + // give up + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + std::string msg = "Conversion from " + PyValue_Get_TypeName(pyValue) + " to float is not possible."; + setValueError(msg,m_strict); + return 0.0; +} + +/// size_t (unsigned integer types) +size_t +PyTypeInterface::PyValue_To_Size_t(PyObject* pyValue) const +{ + // convert objects supporting the number protocol + if (PyNumber_Check(pyValue)) + { + if (m_strict && !PyInt_Check(pyValue) && !PyLong_Check(pyValue)) + setValueError("Strict conversion error: object is not integer type.",m_strict); + // Note: this function handles Bool,Int,Long,Float + // PEP353: Py_ssize_t is size_t but signed ! + Py_ssize_t rValue = PyInt_AsSsize_t(pyValue); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + setValueError("Error while converting integer object.",m_strict); + return 0; + } + if ((unsigned long)rValue > SIZE_T_MAX || (unsigned long)rValue < 0) + { + setValueError("Overflow error. Object can not be converted to size_t.",m_strict); + return 0; + } + return (size_t) rValue; + } + + // in strict mode we will not try harder and throw an exception + // then the caller should decide what to do with it + if (m_strict) { + setValueError("Strict conversion error: object is not integer.",m_strict); + return 0; + } + + // convert string + if (PyString_Check(pyValue)) + { + PyObject* pyLong = PyNumber_Long(pyValue); + if (!pyLong) + { + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + setValueError("String object can not be converted to size_t.",m_strict); + return 0; + } + size_t rValue = this->PyValue_To_Size_t(pyLong); + if (!m_error) { + Py_DECREF(pyLong); + return rValue; + } else { + Py_CLEAR(pyLong); + setValueError (lastError().message,m_strict); + return 0; + } + } + + // convert the first element of iterable sequences + if (PySequence_Check(pyValue) and PySequence_Size(pyValue) > 0) + { + PyObject* item = PySequence_GetItem(pyValue,0); + if (item) + { + size_t rValue = this->PyValue_To_Size_t(item); + if (!m_error) { + Py_DECREF(item); + return rValue; + } else { + Py_CLEAR(item); + std::string msg = "Could not convert sequence element. " + lastError().message; + setValueError(msg,m_strict); + return 0; + } + } + } + + // give up + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to size_t is not possible."; + setValueError(msg,m_strict); + return 0; +} + +bool +PyTypeInterface::PyValue_To_Bool(PyObject* pyValue) const +{ + // convert objects supporting the number protocol + // Note: PyBool is a subclass of PyInt + if (PyNumber_Check(pyValue)) + { + if (m_strict && !PyBool_Check(pyValue)) + setValueError + ("Strict conversion error: object is not boolean type.",m_strict); + + // Note: this function handles Bool,Int,Long,Float + Py_ssize_t rValue = PyInt_AsSsize_t(pyValue); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + setValueError ("Error while converting boolean object.",m_strict); + } + if (rValue != 1 && rValue != 0) + { + setValueError ("Overflow error. Object can not be converted to boolean.",m_strict); + } + return (bool) rValue; + } + + if (m_strict) { + setValueError ("Strict conversion error: object is not numerical type.",m_strict); + return false; + } + + // convert iterables: the rule is the same as of the interpreter: + // empty sequence evaluates to False, anything else is True + if (PySequence_Check(pyValue)) + { + return PySequence_Size(pyValue)?true:false; + } + + // give up + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to boolean is not possible."; + setValueError(msg,m_strict); + return false; +} + +/// string and objects that support .__str__() (TODO: check unicode objects) +std::string +PyTypeInterface::PyValue_To_String(PyObject* pyValue) const +{ + // convert string + if (PyString_Check(pyValue)) + { + char *cstr = PyString_AS_STRING(pyValue); + if (!cstr) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting string object.",m_strict); + return std::string(); + } + return std::string(cstr); + } + // TODO: deal with unicode here (argh!) + + // in strict mode we will not try harder + if (m_strict) { + setValueError("Strict conversion error: object is not string.",m_strict); + return std::string(); + } + + // convert list or tuple: empty lists are turned into empty strings + if (PyList_Check(pyValue) || PyTuple_Check(pyValue)) + { + if (!PySequence_Size(pyValue)) return std::string(); + PyObject* item = PySequence_GetItem(pyValue,0); + if (item) + { + std::string rValue = this->PyValue_To_String(item); + if (m_error) { + Py_DECREF(item); + return rValue; + } else { + Py_CLEAR(item); + std::string msg = "Could not convert sequence element. " + lastError().message; + setValueError(msg,m_strict); + return std::string(); + } + } + } + + // convert any other object that has .__str__() or .__repr__() + PyObject* pyString = PyObject_Str(pyValue); + if (pyString && !PyErr_Occurred()) + { + std::string rValue = this->PyValue_To_String(pyString); + if (m_error) { + Py_DECREF(pyString); + return rValue; + } else { + Py_CLEAR(pyString); + std::string msg = "Object " + this->PyValue_Get_TypeName(pyValue) +" can not be represented as string. " + lastError().message; + setValueError (msg,m_strict); + return std::string(); + } + } + + // give up + PyErr_Print(); PyErr_Clear(); + std::string msg = "Conversion from " + this->PyValue_Get_TypeName(pyValue) + " to string is not possible."; + setValueError(msg,m_strict); + return std::string(); +} + +/* C Values to Py Values */ + + +PyObject* +PyTypeInterface::PyValue_From_CValue(const char* cValue) const +{ + // returns new reference + if (!cValue) + setValueError("Invalid pointer encountered while converting from char* .",m_strict); + PyObject *pyValue = PyString_FromString(cValue); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting from char* or string.",m_strict); + return NULL; + } + return pyValue; +} + +PyObject* +PyTypeInterface::PyValue_From_CValue(size_t cValue) const +{ + // returns new reference + PyObject *pyValue = PyInt_FromSsize_t((Py_ssize_t)cValue); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting from size_t.",m_strict); + return NULL; + } + return pyValue; +} + +PyObject* +PyTypeInterface::PyValue_From_CValue(double cValue) const +{ + // returns new reference + PyObject *pyValue = PyFloat_FromDouble(cValue); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting from float or double.",m_strict); + return NULL; + } + return pyValue; +} + +PyObject* +PyTypeInterface::PyValue_From_CValue(bool cValue) const +{ + // returns new reference + PyObject *pyValue = PyBool_FromLong((long)cValue); + if (!pyValue) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + setValueError("Error while converting from bool.",m_strict); + return NULL; + } + return pyValue; +} + + +/* Sequence Types to C++ Types */ + +//convert Python list to C++ vector of strings +std::vector<std::string> +PyTypeInterface::PyValue_To_StringVector (PyObject *inputList) const +{ + + std::vector<std::string> Output; + std::string ListElement; + PyObject *pyString = NULL; + + if (!PyList_Check(inputList)) return Output; + + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) { + //Get next list item (Borrowed Reference) + pyString = PyList_GET_ITEM(inputList,i); + ListElement = (string) PyString_AsString(PyObject_Str(pyString)); + Output.push_back(ListElement); + } + return Output; +} + +//convert Python list to C++ vector of floats +std::vector<float> +PyTypeInterface::PyValue_To_FloatVector (PyObject *inputList) const +{ + typedef std::vector<float> floatVector; + std::vector<float> Output; + + /// Check for NumPy Array + if (PyObject_HasAttrString(inputList,"__array_struct__")) { + int vectorLength; + float *dataptr = getNumPyObjectData(inputList,vectorLength); + if (dataptr != 0) cerr << "Numpy array found: " << vectorLength << endl; + // Output = *dataptr; + } + + float ListElement; + PyObject *pyFloat = NULL; + + if (!PyList_Check(inputList)) return Output; + + for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { + //Get next list item (Borrowed Reference) + pyFloat = PyList_GET_ITEM(inputList,k); + ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); +#ifdef _DEBUG + cerr << "value: " << ListElement << endl; +#endif + Output.push_back(ListElement); + } + + return Output; +} + + +/* Vamp API Specific Types + +Vamp::Plugin::OutputList +PyTypeInterface::PyValue_To_OutputList(PyObject* pyList) const +{ + Vamp::Plugin::OutputList list; + Vamp::Plugin::OutputDescriptor od; + + // Type checking + if (! PyList_Check(pyList) ) { + Py_CLEAR(pyList); + // cerr << "ERROR: In Python plugin [" << m_class << "::" << method + // << "] Expected List return type." << endl; + return list; + } + + //These will all be borrowed references (no need to DECREF) + PyObject *pyDict; + + //Parse Output List + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + //Get i-th Vamp output descriptor (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyList,i); + od = PyValue_To_OutputDescriptor(pyDict); + list.push_back(od); + } + return list; +} + +Vamp::Plugin::ParameterList +PyTypeInterface::PyValue_To_ParameterList(PyObject* pyList) const +{ + Vamp::Plugin::ParameterList list; + Vamp::Plugin::ParameterDescriptor pd; + + // Type checking + if (! PyList_Check(pyList) ) { + Py_CLEAR(pyList); + // cerr << "ERROR: In Python plugin [" << m_class << "::" << method + // << "] Expected List return type." << endl; + return list; + } + + //These will all be borrowed references (no need to DECREF) + PyObject *pyDict; + + //Parse Output List + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + //Get i-th Vamp output descriptor (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyList,i); + pd = PyValue_To_ParameterDescriptor(pyDict); + list.push_back(pd); + } + return list; +} + +Vamp::Plugin::OutputDescriptor +PyTypeInterface::PyValue_To_OutputDescriptor(PyObject* pyDict) const +{ + //We only care about dictionaries holding output descriptors + if (!PyDict_Check(pyDict)) + return Vamp::Plugin::OutputDescriptor(); + + Py_ssize_t pyPos = 0; + PyObject *pyKey, *pyValue; + initMaps(); + Vamp::Plugin::OutputDescriptor od; + + //Python Dictionary Iterator: + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + std::string key = PyValue_To_String(pyKey); + SetValue(od,key,pyValue); + if (m_error) { + _lastError().location += "parameter: '" + + key +"' descriptor: '" + od.identifier + "'"; + } + } + if (!m_errorQueue.empty()) m_error = true; + return od; +} + +Vamp::Plugin::ParameterDescriptor +PyTypeInterface::PyValue_To_ParameterDescriptor(PyObject* pyDict) const +{ + //We only care about dictionaries holding output descriptors + if (!PyDict_Check(pyDict)) + return Vamp::Plugin::ParameterDescriptor(); + + Py_ssize_t pyPos = 0; + PyObject *pyKey, *pyValue; + initMaps(); + Vamp::Plugin::ParameterDescriptor pd; + + //Python Dictionary Iterator: + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + std::string key = PyValue_To_String(pyKey); + SetValue(pd,key,pyValue); + if (m_error) { + _lastError().location += "parameter: '" + + key +"' descriptor: '" + pd.identifier + "'"; + } + } + if (!m_errorQueue.empty()) m_error = true; + return pd; +} + +Vamp::Plugin::Feature +PyTypeInterface::PyValue_To_Feature(PyObject* pyDict) const +{ + //We only care about dictionaries holding output descriptors + if (!PyDict_Check(pyDict)) + return Vamp::Plugin::Feature(); + + Py_ssize_t pyPos = 0; + PyObject *pyKey, *pyValue; + initMaps(); + Vamp::Plugin::Feature feature; + + //Python Dictionary Iterator: + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) + { + std::string key = PyValue_To_String(pyKey); + float isr = 22050.0; + Feature_SetValue(feature,key,pyValue,isr); + if (m_error) { + _lastError().location += "key: '" + key + "'"; + // _lastError().location += "parameter: '" + // + key +"' descriptor: '" + pd.identifier + "'"; + } + } + if (!m_errorQueue.empty()) m_error = true; + return feature; +} +*/ + +/// FeatureSet (an int map of OutputLists) +Vamp::Plugin::FeatureSet +PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const +{ + Vamp::Plugin::FeatureSet rFeatureSet; /// PyFeatureSet is an int map + if (pyValue == NULL) { + cerr << "NULL FeatureSet" << endl; + return rFeatureSet; + } + + cerr << "PyValue_To_FeatureSet" << endl; + //Convert PyFeatureSet + if (PyFeatureSet_CheckExact(pyValue)) { + cerr << "FeatureSet Return type" << endl; + Py_ssize_t pyPos = 0; + //Borrowed References + PyObject *pyKey, *pyDictValue; + int key; + + //Python Dictionary Iterator: + while (PyDict_Next(pyValue, &pyPos, &pyKey, &pyDictValue)) + { + /// DictValue -> Vamp::FeatureList + key = (int) PyInt_AS_LONG(pyKey); + cerr << "FeatureSet key = " << key << endl; + /// Error checking is done at value assignment + PyValue_To_rValue(pyDictValue,rFeatureSet[key]); + } + if (!m_errorQueue.empty()) m_error = true; + return rFeatureSet; + } + cerr << "not FeatureSet Return type" << endl; + + + //Check return type + if (pyValue == NULL || !PyList_Check(pyValue) ) { + if (pyValue == NULL) { + // cerr << "ERROR: In Python plugin [" << m_class << "::" << method << "] Unexpected result." << endl; + if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } + } else { + // cerr << "ERROR: In Python plugin [" << m_class << "::" << method << "] Expected List return type." << endl; + } + Py_CLEAR(pyValue); + return Vamp::Plugin::FeatureSet(); + } + + // This will be borrowed reference + PyObject *pyFeatureList; + + //Parse Output List for each element (FeatureSet) + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) { + //Get i-th FeatureList (Borrowed Reference) + pyFeatureList = PyList_GET_ITEM(pyValue,i); + PyValue_To_rValue(pyFeatureList,rFeatureSet[i]); + } + // Py_CLEAR(pyOutputList); + return rFeatureSet; +} + +Vamp::RealTime::RealTime +PyTypeInterface::PyValue_To_RealTime(PyObject* pyValue) const +{ +// We accept integer sample counts (for backwards compatibility) +// or PyRealTime objects and convert them to Vamp::RealTime + + if (PyRealTime_CheckExact(pyValue)) + { +#ifdef _DEBUG + cerr << "Converting from PyRealTime" << endl; +#endif + /// just create a copy of the wrapped object + return Vamp::RealTime::RealTime( + *PyRealTime_AS_REALTIME(pyValue)); + } + // assume integer sample count + long sampleCount = PyLong_AsLong(pyValue); + if (PyErr_Occurred()) + { + PyErr_Print(); PyErr_Clear(); + setValueError("Error while converting integer to RealTime.",m_strict); + return Vamp::RealTime::RealTime(); + } +#ifdef _DEBUG + Vamp::RealTime::RealTime rt = + Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate ); + cerr << "RealTime: " << (long)sampleCount << ", ->" << rt.toString() << endl; + return rt; +#else + return Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate ); +#endif + +} + + +/// OutputDescriptor +void +PyTypeInterface::SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const +{ + switch (outKeys[key]) + { + case o::not_found: + cerr << "Unknown key in Vamp OutputDescriptor: " << key << endl; + break; + case o::identifier: + _convert(pyValue,od.identifier); + break; + case o::name: + _convert(pyValue,od.name); + break; + case o::description: + _convert(pyValue,od.description); + break; + case o::unit: + _convert(pyValue,od.unit); + break; + case o::hasFixedBinCount: + _convert(pyValue,od.hasFixedBinCount); + break; + case o::binCount: + _convert(pyValue,od.binCount); + break; + case o::binNames: + _convert(pyValue,od.binNames); + break; + case o::hasKnownExtents: + _convert(pyValue,od.hasKnownExtents); + break; + case o::minValue: + _convert(pyValue,od.minValue); + break; + case o::maxValue: + _convert(pyValue,od.maxValue); + break; + case o::isQuantized: + _convert(pyValue,od.isQuantized); + break; + case o::quantizeStep: + _convert(pyValue,od.quantizeStep); + break; + case o::sampleType: + // implements specific conversion! + od.sampleType = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[PyValue_To_String(pyValue)]; + break; + case o::sampleRate: + _convert(pyValue,od.sampleRate); + break; + case o::hasDuration: + _convert(pyValue,od.hasDuration); + break; + default: + cerr << "Invalid key in Vamp OutputDescriptor: " << key << endl; + } +} + +/// ParameterDescriptor +void +PyTypeInterface::SetValue(Vamp::Plugin::ParameterDescriptor& pd, std::string& key, PyObject* pyValue) const +{ + switch (parmKeys[key]) + { + case p::not_found : + cerr << "Unknown key in Vamp ParameterDescriptor: " << key << endl; + break; + case p::identifier: + _convert(pyValue,pd.identifier); + break; + case p::name: + _convert(pyValue,pd.name); + break; + case p::description: + _convert(pyValue,pd.description); + break; + case p::unit: + _convert(pyValue,pd.unit); + break; + case p::minValue: + _convert(pyValue,pd.minValue); + break; + case p::maxValue: + _convert(pyValue,pd.maxValue); + break; + case p::defaultValue: + _convert(pyValue,pd.defaultValue); + break; + case p::isQuantized: + _convert(pyValue,pd.isQuantized); + break; + case p::quantizeStep: + _convert(pyValue,pd.quantizeStep); + break; + default : + cerr << "Invalid key in Vamp ParameterDescriptor: " << key << endl; + } +} + +/// Feature (it's like a Descriptor) +bool +PyTypeInterface::SetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const +{ + bool found = true; + switch (ffKeys[key]) + { + case unknown : + cerr << "Unknown key in Vamp Feature: " << key << endl; + found = false; + break; + case hasTimestamp: + _convert(pyValue,feature.hasTimestamp); + break; + case timeStamp: + _convert(pyValue,feature.timestamp); + break; + case hasDuration: + _convert(pyValue,feature.hasDuration); + break; + case duration: + _convert(pyValue,feature.duration); + break; + case values: + _convert(pyValue,feature.values); + break; + case label: + _convert(pyValue,feature.label); + break; + default: + found = false; + } + return found; +} + +/* +/// Feature (it's like a Descriptor) +bool +PyTypeInterface::GetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const +{ + bool found = true; + switch (ffKeys[key]) + { + case unknown : + cerr << "Unknown key in Vamp Feature: " << key << endl; + found = false; + break; + case hasTimestamp: + _convert(pyValue,feature.hasTimestamp); + // pyValue = PyValue_From_CValue(feature.hasTimestamp) + break; + case timeStamp: + _convert(pyValue,feature.timestamp); + break; + case hasDuration: + _convert(pyValue,feature.hasDuration); + break; + case duration: + _convert(pyValue,feature.duration); + break; + case values: + _convert(pyValue,feature.values); + break; + case label: + _convert(pyValue,feature.label); //vector<string> + break; + default: + found = false; + } + return found; +} +*/ + +/* Error handling */ + +void +PyTypeInterface::setValueError (std::string message, bool strict) const +{ + m_error = true; + m_errorQueue.push(ValueError(message,strict)); +} + +/// return a reference to the last error or creates a new one. +PyTypeInterface::ValueError& +PyTypeInterface::_lastError() const +{ + m_error = false; + if (!m_errorQueue.empty()) return m_errorQueue.back(); + else { + m_errorQueue.push(ValueError("Type conversion error.",m_strict)); + return m_errorQueue.back(); + } +} + +/// return the last error message and clear the error flag +const PyTypeInterface::ValueError& +PyTypeInterface::lastError() const +{ + // PyTypeInterface *self = const_cast<PyTypeInterface*> (this); + m_error = false; + if (!m_errorQueue.empty()) return m_errorQueue.back(); + else return m_noError; +} + +/// iterate over the error message queue and pop the oldest item +PyTypeInterface::ValueError +PyTypeInterface::getError() const +{ + if (!m_errorQueue.empty()) { + PyTypeInterface::ValueError e = m_errorQueue.front(); + m_errorQueue.pop(); + if (m_errorQueue.empty()) m_error = false; + return e; + } + else { + m_error = false; + return PyTypeInterface::ValueError(); + } +} + +/* Utilities */ + +//return a pointer to the data in the numPy array +float* +PyTypeInterface::getNumPyObjectData(PyObject *object, int &length) const +{ + + char attr_name[]="__array_struct__"; + + //check if we passed in a NumPy array object + if (!PyObject_HasAttrString(object,attr_name)) { + // PyErr_SetString(PyExc_TypeError, + // "Input object has no __array_struct__ attribute. NumPy array required."); + return NULL; + } + + //retrieve __array_struct__ interface + object = PyObject_GetAttrString(object,attr_name); + + //check whether we found CObjects + if (!PyCObject_Check(object)) { + PyErr_SetString(PyExc_TypeError, + "The passed __array_struct__ interface is not a valid C Object."); + return NULL; + } + + + //check if the pointers directed to the integer '2' + int *check = (int *) PyCObject_AsVoidPtr (object); + + if (*check != 2 ) { + PyErr_SetString(PyExc_TypeError, + "A C Object __array_struct__ required as inputs"); + return NULL; + } + + //convert CObjects to Array interfaces + PyArrayInterface *arrayInterface = + (PyArrayInterface *) PyCObject_AsVoidPtr (object); + + //check array dimension: should be 1 + int inputDim = arrayInterface->nd; + + if (inputDim > 1 ) { + PyErr_SetString(PyExc_TypeError, + "Array dimensions must not exceed one."); + return NULL; + } + + // check if vector size is sane + Py_intptr_t arrayLength = arrayInterface->shape[0]; + length = (int) arrayLength; + + // if (arrayLength < 8 || arrayLength > 65536 ) { + // PyErr_SetString(PyExc_TypeError, + // "Array length is out of bounds."); + // return NULL; + // } + + //check type; must be float32 + char arrayType = arrayInterface->typekind; + + if (arrayType != 'f' ) { + PyErr_SetString(PyExc_TypeError, + "Floating point arrays required."); + return NULL; + } + + //return data vector address + return (float*) arrayInterface->data; + +} + +/// get the type name of an object +std::string +PyTypeInterface::PyValue_Get_TypeName(PyObject* pyValue) const +{ + PyObject *pyType = PyObject_Type(pyValue); + if (!pyType) return std::string ("< unknown type >"); + PyObject *pyString = PyObject_Str(pyType); + if (!pyString) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_CLEAR(pyType); + return std::string ("< unknown type >"); + } + char *cstr = PyString_AS_STRING(pyString); + if (!cstr) + { + if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} + Py_DECREF(pyType); + Py_CLEAR(pyString); + cerr << "Warning: Object type name could not be found." << endl; + return std::string("< unknown type >"); + } + Py_DECREF(pyType); + Py_DECREF(pyString); + return std::string(cstr); +} + +bool +PyTypeInterface::initMaps() const +{ + + if (isMapInitialised) return true; + + outKeys["identifier"] = o::identifier; + outKeys["name"] = o::name; + outKeys["description"] = o::description; + outKeys["unit"] = o::unit; + outKeys["hasFixedBinCount"] = o::hasFixedBinCount; + outKeys["binCount"] = o::binCount; + outKeys["binNames"] = o::binNames; + outKeys["hasKnownExtents"] = o::hasKnownExtents; + outKeys["minValue"] = o::minValue; + outKeys["maxValue"] = o::maxValue; + outKeys["isQuantized"] = o::isQuantized; + outKeys["quantizeStep"] = o::quantizeStep; + outKeys["sampleType"] = o::sampleType; + outKeys["sampleRate"] = o::sampleRate; + outKeys["hasDuration"] = o::hasDuration; + + sampleKeys["OneSamplePerStep"] = OneSamplePerStep; + sampleKeys["FixedSampleRate"] = FixedSampleRate; + sampleKeys["VariableSampleRate"] = VariableSampleRate; + + ffKeys["hasTimestamp"] = hasTimestamp; + ffKeys["timeStamp"] = timeStamp; + ffKeys["hasDuration"] = hasDuration; + ffKeys["duration"] = duration; + ffKeys["values"] = values; + ffKeys["label"] = label; + + parmKeys["identifier"] = p::identifier; + parmKeys["name"] = p::name; + parmKeys["description"] = p::description; + parmKeys["unit"] = p::unit; + parmKeys["minValue"] = p::minValue; + parmKeys["maxValue"] = p::maxValue; + parmKeys["defaultValue"] = p::defaultValue; + parmKeys["isQuantized"] = p::isQuantized; + parmKeys["quantizeStep"] = p::quantizeStep; + + isMapInitialised = true; + return true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PyTypeInterface.h Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,304 @@ +/* + +Type safe conversion utilities from Python types to C/C++ types, +mainly using Py/C API macros. + +*/ + +#ifndef _PY_TYPE_INTERFACE_H_ +#define _PY_TYPE_INTERFACE_H_ + +#include "vamp-sdk/Plugin.h" +#include <Python.h> +#include "PyExtensionModule.h" +#include <vector> +#include <queue> +#include <string> +//#include <typeinfo> + + +using std::cerr; +using std::endl; + +namespace o { +enum eOutDescriptors { + not_found, + identifier, + name, + description, + unit, + hasFixedBinCount, + binCount, + binNames, + hasKnownExtents, + minValue, + maxValue, + isQuantized, + quantizeStep, + sampleType, + sampleRate, + hasDuration, + endNode + }; +} + +namespace p { +enum eParmDescriptors { + not_found, + identifier, + name, + description, + unit, + minValue, + maxValue, + defaultValue, + isQuantized, + quantizeStep + }; +} + +enum eSampleTypes { + OneSamplePerStep, + FixedSampleRate, + VariableSampleRate + }; + +enum eFeatureFields { + unknown, + hasTimestamp, + timeStamp, + hasDuration, + duration, + values, + label + }; + + +/// sutructure of NumPy array interface: +/// this is all we need to support numpy without direct dependency +typedef struct { + int two; /* contains the integer 2 -- simple sanity check */ + int nd; /* number of dimensions */ + char typekind; /* kind in array --- character code of typestr */ + int itemsize; /* size of each element */ + int flags; /* flags indicating how the data should be interpreted */ + /* must set ARR_HAS_DESCR bit to validate descr */ + Py_intptr_t *shape; /* A length-nd array of shape information */ + Py_intptr_t *strides; /* A length-nd array of stride information */ + void *data; /* A pointer to the first element of the array */ + PyObject *descr; /* NULL or data-description (same as descr key */ + /* of __array_interface__) -- must set ARR_HAS_DESCR */ + /* flag or this will be ignored. */ +} PyArrayInterface; + +/* C++ mapping of PyNone Type*/ +typedef struct NoneType {}; + +class PyTypeInterface +{ +public: + PyTypeInterface(); + ~PyTypeInterface(); + + // Data + class ValueError + { + public: + ValueError() {} + ValueError(std::string m, bool s) : message(m),strict(s) {} + std::string location; + std::string message; + bool strict; + std::string get() const { return message + "\nLocation: " + location + "\n";} + void print() const { cerr << get(); } + }; + + // Utilities + void setStrictTypingFlag(bool b) {m_strict = b;} + const ValueError &lastError() const; + ValueError getError() const; + std::string PyValue_Get_TypeName(PyObject*) const; + bool initMaps() const; + + // Basic type conversion: Python to C++ + float PyValue_To_Float(PyObject*) const; + size_t PyValue_To_Size_t(PyObject*) const; + bool PyValue_To_Bool(PyObject*) const; + std::string PyValue_To_String(PyObject*) const; + // int PyValue_To_Int(PyObject*) const; + + // C++ to Python + PyObject *PyValue_From_CValue(const char*) const; + PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); } + PyObject *PyValue_From_CValue(size_t) const; + PyObject *PyValue_From_CValue(double) const; + PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); } + PyObject *PyValue_From_CValue(bool) const; + + // Sequence types + std::vector<std::string> PyValue_To_StringVector (PyObject*) const; + std::vector<float> PyValue_To_FloatVector (PyObject*) const; + + // Numpy types + float* getNumPyObjectData(PyObject *object, int &length) const; + + +/* Template functions */ + + + /// Common wrappers to set a value in one of these structs. (to be used in template functions) + void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const; + void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const; + bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const; + PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const + { + if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue); + if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue); + if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue); + return NULL; + } + + + template<typename RET> + RET PyTypeInterface::PyValue_To_VampDescriptor(PyObject* pyValue) const + //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature + { + PyObject* pyDict; + + // Descriptors encoded as dicts + pyDict = GetDescriptor_As_Dict(pyValue); + if (!pyDict) pyDict = pyValue; + + // TODO: support full mapping protocol as fallback. + if (!PyDict_Check(pyDict)) { + setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict); + return RET(); + } + + Py_ssize_t pyPos = 0; + PyObject *pyKey, *pyDictValue; + initMaps(); + RET rd; + + //Python Dictionary Iterator: + while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue)) + { + std::string key = PyValue_To_String(pyKey); + SetValue(rd,key,pyDictValue); + if (m_error) { + _lastError().location += "parameter: '" + key + "'";//"' descriptor: '" + rd.identifier + "'"; + } + } + if (!m_errorQueue.empty()) m_error = true; + return rd; + } + + /// Convert a sequence (tipically list) of PySomething to + /// OutputList,ParameterList or FeatureList + template<typename RET,typename ELEM> //<OutputList> <OutputDescriptor> + RET PyTypeInterface::PyValue_To_VampList(PyObject* pyList) const + { + // Vamp::Plugin::OutputList list; + // Vamp::Plugin::OutputDescriptor od; + RET list; + ELEM element; + + // Type checking + if (! PyList_Check(pyList) ) { + Py_CLEAR(pyList); + // cerr << "ERROR: In Python plugin [" << m_class << "::" << method + // << "] Expected List return type." << endl; + return list; + } + + //This reference will be borrowed + PyObject *pyDict; + + //Parse Output List + for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { + //Get i-th Vamp output descriptor (Borrowed Reference) + pyDict = PyList_GET_ITEM(pyList,i); + element = PyValue_To_VampDescriptor<ELEM>(pyDict); + // Check for empty Feature/Descriptor as before? + list.push_back(element); + } + return list; + } + + + //Vamp specific types + + Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const; + inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const + { r = this->PyValue_To_FeatureSet(pyValue); } + + Vamp::RealTime::RealTime PyValue_To_RealTime(PyObject*) const; + inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime::RealTime &r) const + { r = this->PyValue_To_RealTime(pyValue); } + + + /* Overloaded PyValue_To_rValue() to support generic functions */ + inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const + { float tmp = this->PyValue_To_Float(pyValue); + if(!m_error) defValue = tmp; } + inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const + { size_t tmp = this->PyValue_To_Size_t(pyValue); + if(!m_error) defValue = tmp; } + inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const + { bool tmp = this->PyValue_To_Bool(pyValue); + if(!m_error) defValue = tmp; } + inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const + { std::string tmp = this->PyValue_To_String(pyValue); + if(!m_error) defValue = tmp; } + /*used by templates where we expect no return value, if there is one it will be ignored*/ + inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const + { if (m_strict && pyValue != Py_None) + setValueError("Strict conversion error: expected 'None' type.",m_strict); + } + + /* convert sequence types to Vamp List types */ + inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const + { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); } + inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const + { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); } + inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const + { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); } + + /// this is only needed for RealTime->Frame conversion + void setInputSampleRate(float inputSampleRate) + { m_inputSampleRate = (unsigned int) inputSampleRate; } + +private: + bool m_strict; + ValueError m_noError; + mutable bool m_error; + mutable ValueError& m_lastError; + mutable std::queue<ValueError> m_errorQueue; + // we only use it for RealTime conversion which requires unsigned int + unsigned int m_inputSampleRate; + + void setValueError(std::string,bool) const; + ValueError& _lastError() const; + + /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */ + inline void _convert(PyObject *pyValue,float &r) const + { r = PyValue_To_Float(pyValue); } + inline void _convert(PyObject *pyValue,size_t &r) const + { r = PyValue_To_Size_t(pyValue); } + inline void _convert(PyObject *pyValue,bool &r) const + { r = PyValue_To_Bool(pyValue); } + inline void _convert(PyObject *pyValue,std::string &r) const + { r = PyValue_To_String(pyValue); } + inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const + { r = PyValue_To_StringVector(pyValue); } + inline void _convert(PyObject *pyValue,std::vector<float> &r) const + { r = PyValue_To_FloatVector(pyValue); } + inline void _convert(PyObject *pyValue,Vamp::RealTime::RealTime &r) const + { r = PyValue_To_RealTime(pyValue); } + +public: + const bool& error; + +}; + +#endif \ No newline at end of file
--- a/pyvamp-main.cpp Tue Aug 25 08:49:22 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,224 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/** - * This Vamp plugin is a wrapper for Python Scripts. (VamPy) - * Centre for Digital Music, Queen Mary, University of London. - * Copyright 2008, George Fazekas. - */ - -#include <Python.h> -#include "vamp/vamp.h" -#include "vamp-sdk/PluginAdapter.h" -#include "PyPlugScanner.h" -#include "PyPlugin.h" - -#ifdef _WIN32 -#define pathsep ('\\') -#include <windows.h> -#include <tchar.h> -#else -#define pathsep ('/') -#include <dirent.h> -#include <dlfcn.h> -#endif - -using std::cerr; -using std::endl; -using std::string; -using std::vector; - -//volatile bool mutex = false; -static int adinstcount; - -class PyPluginAdapter : public Vamp::PluginAdapterBase -{ -public: - PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) : - PluginAdapterBase(), - m_plug(pyPlugId), - m_pyClass(pyClass) - { - cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; - adinstcount++; - m_instanceCount = 0; - } - - ~PyPluginAdapter() - { - } - -protected: - Vamp::Plugin *createPlugin(float inputSampleRate) - { - try { - PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass); - m_instanceCount++; - return plugin; - } catch (...) { - cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl; - return 0; - } - } - - std::string m_plug; - bool m_haveInitialized; - PyObject *m_pyClass; - int m_instanceCount; -}; - - -static std::vector<PyPluginAdapter *> adapters; -static bool haveScannedPlugins = false; - -static bool tryPreload(string name) -{ - cerr << "Trying to load Python interpreter library \"" << name << "\"..."; -#ifdef _WIN32 - void *lib = LoadLibrary(name.c_str()); - if (!lib) { - cerr << " failed" << endl; - return false; - } -#else - void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); - if (!lib) { - cerr << " failed" << endl; - return false; - } -#endif - cerr << " succeeded" << endl; - return true; -} - -static bool preloadPython() -{ -#ifdef _WIN32 - // this doesn't seem to be necessary at all on Windows - return true; -#endif - - string pyver = Py_GetVersion(); - int dots = 2; - string shortver; - for (size_t i = 0; i < pyver.length(); ++i) { - if (pyver[i] == '.') { - if (--dots == 0) { - shortver = pyver.substr(0, i); - break; - } - } - } - cerr << "Short version: " << shortver << endl; - - vector<string> pfxs; - pfxs.push_back(""); - pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/"); - pfxs.push_back(string(Py_GetExecPrefix()) + "/"); - pfxs.push_back("/usr/lib/"); - pfxs.push_back("/usr/local/lib/"); - char buffer[5]; - - // hahaha! grossness is like a brother to us -#ifdef __APPLE__ - for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { - for (int minor = 8; minor >= 0; --minor) { - sprintf(buffer, "%d", minor); - if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true; - } - if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true; - if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true; - } -#else - for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { - for (int minor = 8; minor >= 0; --minor) { - sprintf(buffer, "%d", minor); - if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true; - } - if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true; - if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true; - } -#endif - - return false; -} - -/* This doesn't work: don't try it again. -static bool initPython() -{ - // preloadPython(); - Py_Initialize(); -#ifndef _WIN32 - //set dlopen flags form Python - string pyCmd = "from sys import setdlopenflags\nimport dl\nsetdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)\n"; - if (PyRun_SimpleString(pyCmd.c_str()) == -1) - { - cerr << "Warning: Could not set dlopen flasgs. Dynamic loading in scripts will fail." << endl; - return false; - } -#endif - PyEval_InitThreads(); - return Py_IsInitialized(); -} -*/ - -const VampPluginDescriptor -*vampGetPluginDescriptor(unsigned int version,unsigned int index) -{ - if (version < 1) return 0; - - int isPythonInitialized = Py_IsInitialized(); - cerr << "# isPythonInitialized: " << isPythonInitialized << endl; - cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; - - if (!haveScannedPlugins) { - - if (!isPythonInitialized){ - - if (!preloadPython()) - cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl; - Py_Initialize(); - cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl; - // PyEval_InitThreads(); //not sure why this was needed - } - - vector<string> pyPlugs; - vector<string> pyPath; - vector<PyObject *> pyClasses; - static PyPlugScanner *scanner; - - //Scanning Plugins - cerr << "Scanning PyPlugins" << endl; - scanner = PyPlugScanner::getInstance(); - pyPath=scanner->getAllValidPath(); - //add this as extra path for development - //pyPath.push_back("/Users/Shared/Development/vamp-experiments"); - scanner->setPath(pyPath); - pyPlugs = scanner->getPyPlugs(); - cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl; - //TODO: this will support multiple classes per script - pyClasses = scanner->getPyClasses(); - cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl; - - for (size_t i = 0; i < pyPlugs.size(); ++i) { - adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i])); - } - haveScannedPlugins=true; - - } - - cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl; - if (index<adapters.size()) { - const VampPluginDescriptor *tmp = adapters[index]->getDescriptor(); - return tmp; - } else return 0; - - -} - - - - - - - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vampy-main.cpp Sun Sep 20 17:31:20 2009 +0000 @@ -0,0 +1,248 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/** + * This Vamp plugin is a wrapper for Python Scripts. (VamPy) + * Centre for Digital Music, Queen Mary, University of London. + * Copyright 2008, George Fazekas. + */ + +#include <Python.h> +#include "vamp/vamp.h" +#include "vamp-sdk/PluginAdapter.h" +#include "PyPlugScanner.h" +#include "PyPlugin.h" +// #include "host/pyRealTime.h" +#include "PyExtensionModule.h" + + +#ifdef _WIN32 +#define pathsep ('\\') +#include <windows.h> +#include <tchar.h> +#else +#define pathsep ('/') +#include <dirent.h> +#include <dlfcn.h> +#endif + +using std::cerr; +using std::endl; +using std::string; +using std::vector; + + +//volatile bool mutex = false; +static int adinstcount; +static int totinstcount; + +class PyPluginAdapter : public Vamp::PluginAdapterBase +{ +public: + PyPluginAdapter(std::string pyPlugId, PyObject* pyClass) : + PluginAdapterBase(), + m_plug(pyPlugId), + m_pyClass(pyClass) + { + cerr << "PyPluginAdapter:ctor:"<< adinstcount << ": " << m_plug << endl; + adinstcount++; + m_instanceCount = 0; + } + + ~PyPluginAdapter() + { + } + +protected: + Vamp::Plugin *createPlugin(float inputSampleRate) + { + try { + PyPlugin *plugin = new PyPlugin(m_plug, inputSampleRate, m_pyClass, totinstcount); + // m_instanceCount++; /// do this in the ctors + return plugin; + } catch (...) { + cerr << "PyPluginAdapter::createPlugin: Failed to construct PyPlugin" << endl; + return 0; + } + } + + std::string m_plug; + bool m_haveInitialized; + PyObject *m_pyClass; + int m_instanceCount; +}; + + +static std::vector<PyPluginAdapter *> adapters; +static bool haveScannedPlugins = false; +static bool haveVampyInitialised = false; + +static bool tryPreload(string name) +{ + cerr << "Trying to load Python interpreter library \"" << name << "\"..."; +#ifdef _WIN32 + void *lib = LoadLibrary(name.c_str()); + if (!lib) { + cerr << " failed" << endl; + return false; + } +#else + void *lib = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL); + if (!lib) { + cerr << " failed" << endl; + return false; + } +#endif + cerr << " succeeded" << endl; + return true; +} + +static bool preloadPython() +{ +#ifdef _WIN32 + // this doesn't seem to be necessary at all on Windows + return true; +#endif + + string pyver = Py_GetVersion(); + int dots = 2; + string shortver; + for (size_t i = 0; i < pyver.length(); ++i) { + if (pyver[i] == '.') { + if (--dots == 0) { + shortver = pyver.substr(0, i); + break; + } + } + } + cerr << "Short version: " << shortver << endl; + + vector<string> pfxs; + pfxs.push_back(""); + pfxs.push_back(string(Py_GetExecPrefix()) + "/lib/"); + pfxs.push_back(string(Py_GetExecPrefix()) + "/"); + pfxs.push_back("/usr/lib/"); + pfxs.push_back("/usr/local/lib/"); + char buffer[5]; + + // hahaha! grossness is like a brother to us +#ifdef __APPLE__ + for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { + for (int minor = 8; minor >= 0; --minor) { + sprintf(buffer, "%d", minor); + if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib." + buffer)) return true; + } + if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".dylib")) return true; + if (tryPreload(pfxs[pfxidx] + string("libpython.dylib"))) return true; + } +#else + for (size_t pfxidx = 0; pfxidx < pfxs.size(); ++pfxidx) { + for (int minor = 8; minor >= 0; --minor) { + sprintf(buffer, "%d", minor); + if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so." + buffer)) return true; + } + if (tryPreload(pfxs[pfxidx] + string("libpython") + shortver + ".so")) return true; + if (tryPreload(pfxs[pfxidx] + string("libpython.so"))) return true; + } +#endif + + return false; +} + +/* This doesn't work: don't try it again. +static bool initPython() +{ + // preloadPython(); + Py_Initialize(); +#ifndef _WIN32 + //set dlopen flags form Python + string pyCmd = "from sys import setdlopenflags\nimport dl\nsetdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL)\n"; + if (PyRun_SimpleString(pyCmd.c_str()) == -1) + { + cerr << "Warning: Could not set dlopen flasgs. Dynamic loading in scripts will fail." << endl; + return false; + } +#endif + PyEval_InitThreads(); + return Py_IsInitialized(); +} +*/ + +const VampPluginDescriptor +*vampGetPluginDescriptor(unsigned int version,unsigned int index) +{ + if (version < 1) return 0; + + int isPythonInitialized = Py_IsInitialized(); + cerr << "# isPythonInitialized: " << isPythonInitialized << endl; + cerr << "# haveScannedPlugins: " << haveScannedPlugins << endl; + + if (!haveScannedPlugins) { + + if (!isPythonInitialized){ + + if (!preloadPython()) + cerr << "Warning: Could not preload Python. Dynamic loading in scripts will fail." << endl; + + int ext = PyImport_AppendInittab("vampy",initvampy); + if (ext == -1) cerr << "Extension unsuccessful." << endl; + else cerr << "Extension successful." << endl; + Py_Initialize(); + initvampy(); + // if (!PyImport_ImportModule("vampy")) + // cerr << "Could not import extension." << endl; + + // Py_InitModule("vampy", VampyMethods); + + // initpyRealTime(); + cerr << "# isPythonInitialized after initialize: " << Py_IsInitialized() << endl; + // PyEval_InitThreads(); //not sure why this was needed + } + + vector<string> pyPlugs; + vector<string> pyPath; + vector<PyObject *> pyClasses; + static PyPlugScanner *scanner; + + //Scanning Plugins + cerr << "Scanning PyPlugins" << endl; + scanner = PyPlugScanner::getInstance(); + pyPath=scanner->getAllValidPath(); + //add this as extra path for development + //pyPath.push_back("/Users/Shared/Development/vamp-experiments"); + scanner->setPath(pyPath); + pyPlugs = scanner->getPyPlugs(); + cerr << "Found " << pyPlugs.size() << " Scripts ...OK" << endl; + //TODO: this should support multiple classes per script (?) + pyClasses = scanner->getPyClasses(); + cerr << "Found " << pyClasses.size() << " Classes ...OK" << endl; + + for (size_t i = 0; i < pyPlugs.size(); ++i) { + adapters.push_back( new PyPluginAdapter(pyPlugs[i],pyClasses[i])); + } + haveScannedPlugins=true; + // if (!haveVampyInitialised) { + // // PyImport_AddModule("vampy"); + // // if (!PyImport_ImportModule("vampy")) + // cerr << "Could not import extension." << endl; + // initvampy(); + // haveVampyInitialised = true; + // cerr << "Extension initialised from main." << endl; + // } + } + + cerr << "Accessing adapter index: " << index << " (adapters: " << adapters.size() << ")" << endl; + if (index<adapters.size()) { + const VampPluginDescriptor *tmp = adapters[index]->getDescriptor(); + return tmp; + } else return 0; + + +} + + + + + + + +