cannam@18
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@0
|
2 /*
|
fazekasgy@0
|
3 Vamp
|
fazekasgy@0
|
4
|
fazekasgy@0
|
5 An API for audio analysis and feature extraction plugins.
|
fazekasgy@0
|
6
|
fazekasgy@0
|
7 Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
8 Copyright 2006 Chris Cannam.
|
fazekasgy@0
|
9
|
fazekasgy@0
|
10 Permission is hereby granted, free of charge, to any person
|
fazekasgy@0
|
11 obtaining a copy of this software and associated documentation
|
fazekasgy@0
|
12 files (the "Software"), to deal in the Software without
|
fazekasgy@0
|
13 restriction, including without limitation the rights to use, copy,
|
fazekasgy@0
|
14 modify, merge, publish, distribute, sublicense, and/or sell copies
|
fazekasgy@0
|
15 of the Software, and to permit persons to whom the Software is
|
fazekasgy@0
|
16 furnished to do so, subject to the following conditions:
|
fazekasgy@0
|
17
|
fazekasgy@0
|
18 The above copyright notice and this permission notice shall be
|
fazekasgy@0
|
19 included in all copies or substantial portions of the Software.
|
fazekasgy@0
|
20
|
fazekasgy@0
|
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
fazekasgy@0
|
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
fazekasgy@0
|
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
fazekasgy@0
|
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
fazekasgy@0
|
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
fazekasgy@0
|
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
fazekasgy@0
|
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
fazekasgy@0
|
28
|
fazekasgy@0
|
29 Except as contained in this notice, the names of the Centre for
|
fazekasgy@0
|
30 Digital Music; Queen Mary, University of London; and Chris Cannam
|
fazekasgy@0
|
31 shall not be used in advertising or otherwise to promote the sale,
|
fazekasgy@0
|
32 use or other dealings in this Software without prior written
|
fazekasgy@0
|
33 authorization.
|
fazekasgy@0
|
34 */
|
fazekasgy@0
|
35
|
fazekasgy@0
|
36
|
fazekasgy@0
|
37
|
fazekasgy@0
|
38 /**
|
fazekasgy@8
|
39 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
|
fazekasgy@0
|
40 * Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
41 * Copyright 2008, George Fazekas.
|
fazekasgy@0
|
42
|
fazekasgy@0
|
43 TODO: needs more complete error checking
|
fazekasgy@0
|
44 needs correct implementation of Python threading
|
fazekasgy@0
|
45 more efficient data conversion using the buffering interface or ctypes
|
cannam@7
|
46 Vamp programs not implemented
|
fazekasgy@0
|
47 support multiple plugins per script in scanner
|
fazekasgy@0
|
48 ensure proper cleanup, host do a good job though
|
fazekasgy@0
|
49
|
fazekasgy@0
|
50 */
|
fazekasgy@0
|
51
|
cannam@3
|
52 #include <Python.h>
|
fazekasgy@0
|
53 #include "PyPlugin.h"
|
fazekasgy@31
|
54 #include "PyTypeInterface.h"
|
fazekasgy@31
|
55 #include <stdlib.h>
|
fazekasgy@31
|
56 #include "PyExtensionModule.h"
|
fazekasgy@31
|
57 //#include "PyRealTime.h"
|
fazekasgy@31
|
58
|
fazekasgy@0
|
59
|
fazekasgy@0
|
60 #ifdef _WIN32
|
fazekasgy@31
|
61 #define PATHSEP ('\\')
|
fazekasgy@0
|
62 #else
|
fazekasgy@31
|
63 #define PATHSEP ('/')
|
fazekasgy@0
|
64 #endif
|
fazekasgy@0
|
65
|
fazekasgy@6
|
66 //#define _DEBUG
|
fazekasgy@0
|
67
|
fazekasgy@0
|
68 using std::string;
|
fazekasgy@0
|
69 using std::vector;
|
fazekasgy@0
|
70 using std::cerr;
|
fazekasgy@0
|
71 using std::endl;
|
fazekasgy@0
|
72 using std::map;
|
fazekasgy@0
|
73
|
fazekasgy@31
|
74 Mutex PyPlugin::m_pythonInterpreterMutex;
|
fazekasgy@0
|
75
|
fazekasgy@31
|
76 PyPlugin::PyPlugin(std::string pluginKey, float inputSampleRate, PyObject *pyClass, int &instcount) :
|
cannam@24
|
77 Plugin(inputSampleRate),
|
cannam@24
|
78 m_pyClass(pyClass),
|
fazekasgy@31
|
79 m_instcount(instcount),
|
fazekasgy@0
|
80 m_stepSize(0),
|
fazekasgy@6
|
81 m_blockSize(0),
|
cannam@24
|
82 m_channels(0),
|
fazekasgy@0
|
83 m_plugin(pluginKey),
|
fazekasgy@0
|
84 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
|
fazekasgy@31
|
85 m_path((pluginKey.substr(0,pluginKey.rfind(PATHSEP)))),
|
fazekasgy@6
|
86 m_processType(0),
|
fazekasgy@6
|
87 m_pyProcess(NULL),
|
fazekasgy@31
|
88 m_inputDomain(TimeDomain),
|
fazekasgy@31
|
89 m_quitOnErrorFlag(false),
|
fazekasgy@31
|
90 m_debugFlag(false)
|
fazekasgy@0
|
91 {
|
fazekasgy@31
|
92 m_ti.setInputSampleRate(inputSampleRate);
|
fazekasgy@31
|
93 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
94 cerr << "Creating instance " << m_instcount << " of " << pluginKey << endl;
|
fazekasgy@31
|
95
|
fazekasgy@31
|
96 if (m_instcount == 0) initvampy();
|
fazekasgy@31
|
97 m_instcount++;
|
fazekasgy@31
|
98
|
fazekasgy@31
|
99 // if (!PyImport_ImportModule("vampy"))
|
fazekasgy@31
|
100 // cerr << "Could not import extension." << endl;
|
fazekasgy@31
|
101
|
cannam@24
|
102 // Create an instance
|
fazekasgy@27
|
103 Py_INCREF(m_pyClass);
|
cannam@24
|
104 PyObject *pyInputSampleRate = PyFloat_FromDouble(inputSampleRate);
|
cannam@24
|
105 PyObject *args = PyTuple_Pack(1, pyInputSampleRate);
|
fazekasgy@27
|
106 m_pyInstance = PyObject_Call(m_pyClass, args, NULL);
|
fazekasgy@27
|
107
|
fazekasgy@27
|
108 if (!m_pyInstance || PyErr_Occurred()) {
|
fazekasgy@27
|
109 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
|
fazekasgy@28
|
110 Py_DECREF(m_pyClass);
|
fazekasgy@27
|
111 Py_CLEAR(args);
|
fazekasgy@27
|
112 Py_CLEAR(pyInputSampleRate);
|
fazekasgy@31
|
113 cerr << "PyPlugin::PyPlugin: Failed to create Python plugin instance for key \""
|
fazekasgy@31
|
114 << pluginKey << "\" (is the 1-arg class constructor from sample rate correctly provided?)" << endl;
|
cannam@24
|
115 throw std::string("Constructor failed");
|
cannam@24
|
116 }
|
fazekasgy@27
|
117 Py_INCREF(m_pyInstance);
|
cannam@24
|
118 Py_DECREF(args);
|
cannam@24
|
119 Py_DECREF(pyInputSampleRate);
|
fazekasgy@31
|
120
|
fazekasgy@31
|
121 //query the debug flag
|
fazekasgy@31
|
122 m_debugFlag = getBooleanFlag("vampy_debug_messages",true);
|
fazekasgy@31
|
123 if (m_debugFlag) cerr << "Debug messages ON for Vampy plugin: " << m_class << endl;
|
fazekasgy@31
|
124 else cerr << "Debug messages OFF for Vampy plugin: " << m_class << endl;
|
fazekasgy@31
|
125
|
fazekasgy@31
|
126 //query the quit on error flag
|
fazekasgy@31
|
127 m_quitOnErrorFlag = getBooleanFlag("quit_on_type_error",false);
|
fazekasgy@31
|
128 if (m_debugFlag && m_quitOnErrorFlag) cerr << "Quit on type error ON for: " << m_class << endl;
|
fazekasgy@31
|
129
|
fazekasgy@31
|
130 //query the type conversion mode flag
|
fazekasgy@31
|
131 bool st_flag = getBooleanFlag("use_strict_type_conversion",false);
|
fazekasgy@31
|
132 if (m_debugFlag && st_flag) cerr << "Strict type conversion ON for: " << m_class << endl;
|
fazekasgy@31
|
133 m_ti.setStrictTypingFlag(st_flag);
|
fazekasgy@0
|
134 }
|
fazekasgy@0
|
135
|
fazekasgy@0
|
136 PyPlugin::~PyPlugin()
|
fazekasgy@0
|
137 {
|
fazekasgy@31
|
138 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
139 m_instcount--;
|
fazekasgy@31
|
140 cerr << "Deleting plugin instance. Count: " << m_instcount << endl;
|
fazekasgy@31
|
141
|
cannam@24
|
142 if (m_pyInstance) Py_DECREF(m_pyInstance);
|
fazekasgy@31
|
143 //we increase the class refcount before creating an instance
|
fazekasgy@31
|
144 if (m_pyClass) Py_DECREF(m_pyClass);
|
fazekasgy@27
|
145 if (m_pyProcess) Py_CLEAR(m_pyProcess);
|
fazekasgy@31
|
146 if (m_instcount == 0) cleanModule();
|
cannam@24
|
147
|
fazekasgy@6
|
148 #ifdef _DEBUG
|
fazekasgy@31
|
149 cerr << "PyPlugin::PyPlugin:" << m_class << " instance " << m_instcount << " deleted." << endl;
|
fazekasgy@6
|
150 #endif
|
fazekasgy@0
|
151 }
|
fazekasgy@0
|
152
|
fazekasgy@0
|
153
|
fazekasgy@0
|
154 string
|
fazekasgy@0
|
155 PyPlugin::getIdentifier() const
|
fazekasgy@0
|
156 {
|
cannam@3
|
157 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
158 string rString="vampy-xxx";
|
fazekasgy@31
|
159 if (!m_debugFlag) return genericMethodCall("getIdentifier",rString);
|
cannam@3
|
160
|
fazekasgy@31
|
161 rString = genericMethodCall("getIdentifier",rString);
|
fazekasgy@31
|
162 if (rString == "vampy-xxx")
|
fazekasgy@31
|
163 cerr << "Warning: Plugin must return a unique identifier." << endl;
|
fazekasgy@0
|
164 return rString;
|
fazekasgy@0
|
165 }
|
fazekasgy@0
|
166
|
fazekasgy@0
|
167 string
|
fazekasgy@0
|
168 PyPlugin::getName() const
|
fazekasgy@0
|
169 {
|
cannam@3
|
170 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@0
|
171 string rString="VamPy Plugin (Noname)";
|
fazekasgy@31
|
172 return genericMethodCall("getName",rString);
|
fazekasgy@0
|
173 }
|
fazekasgy@0
|
174
|
fazekasgy@0
|
175 string
|
fazekasgy@0
|
176 PyPlugin::getDescription() const
|
fazekasgy@0
|
177 {
|
cannam@3
|
178 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
179 string rString="Not given. (Hint: Implement getDescription method.)";
|
fazekasgy@31
|
180 return genericMethodCall("getDescription",rString);
|
fazekasgy@31
|
181 }
|
cannam@3
|
182
|
fazekasgy@0
|
183
|
fazekasgy@0
|
184 string
|
fazekasgy@0
|
185 PyPlugin::getMaker() const
|
fazekasgy@0
|
186 {
|
cannam@3
|
187 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
188 string rString="VamPy Plugin.";
|
fazekasgy@31
|
189 return genericMethodCall("getMaker",rString);
|
fazekasgy@0
|
190 }
|
fazekasgy@0
|
191
|
fazekasgy@0
|
192 int
|
fazekasgy@0
|
193 PyPlugin::getPluginVersion() const
|
fazekasgy@0
|
194 {
|
fazekasgy@31
|
195 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
196 size_t rValue=2;
|
fazekasgy@31
|
197 return genericMethodCall("getPluginVersion",rValue);
|
fazekasgy@0
|
198 }
|
fazekasgy@0
|
199
|
fazekasgy@0
|
200 string
|
fazekasgy@0
|
201 PyPlugin::getCopyright() const
|
fazekasgy@0
|
202 {
|
cannam@3
|
203 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
204 string rString="Licence information not available.";
|
fazekasgy@31
|
205 return genericMethodCall("getCopyright",rString);
|
fazekasgy@0
|
206 }
|
fazekasgy@0
|
207
|
fazekasgy@0
|
208
|
fazekasgy@0
|
209 bool
|
fazekasgy@0
|
210 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
|
fazekasgy@0
|
211 {
|
fazekasgy@31
|
212
|
cannam@24
|
213 if (channels < getMinChannelCount() ||
|
cannam@24
|
214 channels > getMaxChannelCount()) return false;
|
fazekasgy@31
|
215
|
fazekasgy@6
|
216 m_inputDomain = getInputDomain();
|
fazekasgy@6
|
217
|
fazekasgy@31
|
218 //Note: placing Mutex before the calls above causes deadlock !!
|
cannam@3
|
219 MutexLocker locker(&m_pythonInterpreterMutex);
|
cannam@3
|
220
|
fazekasgy@6
|
221 m_stepSize = stepSize;
|
fazekasgy@6
|
222 m_blockSize = blockSize;
|
fazekasgy@6
|
223 m_channels = channels;
|
fazekasgy@6
|
224
|
fazekasgy@31
|
225 //query the process implementation type
|
fazekasgy@31
|
226 //two optional flags can be used: 'use_numpy_interface' or 'use_legacy_interface'
|
fazekasgy@31
|
227 //if they are not provided, we fall back to the original method
|
fazekasgy@31
|
228 setProcessType();
|
fazekasgy@6
|
229
|
fazekasgy@31
|
230 return genericMethodCallArgs<bool>("initialise",channels,stepSize,blockSize);
|
fazekasgy@0
|
231 }
|
fazekasgy@0
|
232
|
fazekasgy@0
|
233 void
|
fazekasgy@0
|
234 PyPlugin::reset()
|
fazekasgy@0
|
235 {
|
fazekasgy@6
|
236 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
237 genericMethodCall("reset");
|
fazekasgy@0
|
238 }
|
fazekasgy@0
|
239
|
fazekasgy@31
|
240 PyPlugin::InputDomain
|
fazekasgy@31
|
241 PyPlugin::getInputDomain() const
|
fazekasgy@0
|
242 {
|
cannam@3
|
243 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
244 // Note: Vamp enum type is mapped to Python string !!
|
fazekasgy@31
|
245 // Is there a better way? (Enums are not native to Python)
|
fazekasgy@31
|
246 string rValue = "TimeDomain";
|
fazekasgy@31
|
247 genericMethodCall("getInputDomain",rValue);
|
fazekasgy@31
|
248 return (rValue == "FrequencyDomain")?FrequencyDomain:TimeDomain;
|
fazekasgy@0
|
249 }
|
fazekasgy@0
|
250
|
fazekasgy@31
|
251 size_t
|
fazekasgy@31
|
252 PyPlugin::getPreferredBlockSize() const
|
fazekasgy@0
|
253 {
|
cannam@3
|
254 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
255 size_t rValue = 0;
|
fazekasgy@31
|
256 return genericMethodCall("getPreferredBlockSize",rValue);
|
fazekasgy@0
|
257 }
|
fazekasgy@0
|
258
|
fazekasgy@31
|
259 size_t
|
fazekasgy@31
|
260 PyPlugin::getPreferredStepSize() const
|
fazekasgy@0
|
261 {
|
cannam@3
|
262 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
263 size_t rValue = 0;
|
fazekasgy@31
|
264 return genericMethodCall("getPreferredStepSize",rValue);
|
fazekasgy@0
|
265 }
|
fazekasgy@0
|
266
|
fazekasgy@31
|
267 size_t
|
fazekasgy@31
|
268 PyPlugin::getMinChannelCount() const
|
fazekasgy@0
|
269 {
|
cannam@3
|
270 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
271 size_t rValue = 1;
|
fazekasgy@31
|
272 return genericMethodCall("getMinChannelCount",rValue);
|
fazekasgy@0
|
273 }
|
fazekasgy@0
|
274
|
fazekasgy@31
|
275 size_t
|
fazekasgy@31
|
276 PyPlugin::getMaxChannelCount() const
|
fazekasgy@0
|
277 {
|
cannam@3
|
278 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
279 size_t rValue = 1;
|
fazekasgy@31
|
280 return genericMethodCall("getMaxChannelCount",rValue);
|
fazekasgy@31
|
281 }
|
fazekasgy@0
|
282
|
fazekasgy@0
|
283 PyPlugin::OutputList
|
fazekasgy@0
|
284 PyPlugin::getOutputDescriptors() const
|
fazekasgy@0
|
285 {
|
cannam@3
|
286 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@0
|
287 OutputList list;
|
fazekasgy@31
|
288 return genericMethodCall("getOutputDescriptors",list);
|
fazekasgy@0
|
289 }
|
fazekasgy@0
|
290
|
fazekasgy@0
|
291 PyPlugin::ParameterList
|
fazekasgy@0
|
292 PyPlugin::getParameterDescriptors() const
|
fazekasgy@0
|
293 {
|
cannam@3
|
294 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@0
|
295 ParameterList list;
|
fazekasgy@31
|
296 ///Note: This function is often called first by the host.
|
fazekasgy@27
|
297 if (!m_pyInstance) {cerr << "Error: pyInstance is NULL" << endl; return list;}
|
fazekasgy@31
|
298 return genericMethodCall("getParameterDescriptors",list);
|
fazekasgy@0
|
299 }
|
fazekasgy@0
|
300
|
fazekasgy@0
|
301 void PyPlugin::setParameter(std::string paramid, float newval)
|
fazekasgy@0
|
302 {
|
cannam@3
|
303 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
304 genericMethodCallArgs<NoneType>("setParameter",paramid,newval);
|
fazekasgy@0
|
305 }
|
fazekasgy@0
|
306
|
fazekasgy@0
|
307 float PyPlugin::getParameter(std::string paramid) const
|
fazekasgy@0
|
308 {
|
cannam@3
|
309 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
310 return genericMethodCallArgs<float>("getParameter",paramid);
|
fazekasgy@0
|
311 }
|
fazekasgy@0
|
312
|
fazekasgy@0
|
313 #ifdef _DEBUG
|
fazekasgy@0
|
314 static int proccounter = 0;
|
fazekasgy@0
|
315 #endif
|
fazekasgy@0
|
316
|
fazekasgy@0
|
317 PyPlugin::FeatureSet
|
fazekasgy@31
|
318 PyPlugin::process(const float *const *inputBuffers,Vamp::RealTime timestamp)
|
fazekasgy@0
|
319 {
|
cannam@3
|
320 MutexLocker locker(&m_pythonInterpreterMutex);
|
cannam@3
|
321
|
fazekasgy@6
|
322 #ifdef _DEBUG
|
fazekasgy@6
|
323 cerr << "[call] process, frame:" << proccounter << endl;
|
fazekasgy@6
|
324 proccounter++;
|
fazekasgy@6
|
325 #endif
|
fazekasgy@6
|
326
|
fazekasgy@8
|
327 if (m_blockSize == 0 || m_channels == 0) {
|
fazekasgy@0
|
328 cerr << "ERROR: PyPlugin::process: "
|
fazekasgy@0
|
329 << "Plugin has not been initialised" << endl;
|
fazekasgy@0
|
330 return FeatureSet();
|
fazekasgy@0
|
331 }
|
fazekasgy@0
|
332
|
fazekasgy@6
|
333 if (m_processType == not_implemented) {
|
fazekasgy@6
|
334 cerr << "ERROR: In Python plugin [" << m_class
|
fazekasgy@6
|
335 << "] No process implementation found. Returning empty feature set." << endl;
|
fazekasgy@6
|
336 return FeatureSet();
|
fazekasgy@6
|
337 }
|
fazekasgy@0
|
338
|
fazekasgy@31
|
339 // string method=PyString_AsString(m_pyProcess);
|
fazekasgy@6
|
340
|
fazekasgy@6
|
341 PyObject *pyOutputList = NULL;
|
fazekasgy@0
|
342
|
fazekasgy@6
|
343 if (m_processType == numpyProcess) {
|
fazekasgy@31
|
344 pyOutputList = numpyProcessCall(inputBuffers,timestamp);
|
fazekasgy@6
|
345 }
|
fazekasgy@6
|
346
|
fazekasgy@6
|
347 if (m_processType == legacyProcess) {
|
fazekasgy@31
|
348 pyOutputList = legacyProcessCall(inputBuffers,timestamp);
|
fazekasgy@0
|
349 }
|
fazekasgy@6
|
350
|
fazekasgy@31
|
351 FeatureSet rFeatureset;
|
fazekasgy@31
|
352 rFeatureset = m_ti.PyValue_To_FeatureSet(pyOutputList);
|
fazekasgy@0
|
353 Py_CLEAR(pyOutputList);
|
fazekasgy@31
|
354 return rFeatureset;
|
fazekasgy@31
|
355
|
fazekasgy@0
|
356 }
|
fazekasgy@0
|
357
|
fazekasgy@31
|
358 PyObject*
|
fazekasgy@31
|
359 PyPlugin::numpyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
|
fazekasgy@31
|
360 {
|
fazekasgy@31
|
361 PyObject *pyOutputList = NULL;
|
fazekasgy@31
|
362
|
fazekasgy@31
|
363 //create a list of buffers
|
fazekasgy@31
|
364 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
|
fazekasgy@31
|
365 for (size_t i=0; i < m_channels; ++i) {
|
fazekasgy@31
|
366 //Expose memory using the Buffer Interface of C/API
|
fazekasgy@32
|
367 //This will pass a pointer which can be recasted
|
fazekasgy@32
|
368 //in Python code as float or complex array
|
fazekasgy@31
|
369 PyObject *pyBuffer = PyBuffer_FromMemory
|
fazekasgy@31
|
370 ((void *) (float *) inputBuffers[i],
|
fazekasgy@31
|
371 (Py_ssize_t) sizeof(float) * m_blockSize);
|
fazekasgy@0
|
372
|
fazekasgy@31
|
373 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer);
|
fazekasgy@31
|
374 }
|
fazekasgy@31
|
375 /*
|
fazekasgy@31
|
376 //(1) pass RealTime as frameCount
|
fazekasgy@31
|
377 PyObject *pyLongSample = PyLong_FromLong (
|
fazekasgy@31
|
378 Vamp::RealTime::realTime2Frame
|
fazekasgy@31
|
379 (timestamp, (unsigned int) m_inputSampleRate));
|
fazekasgy@31
|
380
|
fazekasgy@31
|
381 //Call python process (returns new reference)
|
fazekasgy@31
|
382 pyOutputList = PyObject_CallMethodObjArgs
|
fazekasgy@31
|
383 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
|
fazekasgy@31
|
384 */
|
fazekasgy@31
|
385 //(2) pass RealTime as PyRealTime
|
fazekasgy@31
|
386 PyObject *pyRealTime = PyRealTime_FromRealTime(timestamp);
|
fazekasgy@31
|
387
|
fazekasgy@31
|
388 //Call python process (returns new reference)
|
fazekasgy@31
|
389 pyOutputList = PyObject_CallMethodObjArgs
|
fazekasgy@31
|
390 (m_pyInstance,m_pyProcess,pyChannelList,pyRealTime,NULL);
|
fazekasgy@31
|
391
|
fazekasgy@31
|
392 Py_DECREF(pyChannelList);
|
fazekasgy@31
|
393 // Py_DECREF(pyLongSample);
|
fazekasgy@31
|
394 Py_DECREF(pyRealTime);
|
fazekasgy@31
|
395 return pyOutputList;
|
fazekasgy@31
|
396 }
|
fazekasgy@31
|
397
|
fazekasgy@31
|
398 PyObject*
|
fazekasgy@31
|
399 PyPlugin::legacyProcessCall(const float *const *inputBuffers,Vamp::RealTime timestamp)
|
fazekasgy@31
|
400 {
|
fazekasgy@31
|
401 PyObject *pyOutputList = NULL;
|
fazekasgy@31
|
402
|
fazekasgy@31
|
403 //create a list of lists
|
fazekasgy@31
|
404 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
|
fazekasgy@31
|
405 for (size_t i=0; i < m_channels; ++i) {
|
fazekasgy@31
|
406 //New list object
|
fazekasgy@31
|
407 PyObject *pyFloat, *pyList;
|
fazekasgy@31
|
408 pyList = PyList_New((Py_ssize_t) m_blockSize);
|
fazekasgy@31
|
409
|
fazekasgy@31
|
410 //Pack samples into a Python List Object
|
fazekasgy@31
|
411 //pyFloat types will always be new references,
|
fazekasgy@31
|
412 //these will be discarded when the list is deallocated
|
fazekasgy@31
|
413 for (size_t j = 0; j < m_blockSize; ++j) {
|
fazekasgy@31
|
414 pyFloat=PyFloat_FromDouble(
|
fazekasgy@31
|
415 (double) inputBuffers[i][j]);
|
fazekasgy@31
|
416 PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat);
|
fazekasgy@31
|
417 }
|
fazekasgy@31
|
418 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList);
|
fazekasgy@31
|
419 }
|
fazekasgy@31
|
420
|
fazekasgy@31
|
421 //pass RealTime as frameCount
|
fazekasgy@31
|
422 PyObject *pyLongSample = PyLong_FromLong (
|
fazekasgy@31
|
423 Vamp::RealTime::realTime2Frame
|
fazekasgy@31
|
424 (timestamp, (unsigned int) m_inputSampleRate));
|
fazekasgy@31
|
425
|
fazekasgy@31
|
426 //Call python process (returns new reference)
|
fazekasgy@31
|
427 pyOutputList = PyObject_CallMethodObjArgs
|
fazekasgy@31
|
428 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
|
fazekasgy@31
|
429
|
fazekasgy@31
|
430 Py_DECREF(pyChannelList);
|
fazekasgy@31
|
431 Py_DECREF(pyLongSample);
|
fazekasgy@31
|
432 return pyOutputList;
|
fazekasgy@31
|
433 }
|
fazekasgy@0
|
434
|
fazekasgy@0
|
435 PyPlugin::FeatureSet
|
fazekasgy@0
|
436 PyPlugin::getRemainingFeatures()
|
fazekasgy@0
|
437 {
|
cannam@3
|
438 MutexLocker locker(&m_pythonInterpreterMutex);
|
fazekasgy@31
|
439 FeatureSet rValue;
|
fazekasgy@31
|
440 return genericMethodCall("getRemainingFeatures",rValue);
|
fazekasgy@0
|
441 }
|
fazekasgy@0
|
442
|
fazekasgy@31
|
443
|
fazekasgy@6
|
444 bool
|
fazekasgy@31
|
445 PyPlugin::getBooleanFlag(char flagName[], bool defValue = false) const
|
fazekasgy@6
|
446 {
|
fazekasgy@31
|
447 bool rValue = defValue;
|
fazekasgy@31
|
448 if (PyObject_HasAttrString(m_pyInstance,flagName))
|
fazekasgy@31
|
449 {
|
fazekasgy@31
|
450 PyObject *pyValue = PyObject_GetAttrString(m_pyInstance,flagName);
|
fazekasgy@31
|
451 if (!pyValue)
|
fazekasgy@31
|
452 {
|
fazekasgy@31
|
453 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
454 } else {
|
fazekasgy@31
|
455 rValue = m_ti.PyValue_To_Bool(pyValue);
|
fazekasgy@31
|
456 if (m_ti.error) {
|
fazekasgy@31
|
457 cerr << m_ti.lastError().message << endl;
|
fazekasgy@31
|
458 Py_CLEAR(pyValue);
|
fazekasgy@31
|
459 rValue = defValue;
|
fazekasgy@31
|
460 } else Py_DECREF(pyValue);
|
fazekasgy@31
|
461 }
|
fazekasgy@31
|
462 }
|
fazekasgy@31
|
463 if (m_debugFlag) cerr << FLAG_VALUE << endl;
|
fazekasgy@31
|
464 return rValue;
|
fazekasgy@6
|
465 }
|
fazekasgy@6
|
466
|
fazekasgy@31
|
467 void
|
fazekasgy@31
|
468 PyPlugin::setProcessType()
|
fazekasgy@31
|
469 {
|
fazekasgy@31
|
470 //quering process implementation type
|
fazekasgy@31
|
471 char legacyMethod[]="process";
|
fazekasgy@31
|
472 char numpyMethod[]="processN";
|
fazekasgy@6
|
473
|
fazekasgy@31
|
474 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &&
|
fazekasgy@31
|
475 m_processType == 0)
|
fazekasgy@31
|
476 {
|
fazekasgy@31
|
477 m_processType = legacyProcess;
|
fazekasgy@31
|
478 m_pyProcess = PyString_FromString(legacyMethod);
|
fazekasgy@31
|
479 }
|
fazekasgy@31
|
480
|
fazekasgy@31
|
481 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &&
|
fazekasgy@31
|
482 m_processType == 0)
|
fazekasgy@31
|
483 {
|
fazekasgy@31
|
484 m_processType = numpyProcess;
|
fazekasgy@31
|
485 m_pyProcess = PyString_FromString(numpyMethod);
|
fazekasgy@31
|
486 }
|
fazekasgy@31
|
487
|
fazekasgy@31
|
488 // These flags are optional. If provided, they override the
|
fazekasgy@31
|
489 // implementation type making the use of the odd processN()
|
fazekasgy@31
|
490 // function redundant.
|
fazekasgy@31
|
491 // However, the code above provides backwards compatibility.
|
fazekasgy@31
|
492 if (getBooleanFlag("use_numpy_interface",false))
|
fazekasgy@31
|
493 m_processType = numpyProcess;
|
fazekasgy@31
|
494 if (getBooleanFlag("use_legacy_interface",false))
|
fazekasgy@31
|
495 m_processType = legacyProcess;
|
fazekasgy@31
|
496 if (m_debugFlag && m_processType)
|
fazekasgy@31
|
497 cerr << "Process type: " << ((m_processType==numpyProcess)?"numpy process":"legacy process") << endl;
|
fazekasgy@6
|
498
|
fazekasgy@31
|
499 if (!m_processType)
|
fazekasgy@31
|
500 {
|
fazekasgy@31
|
501 m_processType = not_implemented;
|
fazekasgy@31
|
502 m_pyProcess = NULL;
|
fazekasgy@31
|
503 char method[]="initialise::setProcessType";
|
fazekasgy@31
|
504 cerr << PLUGIN_ERROR << " No process implementation found. Plugin will do nothing." << endl;
|
fazekasgy@6
|
505 }
|
fazekasgy@6
|
506 }
|
fazekasgy@6
|
507
|