fazekasgy@37
|
1 /*
|
fazekasgy@37
|
2
|
fazekasgy@37
|
3 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
4 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
5
|
fazekasgy@37
|
6 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
7 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
8 * for licence information.)
|
fazekasgy@37
|
9
|
fazekasgy@37
|
10 */
|
fazekasgy@37
|
11
|
fazekasgy@37
|
12 #include <Python.h>
|
fazekasgy@37
|
13 #include "PyPlugin.h"
|
fazekasgy@37
|
14 #include "PyTypeInterface.h"
|
fazekasgy@37
|
15 #include <stdlib.h>
|
fazekasgy@37
|
16 #include "PyExtensionModule.h"
|
Chris@67
|
17 #include "Debug.h"
|
fazekasgy@37
|
18
|
fazekasgy@37
|
19 #ifdef _WIN32
|
fazekasgy@37
|
20 #define PATHSEP ('\\')
|
fazekasgy@37
|
21 #else
|
fazekasgy@37
|
22 #define PATHSEP ('/')
|
fazekasgy@37
|
23 #endif
|
fazekasgy@37
|
24
|
fazekasgy@37
|
25 using std::string;
|
fazekasgy@37
|
26 using std::vector;
|
fazekasgy@37
|
27 using std::cerr;
|
fazekasgy@37
|
28 using std::endl;
|
fazekasgy@37
|
29 using std::map;
|
fazekasgy@37
|
30
|
fazekasgy@37
|
31 Mutex PyPlugin::m_pythonInterpreterMutex;
|
fazekasgy@37
|
32
|
fazekasgy@51
|
33 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount, bool &numpyInstalled) :
|
fazekasgy@37
|
34 Plugin(inputSampleRate),
|
fazekasgy@37
|
35 m_pyClass(pyClass),
|
fazekasgy@37
|
36 m_instcount(instcount),
|
fazekasgy@37
|
37 m_stepSize(0),
|
fazekasgy@37
|
38 m_blockSize(0),
|
fazekasgy@37
|
39 m_channels(0),
|
fazekasgy@37
|
40 m_plugin(pluginKey),
|
fazekasgy@37
|
41 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
|
fazekasgy@37
|
42 m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
|
fazekasgy@37
|
43 m_processType(not_implemented),
|
fazekasgy@37
|
44 m_pyProcess(NULL),
|
fazekasgy@37
|
45 m_inputDomain(TimeDomain),
|
fazekasgy@37
|
46 m_quitOnErrorFlag(false),
|
fazekasgy@51
|
47 m_debugFlag(false),
|
fazekasgy@51
|
48 m_numpyInstalled(numpyInstalled),
|
fazekasgy@51
|
49 m_processFailure(false)
|
fazekasgy@37
|
50 {
|
fazekasgy@37
|
51 m_ti.setInputSampleRate(inputSampleRate);
|
fazekasgy@37
|
52 MutexLocker locker(&m_pythonInterpreterMutex);
|
Chris@67
|
53 DSTREAM << "Creating instance " << m_instcount << " of " << pluginKey << endl;
|
fazekasgy@37
|
54
|
fazekasgy@37
|
55 // Create an instance
|
fazekasgy@37
|
56 Py_INCREF(m_pyClass);
|
fazekasgy@37
|
57 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
|
fazekasgy@37
|
58 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
|
fazekasgy@37
|
59 m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
|
fazekasgy@37
|
60
|
fazekasgy@37
|
61 if (!m_pyInstance || PyErr_Occurred()) {
|
fazekasgy@37
|
62 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
|
fazekasgy@37
|
63 Py_DECREF(m_pyClass);
|
fazekasgy@37
|
64 Py_CLEAR(args);
|
fazekasgy@37
|
65 Py_CLEAR(pyInputSampleRate);
|
fazekasgy@37
|
66 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \""
|
fazekasgy@37
|
67 << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
|
fazekasgy@37
|
68 throw std::string("Constructor failed");
|
fazekasgy@37
|
69 }
|
fazekasgy@37
|
70 Py_INCREF(m_pyInstance);
|
fazekasgy@37
|
71 Py_DECREF(args);
|
fazekasgy@37
|
72 Py_DECREF(pyInputSampleRate);
|
fazekasgy@37
|
73
|
fazekasgy@37
|
74 m_instcount++;
|
fazekasgy@37
|
75
|
fazekasgy@37
|
76 // query and decode vampy flags
|
fazekasgy@37
|
77 m_vampyFlags = getBinaryFlags("vampy_flags",vf_NULL);
|
fazekasgy@37
|
78
|
fazekasgy@37
|
79 m_debugFlag = (bool) (m_vampyFlags & vf_DEBUG);
|
fazekasgy@37
|
80 m_quitOnErrorFlag = (bool) (m_vampyFlags & vf_QUIT);
|
fazekasgy@37
|
81 bool st_flag = (bool) (m_vampyFlags & vf_STRICT);
|
fazekasgy@37
|
82 m_useRealTimeFlag = (bool) (m_vampyFlags & vf_REALTIME);
|
fazekasgy@37
|
83
|
fazekasgy@37
|
84 if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
|
Chris@67
|
85 else DSTREAM << "Debug messages OFF for Vampy plugin: " << m_class << endl;
|
fazekasgy@37
|
86
|
fazekasgy@37
|
87 if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
|
fazekasgy@37
|
88
|
fazekasgy@37
|
89 if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
|
Chris@71
|
90
|
fazekasgy@37
|
91 m_ti.setStrictTypingFlag(st_flag);
|
Chris@71
|
92 m_tc.setStrictTypingFlag(st_flag);
|
Chris@71
|
93
|
fazekasgy@51
|
94 m_ti.setNumpyInstalled(m_numpyInstalled);
|
Chris@71
|
95 m_tc.setNumpyInstalled(m_numpyInstalled);
|
fazekasgy@37
|
96
|
fazekasgy@37
|
97 }
|
fazekasgy@37
|
98
|
fazekasgy@37
|
99 PyPlugin::~PyPlugin()
|
fazekasgy@37
|
100 {
|
fazekasgy@37
|
101 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
102 m_instcount--;
|
fazekasgy@37
|
103 // cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
|
fazekasgy@37
|
104
|
fazekasgy@37
|
105 if (m_pyInstance) Py_DECREF(m_pyInstance);
|
fazekasgy@37
|
106 //we increase the class refcount before creating an instance
|
fazekasgy@37
|
107 if (m_pyClass) Py_DECREF(m_pyClass);
|
fazekasgy@37
|
108 if (m_pyProcess) Py_CLEAR(m_pyProcess);
|
fazekasgy@37
|
109
|
Chris@67
|
110 DSTREAM << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
|
fazekasgy@37
|
111 }
|
fazekasgy@37
|
112
|
fazekasgy@37
|
113 string
|
fazekasgy@37
|
114 PyPlugin::getIdentifier() const
|
fazekasgy@37
|
115 {
|
fazekasgy@37
|
116 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
117 string rString="vampy-xxx";
|
fazekasgy@37
|
118 if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
|
fazekasgy@37
|
119
|
fazekasgy@37
|
120 rString = genericMethodCall("getIdentifier",rString);
|
fazekasgy@37
|
121 if (rString == "vampy-xxx")
|
fazekasgy@37
|
122 cerr << "Warning: Plugin must return a unique identifier." << endl;
|
fazekasgy@37
|
123 return rString;
|
fazekasgy@37
|
124 }
|
fazekasgy@37
|
125
|
fazekasgy@37
|
126 string
|
fazekasgy@37
|
127 PyPlugin::getName() const
|
fazekasgy@37
|
128 {
|
fazekasgy@37
|
129 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
130 string rString="VamPy Plugin (Noname)";
|
fazekasgy@37
|
131 return genericMethodCall("getName",rString);
|
fazekasgy@37
|
132 }
|
fazekasgy@37
|
133
|
fazekasgy@37
|
134 string
|
fazekasgy@37
|
135 PyPlugin::getDescription() const
|
fazekasgy@37
|
136 {
|
fazekasgy@37
|
137 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
138 string rString="Not given. (Hint: Implement getDescription method.)";
|
fazekasgy@37
|
139 return genericMethodCall("getDescription",rString);
|
fazekasgy@37
|
140 }
|
fazekasgy@37
|
141
|
fazekasgy@37
|
142
|
fazekasgy@37
|
143 string
|
fazekasgy@37
|
144 PyPlugin::getMaker() const
|
fazekasgy@37
|
145 {
|
fazekasgy@37
|
146 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
147 string rString="VamPy Plugin.";
|
fazekasgy@37
|
148 return genericMethodCall("getMaker",rString);
|
fazekasgy@37
|
149 }
|
fazekasgy@37
|
150
|
fazekasgy@37
|
151 int
|
fazekasgy@37
|
152 PyPlugin::getPluginVersion() const
|
fazekasgy@37
|
153 {
|
fazekasgy@37
|
154 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
155 size_t rValue=2;
|
fazekasgy@37
|
156 return genericMethodCall("getPluginVersion",rValue);
|
fazekasgy@37
|
157 }
|
fazekasgy@37
|
158
|
fazekasgy@37
|
159 string
|
fazekasgy@37
|
160 PyPlugin::getCopyright() const
|
fazekasgy@37
|
161 {
|
fazekasgy@37
|
162 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
163 string rString="Licence information not available.";
|
fazekasgy@37
|
164 return genericMethodCall("getCopyright",rString);
|
fazekasgy@37
|
165 }
|
fazekasgy@37
|
166
|
fazekasgy@37
|
167
|
fazekasgy@37
|
168 bool
|
fazekasgy@37
|
169 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
fazekasgy@37
|
170 {
|
fazekasgy@37
|
171
|
fazekasgy@37
|
172 if (channels < getMinChannelCount() ||
|
fazekasgy@37
|
173 channels > getMaxChannelCount()) return false;
|
fazekasgy@37
|
174
|
fazekasgy@37
|
175 m_inputDomain = getInputDomain();
|
fazekasgy@37
|
176
|
fazekasgy@37
|
177 //Note: placing Mutex before the calls above causes deadlock !!
|
fazekasgy@37
|
178 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
179
|
fazekasgy@37
|
180 m_stepSize = stepSize;
|
fazekasgy@37
|
181 m_blockSize = blockSize;
|
fazekasgy@37
|
182 m_channels = channels;
|
fazekasgy@37
|
183
|
fazekasgy@37
|
184 //query the process implementation type
|
fazekasgy@37
|
185 //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
|
fazekasgy@37
|
186 //if they are not provided, we fall back to the original method
|
fazekasgy@37
|
187 setProcessType();
|
fazekasgy@37
|
188
|
fazekasgy@37
|
189 return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
|
fazekasgy@37
|
190 }
|
fazekasgy@37
|
191
|
fazekasgy@37
|
192 void
|
fazekasgy@37
|
193 PyPlugin::reset()
|
fazekasgy@37
|
194 {
|
fazekasgy@37
|
195 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@51
|
196 m_processFailure = false;
|
fazekasgy@37
|
197 genericMethodCall("reset");
|
fazekasgy@37
|
198 }
|
fazekasgy@37
|
199
|
fazekasgy@37
|
200 PyPlugin::InputDomain
|
fazekasgy@37
|
201 PyPlugin::getInputDomain() const
|
fazekasgy@37
|
202 {
|
fazekasgy@37
|
203 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
204 return genericMethodCall("getInputDomain",m_inputDomain);
|
fazekasgy@37
|
205 }
|
fazekasgy@37
|
206
|
fazekasgy@37
|
207 size_t
|
fazekasgy@37
|
208 PyPlugin::getPreferredBlockSize() const
|
fazekasgy@37
|
209 {
|
fazekasgy@37
|
210 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
211 size_t rValue = 0;
|
fazekasgy@37
|
212 return genericMethodCall("getPreferredBlockSize",rValue);
|
fazekasgy@37
|
213 }
|
fazekasgy@37
|
214
|
fazekasgy@37
|
215 size_t
|
fazekasgy@37
|
216 PyPlugin::getPreferredStepSize() const
|
fazekasgy@37
|
217 {
|
fazekasgy@37
|
218 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
219 size_t rValue = 0;
|
fazekasgy@37
|
220 return genericMethodCall("getPreferredStepSize",rValue);
|
fazekasgy@37
|
221 }
|
fazekasgy@37
|
222
|
fazekasgy@37
|
223 size_t
|
fazekasgy@37
|
224 PyPlugin::getMinChannelCount() const
|
fazekasgy@37
|
225 {
|
fazekasgy@37
|
226 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
227 size_t rValue = 1;
|
fazekasgy@37
|
228 return genericMethodCall("getMinChannelCount",rValue);
|
fazekasgy@37
|
229 }
|
fazekasgy@37
|
230
|
fazekasgy@37
|
231 size_t
|
fazekasgy@37
|
232 PyPlugin::getMaxChannelCount() const
|
fazekasgy@37
|
233 {
|
fazekasgy@37
|
234 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
235 size_t rValue = 1;
|
fazekasgy@37
|
236 return genericMethodCall("getMaxChannelCount",rValue);
|
fazekasgy@37
|
237 }
|
fazekasgy@37
|
238
|
fazekasgy@37
|
239 PyPlugin::OutputList
|
fazekasgy@37
|
240 PyPlugin::getOutputDescriptors() const
|
fazekasgy@37
|
241 {
|
fazekasgy@37
|
242 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
243 OutputList list;
|
fazekasgy@37
|
244 return genericMethodCall("getOutputDescriptors",list);
|
fazekasgy@37
|
245 }
|
fazekasgy@37
|
246
|
fazekasgy@37
|
247 PyPlugin::ParameterList
|
fazekasgy@37
|
248 PyPlugin::getParameterDescriptors() const
|
fazekasgy@37
|
249 {
|
fazekasgy@37
|
250 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
251 ParameterList list;
|
fazekasgy@37
|
252 #ifdef _DEBUG
|
fazekasgy@37
|
253 ///Note: This function is often called first by the host.
|
fazekasgy@37
|
254 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
|
fazekasgy@37
|
255 #endif
|
fazekasgy@37
|
256
|
fazekasgy@37
|
257 return genericMethodCall("getParameterDescriptors",list);
|
fazekasgy@37
|
258 }
|
fazekasgy@37
|
259
|
fazekasgy@37
|
260 void PyPlugin::setParameter(std::string paramid, float newval)
|
fazekasgy@37
|
261 {
|
fazekasgy@37
|
262 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
263 genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
|
fazekasgy@37
|
264 }
|
fazekasgy@37
|
265
|
fazekasgy@37
|
266 float PyPlugin::getParameter(std::string paramid) const
|
fazekasgy@37
|
267 {
|
fazekasgy@37
|
268 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
269 return genericMethodCallArgs<float>("getParameter",paramid);
|
fazekasgy@37
|
270 }
|
fazekasgy@37
|
271
|
fazekasgy@37
|
272 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
273 static int proccounter = 0;
|
fazekasgy@37
|
274 #endif
|
fazekasgy@37
|
275
|
fazekasgy@37
|
276 PyPlugin::FeatureSet
|
fazekasgy@37
|
277 PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
|
fazekasgy@37
|
278 {
|
fazekasgy@37
|
279 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@37
|
280
|
fazekasgy@37
|
281 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
282 /// we only need this if we'd like to see what frame a set of values belong to
|
fazekasgy@37
|
283 cerr << "[Vampy::call] process, frame:" << proccounter << endl;
|
fazekasgy@37
|
284 proccounter++;
|
fazekasgy@37
|
285 #endif
|
fazekasgy@37
|
286
|
fazekasgy@37
|
287 if (m_blockSize == 0 || m_channels == 0) {
|
fazekasgy@37
|
288 cerr << "ERROR: PyPlugin::process: "
|
fazekasgy@37
|
289 << "Plugin has not been initialised" << endl;
|
fazekasgy@37
|
290 return FeatureSet();
|
fazekasgy@37
|
291 }
|
fazekasgy@37
|
292
|
fazekasgy@37
|
293 if (m_processType == not_implemented) {
|
fazekasgy@37
|
294 cerr << "ERROR: In Python plugin [" << m_class
|
fazekasgy@37
|
295 << "] No process implementation found. Returning empty feature set." << endl;
|
fazekasgy@37
|
296 return FeatureSet();
|
fazekasgy@37
|
297 }
|
fazekasgy@37
|
298
|
fazekasgy@51
|
299 if (m_processFailure) return FeatureSet();
|
fazekasgy@51
|
300
|
fazekasgy@37
|
301 return processMethodCall(inputBuffers,timestamp);
|
fazekasgy@37
|
302
|
fazekasgy@37
|
303 }
|
fazekasgy@37
|
304
|
fazekasgy@37
|
305 PyPlugin::FeatureSet
|
fazekasgy@37
|
306 PyPlugin::getRemainingFeatures()
|
fazekasgy@37
|
307 {
|
fazekasgy@37
|
308 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@51
|
309 if (m_processFailure) return FeatureSet();
|
fazekasgy@37
|
310 FeatureSet rValue;
|
fazekasgy@37
|
311 return genericMethodCall("getRemainingFeatures",rValue);
|
fazekasgy@37
|
312 }
|
fazekasgy@37
|
313
|
fazekasgy@37
|
314 bool
|
Chris@66
|
315 PyPlugin::getBooleanFlag(const char flagName[], bool defValue = false) const
|
fazekasgy@37
|
316 {
|
fazekasgy@37
|
317 bool rValue = defValue;
|
fazekasgy@37
|
318 if (PyObject_HasAttrString(m_pyInstance,flagName))
|
fazekasgy@37
|
319 {
|
fazekasgy@37
|
320 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
|
fazekasgy@37
|
321 if (!pyValue)
|
fazekasgy@37
|
322 {
|
fazekasgy@37
|
323 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@37
|
324 } else {
|
Chris@71
|
325 rValue = m_tc.PyValue_To_Bool(pyValue);
|
Chris@71
|
326 if (m_tc.error) {
|
fazekasgy@37
|
327 Py_CLEAR(pyValue);
|
fazekasgy@37
|
328 typeErrorHandler(flagName);
|
fazekasgy@37
|
329 rValue = defValue;
|
fazekasgy@37
|
330 } else Py_DECREF(pyValue);
|
fazekasgy@37
|
331 }
|
fazekasgy@37
|
332 }
|
fazekasgy@37
|
333 if (m_debugFlag) cerr << FLAG_VALUE << endl;
|
fazekasgy@37
|
334 return rValue;
|
fazekasgy@37
|
335 }
|
fazekasgy@37
|
336
|
fazekasgy@37
|
337 int
|
Chris@66
|
338 PyPlugin::getBinaryFlags(const char flagName[], eVampyFlags defValue = vf_NULL) const
|
fazekasgy@37
|
339 {
|
fazekasgy@37
|
340 int rValue = defValue;
|
fazekasgy@37
|
341 if (PyObject_HasAttrString(m_pyInstance,flagName))
|
fazekasgy@37
|
342 {
|
fazekasgy@37
|
343 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
|
fazekasgy@37
|
344 if (!pyValue)
|
fazekasgy@37
|
345 {
|
fazekasgy@37
|
346 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@37
|
347 } else {
|
Chris@71
|
348 rValue |= (int) m_tc.PyValue_To_Size_t(pyValue);
|
Chris@71
|
349 if (m_tc.error) {
|
fazekasgy@37
|
350 Py_CLEAR(pyValue);
|
fazekasgy@37
|
351 typeErrorHandler(flagName);
|
fazekasgy@37
|
352 rValue = defValue;
|
fazekasgy@37
|
353 } else Py_DECREF(pyValue);
|
fazekasgy@37
|
354 }
|
fazekasgy@37
|
355 }
|
fazekasgy@37
|
356 if (m_debugFlag) cerr << FLAG_VALUE << endl;
|
fazekasgy@37
|
357 return rValue;
|
fazekasgy@37
|
358 }
|
fazekasgy@37
|
359
|
fazekasgy@37
|
360
|
fazekasgy@37
|
361 void
|
fazekasgy@37
|
362 PyPlugin::setProcessType()
|
fazekasgy@37
|
363 {
|
fazekasgy@37
|
364 //quering process implementation type
|
fazekasgy@37
|
365 char legacyMethod[]="process";
|
fazekasgy@37
|
366 char numpyMethod[]="processN";
|
fazekasgy@51
|
367 m_processFailure = false;
|
fazekasgy@37
|
368
|
fazekasgy@37
|
369 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
|
fazekasgy@37
|
370 m_processType == 0)
|
fazekasgy@37
|
371 {
|
fazekasgy@37
|
372 m_processType = legacyProcess;
|
fazekasgy@37
|
373 m_pyProcess = PyString_FromString(legacyMethod);
|
fazekasgy@37
|
374 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
|
fazekasgy@37
|
375 }
|
fazekasgy@37
|
376
|
fazekasgy@37
|
377 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
|
fazekasgy@37
|
378 m_processType == 0)
|
fazekasgy@37
|
379 {
|
fazekasgy@37
|
380 m_processType = numpy_bufferProcess;
|
fazekasgy@37
|
381 m_pyProcess = PyString_FromString(numpyMethod);
|
fazekasgy@37
|
382 m_pyProcessCallable = PyObject_GetAttr(m_pyInstance,m_pyProcess);
|
fazekasgy@37
|
383 }
|
fazekasgy@37
|
384
|
fazekasgy@37
|
385 // These flags are optional. If provided, they override the
|
fazekasgy@37
|
386 // implementation type making the use of the odd processN()
|
fazekasgy@37
|
387 // function redundant.
|
fazekasgy@37
|
388 // However, the code above provides backward compatibility.
|
fazekasgy@37
|
389
|
fazekasgy@37
|
390 if (m_vampyFlags & vf_BUFFER) {
|
fazekasgy@37
|
391 m_processType = numpy_bufferProcess;
|
fazekasgy@37
|
392 if (m_debugFlag) cerr << "Process using (numpy) buffer interface." << endl;
|
fazekasgy@37
|
393 }
|
fazekasgy@37
|
394
|
fazekasgy@37
|
395 if (m_vampyFlags & vf_ARRAY) {
|
fazekasgy@37
|
396 #ifdef HAVE_NUMPY
|
fazekasgy@51
|
397 if (m_numpyInstalled) { m_processType = numpy_arrayProcess;
|
fazekasgy@51
|
398 if (m_debugFlag)
|
fazekasgy@51
|
399 cerr << "Process using numpy array interface." << endl;
|
fazekasgy@51
|
400 }
|
fazekasgy@51
|
401 else {
|
fazekasgy@51
|
402 m_processFailure = true;
|
fazekasgy@51
|
403 char method[]="initialise::setProcessType";
|
fazekasgy@51
|
404 cerr << PLUGIN_ERROR
|
fazekasgy@51
|
405 << "This plugin requests the Numpy array interface by setting "
|
fazekasgy@51
|
406 << " the vf_ARRAY flag in its __init__() function." << endl
|
fazekasgy@51
|
407 << "However, we could not found a version of Numpy compatible with this build of Vampy." << endl
|
fazekasgy@51
|
408 << "If you have a numerical library installed that supports the buffer interface, " << endl
|
fazekasgy@51
|
409 << "you can request this interface instead by setting the vf_BUFFER flag." << endl;
|
fazekasgy@51
|
410 }
|
fazekasgy@37
|
411 #else
|
fazekasgy@51
|
412 m_processFailure = true;
|
fazekasgy@51
|
413 char method[]="initialise::setProcessType";
|
fazekasgy@51
|
414 cerr << PLUGIN_ERROR
|
fazekasgy@51
|
415 << "Error: This version of vampy was compiled without numpy support, "
|
fazekasgy@51
|
416 << "however the vf_ARRAY flag is set for plugin: " << m_class << endl
|
fazekasgy@51
|
417 << "The default behaviour is: passing a python list of samples for each channel in process() "
|
fazekasgy@51
|
418 << "or a list of memory buffers in processN(). " << endl
|
fazekasgy@51
|
419 << "This can be used create numpy arrays using the numpy.frombuffer() command." << endl;
|
fazekasgy@37
|
420 #endif
|
fazekasgy@37
|
421 }
|
fazekasgy@37
|
422
|
fazekasgy@51
|
423 if (!m_pyProcessCallable)
|
fazekasgy@37
|
424 {
|
fazekasgy@37
|
425 m_processType = not_implemented;
|
fazekasgy@37
|
426 m_pyProcess = NULL;
|
fazekasgy@37
|
427 char method[]="initialise::setProcessType";
|
fazekasgy@37
|
428 cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
|
fazekasgy@51
|
429 m_processFailure = true;
|
fazekasgy@37
|
430 }
|
fazekasgy@37
|
431 }
|
fazekasgy@37
|
432
|
fazekasgy@37
|
433 void
|
Chris@66
|
434 PyPlugin::typeErrorHandler(const char *method, bool process) const
|
fazekasgy@37
|
435 {
|
fazekasgy@37
|
436 bool strict = false;
|
Chris@71
|
437 while (m_tc.error || m_ti.error) {
|
Chris@71
|
438 ValueError e;
|
Chris@71
|
439 if (m_tc.error) e = m_tc.getError();
|
Chris@71
|
440 else e = m_ti.getError();
|
fazekasgy@51
|
441 #ifdef HAVE_NUMPY
|
fazekasgy@51
|
442 // disable the process completely if numpy types are returned
|
fazekasgy@51
|
443 // but a compatible version was not loaded.
|
fazekasgy@51
|
444 // This is required because if an object is returned from
|
fazekasgy@51
|
445 // the wrong build, malloc complains about its size
|
fazekasgy@51
|
446 // (i.e. the interpreter doesn't free it properly)
|
fazekasgy@51
|
447 // and the process may be leaking.
|
fazekasgy@51
|
448 // Note: this only happens in the obscure situation when
|
fazekasgy@51
|
449 // someone forces to return wrong numpy types from an
|
fazekasgy@51
|
450 // incompatible version using the buffer interface.
|
fazekasgy@51
|
451 // In this case the incampatible library is still usable,
|
fazekasgy@51
|
452 // but manual conversion to python builtins is required.
|
fazekasgy@51
|
453 // If the ARRAY interface is set but Numpy is not installed
|
fazekasgy@51
|
454 // the process will be disabled already at initialisation.
|
fazekasgy@51
|
455 if (process && !m_numpyInstalled && e.str().find("numpy")!=std::string::npos)
|
fazekasgy@51
|
456 {
|
fazekasgy@51
|
457 m_processFailure = true;
|
fazekasgy@51
|
458 cerr << "Warning: incompatible numpy type encountered. Disabling process." << endl;
|
fazekasgy@51
|
459 }
|
fazekasgy@51
|
460 #endif
|
fazekasgy@37
|
461 cerr << PLUGIN_ERROR << e.str() << endl;
|
fazekasgy@37
|
462 if (e.strict) strict = true;
|
fazekasgy@37
|
463 // e.print();
|
fazekasgy@37
|
464 }
|
fazekasgy@37
|
465 /// quit on hard errors like accessing NULL pointers or strict type conversion
|
fazekasgy@37
|
466 /// errors IF the user sets the quitOnErrorFlag in the plugin.
|
fazekasgy@37
|
467 /// Otherwise most errors will go unnoticed apart from
|
fazekasgy@37
|
468 /// a messages in the terminal.
|
fazekasgy@37
|
469 /// It would be best if hosts could catch an exception instead
|
fazekasgy@37
|
470 /// and display something meaningful to the user.
|
fazekasgy@37
|
471 if (strict && m_quitOnErrorFlag) exit(EXIT_FAILURE);
|
fazekasgy@51
|
472
|
fazekasgy@51
|
473 // this would disable all outputs even if some are valid
|
fazekasgy@51
|
474 // if (process) m_processFailure = true;
|
fazekasgy@51
|
475
|
fazekasgy@37
|
476 }
|
fazekasgy@37
|
477
|
Chris@72
|
478 /// optimised process call
|
Chris@72
|
479 PyPlugin::FeatureSet
|
Chris@72
|
480 PyPlugin::processMethodCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
|
Chris@72
|
481 {
|
Chris@72
|
482
|
Chris@72
|
483 /// Optimizations: 1) we avoid ...ObjArg functions since we know
|
Chris@72
|
484 /// the number of arguments, and we don't like va_list parsing
|
Chris@72
|
485 /// in the process. 2) Also: we're supposed to incref args,
|
Chris@72
|
486 /// but instead, we let the arguments tuple steal the references
|
Chris@72
|
487 /// and decref them when it is deallocated.
|
Chris@72
|
488 /// 3) all conversions are now using the fast sequence protocol
|
Chris@72
|
489 /// (indexing the underlying object array).
|
Chris@72
|
490
|
Chris@72
|
491 FeatureSet rFeatureSet;
|
Chris@72
|
492 PyObject *pyChannelList = NULL;
|
Chris@72
|
493
|
Chris@72
|
494 if (m_processType == numpy_bufferProcess) {
|
Chris@72
|
495 pyChannelList = m_ti.InputBuffers_As_SharedMemoryList(
|
Chris@72
|
496 inputBuffers,m_channels,m_blockSize,m_inputDomain);
|
Chris@72
|
497 }
|
Chris@72
|
498
|
Chris@72
|
499 if (m_processType == legacyProcess) {
|
Chris@72
|
500 pyChannelList = m_ti.InputBuffers_As_PythonLists(
|
Chris@72
|
501 inputBuffers,m_channels,m_blockSize,m_inputDomain);
|
Chris@72
|
502 }
|
Chris@72
|
503
|
Chris@72
|
504 #ifdef HAVE_NUMPY
|
Chris@72
|
505 if (m_processType == numpy_arrayProcess) {
|
Chris@72
|
506 pyChannelList = m_ti.InputBuffers_As_NumpyArray(
|
Chris@72
|
507 inputBuffers,m_channels,m_blockSize,m_inputDomain);
|
Chris@72
|
508 }
|
Chris@72
|
509 #endif
|
Chris@72
|
510
|
Chris@72
|
511 /// we don't expect these to fail unless out of memory (which is very unlikely on modern systems)
|
Chris@72
|
512 #ifdef _DEBUG
|
Chris@72
|
513 if (!pyChannelList) {
|
Chris@72
|
514 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@72
|
515 std::string method = PyString_AsString(m_pyProcess);
|
Chris@72
|
516 cerr << PLUGIN_ERROR << "Failed to create channel list." << endl;
|
Chris@72
|
517 return rFeatureSet;
|
Chris@72
|
518 }
|
Chris@72
|
519 #endif
|
Chris@72
|
520
|
Chris@72
|
521 PyObject *pyTimeStamp = NULL;
|
Chris@72
|
522
|
Chris@72
|
523 if (m_useRealTimeFlag) {
|
Chris@72
|
524 //(1) pass TimeStamp as PyRealTime object
|
Chris@72
|
525 pyTimeStamp = PyRealTime_FromRealTime(timestamp);
|
Chris@72
|
526
|
Chris@72
|
527 } else {
|
Chris@72
|
528 //(2) pass TimeStamp as frame count (long Sample Count)
|
Chris@72
|
529 pyTimeStamp = PyLong_FromLong(Vamp::RealTime::realTime2Frame
|
Chris@72
|
530 (timestamp, (unsigned int) m_inputSampleRate));
|
Chris@72
|
531 }
|
Chris@72
|
532
|
Chris@72
|
533
|
Chris@72
|
534 #ifdef _DEBUG
|
Chris@72
|
535 if (!pyTimeStamp) {
|
Chris@72
|
536 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@72
|
537 std::string method = PyString_AsString(m_pyProcess);
|
Chris@72
|
538 cerr << PLUGIN_ERROR << "Failed to create RealTime time stamp." << endl;
|
Chris@72
|
539 Py_DECREF(pyChannelList);
|
Chris@72
|
540 return rFeatureSet;
|
Chris@72
|
541 }
|
Chris@72
|
542 #endif
|
Chris@72
|
543
|
Chris@72
|
544 /// Old method: Call python process (returns new reference)
|
Chris@72
|
545 /// PyObject *pyValue = PyObject_CallMethodObjArgs
|
Chris@72
|
546 /// (m_pyInstance,m_pyProcess,pyChannelList,pyTimeStamp,NULL);
|
Chris@72
|
547
|
Chris@72
|
548 PyObject *pyArgs = PyTuple_New(2);
|
Chris@72
|
549 PyTuple_SET_ITEM(pyArgs, 0, pyChannelList);
|
Chris@72
|
550 PyTuple_SET_ITEM(pyArgs, 1, pyTimeStamp);
|
Chris@72
|
551
|
Chris@72
|
552 /// Call python process (returns new reference) {kwArgs = NULL}
|
Chris@72
|
553 PyObject *pyValue = PyObject_Call(m_pyProcessCallable,pyArgs,NULL);
|
Chris@72
|
554
|
Chris@72
|
555 if (!pyValue) {
|
Chris@72
|
556 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@72
|
557 std::string method = PyString_AsString(m_pyProcess);
|
Chris@72
|
558 cerr << PLUGIN_ERROR << "An error occurred while evaluating Python process." << endl;
|
Chris@72
|
559 Py_CLEAR(pyValue);
|
Chris@72
|
560 Py_CLEAR(pyArgs);
|
Chris@72
|
561 return rFeatureSet;
|
Chris@72
|
562 }
|
Chris@72
|
563
|
Chris@72
|
564 rFeatureSet = m_ti.PyValue_To_FeatureSet(pyValue);
|
Chris@72
|
565 if (!m_ti.error) {
|
Chris@72
|
566 Py_DECREF(pyValue);
|
Chris@72
|
567 Py_DECREF(pyArgs);
|
Chris@72
|
568 } else {
|
Chris@72
|
569 typeErrorHandler(PyString_AsString(m_pyProcess),true);
|
Chris@72
|
570 Py_CLEAR(pyValue);
|
Chris@72
|
571 Py_CLEAR(pyArgs);
|
Chris@72
|
572 }
|
Chris@72
|
573 return rFeatureSet;
|
Chris@72
|
574 }
|