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