comparison PyPlugin.cpp @ 31:4f1894c7591b vampy2

Created Vampy2 branch
author fazekasgy
date Sun, 20 Sep 2009 17:31:20 +0000
parents 5139bf30f208
children a8231788216c
comparison
equal deleted inserted replaced
28:5139bf30f208 31:4f1894c7591b
49 49
50 */ 50 */
51 51
52 #include <Python.h> 52 #include <Python.h>
53 #include "PyPlugin.h" 53 #include "PyPlugin.h"
54 #include "PyTypeInterface.h"
55 #include <stdlib.h>
56 #include "PyExtensionModule.h"
57 //#include "PyRealTime.h"
58
54 59
55 #ifdef _WIN32 60 #ifdef _WIN32
56 #define pathsep ('\\') 61 #define PATHSEP ('\\')
57 #else 62 #else
58 #define pathsep ('/') 63 #define PATHSEP ('/')
59 #endif 64 #endif
60 65
61 //#define _DEBUG 66 //#define _DEBUG
62 67
63 using std::string; 68 using std::string;
64 using std::vector; 69 using std::vector;
65 using std::cerr; 70 using std::cerr;
66 using std::endl; 71 using std::endl;
67 using std::map; 72 using std::map;
68 73
69 // Maps to associate strings with enum values
70 static std::map<std::string, o::eOutDescriptors> outKeys;
71 static std::map<std::string, eSampleTypes> sampleKeys;
72 static std::map<std::string, eFeatureFields> ffKeys;
73 static std::map<std::string, p::eParmDescriptors> parmKeys;
74
75 Mutex PyPlugin::m_pythonInterpreterMutex; 74 Mutex PyPlugin::m_pythonInterpreterMutex;
76 static bool isMapInitialised = false; 75
77 76 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount) :
78 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass) :
79 Plugin(inputSampleRate), 77 Plugin(inputSampleRate),
80 m_pyClass(pyClass), 78 m_pyClass(pyClass),
79 m_instcount(instcount),
81 m_stepSize(0), 80 m_stepSize(0),
82 m_blockSize(0), 81 m_blockSize(0),
83 m_channels(0), 82 m_channels(0),
84 m_plugin(pluginKey), 83 m_plugin(pluginKey),
85 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), 84 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
86 m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))), 85 m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
87 m_processType(0), 86 m_processType(0),
88 m_pyProcess(NULL), 87 m_pyProcess(NULL),
89 m_inputDomain(TimeDomain) 88 m_inputDomain(TimeDomain),
89 m_quitOnErrorFlag(false),
90 m_debugFlag(false)
90 { 91 {
92 m_ti.setInputSampleRate(inputSampleRate);
93 MutexLocker locker(&m_pythonInterpreterMutex);
94 cerr << "Creating instance " << m_instcount << " of " << pluginKey << endl;
95
96 if (m_instcount == 0) initvampy();
97 m_instcount++;
98
99 // if (!PyImport_ImportModule("vampy"))
100 // cerr << "Could not import extension." << endl;
101
91 // Create an instance 102 // Create an instance
92 MutexLocker locker(&m_pythonInterpreterMutex);
93 Py_INCREF(m_pyClass); 103 Py_INCREF(m_pyClass);
94 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate); 104 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
95 PyObject *args = PyTuple_Pack(1, pyInputSampleRate); 105 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
96 m_pyInstance = PyObject_Call(m_pyClass, args, NULL); 106 m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
97 107
98 if (!m_pyInstance || PyErr_Occurred()) { 108 if (!m_pyInstance || PyErr_Occurred()) {
99 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } 109 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
100 Py_DECREF(m_pyClass); 110 Py_DECREF(m_pyClass);
101 Py_CLEAR(args); 111 Py_CLEAR(args);
102 Py_CLEAR(pyInputSampleRate); 112 Py_CLEAR(pyInputSampleRate);
103 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \"" << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl; 113 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \""
114 << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
104 throw std::string("Constructor failed"); 115 throw std::string("Constructor failed");
105 } 116 }
106 Py_INCREF(m_pyInstance); 117 Py_INCREF(m_pyInstance);
107 Py_DECREF(args); 118 Py_DECREF(args);
108 Py_DECREF(pyInputSampleRate); 119 Py_DECREF(pyInputSampleRate);
109 120
121 //query the debug flag
122 m_debugFlag = getBooleanFlag("vampy_debug_messages",true);
123 if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
124 else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl;
125
126 //query the quit on error flag
127 m_quitOnErrorFlag = getBooleanFlag("quit_on_type_error",false);
128 if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
129
130 //query the type conversion mode flag
131 bool st_flag = getBooleanFlag("use_strict_type_conversion",false);
132 if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
133 m_ti.setStrictTypingFlag(st_flag);
110 } 134 }
111 135
112 PyPlugin::~PyPlugin() 136 PyPlugin::~PyPlugin()
113 { 137 {
138 MutexLocker locker(&m_pythonInterpreterMutex);
139 m_instcount--;
140 cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
141
114 if (m_pyInstance) Py_DECREF(m_pyInstance); 142 if (m_pyInstance) Py_DECREF(m_pyInstance);
115 if (m_pyClass) Py_DECREF(m_pyClass); 143 //we increase the class refcount before creating an instance
144 if (m_pyClass) Py_DECREF(m_pyClass);
116 if (m_pyProcess) Py_CLEAR(m_pyProcess); 145 if (m_pyProcess) Py_CLEAR(m_pyProcess);
146 if (m_instcount == 0) cleanModule();
117 147
118 #ifdef _DEBUG 148 #ifdef _DEBUG
119 cerr << "PyPlugin::PyPlugin:" << m_class 149 cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
120 << " Instance deleted." << endl;
121 #endif 150 #endif
122 } 151 }
123 152
124 153
125 string 154 string
126 PyPlugin::getIdentifier() const 155 PyPlugin::getIdentifier() const
127 { 156 {
128 MutexLocker locker(&m_pythonInterpreterMutex); 157 MutexLocker locker(&m_pythonInterpreterMutex);
129 158 string rString="vampy-xxx";
130 char method[]="getIdentifier"; 159 if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
131 cerr << "[call] " << method << endl; 160
132 string rString="vampy-x"; 161 rString = genericMethodCall("getIdentifier",rString);
133 162 if (rString == "vampy-xxx")
134 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 163 cerr << "Warning: Plugin must return a unique identifier." << endl;
135
136 //Call the method
137 PyObject *pyString =
138 PyObject_CallMethod(m_pyInstance, method, NULL);
139
140 //Check return value
141 if (!PyString_Check(pyString)) {
142 Py_CLEAR(pyString);
143 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
144 << "] Expected String return value." << endl;
145 return rString;
146 }
147
148 rString=PyString_AsString(pyString);
149 Py_CLEAR(pyString);
150 return rString;
151 }
152 cerr << "Warning: Plugin must return a unique identifier." << endl;
153 return rString; 164 return rString;
154 } 165 }
155
156 166
157 string 167 string
158 PyPlugin::getName() const 168 PyPlugin::getName() const
159 { 169 {
160 MutexLocker locker(&m_pythonInterpreterMutex); 170 MutexLocker locker(&m_pythonInterpreterMutex);
161
162 char method[]="getName";
163 cerr << "[call] " << method << endl;
164 string rString="VamPy Plugin (Noname)"; 171 string rString="VamPy Plugin (Noname)";
165 172 return genericMethodCall("getName",rString);
166 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
167
168 //Call the method
169 PyObject *pyString =
170 PyObject_CallMethod(m_pyInstance, method, NULL);
171
172 //Check return value
173 if (!PyString_Check(pyString)) {
174 Py_CLEAR(pyString);
175 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
176 << "] Expected String return value." << endl;
177 return rString;
178 }
179
180 rString=PyString_AsString(pyString);
181 Py_CLEAR(pyString);
182 }
183 return rString;
184 } 173 }
185 174
186 string 175 string
187 PyPlugin::getDescription() const 176 PyPlugin::getDescription() const
188 { 177 {
189 MutexLocker locker(&m_pythonInterpreterMutex); 178 MutexLocker locker(&m_pythonInterpreterMutex);
190
191 char method[]="getDescription";
192 cerr << "[call] " << method << endl;
193 string rString="Not given. (Hint: Implement getDescription method.)"; 179 string rString="Not given. (Hint: Implement getDescription method.)";
194 180 return genericMethodCall("getDescription",rString);
195 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 181 }
196 182
197 //Call the method
198 PyObject *pyString =
199 PyObject_CallMethod(m_pyInstance, method, NULL);
200
201 //Check return value
202 if (!PyString_Check(pyString)) {
203 Py_CLEAR(pyString);
204 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
205 << "] Expected String return value." << endl;
206 return rString;
207 }
208
209 rString=PyString_AsString(pyString);
210 Py_CLEAR(pyString);
211 }
212 return rString;
213 }
214 183
215 string 184 string
216 PyPlugin::getMaker() const 185 PyPlugin::getMaker() const
217 { 186 {
218 MutexLocker locker(&m_pythonInterpreterMutex); 187 MutexLocker locker(&m_pythonInterpreterMutex);
219 188 string rString="VamPy Plugin.";
220 char method[]="getMaker"; 189 return genericMethodCall("getMaker",rString);
221 cerr << "[call] " << method << endl;
222 string rString="Generic VamPy Plugin.";
223
224 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
225
226 //Call the method
227 PyObject *pyString =
228 PyObject_CallMethod(m_pyInstance, method, NULL);
229
230 //Check return value
231 if (!PyString_Check(pyString)) {
232 Py_CLEAR(pyString);
233 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
234 << "] Expected String return value." << endl;
235 return rString;
236 }
237
238 rString=PyString_AsString(pyString);
239 Py_CLEAR(pyString);
240 }
241 return rString;
242 } 190 }
243 191
244 int 192 int
245 PyPlugin::getPluginVersion() const 193 PyPlugin::getPluginVersion() const
246 { 194 {
247 //!!! implement 195 MutexLocker locker(&m_pythonInterpreterMutex);
248 196 size_t rValue=2;
249 return 2; 197 return genericMethodCall("getPluginVersion",rValue);
250 } 198 }
251 199
252 string 200 string
253 PyPlugin::getCopyright() const 201 PyPlugin::getCopyright() const
254 { 202 {
255 MutexLocker locker(&m_pythonInterpreterMutex); 203 MutexLocker locker(&m_pythonInterpreterMutex);
256 204 string rString="Licence information not available.";
257 char method[]="getCopyright"; 205 return genericMethodCall("getCopyright",rString);
258 cerr << "[call] " << method << endl;
259 string rString="BSD License";
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
276 rString=PyString_AsString(pyString);
277 Py_CLEAR(pyString);
278 }
279
280 return rString;
281 } 206 }
282 207
283 208
284 bool 209 bool
285 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) 210 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
286 { 211 {
287 //useful for debugging Python plugins 212
288 char method[]="initialise";
289 cerr << "[call] " << method << endl;
290
291 //placing Mutex before these calls causes deadlock
292 if (channels < getMinChannelCount() || 213 if (channels < getMinChannelCount() ||
293 channels > getMaxChannelCount()) return false; 214 channels > getMaxChannelCount()) return false;
294 215
295 m_inputDomain = getInputDomain(); 216 m_inputDomain = getInputDomain();
296 217
297 MutexLocker locker(&m_pythonInterpreterMutex); 218 //Note: placing Mutex before the calls above causes deadlock !!
298 219 MutexLocker locker(&m_pythonInterpreterMutex);
299 initMaps();
300 220
301 m_stepSize = stepSize; 221 m_stepSize = stepSize;
302 m_blockSize = blockSize; 222 m_blockSize = blockSize;
303 m_channels = channels; 223 m_channels = channels;
304 224
225 //query the process implementation type
226 //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
227 //if they are not provided, we fall back to the original method
228 setProcessType();
229
230 return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
231 }
232
233 void
234 PyPlugin::reset()
235 {
236 MutexLocker locker(&m_pythonInterpreterMutex);
237 genericMethodCall("reset");
238 }
239
240 PyPlugin::InputDomain
241 PyPlugin::getInputDomain() const
242 {
243 MutexLocker locker(&m_pythonInterpreterMutex);
244 // Note: Vamp enum type is mapped to Python string !!
245 // Is there a better way? (Enums are not native to Python)
246 string rValue = "TimeDomain";
247 genericMethodCall("getInputDomain",rValue);
248 return (rValue == "FrequencyDomain")?FrequencyDomain:TimeDomain;
249 }
250
251 size_t
252 PyPlugin::getPreferredBlockSize() const
253 {
254 MutexLocker locker(&m_pythonInterpreterMutex);
255 size_t rValue = 0;
256 return genericMethodCall("getPreferredBlockSize",rValue);
257 }
258
259 size_t
260 PyPlugin::getPreferredStepSize() const
261 {
262 MutexLocker locker(&m_pythonInterpreterMutex);
263 size_t rValue = 0;
264 return genericMethodCall("getPreferredStepSize",rValue);
265 }
266
267 size_t
268 PyPlugin::getMinChannelCount() const
269 {
270 MutexLocker locker(&m_pythonInterpreterMutex);
271 size_t rValue = 1;
272 return genericMethodCall("getMinChannelCount",rValue);
273 }
274
275 size_t
276 PyPlugin::getMaxChannelCount() const
277 {
278 MutexLocker locker(&m_pythonInterpreterMutex);
279 size_t rValue = 1;
280 return genericMethodCall("getMaxChannelCount",rValue);
281 }
282
283 PyPlugin::OutputList
284 PyPlugin::getOutputDescriptors() const
285 {
286 MutexLocker locker(&m_pythonInterpreterMutex);
287 OutputList list;
288 return genericMethodCall("getOutputDescriptors",list);
289 }
290
291 PyPlugin::ParameterList
292 PyPlugin::getParameterDescriptors() const
293 {
294 MutexLocker locker(&m_pythonInterpreterMutex);
295 ParameterList list;
296 ///Note: This function is often called first by the host.
297 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
298 return genericMethodCall("getParameterDescriptors",list);
299 }
300
301 void PyPlugin::setParameter(std::string paramid, float newval)
302 {
303 MutexLocker locker(&m_pythonInterpreterMutex);
304 genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
305 }
306
307 float PyPlugin::getParameter(std::string paramid) const
308 {
309 MutexLocker locker(&m_pythonInterpreterMutex);
310 return genericMethodCallArgs<float>("getParameter",paramid);
311 }
312
313 #ifdef _DEBUG
314 static int proccounter = 0;
315 #endif
316
317 PyPlugin::FeatureSet
318 PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
319 {
320 MutexLocker locker(&m_pythonInterpreterMutex);
321
322 #ifdef _DEBUG
323 cerr << "[call] process, frame:" << proccounter << endl;
324 proccounter++;
325 #endif
326
327 if (m_blockSize == 0 || m_channels == 0) {
328 cerr << "ERROR: PyPlugin::process: "
329 << "Plugin has not been initialised" << endl;
330 return FeatureSet();
331 }
332
333 if (m_processType == not_implemented) {
334 cerr << "ERROR: In Python plugin [" << m_class
335 << "] No process implementation found. Returning empty feature set." << endl;
336 return FeatureSet();
337 }
338
339 // string method=PyString_AsString(m_pyProcess);
340
341 PyObject *pyOutputList = NULL;
342
343 if (m_processType == numpyProcess) {
344 pyOutputList = numpyProcessCall(inputBuffers,timestamp);
345 }
346
347 if (m_processType == legacyProcess) {
348 pyOutputList = legacyProcessCall(inputBuffers,timestamp);
349 }
350
351 FeatureSet rFeatureset;
352 rFeatureset = m_ti.PyValue_To_FeatureSet(pyOutputList);
353 Py_CLEAR(pyOutputList);
354 return rFeatureset;
355
356 }
357
358 PyObject*
359 PyPlugin::numpyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
360 {
361 PyObject *pyOutputList = NULL;
362
363 //create a list of buffers
364 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
365 for (size_t i=0; i < m_channels; ++i) {
366 //Expose memory using the Buffer Interface of C/API
367 //This will virtually pass a pointer which can be
368 //recasted in Python code as float or complex array
369 PyObject *pyBuffer = PyBuffer_FromMemory
370 ((void *) (float *) inputBuffers[i],
371 (Py_ssize_t) sizeof(float) * m_blockSize);
372
373 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer);
374 }
375 /*
376 //(1) pass RealTime as frameCount
377 PyObject *pyLongSample = PyLong_FromLong (
378 Vamp::RealTime::realTime2Frame
379 (timestamp, (unsigned int) m_inputSampleRate));
380
381 //Call python process (returns new reference)
382 pyOutputList = PyObject_CallMethodObjArgs
383 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
384 */
385 //(2) pass RealTime as PyRealTime
386 PyObject *pyRealTime = PyRealTime_FromRealTime(timestamp);
387
388 //Call python process (returns new reference)
389 pyOutputList = PyObject_CallMethodObjArgs
390 (m_pyInstance,m_pyProcess,pyChannelList,pyRealTime,NULL);
391
392 Py_DECREF(pyChannelList);
393 // Py_DECREF(pyLongSample);
394 Py_DECREF(pyRealTime);
395 return pyOutputList;
396 }
397
398 PyObject*
399 PyPlugin::legacyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
400 {
401 PyObject *pyOutputList = NULL;
402
403 //create a list of lists
404 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
405 for (size_t i=0; i < m_channels; ++i) {
406 //New list object
407 PyObject *pyFloat, *pyList;
408 pyList = PyList_New((Py_ssize_t) m_blockSize);
409
410 //Pack samples into a Python List Object
411 //pyFloat types will always be new references,
412 //these will be discarded when the list is deallocated
413 for (size_t j = 0; j < m_blockSize; ++j) {
414 pyFloat=PyFloat_FromDouble(
415 (double) inputBuffers[i][j]);
416 PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat);
417 }
418 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList);
419 }
420
421 //pass RealTime as frameCount
422 PyObject *pyLongSample = PyLong_FromLong (
423 Vamp::RealTime::realTime2Frame
424 (timestamp, (unsigned int) m_inputSampleRate));
425
426 //Call python process (returns new reference)
427 pyOutputList = PyObject_CallMethodObjArgs
428 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
429
430 Py_DECREF(pyChannelList);
431 Py_DECREF(pyLongSample);
432 return pyOutputList;
433 }
434
435 PyPlugin::FeatureSet
436 PyPlugin::getRemainingFeatures()
437 {
438 MutexLocker locker(&m_pythonInterpreterMutex);
439 FeatureSet rValue;
440 return genericMethodCall("getRemainingFeatures",rValue);
441 }
442
443
444 bool
445 PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const
446 {
447 bool rValue = defValue;
448 if (PyObject_HasAttrString(m_pyInstance,flagName))
449 {
450 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
451 if (!pyValue)
452 {
453 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
454 } else {
455 rValue = m_ti.PyValue_To_Bool(pyValue);
456 if (m_ti.error) {
457 cerr << m_ti.lastError().message << endl;
458 Py_CLEAR(pyValue);
459 rValue = defValue;
460 } else Py_DECREF(pyValue);
461 }
462 }
463 if (m_debugFlag) cerr << FLAG_VALUE << endl;
464 return rValue;
465 }
466
467 void
468 PyPlugin::setProcessType()
469 {
305 //quering process implementation type 470 //quering process implementation type
306 char legacyMethod[]="process"; 471 char legacyMethod[]="process";
307 char numpyMethod[]="processN"; 472 char numpyMethod[]="processN";
308 473
309 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) && 474 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
317 m_processType == 0) 482 m_processType == 0)
318 { 483 {
319 m_processType = numpyProcess; 484 m_processType = numpyProcess;
320 m_pyProcess = PyString_FromString(numpyMethod); 485 m_pyProcess = PyString_FromString(numpyMethod);
321 } 486 }
487
488 // These flags are optional. If provided, they override the
489 // implementation type making the use of the odd processN()
490 // function redundant.
491 // However, the code above provides backwards compatibility.
492 if (getBooleanFlag("use_numpy_interface",false))
493 m_processType = numpyProcess;
494 if (getBooleanFlag("use_legacy_interface",false))
495 m_processType = legacyProcess;
496 if (m_debugFlag && m_processType)
497 cerr << "Process type: " << ((m_processType==numpyProcess)?"numpy process":"legacy process") << endl;
322 498
323 if (!m_processType) 499 if (!m_processType)
324 { 500 {
325 m_processType = not_implemented; 501 m_processType = not_implemented;
326 m_pyProcess = NULL; 502 m_pyProcess = NULL;
327 cerr << "Warning: Python plugin [" << m_class << "::" << method 503 char method[]="initialise::setProcessType";
328 << "] No process implementation found. Plugin will do nothing." << endl; 504 cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
329 } 505 }
330 506 }
331 //Check if the method is implemented in Python else return false 507
332 if (PyObject_HasAttrString(m_pyInstance,method)) {
333
334 PyObject *pyMethod = PyString_FromString(method);
335 PyObject *pyChannels = PyInt_FromSsize_t((Py_ssize_t)channels);
336 PyObject *pyStepSize = PyInt_FromSsize_t((Py_ssize_t)m_stepSize);
337 PyObject *pyBlockSize = PyInt_FromSsize_t((Py_ssize_t)blockSize);
338 //Call the method
339 PyObject *pyBool =
340 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,NULL);
341
342 Py_DECREF(pyMethod);
343 Py_DECREF(pyChannels);
344 Py_DECREF(pyStepSize);
345 Py_DECREF(pyBlockSize);
346
347 //Check return value
348 if (PyErr_Occurred() || !PyBool_Check(pyBool)) {
349 PyErr_Print(); PyErr_Clear();
350 Py_CLEAR(pyBool);
351 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
352 << "] Expected Bool return value." << endl;
353 return false;
354 }
355
356 if (pyBool == Py_True) {
357 Py_CLEAR(pyBool);
358 return true;
359 } else {
360 Py_CLEAR(pyBool);
361 return false;
362 }
363 }
364 return false;
365 }
366
367 void
368 PyPlugin::reset()
369 {
370 MutexLocker locker(&m_pythonInterpreterMutex);
371
372 char method[]="reset";
373 cerr << "[call] " << method << endl;
374
375 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
376
377 PyObject_CallMethod(m_pyInstance, method, NULL);
378 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
379
380 }
381 }
382
383 PyPlugin::InputDomain PyPlugin::getInputDomain() const
384 {
385 MutexLocker locker(&m_pythonInterpreterMutex);
386
387 char method[]="getInputDomain";
388 cerr << "[call] " << method << endl;
389 PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain
390
391 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
392
393 PyObject *pyString = PyObject_CallMethod(m_pyInstance, method, NULL);
394
395 //Check return value
396 if (!PyString_Check(pyString)) {
397 Py_CLEAR(pyString);
398 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
399 << "] Expected String return value." << endl;
400 return rValue;
401 }
402
403 string domain = (string) PyString_AsString(pyString);
404 if (domain == "FrequencyDomain") rValue = FrequencyDomain;
405 Py_CLEAR(pyString);
406 }
407 return rValue;
408 }
409
410
411 size_t PyPlugin::getPreferredBlockSize() const
412 {
413 MutexLocker locker(&m_pythonInterpreterMutex);
414
415 char method[]="getPreferredBlockSize";
416 cerr << "[call] " << method << endl;
417 size_t rValue=0; //not set by default
418 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
419 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
420
421 //Check return value
422 if (!PyInt_Check(pyInt)) {
423 Py_CLEAR(pyInt);
424 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
425 << "] Expected Integer return value." << endl;
426 return rValue;
427 }
428
429 rValue=(size_t)PyInt_AS_LONG(pyInt);
430 Py_CLEAR(pyInt);
431 }
432 return rValue;
433 }
434
435 //size_t PyPlugin::getPreferredStepSize() const { return 0; }
436 size_t PyPlugin::getPreferredStepSize() const
437 {
438 MutexLocker locker(&m_pythonInterpreterMutex);
439
440 char method[]="getPreferredStepSize";
441 cerr << "[call] " << method << endl;
442 size_t rValue=1024; //not set by default
443 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
444 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
445
446 //Check return value
447 if (!PyInt_Check(pyInt)) {
448 Py_CLEAR(pyInt);
449 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
450 << "] Expected Integer return value." << endl;
451 return rValue;
452 }
453
454 rValue=(size_t)PyInt_AS_LONG(pyInt);
455 Py_CLEAR(pyInt);
456 }
457 return rValue;
458 }
459
460 size_t PyPlugin::getMinChannelCount() const
461 {
462 MutexLocker locker(&m_pythonInterpreterMutex);
463
464 char method[]="getMinChannelCount";
465 cerr << "[call] " << method << endl;
466 size_t rValue=1; //default value
467 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
468 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
469
470 //Check return value
471 if (!PyInt_Check(pyInt)) {
472 Py_CLEAR(pyInt);
473 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
474 << "] Expected String return value." << endl;
475 return rValue;
476 }
477
478 rValue=(size_t)PyInt_AS_LONG(pyInt);
479 Py_CLEAR(pyInt);
480 }
481 return rValue;
482 }
483
484 size_t PyPlugin::getMaxChannelCount() const
485 {
486 MutexLocker locker(&m_pythonInterpreterMutex);
487
488 char method[]="getMaxChannelCount";
489 cerr << "[call] " << method << endl;
490 size_t rValue=1; //default value
491 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
492 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
493
494 //Check return value
495 if (!PyInt_Check(pyInt)) {
496 Py_CLEAR(pyInt);
497 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
498 << "] Expected String return value." << endl;
499 return rValue;
500 }
501
502 rValue=(size_t)PyInt_AS_LONG(pyInt);
503 Py_CLEAR(pyInt);
504 }
505 return rValue;
506 }
507
508
509 PyPlugin::OutputList
510 PyPlugin::getOutputDescriptors() const
511 {
512
513 MutexLocker locker(&m_pythonInterpreterMutex);
514
515 //PyEval_AcquireThread(newThreadState);
516 OutputList list;
517 OutputDescriptor od;
518 char method[]="getOutputDescriptors";
519 cerr << "[call] " << method << endl;
520
521 //Check if the method is implemented in Python
522 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list;
523
524 //Call the method: must return list object (new reference)
525 PyObject *pyList =
526 PyObject_CallMethod(m_pyInstance,method, NULL);
527
528 //Check return type
529 if (! PyList_Check(pyList) ) {
530 Py_CLEAR(pyList);
531 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
532 << "] Expected List return type." << endl;
533 return list;
534 }
535
536 //These will all be borrowed references (no need to DECREF)
537 PyObject *pyDict, *pyKey, *pyValue;
538
539 //Parse Output List
540 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
541
542 //Get i-th Vamp output descriptor (Borrowed Reference)
543 pyDict = PyList_GET_ITEM(pyList,i);
544
545 //We only care about dictionaries holding output descriptors
546 if ( !PyDict_Check(pyDict) ) continue;
547
548 Py_ssize_t pyPos = NULL;
549 initMaps();
550
551 //Python Sequence Iterator
552 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
553 {
554 switch (outKeys[PyString_AsString(pyKey)])
555 {
556 case o::not_found :
557 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
558 break;
559 case o::identifier:
560 od.identifier = PyString_AsString(pyValue);
561 break;
562 case o::name:
563 od.name = PyString_AsString(pyValue);
564 break;
565 case o::description:
566 od.description = PyString_AsString(pyValue);
567 break;
568 case o::unit:
569 od.unit = PyString_AsString(pyValue);
570 break;
571 case o::hasFixedBinCount:
572 od.hasFixedBinCount = (bool) PyInt_AS_LONG(pyValue);
573 break;
574 case o::binCount:
575 od.binCount = (size_t) PyInt_AS_LONG(pyValue);
576 break;
577 case o::binNames:
578 od.binNames = PyList_To_StringVector(pyValue);
579 break;
580 case o::hasKnownExtents:
581 od.hasKnownExtents = (bool) PyInt_AS_LONG(pyValue);
582 break;
583 case o::minValue:
584 od.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
585 break;
586 case o::maxValue:
587 od.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
588 break;
589 case o::isQuantized:
590 od.isQuantized = (bool) PyInt_AS_LONG(pyValue);
591 break;
592 case o::quantizeStep:
593 od.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue);
594 break;
595 case o::sampleType:
596 od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)];
597 break;
598 case o::sampleRate:
599 od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue);
600 // od.sampleRate = m_inputSampleRate / m_stepSize;
601 cerr << od.sampleRate << endl;
602 break;
603 case o::hasDuration:
604 od.hasDuration = (bool)PyInt_AS_LONG(pyValue);
605 break;
606 default :
607 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
608 }
609 } // while dict
610 list.push_back(od);
611 } // for list
612 Py_CLEAR(pyList);
613 return list;
614 }
615
616 PyPlugin::ParameterList
617 PyPlugin::getParameterDescriptors() const
618 {
619 MutexLocker locker(&m_pythonInterpreterMutex);
620
621 ParameterList list;
622 ParameterDescriptor pd;
623 char method[]="getParameterDescriptors";
624 cerr << "[call] " << method << endl;
625
626 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
627
628 //Check if the method is implemented in Python
629 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) return list;
630
631 //Call the method: must return list object (new reference)
632 PyObject *pyList =
633 PyObject_CallMethod(m_pyInstance,method, NULL);
634
635 //Check return type
636 if (! PyList_Check(pyList) ) {
637 Py_CLEAR(pyList);
638 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
639 << "] Expected List return type." << endl;
640 return list;
641 }
642
643
644 //These will all be borrowed references (no need to DECREF)
645 PyObject *pyDict, *pyKey, *pyValue;
646
647 //Parse Output List
648 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
649
650 //Get i-th Vamp output descriptor (Borrowed Reference)
651 pyDict = PyList_GET_ITEM(pyList,i);
652
653 //We only care about dictionaries holding output descriptors
654 if ( !PyDict_Check(pyDict) ) continue;
655
656 Py_ssize_t pyPos = NULL;
657 initMaps();
658
659 //Python Sequence Iterator
660 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
661 {
662 switch (parmKeys[PyString_AsString(pyKey)])
663 {
664 case p::not_found :
665 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
666 break;
667 case p::identifier:
668 pd.identifier = PyString_AsString(pyValue);
669 break;
670 case p::name:
671 pd.name = PyString_AsString(pyValue);
672 break;
673 case p::description:
674 pd.description = PyString_AsString(pyValue);
675 break;
676 case p::unit:
677 pd.unit = PyString_AsString(pyValue);
678 break;
679 case p::minValue:
680 pd.minValue = (float) PyFloat_AS_DOUBLE(pyValue);
681 break;
682 case p::maxValue:
683 pd.maxValue = (float) PyFloat_AS_DOUBLE(pyValue);
684 break;
685 case p::defaultValue:
686 pd.defaultValue = (float) PyFloat_AS_DOUBLE(pyValue);
687 break;
688 case p::isQuantized:
689 pd.isQuantized = (bool) PyInt_AS_LONG(pyValue);
690 break;
691 case p::quantizeStep:
692 pd.quantizeStep = (float) PyFloat_AS_DOUBLE(pyValue);
693 break;
694 default :
695 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl;
696 }
697 } // while dict
698 list.push_back(pd);
699 } // for list
700 Py_CLEAR(pyList);
701 return list;
702 }
703
704 void PyPlugin::setParameter(std::string paramid, float newval)
705 {
706 MutexLocker locker(&m_pythonInterpreterMutex);
707
708 char method[]="setParameter";
709 cerr << "[call] " << method << endl;
710
711 //Check if the method is implemented in Python
712 if (PyObject_HasAttrString(m_pyInstance,method)) {
713
714 PyObject *pyMethod = PyString_FromString(method);
715 PyObject *pyParamid = PyString_FromString(paramid.c_str());
716 PyObject *pyNewval = PyFloat_FromDouble((double)newval);
717
718 //Call the method
719 PyObject *pyBool =
720 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,pyNewval,NULL);
721
722 //This only happens if there is a syntax error or so
723 if (pyBool == NULL) {
724 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
725 << "] Error setting parameter: " << paramid << endl;
726 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
727 }
728
729 Py_DECREF(pyMethod);
730 Py_DECREF(pyParamid);
731 Py_DECREF(pyNewval);
732 }
733 }
734
735 float PyPlugin::getParameter(std::string paramid) const
736 {
737 MutexLocker locker(&m_pythonInterpreterMutex);
738
739 char method[]="getParameter";
740 cerr << "[call] " << method << endl;
741 float rValue = 0.0f;
742
743 //Check if the method is implemented in Python
744 if (PyObject_HasAttrString(m_pyInstance,method)) {
745
746 PyObject *pyMethod = PyString_FromString(method);
747 PyObject *pyParamid = PyString_FromString(paramid.c_str());
748
749 //Call the method
750 PyObject *pyFloat =
751 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyParamid,NULL);
752
753 //Check return type
754 if (! PyFloat_Check(pyFloat) ) {
755 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
756 << "] Expected Float return type." << endl;
757 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
758 Py_CLEAR(pyFloat);
759 return rValue;
760 }
761
762 rValue = (float) PyFloat_AS_DOUBLE(pyFloat);
763
764 Py_DECREF(pyMethod);
765 Py_DECREF(pyParamid);
766 Py_DECREF(pyFloat);
767 }
768
769 return rValue;
770 }
771
772 #ifdef _DEBUG
773 static int proccounter = 0;
774 #endif
775
776 PyPlugin::FeatureSet
777 PyPlugin::process(const float *const *inputBuffers,
778 Vamp::RealTime timestamp)
779 {
780 MutexLocker locker(&m_pythonInterpreterMutex);
781
782 #ifdef _DEBUG
783 cerr << "[call] process, frame:" << proccounter << endl;
784 proccounter++;
785 #endif
786
787 if (m_blockSize == 0 || m_channels == 0) {
788 cerr << "ERROR: PyPlugin::process: "
789 << "Plugin has not been initialised" << endl;
790 return FeatureSet();
791 }
792
793 if (m_processType == not_implemented) {
794 cerr << "ERROR: In Python plugin [" << m_class
795 << "] No process implementation found. Returning empty feature set." << endl;
796 return FeatureSet();
797 }
798
799 string method=PyString_AsString(m_pyProcess);
800
801 PyObject *pyOutputList = NULL;
802
803 /*new numPy support*/
804 if (m_processType == numpyProcess) {
805
806 //create a list of buffers
807 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
808 for (size_t i=0; i < m_channels; ++i) {
809
810 //Expose memory using the Buffer Interface of C/API
811 //This will virtually pass a pointer which can be
812 //recasted in Python code as float or complex array
813 PyObject *pyBuffer = PyBuffer_FromMemory
814 ((void *) (float *) inputBuffers[i],
815 (Py_ssize_t) sizeof(float) * m_blockSize);
816
817 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer);
818 }
819
820 //pass RealTime as frameCount
821 PyObject *pyLongSample = PyLong_FromLong (
822 Vamp::RealTime::realTime2Frame
823 (timestamp, (unsigned int) m_inputSampleRate));
824
825 //Call python process (returns new reference)
826 pyOutputList = PyObject_CallMethodObjArgs
827 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
828
829 Py_DECREF(pyChannelList);
830 Py_DECREF(pyLongSample);
831
832 }
833
834 if (m_processType == legacyProcess) {
835
836 //create a list of lists
837 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
838 for (size_t i=0; i < m_channels; ++i) {
839
840 //Declare new list object
841 PyObject *pyFloat, *pyList;
842 pyList = PyList_New((Py_ssize_t) m_blockSize);
843
844 //Pack samples into a Python List Object
845 //pyFloat types will always be new references,
846 //these will be discarded when the list is deallocated
847 for (size_t j = 0; j < m_blockSize; ++j) {
848 pyFloat=PyFloat_FromDouble(
849 (double) inputBuffers[i][j]);
850 PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat);
851 }
852 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList);
853 }
854
855 //pass RealTime as frameCount
856 PyObject *pyLongSample = PyLong_FromLong (
857 Vamp::RealTime::realTime2Frame
858 (timestamp, (unsigned int) m_inputSampleRate));
859
860 //Call python process (returns new reference)
861 pyOutputList = PyObject_CallMethodObjArgs
862 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
863
864 Py_DECREF(pyChannelList);
865 Py_DECREF(pyLongSample);
866
867 }
868
869 //return nothing
870 //Py_CLEAR(pyOutputList);
871 //return FeatureSet();
872
873 //Check return type
874 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
875 if (pyOutputList == NULL) {
876 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
877 << "] Unexpected result." << endl;
878 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
879 } else {
880 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
881 << "] Expected List return type." << endl;
882 }
883 Py_CLEAR(pyOutputList);
884 return FeatureSet();
885 }
886
887 // Py_DECREF(pyList);
888 // This appears to be tracked by the cyclic garbage collector
889 // hence decrefing produces GC error
890 #ifdef _DEBUG
891 cerr << "Process Returned Features" << endl;
892 #endif
893 // These will ALL be borrowed references
894 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
895
896 FeatureSet returnFeatures;
897
898 //Parse Output List for each element (FeatureSet)
899 for (Py_ssize_t i = 0;
900 i < PyList_GET_SIZE(pyOutputList); ++i) {
901 //cerr << "output (FeatureSet): " << i << endl;
902
903 //Get i-th FeatureList (Borrowed Reference)
904 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
905
906 //Parse FeatureList for each element (Feature)
907 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
908 //cerr << "element (FeatureList): " << j << endl;
909
910 //Get j-th Feature (Borrowed Reference)
911 pyDict = PyList_GET_ITEM(pyFeatureList,j);
912
913 //We only care about dictionaries holding a Feature struct
914 if ( !PyDict_Check(pyDict) ) continue;
915
916 Py_ssize_t pyPos = NULL;
917 bool emptyFeature = true;
918 Feature feature;
919
920 //process::Python Sequence Iterator for dictionary
921 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
922 {
923 emptyFeature = false;
924 switch (ffKeys[PyString_AsString(pyKey)])
925 {
926 case unknown:
927 cerr << "Unknown key in Vamp FeatureSet: "
928 << PyString_AsString(pyKey) << endl;
929 break;
930 case hasTimestamp:
931 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
932 break;
933 case timeStamp:
934 feature.timestamp =
935 Vamp::RealTime::frame2RealTime(
936 PyLong_AsLong(pyValue),
937 (unsigned int) m_inputSampleRate );
938 #ifdef _DEBUG
939 cerr << "Timestamp: "
940 << (long)PyLong_AsLong(pyValue) << ", ->"
941 << feature.timestamp.toString() << endl;
942 #endif
943 break;
944 case hasDuration:
945 feature.hasDuration = (bool) PyInt_AS_LONG(pyValue);
946 break;
947 case duration:
948 feature.duration =
949 Vamp::RealTime::frame2RealTime(
950 PyLong_AsLong(pyValue),
951 (unsigned int) m_inputSampleRate );
952 #ifdef _DEBUG
953 cerr << "Duration: "
954 << (long)PyLong_AsLong(pyValue) << ", ->"
955 << feature.duration.toString() << endl;
956 #endif
957 break;
958 case values:
959 feature.values = PyList_As_FloatVector(pyValue);
960 break;
961 case label:
962 feature.label = PyString_AsString(pyValue);
963 break;
964 default :
965 cerr << "Invalid key in Vamp FeatureSet: "
966 << PyString_AsString(pyKey) << endl;
967 } // switch
968
969 } // while
970 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
971 else returnFeatures[i].push_back(feature);
972
973 }// for j = FeatureList
974
975 }//for i = FeatureSet
976 Py_CLEAR(pyOutputList);
977 return returnFeatures;
978 }
979
980
981
982 PyPlugin::FeatureSet
983 PyPlugin::getRemainingFeatures()
984 {
985 MutexLocker locker(&m_pythonInterpreterMutex);
986
987 static char method[]="getRemainingFeatures";
988 cerr << "[call] " << method << endl;
989
990 //check if the method is implemented
991 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) {
992 return FeatureSet();
993 }
994
995 PyObject *pyMethod = PyString_FromString(method);
996
997 PyObject *pyOutputList =
998 PyObject_CallMethod(m_pyInstance,method, NULL);
999
1000 //Check return type
1001 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
1002 if (pyOutputList == NULL) {
1003 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
1004 << "] Unexpected result." << endl;
1005 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
1006 } else {
1007 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
1008 << "] Expected List return type." << endl;
1009 }
1010 Py_CLEAR(pyMethod);
1011 Py_CLEAR(pyOutputList);
1012 return FeatureSet();
1013 }
1014 Py_DECREF(pyMethod);
1015
1016 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
1017 FeatureSet returnFeatures;
1018
1019 //iterate through list of outputs
1020 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) {
1021
1022 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
1023
1024 //iterate list of Features
1025 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
1026 #ifdef _DEBUG
1027 cerr << "feature: " << j << endl;
1028 #endif
1029 pyDict = PyList_GET_ITEM(pyFeatureList,j);
1030
1031 if ( !PyDict_Check(pyDict) ) continue;
1032
1033 Py_ssize_t pyPos = NULL;
1034 bool emptyFeature = true;
1035 Feature feature;
1036
1037 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
1038 {
1039 emptyFeature = false;
1040 switch (ffKeys[PyString_AsString(pyKey)])
1041 {
1042 case unknown :
1043 cerr << "Unknown key in Vamp FeatureSet: "
1044 << PyString_AsString(pyKey) << endl;
1045 break;
1046 case hasTimestamp:
1047 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
1048 break;
1049 case timeStamp:
1050 feature.timestamp =
1051 Vamp::RealTime::frame2RealTime(
1052 PyLong_AsLong(pyValue),
1053 (unsigned int) m_inputSampleRate );
1054 #ifdef _DEBUG
1055 cerr << "Timestamp: "
1056 << (long)PyLong_AsLong(pyValue) << ", ->"
1057 << feature.timestamp.toString() << endl;
1058 #endif
1059 break;
1060 case hasDuration:
1061 feature.hasDuration = (bool) PyInt_AS_LONG(pyValue);
1062 break;
1063 case duration:
1064 feature.duration =
1065 Vamp::RealTime::frame2RealTime(
1066 PyLong_AsLong(pyValue),
1067 (unsigned int) m_inputSampleRate );
1068 #ifdef _DEBUG
1069 cerr << "Duration: "
1070 << (long)PyLong_AsLong(pyValue) << ", ->"
1071 << feature.duration.toString() << endl;
1072 #endif
1073 break;
1074 case values:
1075 feature.values = PyList_As_FloatVector(pyValue);
1076 break;
1077 case label:
1078 feature.label = PyString_AsString(pyValue);
1079 break;
1080 } // switch
1081 } // while
1082 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
1083 else returnFeatures[i].push_back(feature);
1084 }// for j
1085 }//for i
1086 Py_CLEAR(pyOutputList);
1087 return returnFeatures;
1088 }
1089
1090 bool
1091 PyPlugin::initMaps() const
1092 {
1093
1094 if (isMapInitialised) return true;
1095
1096 outKeys["identifier"] = o::identifier;
1097 outKeys["name"] = o::name;
1098 outKeys["description"] = o::description;
1099 outKeys["unit"] = o::unit;
1100 outKeys["hasFixedBinCount"] = o::hasFixedBinCount;
1101 outKeys["binCount"] = o::binCount;
1102 outKeys["binNames"] = o::binNames;
1103 outKeys["hasKnownExtents"] = o::hasKnownExtents;
1104 outKeys["minValue"] = o::minValue;
1105 outKeys["maxValue"] = o::maxValue;
1106 outKeys["isQuantized"] = o::isQuantized;
1107 outKeys["quantizeStep"] = o::quantizeStep;
1108 outKeys["sampleType"] = o::sampleType;
1109 outKeys["sampleRate"] = o::sampleRate;
1110 outKeys["hasDuration"] = o::hasDuration;
1111
1112 sampleKeys["OneSamplePerStep"] = OneSamplePerStep;
1113 sampleKeys["FixedSampleRate"] = FixedSampleRate;
1114 sampleKeys["VariableSampleRate"] = VariableSampleRate;
1115
1116 ffKeys["hasTimestamp"] = hasTimestamp;
1117 ffKeys["timeStamp"] = timeStamp;
1118 ffKeys["hasDuration"] = hasDuration;
1119 ffKeys["duration"] = duration;
1120 ffKeys["values"] = values;
1121 ffKeys["label"] = label;
1122
1123 parmKeys["identifier"] = p::identifier;
1124 parmKeys["name"] = p::name;
1125 parmKeys["description"] = p::description;
1126 parmKeys["unit"] = p::unit;
1127 parmKeys["minValue"] = p::minValue;
1128 parmKeys["maxValue"] = p::maxValue;
1129 parmKeys["defaultValue"] = p::defaultValue;
1130 parmKeys["isQuantized"] = p::isQuantized;
1131 parmKeys["quantizeStep"] = p::quantizeStep;
1132
1133 isMapInitialised = true;
1134 return true;
1135 }
1136
1137
1138 //missing API helper: convert Python list to C++ vector of strings
1139 //TODO: these could be templates if we need more of this kind
1140 std::vector<std::string>
1141 PyPlugin::PyList_To_StringVector (PyObject *inputList) const {
1142
1143 std::vector<std::string> Output;
1144 std::string ListElement;
1145 PyObject *pyString = NULL;
1146
1147 if (!PyList_Check(inputList)) return Output;
1148
1149 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) {
1150 //Get next list item (Borrowed Reference)
1151 pyString = PyList_GET_ITEM(inputList,i);
1152 ListElement = (string) PyString_AsString(PyObject_Str(pyString));
1153 Output.push_back(ListElement);
1154 }
1155 return Output;
1156 }
1157
1158 //missing API helper: convert Python list to C++ vector of floats
1159 std::vector<float>
1160 PyPlugin::PyList_As_FloatVector (PyObject *inputList) const {
1161
1162 std::vector<float> Output;
1163 float ListElement;
1164 PyObject *pyFloat = NULL;
1165
1166 if (!PyList_Check(inputList)) return Output;
1167
1168 for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) {
1169 //Get next list item (Borrowed Reference)
1170 pyFloat = PyList_GET_ITEM(inputList,k);
1171 ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
1172 #ifdef _DEBUG
1173 cerr << "value: " << ListElement << endl;
1174 #endif
1175 Output.push_back(ListElement);
1176 }
1177
1178 return Output;
1179 }
1180
1181 /* TODO: find out why this produces error, also
1182 do sg more clever about handling RealTime
1183 Vamp::RealTime
1184 PyFrame_As_RealTime (PyObject *frameNo,size_t inputSampleRate) {
1185 Vamp::RealTime result =
1186 Vamp::RealTime::frame2RealTime((size_t)PyInt_AS_LONG(frameNo), inputSampleRate);
1187 return result;
1188 }
1189 */
1190