fazekasgy@37
|
1 /*
|
fazekasgy@37
|
2
|
fazekasgy@37
|
3 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
4 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
5
|
fazekasgy@37
|
6 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
8 * for licence information.)
|
fazekasgy@37
|
9
|
fazekasgy@37
|
10 */
|
fazekasgy@37
|
11
|
fazekasgy@37
|
12 #include <Python.h>
|
fazekasgy@37
|
13 #include "PyExtensionModule.h"
|
fazekasgy@37
|
14 #include "PyRealTime.h"
|
fazekasgy@37
|
15 #include "PyFeature.h"
|
fazekasgy@37
|
16 #include "PyFeatureSet.h"
|
fazekasgy@37
|
17 #include "PyParameterDescriptor.h"
|
fazekasgy@37
|
18 #include "PyOutputDescriptor.h"
|
Chris@67
|
19 #include "Debug.h"
|
fazekasgy@37
|
20 #include "vamp/vamp.h"
|
fazekasgy@37
|
21 #include "vamp-sdk/Plugin.h"
|
fazekasgy@37
|
22
|
fazekasgy@37
|
23 using namespace std;
|
fazekasgy@37
|
24 using namespace Vamp;
|
fazekasgy@37
|
25 using Vamp::Plugin;
|
fazekasgy@37
|
26 using Vamp::RealTime;
|
fazekasgy@37
|
27
|
fazekasgy@37
|
28 /* Functions Exposed by Vampy */
|
fazekasgy@37
|
29
|
fazekasgy@37
|
30 /* Creating PyRealTime Objects from frame count */
|
fazekasgy@37
|
31
|
fazekasgy@37
|
32 /* New RealTime object from Frame (with given samplerate) */
|
fazekasgy@37
|
33 static PyObject *
|
fazekasgy@37
|
34 RealTime_frame2RealTime(PyObject *ignored, PyObject *args)
|
fazekasgy@37
|
35 {
|
fazekasgy@37
|
36 long frame;
|
fazekasgy@37
|
37 unsigned int sampleRate;
|
fazekasgy@37
|
38
|
fazekasgy@37
|
39 if (!(args && PyTuple_GET_SIZE(args) == 2)) {
|
fazekasgy@37
|
40 PyErr_SetString(PyExc_ValueError,"frame2RealTime requires two arguments: frame and sample rate.");
|
fazekasgy@37
|
41 return NULL;
|
fazekasgy@37
|
42 }
|
fazekasgy@37
|
43
|
fazekasgy@37
|
44 PyObject* pyFrame = PyTuple_GET_ITEM(args,0);
|
fazekasgy@37
|
45 PyObject* pySampleRate = PyTuple_GET_ITEM(args,1);
|
fazekasgy@37
|
46
|
fazekasgy@37
|
47 /// frame
|
fazekasgy@37
|
48 if (PyInt_Check(pyFrame)) frame = PyInt_AS_LONG(pyFrame);
|
fazekasgy@37
|
49 else if (PyLong_Check(pyFrame)) frame = PyLong_AsLong(pyFrame);
|
fazekasgy@37
|
50 else {
|
fazekasgy@37
|
51 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'frame' argument must be long integer.");
|
fazekasgy@37
|
52 return NULL;
|
fazekasgy@37
|
53 }
|
fazekasgy@37
|
54
|
fazekasgy@37
|
55 /// sample rate
|
fazekasgy@37
|
56 if (PyInt_Check(pySampleRate))
|
fazekasgy@37
|
57 sampleRate = _long2uint(PyInt_AS_LONG(pySampleRate));
|
fazekasgy@37
|
58 else if (PyFloat_Check(pySampleRate))
|
fazekasgy@37
|
59 sampleRate = _dbl2uint(PyFloat_AS_DOUBLE(pySampleRate));
|
fazekasgy@37
|
60 else if (PyLong_Check(pySampleRate))
|
fazekasgy@37
|
61 sampleRate = _long2uint(PyLong_AsLong(pySampleRate));
|
fazekasgy@37
|
62 else {
|
fazekasgy@37
|
63 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument must be int, long or float.");
|
fazekasgy@37
|
64 return NULL;
|
fazekasgy@37
|
65 }
|
fazekasgy@37
|
66
|
fazekasgy@37
|
67 if (!sampleRate) {
|
fazekasgy@37
|
68 PyErr_SetString(PyExc_ValueError,"frame2RealTime 'sample rate' argument overflow error. Argument must be 0 < arg < UINT_MAX.");
|
fazekasgy@37
|
69 cerr << "Value: " << sampleRate << endl;
|
fazekasgy@37
|
70 return NULL;
|
fazekasgy@37
|
71 }
|
fazekasgy@37
|
72
|
fazekasgy@37
|
73 // simpler but slower:
|
fazekasgy@37
|
74 // if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ",
|
fazekasgy@37
|
75 // &frame,&sampleRate))
|
fazekasgy@37
|
76 // return NULL;
|
fazekasgy@37
|
77
|
fazekasgy@37
|
78 RealTimeObject *self;
|
fazekasgy@37
|
79 self = PyObject_New(RealTimeObject, &RealTime_Type);
|
fazekasgy@37
|
80 if (self == NULL) return NULL;
|
fazekasgy@37
|
81
|
fazekasgy@37
|
82 self->rt = new RealTime(
|
fazekasgy@37
|
83 RealTime::frame2RealTime(frame,sampleRate));
|
fazekasgy@37
|
84
|
fazekasgy@37
|
85 return (PyObject *) self;
|
fazekasgy@37
|
86 }
|
fazekasgy@37
|
87
|
fazekasgy@37
|
88 /*
|
fazekasgy@37
|
89
|
fazekasgy@37
|
90 Note: these functions are not very interesting on their own, but
|
fazekasgy@37
|
91 they can be used to make the semantics of the plugin clearer.
|
fazekasgy@37
|
92 They return ordinary Python list objects. All type checking
|
fazekasgy@37
|
93 is performed in the type interface.
|
fazekasgy@37
|
94
|
fazekasgy@37
|
95 */
|
fazekasgy@37
|
96
|
fazekasgy@37
|
97 /* New PyOutputList Objects */
|
fazekasgy@37
|
98 static PyObject *
|
fazekasgy@37
|
99 OutputList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@37
|
100 {
|
cannam@46
|
101 if (args && PyTuple_Check(args))
|
fazekasgy@37
|
102 return PySequence_List(args);
|
fazekasgy@37
|
103 else return (PyObject *) PyList_New(0);
|
fazekasgy@37
|
104 }
|
fazekasgy@37
|
105
|
fazekasgy@37
|
106
|
fazekasgy@37
|
107 /* New PyParameterList Objects */
|
fazekasgy@37
|
108 static PyObject *
|
fazekasgy@37
|
109 ParameterList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@37
|
110 {
|
cannam@46
|
111 if (args && PyTuple_Check(args))
|
fazekasgy@37
|
112 return PySequence_List(args);
|
fazekasgy@37
|
113 else return (PyObject *) PyList_New(0);
|
fazekasgy@37
|
114 }
|
fazekasgy@37
|
115
|
fazekasgy@37
|
116 /* New PyFeatureList Objects */
|
fazekasgy@37
|
117 static PyObject *
|
fazekasgy@37
|
118 FeatureList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@37
|
119 {
|
cannam@46
|
120 if (args && PyTuple_Check(args))
|
fazekasgy@37
|
121 return PySequence_List(args);
|
fazekasgy@37
|
122 else return (PyObject *) PyList_New(0);
|
fazekasgy@37
|
123 }
|
fazekasgy@37
|
124
|
fazekasgy@37
|
125
|
fazekasgy@37
|
126 /* Declare the methods exposed by the vampy module */
|
fazekasgy@37
|
127
|
fazekasgy@37
|
128
|
fazekasgy@37
|
129 PyMethodDef VampyMethods[] = {
|
fazekasgy@37
|
130 /*NOTE: This is conventionally static, but limiting the scope
|
fazekasgy@37
|
131 here will cause seg fault if the declared functions are
|
fazekasgy@37
|
132 called back from a Python function wrapped in a C++ class.*/
|
fazekasgy@37
|
133
|
fazekasgy@37
|
134 {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS,
|
fazekasgy@37
|
135 PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")},
|
fazekasgy@37
|
136
|
fazekasgy@37
|
137 {"OutputList", OutputList_new, METH_VARARGS,
|
fazekasgy@37
|
138 PyDoc_STR("OutputList() -> returns new OutputList object")},
|
fazekasgy@37
|
139
|
fazekasgy@37
|
140 {"ParameterList", ParameterList_new, METH_VARARGS,
|
fazekasgy@37
|
141 PyDoc_STR("ParameterList() -> returns new ParameterList object")},
|
fazekasgy@37
|
142
|
fazekasgy@37
|
143 {"FeatureList", FeatureList_new, METH_VARARGS,
|
fazekasgy@37
|
144 PyDoc_STR("FeatureList() -> returns new FeatureList object")},
|
fazekasgy@37
|
145
|
fazekasgy@37
|
146 {NULL, NULL, 0, NULL}
|
fazekasgy@37
|
147 };
|
fazekasgy@37
|
148
|
fazekasgy@37
|
149 /* Module Documentation */
|
fazekasgy@37
|
150 // PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers.");
|
fazekasgy@37
|
151
|
fazekasgy@37
|
152 static int
|
Chris@66
|
153 setint(PyObject *d, const char *name, int value)
|
fazekasgy@37
|
154 {
|
fazekasgy@37
|
155 PyObject *v;
|
fazekasgy@37
|
156 int err;
|
fazekasgy@37
|
157 v = PyInt_FromLong((long)value);
|
fazekasgy@37
|
158 err = PyDict_SetItemString(d, name, v);
|
fazekasgy@37
|
159 Py_XDECREF(v);
|
fazekasgy@37
|
160 return err;
|
fazekasgy@37
|
161 }
|
fazekasgy@37
|
162
|
fazekasgy@37
|
163 static int
|
Chris@66
|
164 setdbl(PyObject *d, const char *name, double value)
|
fazekasgy@37
|
165 {
|
fazekasgy@37
|
166 PyObject *v;
|
fazekasgy@37
|
167 int err;
|
fazekasgy@37
|
168 v = PyFloat_FromDouble(value);
|
fazekasgy@37
|
169 err = PyDict_SetItemString(d, name, v);
|
fazekasgy@37
|
170 Py_XDECREF(v);
|
fazekasgy@37
|
171 return err;
|
fazekasgy@37
|
172 }
|
fazekasgy@37
|
173
|
fazekasgy@37
|
174 static int
|
Chris@66
|
175 setstr(PyObject *d, const char *name, const char *value)
|
fazekasgy@37
|
176 {
|
fazekasgy@37
|
177 PyObject *v;
|
fazekasgy@37
|
178 int err;
|
fazekasgy@37
|
179 v = PyString_FromString(value);
|
fazekasgy@37
|
180 err = PyDict_SetItemString(d, name, v);
|
fazekasgy@37
|
181 Py_XDECREF(v);
|
fazekasgy@37
|
182 return err;
|
fazekasgy@37
|
183 }
|
fazekasgy@37
|
184
|
fazekasgy@37
|
185
|
fazekasgy@37
|
186 PyMODINIT_FUNC
|
fazekasgy@37
|
187 initvampy(void)
|
fazekasgy@37
|
188 {
|
fazekasgy@37
|
189 PyObject *module, *mdict;
|
fazekasgy@37
|
190
|
fazekasgy@37
|
191 /* if (PyType_Ready(&Feature_Type) < 0) return;
|
fazekasgy@37
|
192 Note: Why do we get a segfault if this is initialised here?
|
fazekasgy@37
|
193 PyType_Ready adds these object to the GC.
|
fazekasgy@37
|
194 This is OK for an extension module, but it is a mistake here,
|
fazekasgy@37
|
195 because the adresses become invalid when the shared library
|
fazekasgy@37
|
196 is unloaded. When the GC tries to visit a these objects,
|
fazekasgy@37
|
197 it will fail.*/
|
fazekasgy@37
|
198
|
fazekasgy@37
|
199 RealTime_Type.ob_type = &PyType_Type;
|
fazekasgy@37
|
200 Feature_Type.ob_type = &PyType_Type;
|
fazekasgy@37
|
201 OutputDescriptor_Type.ob_type = &PyType_Type;
|
fazekasgy@37
|
202 ParameterDescriptor_Type.ob_type = &PyType_Type;
|
fazekasgy@37
|
203 initFeatureSetType(); // this is derived from the builtin dict
|
fazekasgy@37
|
204
|
fazekasgy@37
|
205 PyImport_AddModule("vampy");
|
fazekasgy@37
|
206 module = Py_InitModule("vampy", VampyMethods);
|
fazekasgy@37
|
207 if (!module) goto failure;
|
fazekasgy@37
|
208 mdict = PyModule_GetDict(module);
|
fazekasgy@37
|
209 if (!mdict) goto failure;
|
fazekasgy@37
|
210
|
fazekasgy@37
|
211 /// vampy plugin wrapper flags
|
fazekasgy@37
|
212 if (setint(mdict, "vf_NULL", vf_NULL) < 0) goto failure;
|
fazekasgy@37
|
213 if (setint(mdict, "vf_DEBUG", vf_DEBUG) < 0) goto failure;
|
fazekasgy@37
|
214 if (setint(mdict, "vf_STRICT", vf_STRICT) < 0) goto failure;
|
fazekasgy@37
|
215 if (setint(mdict, "vf_QUIT", vf_QUIT) < 0) goto failure;
|
fazekasgy@37
|
216 if (setint(mdict, "vf_REALTIME", vf_REALTIME) < 0) goto failure;
|
fazekasgy@37
|
217 if (setint(mdict, "vf_BUFFER", vf_BUFFER) < 0) goto failure;
|
fazekasgy@37
|
218 if (setint(mdict, "vf_ARRAY", vf_ARRAY) < 0) goto failure;
|
fazekasgy@37
|
219 if (setint(mdict, "vf_DEFAULT_V2", vf_DEFAULT_V2) < 0) goto failure;
|
fazekasgy@37
|
220
|
fazekasgy@37
|
221 /// Vamp enum types simulation
|
fazekasgy@37
|
222 if (setint(mdict, "OneSamplePerStep", Vamp::Plugin::OutputDescriptor::OneSamplePerStep) < 0) goto failure;
|
fazekasgy@37
|
223 if (setint(mdict, "FixedSampleRate", Vamp::Plugin::OutputDescriptor::FixedSampleRate) < 0) goto failure;
|
fazekasgy@37
|
224 if (setint(mdict, "VariableSampleRate", Vamp::Plugin::OutputDescriptor::VariableSampleRate) < 0) goto failure;
|
fazekasgy@37
|
225 if (setint(mdict, "TimeDomain", Vamp::Plugin::TimeDomain) < 0) goto failure;
|
fazekasgy@37
|
226 if (setint(mdict, "FrequencyDomain", Vamp::Plugin::FrequencyDomain) < 0) goto failure;
|
fazekasgy@37
|
227
|
fazekasgy@37
|
228 /// module attributes
|
fazekasgy@37
|
229 if (setstr(mdict, "__name__", "vampy") < 0) goto failure;
|
fazekasgy@37
|
230 if (setdbl(mdict, "__version__", 2.0) < 0) goto failure;
|
fazekasgy@37
|
231 if (setdbl(mdict, "__VAMP_API_VERSION__", (double) VAMP_API_VERSION) < 0) goto failure;
|
fazekasgy@37
|
232 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
233 if (setint(mdict, "__numpy__", 1) < 0) goto failure;
|
fazekasgy@37
|
234 #else
|
fazekasgy@37
|
235 if (setint(mdict, "__numpy__", 0) < 0) goto failure;
|
fazekasgy@37
|
236 #endif
|
fazekasgy@37
|
237
|
fazekasgy@37
|
238 /// type objects
|
fazekasgy@37
|
239 Py_INCREF(&RealTime_Type);
|
fazekasgy@37
|
240 if (PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type) !=0) goto failure;
|
fazekasgy@37
|
241
|
fazekasgy@37
|
242 Py_INCREF((PyObject*)&Feature_Type);
|
fazekasgy@37
|
243 if (PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type) !=0) goto failure;
|
fazekasgy@37
|
244
|
fazekasgy@37
|
245 Py_INCREF((PyObject*)&FeatureSet_Type);
|
fazekasgy@37
|
246 if (PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type) !=0) goto failure;
|
fazekasgy@37
|
247
|
fazekasgy@37
|
248 Py_INCREF((PyObject*)&OutputDescriptor_Type);
|
fazekasgy@37
|
249 if (PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type) !=0) goto failure;
|
fazekasgy@37
|
250
|
fazekasgy@37
|
251 Py_INCREF((PyObject*)&ParameterDescriptor_Type);
|
fazekasgy@37
|
252 if (PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type) !=0) goto failure;
|
fazekasgy@37
|
253
|
Chris@67
|
254 DSTREAM << "Vampy: extension module initialised." << endl;
|
fazekasgy@37
|
255
|
fazekasgy@37
|
256 return;
|
fazekasgy@37
|
257
|
fazekasgy@37
|
258 failure :
|
fazekasgy@37
|
259 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@37
|
260 cerr << "Vampy::PyExtensionModule::initvampy: Failed to initialise extension module." << endl;
|
fazekasgy@37
|
261 return;
|
fazekasgy@37
|
262 }
|
fazekasgy@37
|
263
|
fazekasgy@37
|
264
|