comparison PyPlugin.cpp @ 0:e20e214bdfb5

Added VAMP-Python binding project vampy
author fazekasgy
date Tue, 11 Mar 2008 19:47:34 +0000
parents
children 134313c59d82
comparison
equal deleted inserted replaced
-1:000000000000 0:e20e214bdfb5
1 /*
2 Vamp
3
4 An API for audio analysis and feature extraction plugins.
5
6 Centre for Digital Music, Queen Mary, University of London.
7 Copyright 2006 Chris Cannam.
8
9 Permission is hereby granted, free of charge, to any person
10 obtaining a copy of this software and associated documentation
11 files (the "Software"), to deal in the Software without
12 restriction, including without limitation the rights to use, copy,
13 modify, merge, publish, distribute, sublicense, and/or sell copies
14 of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions:
16
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
24 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 Except as contained in this notice, the names of the Centre for
29 Digital Music; Queen Mary, University of London; and Chris Cannam
30 shall not be used in advertising or otherwise to promote the sale,
31 use or other dealings in this Software without prior written
32 authorization.
33 */
34
35
36
37 /**
38 * This VAMP plugin is a wrapper for Python Scripts. (vampy)
39 * Centre for Digital Music, Queen Mary, University of London.
40 * Copyright 2008, George Fazekas.
41
42 TODO: needs more complete error checking
43 needs correct implementation of Python threading
44 more efficient data conversion using the buffering interface or ctypes
45 VAMP programs not implemented
46 support multiple plugins per script in scanner
47 ensure proper cleanup, host do a good job though
48
49 */
50
51 //#include "Python.h"
52 #include "/usr/include/python/Python.h"
53 #include "PyPlugin.h"
54
55 #ifdef _WIN32
56 #define pathsep ('\\')
57 #else
58 #define pathsep ('/')
59 #endif
60
61 #define _DEBUG
62
63 using std::string;
64 using std::vector;
65 using std::cerr;
66 using std::endl;
67 using std::map;
68
69 extern volatile bool mutex;
70
71 // Maps to associate strings with enum values
72 static std::map<std::string, eOutDescriptors> outKeys;
73 static std::map<std::string, eSampleTypes> sampleKeys;
74 static std::map<std::string, eFeatureFields> ffKeys;
75 static std::map<std::string, p::eParmDescriptors> parmKeys;
76
77 void initMaps()
78 {
79 outKeys["identifier"] = identifier;
80 outKeys["name"] = name;
81 outKeys["description"] = description;
82 outKeys["unit"] = unit;
83 outKeys["hasFixedBinCount"] = hasFixedBinCount;
84 outKeys["binCount"] = binCount;
85 outKeys["binNames"] = binNames;
86 outKeys["hasKnownExtents"] = hasKnownExtents;
87 outKeys["minValue"] = minValue;
88 outKeys["maxValue"] = maxValue;
89 outKeys["isQuantized"] = isQuantized;
90 outKeys["quantizeStep"] = quantizeStep;
91 outKeys["sampleType"] = sampleType;
92 outKeys["sampleRate"] = sampleRate;
93
94 sampleKeys["OneSamplePerStep"] = OneSamplePerStep;
95 sampleKeys["FixedSampleRate"] = FixedSampleRate;
96 sampleKeys["VariableSampleRate"] = VariableSampleRate;
97
98 ffKeys["hasTimestamp"] = hasTimestamp;
99 ffKeys["timeStamp"] = timeStamp;
100 ffKeys["values"] = values;
101 ffKeys["label"] = label;
102
103 parmKeys["identifier"] = p::identifier;
104 parmKeys["name"] = p::name;
105 parmKeys["description"] = p::description;
106 parmKeys["unit"] = p::unit;
107 parmKeys["minValue"] = p::minValue;
108 parmKeys["maxValue"] = p::maxValue;
109 parmKeys["defaultValue"] = p::defaultValue;
110 parmKeys["isQuantized"] = p::isQuantized;
111
112 }
113
114
115 //missing API helper: convert Python list to C++ vector of strings
116 //TODO: these could be templates if we need more of this kind
117 std::vector<std::string> PyList_To_StringVector (PyObject *inputList) {
118
119 std::vector<std::string> Output;
120 std::string ListElement;
121 PyObject *pyString = NULL;
122
123 if (!PyList_Check(inputList)) return Output;
124
125 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) {
126 //Get next list item (Borrowed Reference)
127 pyString = PyList_GET_ITEM(inputList,i);
128 ListElement = (string) PyString_AsString(PyObject_Str(pyString));
129 Output.push_back(ListElement);
130 }
131 return Output;
132 }
133
134 //missing API helper: convert Python list to C++ vector of floats
135 std::vector<float> PyList_As_FloatVector (PyObject *inputList) {
136
137 std::vector<float> Output;
138 float ListElement;
139 PyObject *pyFloat = NULL;
140
141 if (!PyList_Check(inputList)) return Output;
142
143 for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) {
144 //Get next list item (Borrowed Reference)
145 pyFloat = PyList_GET_ITEM(inputList,k);
146 ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
147 Output.push_back(ListElement);
148 }
149
150 return Output;
151 }
152
153 /* TODO: find out why this produces error, also
154 do sg more clever about handling RealTime
155 Vamp::RealTime
156 PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) {
157 Vamp::RealTime result =
158 Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate);
159 return result;
160 }
161 */
162
163
164 PyPlugin::PyPlugin(std::string pluginKey,float inputSampleRate, PyObject *pyInstance) :
165 Plugin(inputSampleRate),
166 m_pyInstance(pyInstance),
167 m_stepSize(0),
168 m_previousSample(0.0f),
169 m_plugin(pluginKey),
170 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
171 m_path((pluginKey.substr(0,pluginKey.rfind(pathsep))))
172 {
173
174 /*TODO: this is a nasty way of ensuring the process is
175 finished before we create a new instance accessing the Python/C API.
176 The Python/C API is not fully thread safe.
177 Calling into a python class while the process is doing heavy
178 computation may cause segmentation fault.
179 Manipulating the GIL and thread states only gave me a grief so far.*/
180
181 if (mutex) {
182 cerr << "PyPlugin::PyPlugin:" << m_class
183 << " Warning: Waiting for clear signal from parallel process." << endl;
184 while (mutex) { }
185 }
186 }
187
188 PyPlugin::~PyPlugin()
189 {
190 cerr << "PyPlugin::PyPlugin:" << m_class
191 << " Instance deleted." << endl;
192 //for safety only. has been cleared after process.
193 mutex = false;
194 }
195
196
197 string
198 PyPlugin::getIdentifier() const
199 {
200 char method[]="getIdentifier";
201 cerr << "[call] " << method << endl;
202 string rString="VampPy-x";
203
204 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
205
206 //Call the method
207 PyObject *pyString =
208 PyObject_CallMethod(m_pyInstance, method, NULL);
209
210 //Check return value
211 if (!PyString_Check(pyString)) {
212 Py_CLEAR(pyString);
213 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
214 << "] Expected String return value." << endl;
215 return rString;
216 }
217
218 rString=PyString_AsString(pyString);
219 Py_CLEAR(pyString);
220 }
221 cerr << "Warning: Plugin must return a unique identifier." << endl;
222 return rString;
223 }
224
225
226 string
227 PyPlugin::getName() const
228 {
229
230 char method[]="getName";
231 cerr << "[call] " << method << endl;
232 string rString="VamPy Plugin (Noname)";
233
234 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
235
236 //Call the method
237 PyObject *pyString =
238 PyObject_CallMethod(m_pyInstance, method, NULL);
239
240 //Check return value
241 if (!PyString_Check(pyString)) {
242 Py_CLEAR(pyString);
243 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
244 << "] Expected String return value." << endl;
245 return rString;
246 }
247
248 rString=PyString_AsString(pyString);
249 Py_CLEAR(pyString);
250 }
251 return rString;
252 }
253
254 string
255 PyPlugin::getDescription() const
256 {
257 char method[]="getDescription";
258 cerr << "[call] " << method << endl;
259 string rString="Not given. (Hint: Implement getDescription method.)";
260
261 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
262
263 //Call the method
264 PyObject *pyString =
265 PyObject_CallMethod(m_pyInstance, method, NULL);
266
267 //Check return value
268 if (!PyString_Check(pyString)) {
269 Py_CLEAR(pyString);
270 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
271 << "] Expected String return value." << endl;
272 return rString;
273 }
274
275 rString=PyString_AsString(pyString);
276 Py_CLEAR(pyString);
277 }
278 return rString;
279 }
280
281 string
282 PyPlugin::getMaker() const
283 {
284 char method[]="getMaker";
285 cerr << "[call] " << method << endl;
286 string rString="Generic VamPy Plugin.";
287
288 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
289
290 //Call the method
291 PyObject *pyString =
292 PyObject_CallMethod(m_pyInstance, method, NULL);
293
294 //Check return value
295 if (!PyString_Check(pyString)) {
296 Py_CLEAR(pyString);
297 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
298 << "] Expected String return value." << endl;
299 return rString;
300 }
301
302 rString=PyString_AsString(pyString);
303 Py_CLEAR(pyString);
304 }
305 return rString;
306 }
307
308 int
309 PyPlugin::getPluginVersion() const
310 {
311 return 2;
312 }
313
314 string
315 PyPlugin::getCopyright() const
316 {
317 char method[]="getCopyright";
318 cerr << "[call] " << method << endl;
319 string rString="BSD License";
320
321 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
322
323 //Call the method
324 PyObject *pyString =
325 PyObject_CallMethod(m_pyInstance, method, NULL);
326
327 //Check return value
328 if (!PyString_Check(pyString)) {
329 Py_CLEAR(pyString);
330 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
331 << "] Expected String return value." << endl;
332 return rString;
333 }
334
335
336 rString=PyString_AsString(pyString);
337 Py_CLEAR(pyString);
338 }
339 return rString;
340 }
341
342
343 bool
344 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
345 {
346 if (channels < getMinChannelCount() ||
347 channels > getMaxChannelCount()) return false;
348
349 m_stepSize = std::min(stepSize, blockSize);
350 char method[]="initialise";
351 cerr << "[call] " << method << endl;
352
353 //Check if the method is implemented in Python else return false
354 if (PyObject_HasAttrString(m_pyInstance,method)) {
355
356 PyObject *pyMethod = PyString_FromString(method);
357 PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels);
358 PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize);
359 PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize);
360 PyObject *pyInputSampleRate = PyFloat_FromDouble((double)m_inputSampleRate);
361
362 //Call the method
363 PyObject *pyBool =
364 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL);
365
366 Py_DECREF(pyMethod);
367 Py_DECREF(pyChannels);
368 Py_DECREF(pyStepSize);
369 Py_DECREF(pyBlockSize);
370 Py_DECREF(pyInputSampleRate);
371
372 //Check return value
373 if (!PyBool_Check(pyBool)) {
374 Py_CLEAR(pyBool);
375 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
376 << "] Expected Bool return value." << endl;
377 return false;
378 }
379
380 if (pyBool == Py_True) {
381 Py_CLEAR(pyBool);
382 return true;
383 } else {
384 Py_CLEAR(pyBool);
385 return false;}
386 }
387 return true;
388 }
389
390 void
391 PyPlugin::reset()
392 {
393 m_previousSample = 0.0f;
394 }
395
396 PyPlugin::InputDomain PyPlugin::getInputDomain() const
397 {
398 char method[]="getInputDomain";
399 cerr << "[call] " << method << endl;
400 PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain
401
402 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
403
404 PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL);
405
406 //Check return value
407 if (!PyString_Check(pyString)) {
408 Py_CLEAR(pyString);
409 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
410 << "] Expected String return value." << endl;
411 return rValue;
412 }
413
414 string domain = (string) PyString_AsString(pyString);
415 if (domain == "FrequencyDomain") rValue = FrequencyDomain;
416 Py_CLEAR(pyString);
417 }
418 return rValue;
419 }
420
421 size_t PyPlugin::getPreferredBlockSize() const
422 {
423 char method[]="getPreferredBlockSize";
424 cerr << "[call] " << method << endl;
425 size_t rValue=0; //not set by default
426 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
427 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
428
429 //Check return value
430 if (!PyInt_Check(pyInt)) {
431 Py_CLEAR(pyInt);
432 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
433 << "] Expected Integer return value." << endl;
434 return rValue;
435 }
436
437 rValue=(size_t)PyInt_AS_LONG(pyInt);
438 Py_CLEAR(pyInt);
439 }
440 return rValue;
441 }
442
443 //size_t PyPlugin::getPreferredStepSize() const { return 0; }
444 size_t PyPlugin::getPreferredStepSize() const
445 {
446 char method[]="getPreferredStepSize";
447 cerr << "[call] " << method << endl;
448 size_t rValue=0; //not set by default
449 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
450 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
451
452 //Check return value
453 if (!PyInt_Check(pyInt)) {
454 Py_CLEAR(pyInt);
455 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
456 << "] Expected Integer return value." << endl;
457 return rValue;
458 }
459
460 rValue=(size_t)PyInt_AS_LONG(pyInt);
461 Py_CLEAR(pyInt);
462 }
463 return rValue;
464 }
465
466 size_t PyPlugin::getMinChannelCount() const
467 {
468 char method[]="getMinChannelCount";
469 cerr << "[call] " << method << endl;
470 size_t rValue=1; //default value
471 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
472 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
473
474 //Check return value
475 if (!PyInt_Check(pyInt)) {
476 Py_CLEAR(pyInt);
477 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
478 << "] Expected String return value." << endl;
479 return rValue;
480 }
481
482 rValue=(size_t)PyInt_AS_LONG(pyInt);
483 Py_CLEAR(pyInt);
484 }
485 return rValue;
486 }
487
488 size_t PyPlugin::getMaxChannelCount() const
489 {
490 char method[]="getMaxChannelCount";
491 cerr << "[call] " << method << endl;
492 size_t rValue=1; //default value
493 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
494 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
495
496 //Check return value
497 if (!PyInt_Check(pyInt)) {
498 Py_CLEAR(pyInt);
499 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
500 << "] Expected String return value." << endl;
501 return rValue;
502 }
503
504 rValue=(size_t)PyInt_AS_LONG(pyInt);
505 Py_CLEAR(pyInt);
506 }
507 return rValue;
508 }
509
510
511 PyPlugin::OutputList
512 PyPlugin::getOutputDescriptors() const
513 {
514 //PyEval_AcquireThread(newThreadState);
515 OutputList list;
516 OutputDescriptor od;
517 char method[]="getOutputDescriptors";
518 cerr << "[call] " << method << endl;
519
520 //Check if the method is implemented in Python
521 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
522
523 //Call the method: must return list object (new reference)
524 PyObject *pyList =
525 PyObject_CallMethod(m_pyInstance,method, NULL);
526
527 //Check return type
528 if (! PyList_Check(pyList) ) {
529 Py_CLEAR(pyList);
530 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
531 << "] Expected List return type." << endl;
532 return list;
533 }
534
535 //These will all be borrowed references (no need to DECREF)
536 PyObject *pyDict, *pyKey, *pyValue;
537
538 //Parse Output List
539 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
540
541 //Get i-th VAMP output descriptor (Borrowed Reference)
542 pyDict = PyList_GET_ITEM(pyList,i);
543
544 //We only care about dictionaries holding output descriptors
545 if ( !PyDict_Check(pyDict) ) continue;
546
547 Py_ssize_t pyPos = NULL;
548 initMaps();
549
550 //Python Sequence Iterator
551 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
552 {
553 switch (outKeys[PyString_AsString(pyKey)])
554 {
555 case not_found :
556 cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
557 break;
558 case identifier:
559 od.identifier = PyString_AsString(pyValue);
560 break;
561 case name:
562 od.name = PyString_AsString(pyValue);
563 break;
564 case description:
565 od.description = PyString_AsString(pyValue);
566 break;
567 case unit:
568 od.unit = PyString_AsString(pyValue);
569 break;
570 case hasFixedBinCount:
571 od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue);
572 break;
573 case binCount:
574 od.binCount = (size_t) PyInt_AS_LONG(pyValue);
575 break;
576 case binNames:
577 od.binNames = PyList_To_StringVector(pyValue);
578 break;
579 case hasKnownExtents:
580 od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue);
581 break;
582 case minValue:
583 od.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
584 break;
585 case maxValue:
586 od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
587 break;
588 case isQuantized:
589 od.isQuantized = (bool) PyInt_AS_LONG(pyValue);
590 break;
591 case quantizeStep:
592 od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue);
593 break;
594 case sampleType:
595 od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)];
596 break;
597 case sampleRate:
598 od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue);
599 break;
600 default :
601 cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
602 } // switch
603 } // while dict keys
604 list.push_back(od);
605 } // for each memeber in outputlist
606 Py_CLEAR(pyList);
607 } // if method is implemented
608 //PyEval_ReleaseThread(newThreadState);
609 return list;
610 }
611
612 PyPlugin::ParameterList
613 PyPlugin::getParameterDescriptors() const
614 {
615 ParameterList list;
616 ParameterDescriptor pd;
617 char method[]="getParameterDescriptors";
618 cerr << "[call] " << method << endl;
619
620 //Check if the method is implemented in Python
621 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
622
623 //Call the method: must return list object (new reference)
624 PyObject *pyList =
625 PyObject_CallMethod(m_pyInstance,method, NULL);
626
627 //Check return type
628 if (! PyList_Check(pyList) ) {
629 Py_CLEAR(pyList);
630 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
631 << "] Expected List return type." << endl;
632 return list;
633 }
634
635
636 //These will all be borrowed references (no need to DECREF)
637 PyObject *pyDict, *pyKey, *pyValue;
638
639 //Parse Output List
640 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
641
642 //Get i-th VAMP output descriptor (Borrowed Reference)
643 pyDict = PyList_GET_ITEM(pyList,i);
644
645 //We only care about dictionaries holding output descriptors
646 if ( !PyDict_Check(pyDict) ) continue;
647
648 Py_ssize_t pyPos = NULL;
649 initMaps();
650
651 //Python Sequence Iterator
652 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
653 {
654 switch (parmKeys[PyString_AsString(pyKey)])
655 {
656 case not_found :
657 cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
658 break;
659 case p::identifier:
660 pd.identifier = PyString_AsString(pyValue);
661 break;
662 case p::name:
663 pd.name = PyString_AsString(pyValue);
664 break;
665 case p::description:
666 pd.description = PyString_AsString(pyValue);
667 break;
668 case p::unit:
669 pd.unit = PyString_AsString(pyValue);
670 break;
671 case p::minValue:
672 pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
673 break;
674 case p::maxValue:
675 pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
676 break;
677 case p::defaultValue:
678 pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue);
679 break;
680 case p::isQuantized:
681 pd.isQuantized = (bool) PyInt_AS_LONG(pyValue);
682 break;
683 default :
684 cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
685 } // switch
686 } // while dict keys
687 list.push_back(pd);
688 } // for each memeber in outputlist
689 Py_CLEAR(pyList);
690 } // if
691 return list;
692 }
693
694 void PyPlugin::setParameter(std::string paramid, float newval)
695 {
696 char method[]="setParameter";
697 cerr << "[call] " << method << endl;
698
699 //Check if the method is implemented in Python
700 if (PyObject_HasAttrString(m_pyInstance,method)) {
701
702 PyObject *pyMethod = PyString_FromString(method);
703 PyObject *pyParamid = PyString_FromString(paramid.c_str());
704 PyObject *pyNewval = PyFloat_FromDouble((double)newval);
705
706 //Call the method
707 PyObject *pyBool =
708 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL);
709
710 //This only happens if there is a syntax error or so
711 if (pyBool == NULL) {
712 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
713 << "] Error setting parameter: " << paramid << endl;
714 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
715 }
716
717 Py_DECREF(pyMethod);
718 Py_DECREF(pyParamid);
719 Py_DECREF(pyNewval);
720 }
721 }
722
723 float PyPlugin::getParameter(std::string paramid) const
724 {
725 char method[]="getParameter";
726 cerr << "[call] " << method << endl;
727 float rValue = 0.0f;
728
729 //Check if the method is implemented in Python
730 if (PyObject_HasAttrString(m_pyInstance,method)) {
731
732 PyObject *pyMethod = PyString_FromString(method);
733 PyObject *pyParamid = PyString_FromString(paramid.c_str());
734
735 //Call the method
736 PyObject *pyFloat =
737 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL);
738
739 //Check return type
740 if (! PyFloat_Check(pyFloat) ) {
741 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
742 << "] Expected Float return type." << endl;
743 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
744 Py_CLEAR(pyFloat);
745 return rValue;
746 }
747
748 rValue = (float) PyFloat_AS_DOUBLE(pyFloat);
749
750 Py_DECREF(pyMethod);
751 Py_DECREF(pyParamid);
752 Py_DECREF(pyFloat);
753 }
754
755 return rValue;
756 }
757
758 #ifdef _DEBUG
759 static int proccounter = 0;
760 #endif
761
762 PyPlugin::FeatureSet
763 PyPlugin::process(const float *const *inputBuffers,
764 Vamp::RealTime timestamp)
765 {
766 if (m_stepSize == 0) {
767 cerr << "ERROR: PyPlugin::process: "
768 << "Plugin has not been initialised" << endl;
769 return FeatureSet();
770 }
771 mutex = true;
772 static char method[]="process";
773
774 #ifdef _DEBUG
775 cerr << "[call] " << method << " frame:" << proccounter << endl;
776 proccounter++;
777 //cerr << "C: proc..." << proccounter << " " << endl;
778 #endif
779
780 //proces::Check if the method is implemented in Python
781 if ( PyObject_HasAttrString(m_pyInstance,method) )
782 {
783
784 //Pack method name into Python Object
785 PyObject *pyMethod = PyString_FromString(method);
786
787 //Declare new list object
788 PyObject *pyFloat, *pyList;
789 pyList = PyList_New((Py_ssize_t) m_stepSize);
790
791 //Pack samples into a Python List Object
792 //pyFloat will always be new references,
793 //these will be discarded when the list is deallocated
794 for (size_t i = 0; i < m_stepSize; ++i) {
795 pyFloat=PyFloat_FromDouble((double) inputBuffers[0][i]);
796 PyList_SET_ITEM(pyList, (Py_ssize_t) i, pyFloat);
797 }
798
799 //Call python process (returns new reference)
800 PyObject *pyOutputList =
801 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyList,NULL);
802
803 //Check return type
804 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
805 if (pyOutputList == NULL) {
806 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
807 << "] Unexpected result." << endl;
808 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
809 } else {
810 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
811 << "] Expected List return type." << endl;
812 }
813 Py_CLEAR(pyMethod);
814 Py_CLEAR(pyOutputList);
815 mutex = false;
816 return FeatureSet();
817 }
818
819 Py_DECREF(pyMethod);
820 // Py_DECREF(pyList);
821 // This appears to be tracked by the cyclic garbage collector
822 // hence decrefing produces GC error
823 #ifdef _DEBUG
824 cerr << "Process Returned Features" << endl;
825 #endif
826 // These will ALL be borrowed references
827 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
828
829 FeatureSet returnFeatures;
830
831 //Parse Output List for each element (FeatureSet)
832 for (Py_ssize_t i = 0;
833 i < PyList_GET_SIZE(pyOutputList); ++i) {
834 //cerr << "output (FeatureSet): " << i << endl;
835
836 //Get i-th FeatureList (Borrowed Reference)
837 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
838
839 //Parse FeatureList for each element (Feature)
840 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
841 //cerr << "element (FeatureList): " << j << endl;
842
843 //Get j-th Feature (Borrowed Reference)
844 pyDict = PyList_GET_ITEM(pyFeatureList,j);
845
846 //We only care about dictionaries holding a Feature struct
847 if ( !PyDict_Check(pyDict) ) continue;
848
849 Py_ssize_t pyPos = NULL;
850 bool emptyFeature = true;
851 Feature feature;
852
853 //process::Python Sequence Iterator for dictionary
854 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
855 {
856 emptyFeature = false;
857 switch (ffKeys[PyString_AsString(pyKey)])
858 {
859 case not_found :
860 cerr << "Unknown key in VAMP FeatureSet: "
861 << PyString_AsString(pyKey) << endl;
862 break;
863 case hasTimestamp:
864 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
865 break;
866 case timeStamp:
867 feature.timestamp = timestamp +
868 Vamp::RealTime::frame2RealTime
869 ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate);
870 break;
871 case values:
872 feature.values = PyList_As_FloatVector(pyValue);
873 break;
874 case label:
875 feature.label = PyString_AsString(pyValue);
876 break;
877 default :
878 cerr << "Invalid key in VAMP FeatureSet: "
879 << PyString_AsString(pyKey) << endl;
880 } // switch
881
882 } // while
883 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
884 else returnFeatures[i].push_back(feature);
885
886 }// for j = FeatureList
887
888 }//for i = FeatureSet
889 Py_CLEAR(pyOutputList);
890 mutex = false;
891 return returnFeatures;
892
893 }//if (has_attribute)
894 mutex = false;
895 return FeatureSet();
896 }
897
898
899
900 PyPlugin::FeatureSet
901 PyPlugin::getRemainingFeatures()
902 {
903 static char method[]="getRemainingFeatures";
904 cerr << "[call] " << method << endl;
905
906 //check if the method is implemented
907 mutex = true;
908 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) {
909 return FeatureSet();
910 }
911
912 PyObject *pyMethod = PyString_FromString(method);
913
914 PyObject *pyOutputList =
915 PyObject_CallMethod(m_pyInstance,method, NULL);
916
917 //Check return type
918 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
919 if (pyOutputList == NULL) {
920 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
921 << "] Unexpected result." << endl;
922 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
923 } else {
924 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
925 << "] Expected List return type." << endl;
926 }
927 Py_CLEAR(pyMethod);
928 Py_CLEAR(pyOutputList);
929 mutex = false;
930 return FeatureSet();
931 }
932 Py_DECREF(pyMethod);
933
934 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
935 FeatureSet returnFeatures;
936
937 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) {
938
939 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
940
941 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
942
943 pyDict = PyList_GET_ITEM(pyFeatureList,j);
944
945 if ( !PyDict_Check(pyDict) ) continue;
946
947 Py_ssize_t pyPos = NULL;
948 bool emptyFeature = true;
949 Feature feature;
950
951 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
952 {
953 emptyFeature = false;
954 switch (ffKeys[PyString_AsString(pyKey)])
955 {
956 case not_found :
957 cerr << "Unknown key in VAMP FeatureSet: "
958 << PyString_AsString(pyKey) << endl;
959 break;
960 case hasTimestamp:
961 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
962 break;
963 // TODO: clarify what to do here
964 case timeStamp:
965 feature.timestamp =
966 Vamp::RealTime::frame2RealTime
967 ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate);
968 break;
969 case values:
970 feature.values = PyList_As_FloatVector(pyValue);
971 break;
972 case label:
973 feature.label = PyString_AsString(pyValue);
974 break;
975 } // switch
976 } // while
977 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
978 else returnFeatures[i].push_back(feature);
979 }// for j
980 }//for i
981 Py_CLEAR(pyOutputList);
982 mutex = false;
983 return returnFeatures;
984 }
985