comparison PyPlugin.cpp @ 37:27bab3a16c9a vampy2final

new branch Vampy2final
author fazekasgy
date Mon, 05 Oct 2009 11:28:00 +0000
parents
children c1e4f706ca9a
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 #include <Python.h>
13 #include "PyPlugin.h"
14 #include "PyTypeInterface.h"
15 #include <stdlib.h>
16 #include "PyExtensionModule.h"
17
18
19 #ifdef _WIN32
20 #define PATHSEP ('\\')
21 #else
22 #define PATHSEP ('/')
23 #endif
24
25 using std::string;
26 using std::vector;
27 using std::cerr;
28 using std::endl;
29 using std::map;
30
31 Mutex PyPlugin::m_pythonInterpreterMutex;
32
33 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount) :
34 Plugin(inputSampleRate),
35 m_pyClass(pyClass),
36 m_instcount(instcount),
37 m_stepSize(0),
38 m_blockSize(0),
39 m_channels(0),
40 m_plugin(pluginKey),
41 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
42 m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
43 m_processType(not_implemented),
44 m_pyProcess(NULL),
45 m_inputDomain(TimeDomain),
46 m_quitOnErrorFlag(false),
47 m_debugFlag(false)
48 {
49 m_ti.setInputSampleRate(inputSampleRate);
50 MutexLocker locker(&m_pythonInterpreterMutex);
51 cerr << "Creating instance " << m_instcount << " of " << pluginKey << endl;
52
53 // Create an instance
54 Py_INCREF(m_pyClass);
55 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
56 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
57 m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
58
59 if (!m_pyInstance || PyErr_Occurred()) {
60 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
61 Py_DECREF(m_pyClass);
62 Py_CLEAR(args);
63 Py_CLEAR(pyInputSampleRate);
64 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \""
65 << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
66 throw std::string("Constructor failed");
67 }
68 Py_INCREF(m_pyInstance);
69 Py_DECREF(args);
70 Py_DECREF(pyInputSampleRate);
71
72 m_instcount++;
73
74 // query and decode vampy flags
75 m_vampyFlags = getBinaryFlags("vampy_flags",vf_NULL);
76
77 m_debugFlag = (bool) (m_vampyFlags & vf_DEBUG);
78 m_quitOnErrorFlag = (bool) (m_vampyFlags & vf_QUIT);
79 bool st_flag = (bool) (m_vampyFlags & vf_STRICT);
80 m_useRealTimeFlag = (bool) (m_vampyFlags & vf_REALTIME);
81
82 if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
83 else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl;
84
85 if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
86
87 if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
88 m_ti.setStrictTypingFlag(st_flag);
89
90 }
91
92 PyPlugin::~PyPlugin()
93 {
94 MutexLocker locker(&m_pythonInterpreterMutex);
95 m_instcount--;
96 // cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
97
98 if (m_pyInstance) Py_DECREF(m_pyInstance);
99 //we increase the class refcount before creating an instance
100 if (m_pyClass) Py_DECREF(m_pyClass);
101 if (m_pyProcess) Py_CLEAR(m_pyProcess);
102
103 #ifdef _DEBUG
104 cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
105 #endif
106 }
107
108 string
109 PyPlugin::getIdentifier() const
110 {
111 MutexLocker locker(&m_pythonInterpreterMutex);
112 string rString="vampy-xxx";
113 if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
114
115 rString = genericMethodCall("getIdentifier",rString);
116 if (rString == "vampy-xxx")
117 cerr << "Warning: Plugin must return a unique identifier." << endl;
118 return rString;
119 }
120
121 string
122 PyPlugin::getName() const
123 {
124 MutexLocker locker(&m_pythonInterpreterMutex);
125 string rString="VamPy Plugin (Noname)";
126 return genericMethodCall("getName",rString);
127 }
128
129 string
130 PyPlugin::getDescription() const
131 {
132 MutexLocker locker(&m_pythonInterpreterMutex);
133 string rString="Not given. (Hint: Implement getDescription method.)";
134 return genericMethodCall("getDescription",rString);
135 }
136
137
138 string
139 PyPlugin::getMaker() const
140 {
141 MutexLocker locker(&m_pythonInterpreterMutex);
142 string rString="VamPy Plugin.";
143 return genericMethodCall("getMaker",rString);
144 }
145
146 int
147 PyPlugin::getPluginVersion() const
148 {
149 MutexLocker locker(&m_pythonInterpreterMutex);
150 size_t rValue=2;
151 return genericMethodCall("getPluginVersion",rValue);
152 }
153
154 string
155 PyPlugin::getCopyright() const
156 {
157 MutexLocker locker(&m_pythonInterpreterMutex);
158 string rString="Licence information not available.";
159 return genericMethodCall("getCopyright",rString);
160 }
161
162
163 bool
164 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
165 {
166
167 if (channels < getMinChannelCount() ||
168 channels > getMaxChannelCount()) return false;
169
170 m_inputDomain = getInputDomain();
171
172 //Note: placing Mutex before the calls above causes deadlock !!
173 MutexLocker locker(&m_pythonInterpreterMutex);
174
175 m_stepSize = stepSize;
176 m_blockSize = blockSize;
177 m_channels = channels;
178
179 //query the process implementation type
180 //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
181 //if they are not provided, we fall back to the original method
182 setProcessType();
183
184 return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
185 }
186
187 void
188 PyPlugin::reset()
189 {
190 MutexLocker locker(&m_pythonInterpreterMutex);
191 genericMethodCall("reset");
192 }
193
194 PyPlugin::InputDomain
195 PyPlugin::getInputDomain() const
196 {
197 MutexLocker locker(&m_pythonInterpreterMutex);
198 return genericMethodCall("getInputDomain",m_inputDomain);
199 }
200
201 size_t
202 PyPlugin::getPreferredBlockSize() const
203 {
204 MutexLocker locker(&m_pythonInterpreterMutex);
205 size_t rValue = 0;
206 return genericMethodCall("getPreferredBlockSize",rValue);
207 }
208
209 size_t
210 PyPlugin::getPreferredStepSize() const
211 {
212 MutexLocker locker(&m_pythonInterpreterMutex);
213 size_t rValue = 0;
214 return genericMethodCall("getPreferredStepSize",rValue);
215 }
216
217 size_t
218 PyPlugin::getMinChannelCount() const
219 {
220 MutexLocker locker(&m_pythonInterpreterMutex);
221 size_t rValue = 1;
222 return genericMethodCall("getMinChannelCount",rValue);
223 }
224
225 size_t
226 PyPlugin::getMaxChannelCount() const
227 {
228 MutexLocker locker(&m_pythonInterpreterMutex);
229 size_t rValue = 1;
230 return genericMethodCall("getMaxChannelCount",rValue);
231 }
232
233 PyPlugin::OutputList
234 PyPlugin::getOutputDescriptors() const
235 {
236 MutexLocker locker(&m_pythonInterpreterMutex);
237 OutputList list;
238 return genericMethodCall("getOutputDescriptors",list);
239 }
240
241 PyPlugin::ParameterList
242 PyPlugin::getParameterDescriptors() const
243 {
244 MutexLocker locker(&m_pythonInterpreterMutex);
245 ParameterList list;
246 #ifdef _DEBUG
247 ///Note: This function is often called first by the host.
248 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
249 #endif
250
251 return genericMethodCall("getParameterDescriptors",list);
252 }
253
254 void PyPlugin::setParameter(std::string paramid, float newval)
255 {
256 MutexLocker locker(&m_pythonInterpreterMutex);
257 genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
258 }
259
260 float PyPlugin::getParameter(std::string paramid) const
261 {
262 MutexLocker locker(&m_pythonInterpreterMutex);
263 return genericMethodCallArgs<float>("getParameter",paramid);
264 }
265
266 #ifdef _DEBUG_VALUES
267 static int proccounter = 0;
268 #endif
269
270 PyPlugin::FeatureSet
271 PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
272 {
273 MutexLocker locker(&m_pythonInterpreterMutex);
274
275 #ifdef _DEBUG_VALUES
276 /// we only need this if we'd like to see what frame a set of values belong to
277 cerr << "[Vampy::call] process, frame:" << proccounter << endl;
278 proccounter++;
279 #endif
280
281 if (m_blockSize == 0 || m_channels == 0) {
282 cerr << "ERROR: PyPlugin::process: "
283 << "Plugin has not been initialised" << endl;
284 return FeatureSet();
285 }
286
287 if (m_processType == not_implemented) {
288 cerr << "ERROR: In Python plugin [" << m_class
289 << "] No process implementation found. Returning empty feature set." << endl;
290 return FeatureSet();
291 }
292
293 return processMethodCall(inputBuffers,timestamp);
294
295 }
296
297 PyPlugin::FeatureSet
298 PyPlugin::getRemainingFeatures()
299 {
300 MutexLocker locker(&m_pythonInterpreterMutex);
301 FeatureSet rValue;
302 return genericMethodCall("getRemainingFeatures",rValue);
303 }
304
305 bool
306 PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const
307 {
308 bool rValue = defValue;
309 if (PyObject_HasAttrString(m_pyInstance,flagName))
310 {
311 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
312 if (!pyValue)
313 {
314 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
315 } else {
316 rValue = m_ti.PyValue_To_Bool(pyValue);
317 if (m_ti.error) {
318 Py_CLEAR(pyValue);
319 typeErrorHandler(flagName);
320 rValue = defValue;
321 } else Py_DECREF(pyValue);
322 }
323 }
324 if (m_debugFlag) cerr << FLAG_VALUE << endl;
325 return rValue;
326 }
327
328 int
329 PyPlugin::getBinaryFlags(char flagName[], eVampyFlags defValue = vf_NULL) const
330 {
331 int rValue = defValue;
332 if (PyObject_HasAttrString(m_pyInstance,flagName))
333 {
334 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
335 if (!pyValue)
336 {
337 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
338 } else {
339 rValue |= (int) m_ti.PyValue_To_Size_t(pyValue);
340 if (m_ti.error) {
341 Py_CLEAR(pyValue);
342 typeErrorHandler(flagName);
343 rValue = defValue;
344 } else Py_DECREF(pyValue);
345 }
346 }
347 if (m_debugFlag) cerr << FLAG_VALUE << endl;
348 return rValue;
349 }
350
351
352 void
353 PyPlugin::setProcessType()
354 {
355 //quering process implementation type
356 char legacyMethod[]="process";
357 char numpyMethod[]="processN";
358
359 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
360 m_processType == 0)
361 {
362 m_processType = legacyProcess;
363 m_pyProcess = PyString_FromString(legacyMethod);
364 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
365 }
366
367 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
368 m_processType == 0)
369 {
370 m_processType = numpy_bufferProcess;
371 m_pyProcess = PyString_FromString(numpyMethod);
372 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
373 }
374
375 // These flags are optional. If provided, they override the
376 // implementation type making the use of the odd processN()
377 // function redundant.
378 // However, the code above provides backward compatibility.
379
380 if (m_vampyFlags & vf_BUFFER) {
381 m_processType = numpy_bufferProcess;
382 if (m_debugFlag) cerr << "Process using (numpy) buffer interface." << endl;
383 }
384
385 if (m_vampyFlags & vf_ARRAY) {
386 #ifdef HAVE_NUMPY
387 m_processType = numpy_arrayProcess;
388 if (m_debugFlag) cerr << "Process using numpy array interface." << endl;
389 #else
390 cerr << "Error: This version of vampy was compiled without numpy support, "
391 << "however the vf_ARRAY flag is set for plugin: " << m_class << endl
392 << "The default behaviour is: passing a python list of samples for each channel in process() "
393 << "or a list of memory buffers in processN(). " << endl
394 << "This can be used create numpy arrays using the numpy.frombuffer() command." << endl;
395 #endif
396 }
397
398 if (!m_processType)
399 {
400 m_processType = not_implemented;
401 m_pyProcess = NULL;
402 m_pyProcessCallable = NULL;
403 char method[]="initialise::setProcessType";
404 cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
405 }
406 }
407
408 void
409 PyPlugin::typeErrorHandler(char *method) const
410 {
411 bool strict = false;
412 while (m_ti.error) {
413 PyTypeInterface::ValueError e = m_ti.getError();
414 cerr << PLUGIN_ERROR << e.str() << endl;
415 if (e.strict) strict = true;
416 // e.print();
417 }
418 /// quit on hard errors like accessing NULL pointers or strict type conversion
419 /// errors IF the user sets the quitOnErrorFlag in the plugin.
420 /// Otherwise most errors will go unnoticed apart from
421 /// a messages in the terminal.
422 /// It would be best if hosts could catch an exception instead
423 /// and display something meaningful to the user.
424 if (strict && m_quitOnErrorFlag) exit(EXIT_FAILURE);
425 }
426