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