comparison native/PyPluginObject.cpp @ 52:b56513f872a5

Move files into subdirs
author Chris Cannam
date Wed, 14 Jan 2015 08:30:47 +0000
parents PyPluginObject.cpp@d4a3cd9dcf2c
children ee7542afa98e
comparison
equal deleted inserted replaced
51:a78b14c41c74 52:b56513f872a5
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 "structmember.h"
47
48 #include "FloatConversion.h"
49 #include "VectorConversion.h"
50 #include "PyRealTime.h"
51
52 #include <string>
53 #include <vector>
54 #include <cstddef>
55 #include <set>
56
57 using namespace std;
58 using namespace Vamp;
59
60 static
61 PyPluginObject *
62 getPluginObject(PyObject *pyPluginHandle)
63 {
64 PyPluginObject *pd = 0;
65 if (PyPlugin_Check(pyPluginHandle)) {
66 pd = (PyPluginObject *)pyPluginHandle;
67 }
68 if (!pd || !pd->plugin) {
69 PyErr_SetString(PyExc_AttributeError,
70 "Invalid or already deleted plugin handle.");
71 return 0;
72 } else {
73 return pd;
74 }
75 }
76
77 static
78 PyObject *
79 pystr(const string &s)
80 {
81 return PyString_FromString(s.c_str());
82 }
83
84 PyObject *
85 PyPluginObject_From_Plugin(Plugin *plugin)
86 {
87 PyPluginObject *pd =
88 (PyPluginObject *)PyType_GenericAlloc(&Plugin_Type, 0);
89 pd->plugin = plugin;
90 pd->isInitialised = false;
91 pd->channels = 0;
92 pd->blockSize = 0;
93 pd->stepSize = 0;
94
95 PyObject *infodict = PyDict_New();
96 PyDict_SetItemString
97 (infodict, "apiVersion", PyInt_FromLong(plugin->getVampApiVersion()));
98 PyDict_SetItemString
99 (infodict, "pluginVersion", PyInt_FromLong(plugin->getPluginVersion()));
100 PyDict_SetItemString
101 (infodict, "identifier", pystr(plugin->getIdentifier()));
102 PyDict_SetItemString
103 (infodict, "name", pystr(plugin->getName()));
104 PyDict_SetItemString
105 (infodict, "description", pystr(plugin->getDescription()));
106 PyDict_SetItemString
107 (infodict, "maker", pystr(plugin->getMaker()));
108 PyDict_SetItemString
109 (infodict, "copyright", pystr(plugin->getCopyright()));
110 pd->info = infodict;
111
112 pd->inputDomain = plugin->getInputDomain();
113
114 VectorConversion conv;
115
116 Plugin::ParameterList pl = plugin->getParameterDescriptors();
117 PyObject *params = PyList_New(pl.size());
118
119 for (int i = 0; i < (int)pl.size(); ++i) {
120 PyObject *paramdict = PyDict_New();
121 PyDict_SetItemString
122 (paramdict, "identifier", pystr(pl[i].identifier));
123 PyDict_SetItemString
124 (paramdict, "name", pystr(pl[i].name));
125 PyDict_SetItemString
126 (paramdict, "description", pystr(pl[i].description));
127 PyDict_SetItemString
128 (paramdict, "unit", pystr(pl[i].unit));
129 PyDict_SetItemString
130 (paramdict, "minValue", PyFloat_FromDouble(pl[i].minValue));
131 PyDict_SetItemString
132 (paramdict, "maxValue", PyFloat_FromDouble(pl[i].maxValue));
133 PyDict_SetItemString
134 (paramdict, "defaultValue", PyFloat_FromDouble(pl[i].defaultValue));
135 if (pl[i].isQuantized) {
136 PyDict_SetItemString
137 (paramdict, "isQuantized", Py_True);
138 PyDict_SetItemString
139 (paramdict, "quantizeStep", PyFloat_FromDouble(pl[i].quantizeStep));
140 if (!pl[i].valueNames.empty()) {
141 PyDict_SetItemString
142 (paramdict, "valueNames", conv.PyValue_From_StringVector(pl[i].valueNames));
143 }
144 } else {
145 PyDict_SetItemString
146 (paramdict, "isQuantized", Py_False);
147 }
148
149 PyList_SET_ITEM(params, i, paramdict);
150 }
151
152 pd->parameters = params;
153
154 Plugin::ProgramList prl = plugin->getPrograms();
155 PyObject *progs = PyList_New(prl.size());
156
157 for (int i = 0; i < (int)prl.size(); ++i) {
158 PyList_SET_ITEM(progs, i, pystr(prl[i]));
159 }
160
161 pd->programs = progs;
162
163 return (PyObject *)pd;
164 }
165
166 static void
167 PyPluginObject_dealloc(PyPluginObject *self)
168 {
169 delete self->plugin;
170 PyObject_Del(self);
171 }
172
173 static PyObject *
174 getOutputs(PyObject *self, PyObject *args)
175 {
176 PyPluginObject *pd = getPluginObject(self);
177 if (!pd) return 0;
178
179 Plugin::OutputList ol = pd->plugin->getOutputDescriptors();
180 PyObject *outputs = PyList_New(ol.size());
181
182 for (int i = 0; i < (int)ol.size(); ++i) {
183 PyObject *outdict = PyDict_New();
184 PyDict_SetItemString
185 (outdict, "identifier", pystr(ol[i].identifier));
186 PyDict_SetItemString
187 (outdict, "name", pystr(ol[i].name));
188 PyDict_SetItemString
189 (outdict, "description", pystr(ol[i].description));
190 PyDict_SetItemString
191 (outdict, "binCount", PyInt_FromLong(ol[i].binCount));
192 if (ol[i].binCount > 0) {
193 if (ol[i].hasKnownExtents) {
194 PyDict_SetItemString
195 (outdict, "hasKnownExtents", Py_True);
196 PyDict_SetItemString
197 (outdict, "minValue", PyFloat_FromDouble(ol[i].minValue));
198 PyDict_SetItemString
199 (outdict, "maxValue", PyFloat_FromDouble(ol[i].maxValue));
200 } else {
201 PyDict_SetItemString
202 (outdict, "hasKnownExtents", Py_False);
203 }
204 if (ol[i].isQuantized) {
205 PyDict_SetItemString
206 (outdict, "isQuantized", Py_True);
207 PyDict_SetItemString
208 (outdict, "quantizeStep", PyFloat_FromDouble(ol[i].quantizeStep));
209 } else {
210 PyDict_SetItemString
211 (outdict, "isQuantized", Py_False);
212 }
213 }
214 PyDict_SetItemString
215 (outdict, "sampleType", PyInt_FromLong((int)ol[i].sampleType));
216 PyDict_SetItemString
217 (outdict, "sampleRate", PyFloat_FromDouble(ol[i].sampleRate));
218 PyDict_SetItemString
219 (outdict, "hasDuration", ol[i].hasDuration ? Py_True : Py_False);
220
221 PyList_SET_ITEM(outputs, i, outdict);
222 }
223
224 return outputs;
225 }
226
227 static PyObject *
228 initialise(PyObject *self, PyObject *args)
229 {
230 size_t channels, blockSize, stepSize;
231
232 if (!PyArg_ParseTuple (args, "nnn",
233 (size_t) &channels,
234 (size_t) &stepSize,
235 (size_t) &blockSize)) {
236 PyErr_SetString(PyExc_TypeError,
237 "initialise() takes channel count, step size, and block size arguments");
238 return 0;
239 }
240
241 PyPluginObject *pd = getPluginObject(self);
242 if (!pd) return 0;
243
244 pd->channels = channels;
245 pd->stepSize = stepSize;
246 pd->blockSize = blockSize;
247
248 if (!pd->plugin->initialise(channels, stepSize, blockSize)) {
249 cerr << "Failed to initialise native plugin adapter with channels = " << channels << ", stepSize = " << stepSize << ", blockSize = " << blockSize << endl;
250 PyErr_SetString(PyExc_TypeError,
251 "Plugin initialization failed");
252 return 0;
253 }
254
255 pd->isInitialised = true;
256
257 return Py_True;
258 }
259
260 static PyObject *
261 reset(PyObject *self, PyObject *)
262 {
263 PyPluginObject *pd = getPluginObject(self);
264 if (!pd) return 0;
265
266 if (!pd->isInitialised) {
267 PyErr_SetString(PyExc_StandardError,
268 "Plugin has not been initialised");
269 return 0;
270 }
271
272 pd->plugin->reset();
273 return Py_True;
274 }
275
276 static bool
277 hasParameter(PyPluginObject *pd, string id)
278 {
279 PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors();
280 for (int i = 0; i < (int)pl.size(); ++i) {
281 if (pl[i].identifier == id) {
282 return true;
283 }
284 }
285 return false;
286 }
287
288 static PyObject *
289 getParameterValue(PyObject *self, PyObject *args)
290 {
291 PyObject *pyParam;
292
293 if (!PyArg_ParseTuple(args, "S", &pyParam)) {
294 PyErr_SetString(PyExc_TypeError,
295 "getParameterValue() takes parameter id (string) argument");
296 return 0; }
297
298 PyPluginObject *pd = getPluginObject(self);
299 if (!pd) return 0;
300
301 string param = PyString_AS_STRING(pyParam);
302
303 if (!hasParameter(pd, param)) {
304 PyErr_SetString(PyExc_StandardError,
305 (string("Unknown parameter id \"") + param + "\"").c_str());
306 return 0;
307 }
308
309 float value = pd->plugin->getParameter(param);
310 return PyFloat_FromDouble(double(value));
311 }
312
313 static PyObject *
314 setParameterValue(PyObject *self, PyObject *args)
315 {
316 PyObject *pyParam;
317 float value;
318
319 if (!PyArg_ParseTuple(args, "Sf", &pyParam, &value)) {
320 PyErr_SetString(PyExc_TypeError,
321 "setParameterValue() takes parameter id (string), and value (float) arguments");
322 return 0; }
323
324 PyPluginObject *pd = getPluginObject(self);
325 if (!pd) return 0;
326
327 string param = PyString_AS_STRING(pyParam);
328
329 if (!hasParameter(pd, param)) {
330 PyErr_SetString(PyExc_StandardError,
331 (string("Unknown parameter id \"") + param + "\"").c_str());
332 return 0;
333 }
334
335 pd->plugin->setParameter(param, value);
336 return Py_True;
337 }
338
339 static PyObject *
340 setParameterValues(PyObject *self, PyObject *args)
341 {
342 PyObject *dict;
343
344 if (!PyArg_ParseTuple(args, "O", &dict)) {
345 PyErr_SetString(PyExc_TypeError,
346 "setParameterValues() takes dict argument");
347 return 0; }
348
349 if (!PyDict_Check(dict)) {
350 PyErr_SetString(PyExc_TypeError,
351 "setParameterValues() takes dict argument");
352 return 0; }
353
354 PyPluginObject *pd = getPluginObject(self);
355 if (!pd) return 0;
356
357 PluginBase::ParameterList pl = pd->plugin->getParameterDescriptors();
358 set<string> paramIds;
359 for (int i = 0; i < (int)pl.size(); ++i) {
360 paramIds.insert(pl[i].identifier);
361 }
362
363 Py_ssize_t pos = 0;
364 PyObject *key, *value;
365 while (PyDict_Next(dict, &pos, &key, &value)) {
366 if (!key || !PyString_CheckExact(key)) {
367 PyErr_SetString(PyExc_TypeError,
368 "Parameter dict keys must all have string type");
369 return 0;
370 }
371 if (!value || !FloatConversion::check(value)) {
372 PyErr_SetString(PyExc_TypeError,
373 "Parameter dict values must be convertible to float");
374 return 0;
375 }
376 string param = PyString_AS_STRING(key);
377 if (paramIds.find(param) == paramIds.end()) {
378 PyErr_SetString(PyExc_StandardError,
379 (string("Unknown parameter id \"") + param + "\"").c_str());
380 return 0;
381 }
382 pd->plugin->setParameter(param, FloatConversion::convert(value));
383 }
384
385 return Py_True;
386 }
387
388 static PyObject *
389 selectProgram(PyObject *self, PyObject *args)
390 {
391 PyObject *pyParam;
392
393 if (!PyArg_ParseTuple(args, "S", &pyParam)) {
394 PyErr_SetString(PyExc_TypeError,
395 "selectProgram() takes parameter id (string) argument");
396 return 0; }
397
398 PyPluginObject *pd = getPluginObject(self);
399 if (!pd) return 0;
400
401 pd->plugin->selectProgram(PyString_AS_STRING(pyParam));
402 return Py_True;
403 }
404
405 static
406 PyObject *
407 convertFeatureSet(const Plugin::FeatureSet &fs)
408 {
409 VectorConversion conv;
410
411 PyObject *pyFs = PyDict_New();
412
413 for (Plugin::FeatureSet::const_iterator fsi = fs.begin();
414 fsi != fs.end(); ++fsi) {
415
416 int fno = fsi->first;
417 const Plugin::FeatureList &fl = fsi->second;
418
419 if (!fl.empty()) {
420
421 PyObject *pyFl = PyList_New(fl.size());
422
423 for (int fli = 0; fli < (int)fl.size(); ++fli) {
424
425 const Plugin::Feature &f = fl[fli];
426 PyObject *pyF = PyDict_New();
427
428 if (f.hasTimestamp) {
429 PyDict_SetItemString
430 (pyF, "timestamp", PyRealTime_FromRealTime(f.timestamp));
431 }
432 if (f.hasDuration) {
433 PyDict_SetItemString
434 (pyF, "duration", PyRealTime_FromRealTime(f.duration));
435 }
436
437 PyDict_SetItemString
438 (pyF, "label", pystr(f.label));
439
440 if (!f.values.empty()) {
441 PyDict_SetItemString
442 (pyF, "values", conv.PyArray_From_FloatVector(f.values));
443 }
444
445 PyList_SET_ITEM(pyFl, fli, pyF);
446 }
447
448 PyObject *pyN = PyInt_FromLong(fno);
449 PyDict_SetItem(pyFs, pyN, pyFl);
450 }
451 }
452
453 return pyFs;
454 }
455
456 static vector<vector<float> >
457 convertPluginInput(PyObject *pyBuffer, int channels, int blockSize)
458 {
459 vector<vector<float> > data;
460
461 VectorConversion conv;
462
463 if (PyArray_CheckExact(pyBuffer)) {
464
465 data = conv.Py2DArray_To_FloatVector(pyBuffer);
466
467 if (conv.error) {
468 PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str());
469 return data;
470 }
471
472 if ((int)data.size() != channels) {
473 // cerr << "Wrong number of channels: got " << data.size() << ", expected " << channels << endl;
474 PyErr_SetString(PyExc_TypeError, "Wrong number of channels");
475 return vector<vector<float> >();
476 }
477
478 } else {
479
480 if (!PyList_Check(pyBuffer)) {
481 PyErr_SetString(PyExc_TypeError, "List of NumPy arrays or lists of numbers required for process input");
482 return data;
483 }
484
485 if (PyList_GET_SIZE(pyBuffer) != channels) {
486 // cerr << "Wrong number of channels: got " << PyList_GET_SIZE(pyBuffer) << ", expected " << channels << endl;
487 PyErr_SetString(PyExc_TypeError, "Wrong number of channels");
488 return data;
489 }
490
491 for (int c = 0; c < channels; ++c) {
492 PyObject *cbuf = PyList_GET_ITEM(pyBuffer, c);
493 data.push_back(conv.PyValue_To_FloatVector(cbuf));
494 if (conv.error) {
495 PyErr_SetString(PyExc_TypeError, conv.getError().str().c_str());
496 return vector<vector<float> >();
497 }
498 }
499 }
500
501 for (int c = 0; c < channels; ++c) {
502 if ((int)data[c].size() != blockSize) {
503 // cerr << "Wrong number of samples on channel " << c << ": expected " << blockSize << " (plugin's block size), got " << data[c].size() << endl;
504 PyErr_SetString(PyExc_TypeError, "Wrong number of samples for process block");
505 return vector<vector<float> >();
506 }
507 }
508
509 return data;
510 }
511
512 static PyObject *
513 processBlock(PyObject *self, PyObject *args)
514 {
515 PyObject *pyBuffer;
516 PyObject *pyRealTime;
517
518 if (!PyArg_ParseTuple(args, "OO",
519 &pyBuffer, // Audio data
520 &pyRealTime)) { // TimeStamp
521 PyErr_SetString(PyExc_TypeError,
522 "processBlock() takes buffer (2D array or list of arrays, one row per channel) and timestamp (RealTime) arguments");
523 return 0; }
524
525 if (!PyRealTime_Check(pyRealTime)) {
526 PyErr_SetString(PyExc_TypeError, "Valid timestamp required.");
527 return 0; }
528
529 PyPluginObject *pd = getPluginObject(self);
530 if (!pd) return 0;
531
532 if (!pd->isInitialised) {
533 PyErr_SetString(PyExc_StandardError,
534 "Plugin has not been initialised.");
535 return 0;
536 }
537
538 int channels = pd->channels;
539 vector<vector<float> > data =
540 convertPluginInput(pyBuffer, channels, pd->blockSize);
541 if (data.empty()) return 0;
542
543 float **inbuf = new float *[channels];
544 for (int c = 0; c < channels; ++c) {
545 inbuf[c] = &data[c][0];
546 }
547 RealTime timeStamp = *PyRealTime_AsRealTime(pyRealTime);
548 Plugin::FeatureSet fs = pd->plugin->process(inbuf, timeStamp);
549 delete[] inbuf;
550
551 return convertFeatureSet(fs);
552 }
553
554 static PyObject *
555 getRemainingFeatures(PyObject *self, PyObject *)
556 {
557 PyPluginObject *pd = getPluginObject(self);
558 if (!pd) return 0;
559
560 if (!pd->isInitialised) {
561 PyErr_SetString(PyExc_StandardError,
562 "Plugin has not been initialised.");
563 return 0;
564 }
565
566 Plugin::FeatureSet fs = pd->plugin->getRemainingFeatures();
567
568 return convertFeatureSet(fs);
569 }
570
571 static PyObject *
572 getPreferredBlockSize(PyObject *self, PyObject *)
573 {
574 PyPluginObject *pd = getPluginObject(self);
575 if (!pd) return 0;
576 return PyInt_FromLong(pd->plugin->getPreferredBlockSize());
577 }
578
579 static PyObject *
580 getPreferredStepSize(PyObject *self, PyObject *)
581 {
582 PyPluginObject *pd = getPluginObject(self);
583 if (!pd) return 0;
584 return PyInt_FromLong(pd->plugin->getPreferredStepSize());
585 }
586
587 static PyObject *
588 getMinChannelCount(PyObject *self, PyObject *)
589 {
590 PyPluginObject *pd = getPluginObject(self);
591 if (!pd) return 0;
592 return PyInt_FromLong(pd->plugin->getMinChannelCount());
593 }
594
595 static PyObject *
596 getMaxChannelCount(PyObject *self, PyObject *)
597 {
598 PyPluginObject *pd = getPluginObject(self);
599 if (!pd) return 0;
600 return PyInt_FromLong(pd->plugin->getMaxChannelCount());
601 }
602
603 static PyObject *
604 unload(PyObject *self, PyObject *)
605 {
606 PyPluginObject *pd = getPluginObject(self);
607 if (!pd) return 0;
608
609 delete pd->plugin;
610 pd->plugin = 0; // This is checked by getPluginObject, so we avoid
611 // blowing up if called repeatedly
612
613 return Py_True;
614 }
615
616 static PyMemberDef PyPluginObject_members[] =
617 {
618 {(char *)"info", T_OBJECT, offsetof(PyPluginObject, info), READONLY,
619 (char *)"info -> A read-only dictionary of plugin metadata."},
620
621 {(char *)"inputDomain", T_INT, offsetof(PyPluginObject, inputDomain), READONLY,
622 (char *)"inputDomain -> The format of input audio required by the plugin, either vampyhost.TimeDomain or vampyhost.FrequencyDomain."},
623
624 {(char *)"parameters", T_OBJECT, offsetof(PyPluginObject, parameters), READONLY,
625 (char *)"parameters -> A list of metadata dictionaries describing the plugin's configurable parameters."},
626
627 {(char *)"programs", T_OBJECT, offsetof(PyPluginObject, programs), READONLY,
628 (char *)"programs -> A list of the programs available for this plugin, if any."},
629
630 {0, 0}
631 };
632
633 static PyMethodDef PyPluginObject_methods[] =
634 {
635 {"getOutputs", getOutputs, METH_NOARGS,
636 "getOutputs() -> Obtain the output descriptors for all of the plugin's outputs."},
637
638 {"getParameterValue", getParameterValue, METH_VARARGS,
639 "getParameterValue(identifier) -> Return the value of the parameter with the given identifier."},
640
641 {"setParameterValue", setParameterValue, METH_VARARGS,
642 "setParameterValue(identifier, value) -> Set the parameter with the given identifier to the given value."},
643
644 {"setParameterValues", setParameterValues, METH_VARARGS,
645 "setParameterValues(dict) -> Set multiple parameters to values corresponding to the key/value pairs in the dict. Any parameters not mentioned in the dict are unchanged."},
646
647 {"selectProgram", selectProgram, METH_VARARGS,
648 "selectProgram(name) -> Select the processing program with the given name."},
649
650 {"getPreferredBlockSize", getPreferredBlockSize, METH_VARARGS,
651 "getPreferredBlockSize() -> Return the plugin's preferred processing block size, or 0 if the plugin accepts any block size."},
652
653 {"getPreferredStepSize", getPreferredStepSize, METH_VARARGS,
654 "getPreferredStepSize() -> Return the plugin's preferred processing step size, or 0 if the plugin allows the host to select. If this is 0, the host should normally choose the same step as block size for time-domain plugins, or half the block size for frequency-domain plugins."},
655
656 {"getMinChannelCount", getMinChannelCount, METH_VARARGS,
657 "getMinChannelCount() -> Return the minimum number of channels of audio data the plugin accepts as input."},
658
659 {"getMaxChannelCount", getMaxChannelCount, METH_VARARGS,
660 "getMaxChannelCount() -> Return the maximum number of channels of audio data the plugin accepts as input."},
661
662 {"initialise", initialise, METH_VARARGS,
663 "initialise(channels, stepSize, blockSize) -> Initialise the plugin for the given number of channels and processing frame sizes. This must be called before processBlock() can be used."},
664
665 {"reset", reset, METH_NOARGS,
666 "reset() -> Reset the plugin after processing, to prepare for another processing run with the same parameters."},
667
668 {"processBlock", processBlock, METH_VARARGS,
669 "processBlock(block, timestamp) -> Provide one processing frame to the plugin, with its timestamp, and obtain any features that were extracted immediately from this frame."},
670
671 {"getRemainingFeatures", getRemainingFeatures, METH_NOARGS,
672 "getRemainingFeatures() -> Obtain any features extracted at the end of processing."},
673
674 {"unload", unload, METH_NOARGS,
675 "unload() -> Dispose of the plugin. You cannot use the plugin object again after calling this. Note that unloading also happens automatically when the plugin object's reference count reaches zero; this function is only necessary if you wish to ensure the native part of the plugin is disposed of before then."},
676
677 {0, 0}
678 };
679
680 /* Doc:: 10.3 Type Objects */ /* static */
681 PyTypeObject Plugin_Type =
682 {
683 PyObject_HEAD_INIT(NULL)
684 0, /*ob_size*/
685 "vampyhost.Plugin", /*tp_name*/
686 sizeof(PyPluginObject), /*tp_basicsize*/
687 0, /*tp_itemsize*/
688 (destructor)PyPluginObject_dealloc, /*tp_dealloc*/
689 0, /*tp_print*/
690 0, /*tp_getattr*/
691 0, /*tp_setattr*/
692 0, /*tp_compare*/
693 0, /*tp_repr*/
694 0, /*tp_as_number*/
695 0, /*tp_as_sequence*/
696 0, /*tp_as_mapping*/
697 0, /*tp_hash*/
698 0, /*tp_call*/
699 0, /*tp_str*/
700 PyObject_GenericGetAttr, /*tp_getattro*/
701 PyObject_GenericSetAttr, /*tp_setattro*/
702 0, /*tp_as_buffer*/
703 Py_TPFLAGS_DEFAULT, /*tp_flags*/
704 "Plugin object, providing a low-level API for running a Vamp plugin.", /*tp_doc*/
705 0, /*tp_traverse*/
706 0, /*tp_clear*/
707 0, /*tp_richcompare*/
708 0, /*tp_weaklistoffset*/
709 0, /*tp_iter*/
710 0, /*tp_iternext*/
711 PyPluginObject_methods, /*tp_methods*/
712 PyPluginObject_members, /*tp_members*/
713 0, /*tp_getset*/
714 0, /*tp_base*/
715 0, /*tp_dict*/
716 0, /*tp_descr_get*/
717 0, /*tp_descr_set*/
718 0, /*tp_dictoffset*/
719 0, /*tp_init*/
720 PyType_GenericAlloc, /*tp_alloc*/
721 0, /*tp_new*/
722 PyObject_Del, /*tp_free*/
723 0, /*tp_is_gc*/
724 };
725