Mercurial > hg > vampy-host
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 |