Mercurial > hg > vampy
comparison PyPlugin.h @ 37:27bab3a16c9a vampy2final
new branch Vampy2final
author | fazekasgy |
---|---|
date | Mon, 05 Oct 2009 11:28:00 +0000 |
parents | |
children | 8b2eddf686da |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 37:27bab3a16c9a |
---|---|
1 /* | |
2 | |
3 * Vampy : This plugin is a wrapper around the Vamp plugin API. | |
4 * It allows for writing Vamp plugins in Python. | |
5 | |
6 * Centre for Digital Music, Queen Mary University of London. | |
7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources | |
8 * for licence information.) | |
9 | |
10 */ | |
11 | |
12 /* | |
13 Vamp | |
14 | |
15 An API for audio analysis and feature extraction plugins. | |
16 | |
17 Centre for Digital Music, Queen Mary, University of London. | |
18 Copyright 2006 Chris Cannam. | |
19 | |
20 Permission is hereby granted, free of charge, to any person | |
21 obtaining a copy of this software and associated documentation | |
22 files (the "Software"), to deal in the Software without | |
23 restriction, including without limitation the rights to use, copy, | |
24 modify, merge, publish, distribute, sublicense, and/or sell copies | |
25 of the Software, and to permit persons to whom the Software is | |
26 furnished to do so, subject to the following conditions: | |
27 | |
28 The above copyright notice and this permission notice shall be | |
29 included in all copies or substantial portions of the Software. | |
30 | |
31 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
32 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
33 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
34 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | |
35 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
36 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
37 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
38 | |
39 Except as contained in this notice, the names of the Centre for | |
40 Digital Music; Queen Mary, University of London; and Chris Cannam | |
41 shall not be used in advertising or otherwise to promote the sale, | |
42 use or other dealings in this Software without prior written | |
43 authorization. | |
44 */ | |
45 | |
46 #ifndef _PYTHON_WRAPPER_PLUGIN_H_ | |
47 #define _PYTHON_WRAPPER_PLUGIN_H_ | |
48 | |
49 #define _CLASS_METHOD_ m_class << "::" << method | |
50 #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: " | |
51 #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " " | |
52 #define DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented. Returning default value." | |
53 #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True") | |
54 | |
55 #include <Python.h> | |
56 #include "PyExtensionModule.h" | |
57 #include "PyTypeInterface.h" | |
58 #include "vamp-sdk/Plugin.h" | |
59 #include "Mutex.h" | |
60 | |
61 using std::string; | |
62 using std::cerr; | |
63 using std::endl; | |
64 | |
65 enum eProcessType { | |
66 not_implemented, | |
67 legacyProcess, | |
68 numpyProcess, | |
69 numpy_bufferProcess, | |
70 numpy_arrayProcess | |
71 }; | |
72 | |
73 class PyPlugin : public Vamp::Plugin | |
74 { | |
75 public: | |
76 PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount); | |
77 virtual ~PyPlugin(); | |
78 | |
79 bool initialise(size_t channels, size_t stepSize, size_t blockSize); | |
80 void reset(); | |
81 | |
82 InputDomain getInputDomain() const; | |
83 size_t getPreferredBlockSize() const; | |
84 size_t getPreferredStepSize() const; | |
85 size_t getMinChannelCount() const; | |
86 size_t getMaxChannelCount() const; | |
87 | |
88 std::string getIdentifier() const; | |
89 std::string getName() const; | |
90 std::string getDescription() const; | |
91 std::string getMaker() const; | |
92 int getPluginVersion() const; | |
93 std::string getCopyright() const; | |
94 | |
95 OutputList getOutputDescriptors() const; | |
96 ParameterList getParameterDescriptors() const; | |
97 float getParameter(std::string paramid) const; | |
98 void setParameter(std::string paramid, float newval); | |
99 | |
100 FeatureSet process(const float *const *inputBuffers, | |
101 Vamp::RealTime timestamp); | |
102 | |
103 FeatureSet getRemainingFeatures(); | |
104 | |
105 protected: | |
106 static Mutex m_pythonInterpreterMutex; | |
107 PyObject *m_pyClass; | |
108 PyObject *m_pyInstance; | |
109 int &m_instcount; | |
110 size_t m_stepSize; | |
111 size_t m_blockSize; | |
112 size_t m_channels; | |
113 std::string m_plugin; | |
114 std::string m_class; | |
115 std::string m_path; | |
116 eProcessType m_processType; | |
117 PyObject *m_pyProcess; | |
118 PyObject *m_pyProcessCallable; | |
119 mutable InputDomain m_inputDomain; | |
120 PyTypeInterface m_ti; | |
121 int m_vampyFlags; | |
122 bool m_quitOnErrorFlag; | |
123 bool m_debugFlag; | |
124 bool m_useRealTimeFlag; | |
125 | |
126 void setProcessType(); | |
127 | |
128 FeatureSet processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp); | |
129 | |
130 bool getBooleanFlag(char flagName[],bool) const; | |
131 int getBinaryFlags(char flagName[], eVampyFlags) const; | |
132 void typeErrorHandler(char *method) const; | |
133 | |
134 /// simple 'void return' call with no args | |
135 void genericMethodCall(char *method) const | |
136 { | |
137 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
138 if ( PyObject_HasAttrString(m_pyInstance,method) ) | |
139 { | |
140 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); | |
141 if (!pyValue) { | |
142 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
143 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
144 } | |
145 } | |
146 } | |
147 | |
148 /// 'no arg with default return value' call | |
149 template<typename RET> | |
150 RET &genericMethodCall(char *method, RET &rValue) const | |
151 { | |
152 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
153 if ( PyObject_HasAttrString(m_pyInstance,method) ) | |
154 { | |
155 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL); | |
156 if (!pyValue) { | |
157 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
158 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
159 return rValue; | |
160 } | |
161 | |
162 /// convert the returned value | |
163 m_ti.PyValue_To_rValue(pyValue,rValue); | |
164 if (!m_ti.error) { | |
165 Py_DECREF(pyValue); | |
166 } else { | |
167 Py_CLEAR(pyValue); | |
168 typeErrorHandler(method); | |
169 } | |
170 return rValue; | |
171 } | |
172 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
173 return rValue; | |
174 } | |
175 | |
176 /// unary call | |
177 template<typename RET,typename A1> | |
178 RET genericMethodCallArgs(char *method, A1 arg1) const | |
179 { | |
180 RET rValue = RET(); | |
181 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
182 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
183 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
184 return rValue; | |
185 } | |
186 | |
187 /// prepare arguments for fast method call | |
188 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
189 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
190 PyObject* pyArgs = PyTuple_New(1); | |
191 if (!(pyArgs && pyCallable && pyMethod)) { | |
192 cerr << PLUGIN_ERROR << "Failed to prepare argument for calling method." << endl; | |
193 Py_CLEAR(pyMethod); | |
194 Py_CLEAR(pyCallable); | |
195 Py_CLEAR(pyArgs); | |
196 return rValue; | |
197 } | |
198 | |
199 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
200 if (m_ti.error) { | |
201 cerr << PLUGIN_ERROR << "Failed to convert argument for calling method." << endl; | |
202 typeErrorHandler(method); | |
203 Py_CLEAR(pyMethod); | |
204 Py_CLEAR(pyCallable); | |
205 Py_CLEAR(pyArg1); | |
206 Py_CLEAR(pyArgs); | |
207 return rValue; | |
208 } | |
209 | |
210 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
211 Py_INCREF(pyArg1); | |
212 | |
213 /// call the method | |
214 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
215 if (!pyValue) | |
216 { | |
217 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
218 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
219 Py_CLEAR(pyMethod); | |
220 Py_CLEAR(pyCallable); | |
221 Py_CLEAR(pyArg1); | |
222 Py_CLEAR(pyArgs); | |
223 return rValue; | |
224 } | |
225 | |
226 Py_DECREF(pyMethod); | |
227 Py_DECREF(pyCallable); | |
228 Py_DECREF(pyArg1); | |
229 Py_DECREF(pyArgs); | |
230 | |
231 /// convert the returned value | |
232 m_ti.PyValue_To_rValue(pyValue,rValue); | |
233 if (!m_ti.error) { | |
234 Py_DECREF(pyValue); | |
235 } else { | |
236 Py_CLEAR(pyValue); | |
237 typeErrorHandler(method); | |
238 } | |
239 return rValue; | |
240 } | |
241 | |
242 /// binary call | |
243 template<typename RET,typename A1,typename A2> | |
244 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2) const | |
245 { | |
246 RET rValue = RET(); | |
247 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
248 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
249 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
250 return rValue; | |
251 } | |
252 | |
253 /// prepare arguments for fast method call | |
254 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
255 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
256 PyObject* pyArgs = PyTuple_New(2); | |
257 if (!(pyArgs && pyCallable && pyMethod)) { | |
258 cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; | |
259 Py_CLEAR(pyMethod); | |
260 Py_CLEAR(pyCallable); | |
261 Py_CLEAR(pyArgs); | |
262 return rValue; | |
263 } | |
264 | |
265 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
266 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); | |
267 if (m_ti.error) { | |
268 cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; | |
269 typeErrorHandler(method); | |
270 Py_CLEAR(pyMethod); | |
271 Py_CLEAR(pyCallable); | |
272 Py_CLEAR(pyArg1); | |
273 Py_CLEAR(pyArg2); | |
274 Py_CLEAR(pyArgs); | |
275 return rValue; | |
276 } | |
277 | |
278 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
279 Py_INCREF(pyArg1); | |
280 PyTuple_SET_ITEM(pyArgs, 1, pyArg2); | |
281 Py_INCREF(pyArg2); | |
282 | |
283 // calls the method | |
284 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
285 if (!pyValue) | |
286 { | |
287 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
288 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
289 Py_CLEAR(pyMethod); | |
290 Py_CLEAR(pyCallable); | |
291 Py_CLEAR(pyArg1); | |
292 Py_CLEAR(pyArg2); | |
293 Py_CLEAR(pyArgs); | |
294 return rValue; | |
295 } | |
296 | |
297 Py_DECREF(pyMethod); | |
298 Py_DECREF(pyCallable); | |
299 Py_DECREF(pyArg1); | |
300 Py_DECREF(pyArg2); | |
301 Py_DECREF(pyArgs); | |
302 | |
303 /// convert the returned value | |
304 m_ti.PyValue_To_rValue(pyValue,rValue); | |
305 if (!m_ti.error) { | |
306 Py_DECREF(pyValue); | |
307 } else { | |
308 Py_CLEAR(pyValue); | |
309 typeErrorHandler(method); | |
310 } | |
311 return rValue; | |
312 } | |
313 | |
314 /// trenary call | |
315 template<typename RET,typename A1,typename A2,typename A3> | |
316 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3) const | |
317 { | |
318 RET rValue = RET(); | |
319 if (m_debugFlag) cerr << DEBUG_NAME << endl; | |
320 if (!PyObject_HasAttrString(m_pyInstance,method)) { | |
321 if (m_debugFlag) cerr << DEAFULT_RETURN << endl; | |
322 return rValue; | |
323 } | |
324 | |
325 /// prepare arguments for fast method call | |
326 PyObject *pyMethod = m_ti.PyValue_From_CValue(method); | |
327 PyObject *pyCallable = PyObject_GetAttr(m_pyInstance,pyMethod); | |
328 PyObject* pyArgs = PyTuple_New(3); | |
329 if (!(pyArgs && pyCallable && pyMethod)) { | |
330 cerr << PLUGIN_ERROR << "Failed to prepare arguments for calling method." << endl; | |
331 Py_CLEAR(pyMethod); | |
332 Py_CLEAR(pyCallable); | |
333 Py_CLEAR(pyArgs); | |
334 return rValue; | |
335 } | |
336 | |
337 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1); | |
338 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2); | |
339 PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3); | |
340 if (m_ti.error) { | |
341 cerr << PLUGIN_ERROR << "Failed to convert arguments for calling method." << endl; | |
342 typeErrorHandler(method); | |
343 Py_CLEAR(pyMethod); | |
344 Py_CLEAR(pyCallable); | |
345 Py_CLEAR(pyArg1); | |
346 Py_CLEAR(pyArg2); | |
347 Py_CLEAR(pyArg3); | |
348 Py_CLEAR(pyArgs); | |
349 return rValue; | |
350 } | |
351 | |
352 /// Optimization: Pack args in a tuple to avoid va_list parsing. | |
353 PyTuple_SET_ITEM(pyArgs, 0, pyArg1); | |
354 Py_INCREF(pyArg1); | |
355 PyTuple_SET_ITEM(pyArgs, 1, pyArg2); | |
356 Py_INCREF(pyArg2); | |
357 PyTuple_SET_ITEM(pyArgs, 2, pyArg3); | |
358 Py_INCREF(pyArg3); | |
359 | |
360 // PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL); | |
361 /// fast method call | |
362 PyObject *pyValue = PyObject_Call(pyCallable,pyArgs,NULL); | |
363 if (!pyValue) | |
364 { | |
365 cerr << PLUGIN_ERROR << "Failed to call method." << endl; | |
366 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
367 Py_CLEAR(pyMethod); | |
368 Py_CLEAR(pyCallable); | |
369 Py_CLEAR(pyArg1); | |
370 Py_CLEAR(pyArg2); | |
371 Py_CLEAR(pyArg3); | |
372 Py_CLEAR(pyArgs); | |
373 return rValue; | |
374 } | |
375 | |
376 Py_DECREF(pyMethod); | |
377 Py_DECREF(pyCallable); | |
378 Py_DECREF(pyArg1); | |
379 Py_DECREF(pyArg2); | |
380 Py_DECREF(pyArg3); | |
381 Py_DECREF(pyArgs); | |
382 | |
383 /// convert the returned value | |
384 m_ti.PyValue_To_rValue(pyValue,rValue); | |
385 if (!m_ti.error) { | |
386 Py_DECREF(pyValue); | |
387 } else { | |
388 Py_CLEAR(pyValue); | |
389 typeErrorHandler(method); | |
390 } | |
391 return rValue; | |
392 } | |
393 | |
394 }; | |
395 | |
396 /// optimised process call | |
397 inline PyPlugin::FeatureSet | |
398 PyPlugin::processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp) | |
399 { | |
400 | |
401 /// Optimizations: 1) we avoid ...ObjArg functions since we know | |
402 /// the number of arguments, and we don't like va_list parsing | |
403 /// in the process. 2) Also: we're supposed to incref args, | |
404 /// but instead, we let the arguments tuple steal the references | |
405 /// and decref them when it is deallocated. | |
406 /// 3) all conversions are now using the fast sequence protocol | |
407 /// (indexing the underlying object array). | |
408 | |
409 FeatureSet rFeatureSet; | |
410 PyObject *pyChannelList = NULL; | |
411 | |
412 if (m_processType == numpy_bufferProcess) { | |
413 pyChannelList = m_ti.InputBuffers_As_SharedMemoryList(inputBuffers,m_channels,m_blockSize); | |
414 } | |
415 | |
416 if (m_processType == legacyProcess) { | |
417 pyChannelList = m_ti.InputBuffers_As_PythonLists(inputBuffers,m_channels,m_blockSize,m_inputDomain); | |
418 } | |
419 | |
420 #ifdef HAVE_NUMPY | |
421 if (m_processType == numpy_arrayProcess) { | |
422 pyChannelList = m_ti.InputBuffers_As_NumpyArray(inputBuffers,m_channels,m_blockSize,m_inputDomain); | |
423 } | |
424 #endif | |
425 | |
426 /// we don't expect these to fail unless out of memory (which is very unlikely on modern systems) | |
427 #ifdef _DEBUG | |
428 if (!pyChannelList) { | |
429 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
430 std::string method = PyString_AsString(m_pyProcess); | |
431 cerr << PLUGIN_ERROR << "Failed to create channel list." << endl; | |
432 return rFeatureSet; | |
433 } | |
434 #endif | |
435 | |
436 PyObject *pyTimeStamp = NULL; | |
437 | |
438 if (m_useRealTimeFlag) { | |
439 //(1) pass TimeStamp as PyRealTime object | |
440 pyTimeStamp = PyRealTime_FromRealTime(timestamp); | |
441 | |
442 } else { | |
443 //(2) pass TimeStamp as frame count (long Sample Count) | |
444 pyTimeStamp = PyLong_FromLong(Vamp::RealTime::realTime2Frame | |
445 (timestamp, (unsigned int) m_inputSampleRate)); | |
446 } | |
447 | |
448 | |
449 #ifdef _DEBUG | |
450 if (!pyTimeStamp) { | |
451 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
452 std::string method = PyString_AsString(m_pyProcess); | |
453 cerr << PLUGIN_ERROR << "Failed to create RealTime time stamp." << endl; | |
454 Py_DECREF(pyChannelList); | |
455 return rFeatureSet; | |
456 } | |
457 #endif | |
458 | |
459 /// Old method: Call python process (returns new reference) | |
460 /// PyObject *pyValue = PyObject_CallMethodObjArgs | |
461 /// (m_pyInstance,m_pyProcess,pyChannelList,pyTimeStamp,NULL); | |
462 | |
463 PyObject *pyArgs = PyTuple_New(2); | |
464 PyTuple_SET_ITEM(pyArgs, 0, pyChannelList); | |
465 PyTuple_SET_ITEM(pyArgs, 1, pyTimeStamp); | |
466 | |
467 /// Call python process (returns new reference) {kwArgs = NULL} | |
468 PyObject *pyValue = PyObject_Call(m_pyProcessCallable,pyArgs,NULL); | |
469 | |
470 if (!pyValue) { | |
471 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();} | |
472 std::string method = PyString_AsString(m_pyProcess); | |
473 cerr << PLUGIN_ERROR << "An error occurred while evaluating Python process." << endl; | |
474 Py_CLEAR(pyValue); | |
475 Py_CLEAR(pyArgs); | |
476 return rFeatureSet; | |
477 } | |
478 | |
479 rFeatureSet = m_ti.PyValue_To_FeatureSet(pyValue); | |
480 if (!m_ti.error) { | |
481 Py_DECREF(pyValue); | |
482 Py_DECREF(pyArgs); | |
483 } else { | |
484 typeErrorHandler(PyString_AsString(m_pyProcess)); | |
485 Py_CLEAR(pyValue); | |
486 Py_CLEAR(pyArgs); | |
487 } | |
488 return rFeatureSet; | |
489 } | |
490 | |
491 #endif |