comparison PyPluginObject.cpp @ 31:f565f4b5cbaa

Pull out plugin object implementation into separate source file
author Chris Cannam
date Wed, 26 Nov 2014 11:12:00 +0000
parents vampyhost.cpp@7e7f2f7d9542
children d5aba4c3c229
comparison
equal deleted inserted replaced
30:e1e54546d5fd 31:f565f4b5cbaa
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 VampyHost
5
6 Use Vamp audio analysis plugins in Python
7
8 Gyorgy Fazekas and Chris Cannam
9 Centre for Digital Music, Queen Mary, University of London
10 Copyright 2008-2014 Queen Mary, University of London
11
12 Permission is hereby granted, free of charge, to any person
13 obtaining a copy of this software and associated documentation
14 files (the "Software"), to deal in the Software without
15 restriction, including without limitation the rights to use, copy,
16 modify, merge, publish, distribute, sublicense, and/or sell copies
17 of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be
21 included in all copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31 Except as contained in this notice, the names of the Centre for
32 Digital Music; Queen Mary, University of London; and the authors
33 shall not be used in advertising or otherwise to promote the sale,
34 use or other dealings in this Software without prior written
35 authorization.
36 */
37
38 #include "PyPluginObject.h"
39
40 // define a unique API pointer
41 #define PY_ARRAY_UNIQUE_SYMBOL VAMPYHOST_ARRAY_API
42 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
43 #define NO_IMPORT_ARRAY
44 #include "numpy/arrayobject.h"
45
46 #include "VectorConversion.h"
47 #include "PyRealTime.h"
48
49 #include <string>
50 #include <vector>
51
52 using namespace std;
53 using namespace Vamp;
54
55 static void
56 PyPluginObject_dealloc(PyPluginObject *self)
57 {
58 cerr << "PyPluginObject_dealloc" << endl;
59 delete self->plugin;
60 PyObject_Del(self);
61 }
62
63 PyDoc_STRVAR(xx_foo_doc, "Some description"); //!!!
64
65 //!!! todo: conv errors
66
67 static
68 PyPluginObject *
69 getPluginObject(PyObject *pyPluginHandle)
70 {
71 cerr << "getPluginObject" << endl;
72
73 PyPluginObject *pd = 0;
74 if (PyPlugin_Check(pyPluginHandle)) {
75 pd = (PyPluginObject *)pyPluginHandle;
76 }
77 if (!pd || !pd->plugin) {
78 PyErr_SetString(PyExc_AttributeError,
79 "Invalid or already deleted plugin handle.");
80 return 0;
81 } else {
82 return pd;
83 }
84 }
85
86 PyObject *
87 PyPluginObject_From_Plugin(Plugin *plugin)
88 {
89 PyPluginObject *pd =
90 (PyPluginObject *)PyType_GenericAlloc(&Plugin_Type, 0);
91 pd->plugin = plugin;
92 pd->isInitialised = false;
93 pd->channels = 0;
94 pd->blockSize = 0;
95 pd->stepSize = 0;
96 return (PyObject *)pd;
97 }
98
99 static PyObject *
100 vampyhost_initialise(PyObject *self, PyObject *args)
101 {
102 cerr << "vampyhost_initialise" << endl;
103
104 size_t channels, blockSize, stepSize;
105
106 if (!PyArg_ParseTuple (args, "nnn",
107 (size_t) &channels,
108 (size_t) &stepSize,
109 (size_t) &blockSize)) {
110 PyErr_SetString(PyExc_TypeError,
111 "initialise() takes channel count, step size, and block size arguments");
112 return 0;
113 }
114
115 PyPluginObject *pd = getPluginObject(self);
116 if (!pd) return 0;
117
118 pd->channels = channels;
119 pd->stepSize = stepSize;
120 pd->blockSize = blockSize;
121
122 if (!pd->plugin->initialise(channels, stepSize, blockSize)) {
123 cerr << "Failed to initialise native plugin adapter with channels = " << channels << ", stepSize = " << stepSize << ", blockSize = " << blockSize << " and ADAPT_ALL_SAFE set" << endl;
124 PyErr_SetString(PyExc_TypeError,
125 "Plugin initialization failed");
126 return 0;
127 }
128
129 pd->isInitialised = true;
130
131 return Py_True;
132 }
133
134 static PyObject *
135 vampyhost_reset(PyObject *self, PyObject *)
136 {
137 cerr << "vampyhost_reset" << endl;
138
139 PyPluginObject *pd = getPluginObject(self);
140 if (!pd) return 0;
141
142 if (!pd->isInitialised) {
143 PyErr_SetString(PyExc_StandardError,
144 "Plugin has not been initialised");
145 return 0;
146 }
147
148 pd->plugin->reset();
149 return Py_True;
150 }
151
152 static PyObject *
153 vampyhost_getParameter(PyObject *self, PyObject *args)
154 {
155 cerr << "vampyhost_getParameter" << endl;
156
157 PyObject *pyParam;
158
159 if (!PyArg_ParseTuple(args, "S", &pyParam)) {
160 PyErr_SetString(PyExc_TypeError,
161 "getParameter() takes parameter id (string) argument");
162 return 0; }
163
164 PyPluginObject *pd = getPluginObject(self);
165 if (!pd) return 0;
166
167 float value = pd->plugin->getParameter(PyString_AS_STRING(pyParam));
168 return PyFloat_FromDouble(double(value));
169 }
170
171 static PyObject *
172 vampyhost_setParameter(PyObject *self, PyObject *args)
173 {
174 cerr << "vampyhost_setParameter" << endl;
175
176 PyObject *pyParam;
177 float value;
178
179 if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) {
180 PyErr_SetString(PyExc_TypeError,
181 "setParameter() takes parameter id (string), and value (float) arguments");
182 return 0; }
183
184 PyPluginObject *pd = getPluginObject(self);
185 if (!pd) return 0;
186
187 pd->plugin->setParameter(PyString_AS_STRING(pyParam), value);
188 return Py_True;
189 }
190
191 static PyObject *
192 vampyhost_process(PyObject *self, PyObject *args)
193 {
194 cerr << "vampyhost_process" << endl;
195
196 PyObject *pyBuffer;
197 PyObject *pyRealTime;
198
199 if (!PyArg_ParseTuple(args, "OO",
200 &pyBuffer, // Audio data
201 &pyRealTime)) { // TimeStamp
202 PyErr_SetString(PyExc_TypeError,
203 "process() takes plugin handle (object), buffer (2D array of channels * samples floats) and timestamp (RealTime) arguments");
204 return 0; }
205
206 if (!PyRealTime_Check(pyRealTime)) {
207 PyErr_SetString(PyExc_TypeError,"Valid timestamp required.");
208 return 0; }
209
210 if (!PyList_Check(pyBuffer)) {
211 PyErr_SetString(PyExc_TypeError, "List of NumPy Array required for process input.");
212 return 0;
213 }
214
215 PyPluginObject *pd = getPluginObject(self);
216 if (!pd) return 0;
217
218 if (!pd->isInitialised) {
219 PyErr_SetString(PyExc_StandardError,
220 "Plugin has not been initialised.");
221 return 0;
222 }
223
224 int channels = pd->channels;
225
226 if (PyList_GET_SIZE(pyBuffer) != channels) {
227 cerr << "Wrong number of channels: got " << PyList_GET_SIZE(pyBuffer) << ", expected " << channels << endl;
228 PyErr_SetString(PyExc_TypeError, "Wrong number of channels");
229 return 0;
230 }
231
232 float **inbuf = new float *[channels];
233
234 VectorConversion typeConv;
235
236 cerr << "here!" << endl;
237
238 vector<vector<float> > data;
239 for (int c = 0; c < channels; ++c) {
240 PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c);
241 data.push_back(typeConv.PyValue_To_FloatVector(cbuf));
242 }
243
244 for (int c = 0; c < channels; ++c) {
245 if (data[c].size() != pd->blockSize) {
246 cerr << "Wrong number of samples on channel " << c << ": expected " << pd->blockSize << " (plugin's block size), got " << data[c].size() << endl;
247 PyErr_SetString(PyExc_TypeError, "Wrong number of samples");
248 return 0;
249 }
250 inbuf[c] = &data[c][0];
251 }
252
253 cerr << "no, here!" << endl;
254
255 RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime);
256
257 cerr << "no no, here!" << endl;
258
259 Plugin::FeatureSet fs = pd->plugin->process(inbuf, timeStamp);
260
261 delete[] inbuf;
262
263 cerr << "no no no, here!" << endl;
264
265 VectorConversion conv;
266
267 PyObject *pyFs = PyDict_New();
268
269 for (Plugin::FeatureSet::const_iterator fsi = fs.begin();
270 fsi != fs.end(); ++fsi) {
271
272 int fno = fsi->first;
273 const Plugin::FeatureList &fl = fsi->second;
274
275 if (!fl.empty()) {
276
277 PyObject *pyFl = PyList_New(fl.size());
278
279 for (int fli = 0; fli < (int)fl.size(); ++fli) {
280
281 const Plugin::Feature &f = fl[fli];
282 PyObject *pyF = PyDict_New();
283
284 if (f.hasTimestamp) {
285 PyDict_SetItemString
286 (pyF, "timestamp", PyRealTime_FromRealTime(f.timestamp));
287 }
288 if (f.hasDuration) {
289 PyDict_SetItemString
290 (pyF, "duration", PyRealTime_FromRealTime(f.duration));
291 }
292
293 PyDict_SetItemString
294 (pyF, "label", PyString_FromString(f.label.c_str()));
295
296 if (!f.values.empty()) {
297 PyDict_SetItemString
298 (pyF, "values", conv.PyArray_From_FloatVector(f.values));
299 }
300
301 PyList_SET_ITEM(pyFl, fli, pyF);
302 }
303
304 PyObject *pyN = PyInt_FromLong(fno);
305 PyDict_SetItem(pyFs, pyN, pyFl);
306 }
307 }
308
309 cerr << "no you fool, here!" << endl;
310
311 return pyFs;
312 }
313
314 static PyObject *
315 vampyhost_unload(PyObject *self, PyObject *)
316 {
317 cerr << "vampyhost_unloadPlugin" << endl;
318
319 PyPluginObject *pd = getPluginObject(self);
320 if (!pd) return 0;
321
322 delete pd->plugin;
323 pd->plugin = 0; // This is checked by getPluginObject, so we
324 // attempt to avoid repeated calls from blowing up
325
326 return Py_True;
327 }
328
329 static PyMethodDef PyPluginObject_methods[] =
330 {
331 {"getParameter", vampyhost_getParameter, METH_VARARGS,
332 xx_foo_doc}, //!!! fix all these!
333
334 {"setParameter", vampyhost_setParameter, METH_VARARGS,
335 xx_foo_doc},
336
337 {"initialise", vampyhost_initialise, METH_VARARGS,
338 xx_foo_doc},
339
340 {"reset", vampyhost_reset, METH_NOARGS,
341 xx_foo_doc},
342
343 {"process", vampyhost_process, METH_VARARGS,
344 xx_foo_doc},
345
346 {"unload", vampyhost_unload, METH_NOARGS,
347 xx_foo_doc},
348
349 {0, 0}
350 };
351
352 static int
353 PyPluginObject_setattr(PyPluginObject *self, char *name, PyObject *value)
354 {
355 return -1;
356 }
357
358 static PyObject *
359 PyPluginObject_getattr(PyPluginObject *self, char *name)
360 {
361 return Py_FindMethod(PyPluginObject_methods, (PyObject *)self, name);
362 }
363
364 /* Doc:: 10.3 Type Objects */ /* static */
365 PyTypeObject Plugin_Type =
366 {
367 PyObject_HEAD_INIT(NULL)
368 0, /*ob_size*/
369 "vampyhost.Plugin", /*tp_name*/
370 sizeof(PyPluginObject), /*tp_basicsize*/
371 0, /*tp_itemsize*/
372 (destructor)PyPluginObject_dealloc, /*tp_dealloc*/
373 0, /*tp_print*/
374 (getattrfunc)PyPluginObject_getattr, /*tp_getattr*/
375 (setattrfunc)PyPluginObject_setattr, /*tp_setattr*/
376 0, /*tp_compare*/
377 0, /*tp_repr*/
378 0, /*tp_as_number*/
379 0, /*tp_as_sequence*/
380 0, /*tp_as_mapping*/
381 0, /*tp_hash*/
382 0, /*tp_call*/
383 0, /*tp_str*/
384 0, /*tp_getattro*/
385 0, /*tp_setattro*/
386 0, /*tp_as_buffer*/
387 Py_TPFLAGS_DEFAULT, /*tp_flags*/
388 "Plugin Object", /*tp_doc*/
389 0, /*tp_traverse*/
390 0, /*tp_clear*/
391 0, /*tp_richcompare*/
392 0, /*tp_weaklistoffset*/
393 0, /*tp_iter*/
394 0, /*tp_iternext*/
395 PyPluginObject_methods, /*tp_methods*/
396 0, /*tp_members*/
397 0, /*tp_getset*/
398 0, /*tp_base*/
399 0, /*tp_dict*/
400 0, /*tp_descr_get*/
401 0, /*tp_descr_set*/
402 0, /*tp_dictoffset*/
403 0, /*tp_init*/
404 PyType_GenericAlloc, /*tp_alloc*/
405 0, /*tp_new*/
406 PyObject_Del, /*tp_free*/
407 0, /*tp_is_gc*/
408 };
409