Chris@66
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@37
|
2 /*
|
fazekasgy@37
|
3
|
fazekasgy@37
|
4 * Vampy : This plugin is a wrapper around the Vamp plugin API.
|
fazekasgy@37
|
5 * It allows for writing Vamp plugins in Python.
|
fazekasgy@37
|
6
|
fazekasgy@37
|
7 * Centre for Digital Music, Queen Mary University of London.
|
fazekasgy@37
|
8 * Copyright (C) 2008-2009 Gyorgy Fazekas, QMUL. (See Vamp sources
|
fazekasgy@37
|
9 * for licence information.)
|
fazekasgy@37
|
10
|
fazekasgy@37
|
11 */
|
fazekasgy@37
|
12
|
fazekasgy@37
|
13 #include <Python.h>
|
fazekasgy@37
|
14
|
fazekasgy@37
|
15 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
16 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
|
fazekasgy@37
|
17 #define NO_IMPORT_ARRAY
|
Chris@66
|
18 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
fazekasgy@37
|
19 #include "numpy/arrayobject.h"
|
fazekasgy@37
|
20 #endif
|
fazekasgy@37
|
21
|
fazekasgy@37
|
22 #include "PyTypeInterface.h"
|
fazekasgy@37
|
23 #include "PyRealTime.h"
|
fazekasgy@37
|
24 #include "PyExtensionModule.h"
|
fazekasgy@37
|
25 #include <math.h>
|
fazekasgy@37
|
26 #include <float.h>
|
fazekasgy@37
|
27 #include <limits.h>
|
fazekasgy@37
|
28 #ifndef SIZE_T_MAX
|
fazekasgy@37
|
29 #define SIZE_T_MAX ((size_t) -1)
|
fazekasgy@37
|
30 #endif
|
fazekasgy@37
|
31
|
fazekasgy@37
|
32 using std::string;
|
fazekasgy@37
|
33 using std::vector;
|
fazekasgy@37
|
34 using std::cerr;
|
fazekasgy@37
|
35 using std::endl;
|
fazekasgy@37
|
36 using std::map;
|
fazekasgy@37
|
37
|
fazekasgy@37
|
38 static std::map<std::string, o::eOutDescriptors> outKeys;
|
fazekasgy@37
|
39 static std::map<std::string, p::eParmDescriptors> parmKeys;
|
fazekasgy@37
|
40 static std::map<std::string, eSampleTypes> sampleKeys;
|
fazekasgy@37
|
41 static std::map<std::string, eFeatureFields> ffKeys;
|
fazekasgy@37
|
42 static bool isMapInitialised = false;
|
fazekasgy@37
|
43
|
fazekasgy@37
|
44 /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
|
Chris@66
|
45 (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */
|
fazekasgy@37
|
46
|
fazekasgy@37
|
47 PyTypeInterface::PyTypeInterface() :
|
fazekasgy@37
|
48 m_strict(false),
|
fazekasgy@37
|
49 m_error(false),
|
fazekasgy@51
|
50 m_numpyInstalled(false),
|
fazekasgy@37
|
51 error(m_error) // const public reference for easy access
|
fazekasgy@37
|
52 {
|
fazekasgy@37
|
53 }
|
fazekasgy@37
|
54
|
fazekasgy@37
|
55 PyTypeInterface::~PyTypeInterface()
|
fazekasgy@37
|
56 {
|
fazekasgy@37
|
57 }
|
fazekasgy@37
|
58
|
fazekasgy@51
|
59 /// FeatureSet (an integer map of FeatureLists)
|
fazekasgy@37
|
60 Vamp::Plugin::FeatureSet
|
fazekasgy@37
|
61 PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const
|
fazekasgy@37
|
62 {
|
fazekasgy@37
|
63 Vamp::Plugin::FeatureSet rFeatureSet;
|
fazekasgy@37
|
64
|
fazekasgy@37
|
65 /// Convert PyFeatureSet
|
fazekasgy@37
|
66 if (PyFeatureSet_CheckExact(pyValue)) {
|
fazekasgy@37
|
67
|
fazekasgy@37
|
68 Py_ssize_t pyPos = 0;
|
fazekasgy@37
|
69 PyObject *pyKey, *pyDictValue; // Borrowed References
|
fazekasgy@37
|
70 int key;
|
fazekasgy@37
|
71 // bool it_error = false;
|
fazekasgy@37
|
72
|
fazekasgy@37
|
73 m_error = false;
|
fazekasgy@37
|
74 while (PyDict_Next(pyValue, &pyPos, &pyKey, &pyDictValue))
|
fazekasgy@37
|
75 {
|
fazekasgy@37
|
76 key = (int) PyInt_AS_LONG(pyKey);
|
fazekasgy@37
|
77 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
78 cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl;
|
fazekasgy@37
|
79 #endif
|
fazekasgy@37
|
80 // DictValue -> Vamp::FeatureList
|
fazekasgy@37
|
81 PyValue_To_rValue(pyDictValue,rFeatureSet[key]);
|
fazekasgy@37
|
82 if (m_error) {
|
fazekasgy@37
|
83 // it_error = true;
|
fazekasgy@37
|
84 lastError() << " in output number: " << key;
|
fazekasgy@37
|
85 }
|
fazekasgy@37
|
86 }
|
fazekasgy@37
|
87 // if (it_error) m_error = true;
|
fazekasgy@37
|
88 if (!m_errorQueue.empty()) {
|
fazekasgy@37
|
89 setValueError("Error while converting FeatureSet.",m_strict);
|
fazekasgy@37
|
90 }
|
fazekasgy@37
|
91 return rFeatureSet;
|
fazekasgy@37
|
92 }
|
fazekasgy@37
|
93
|
fazekasgy@37
|
94 /// Convert Python list (backward compatibility)
|
fazekasgy@37
|
95 if (PyList_Check(pyValue)) {
|
fazekasgy@37
|
96
|
fazekasgy@37
|
97 PyObject *pyFeatureList; // This will be borrowed reference
|
fazekasgy@37
|
98
|
fazekasgy@37
|
99 //Parse Output List for each element (FeatureSet)
|
fazekasgy@37
|
100 m_error = false;
|
fazekasgy@37
|
101 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
|
fazekasgy@37
|
102 //Get i-th FeatureList (Borrowed Reference)
|
fazekasgy@37
|
103 pyFeatureList = PyList_GET_ITEM(pyValue,i);
|
fazekasgy@37
|
104 PyValue_To_rValue(pyFeatureList,rFeatureSet[i]);
|
fazekasgy@37
|
105 if (m_error) {
|
fazekasgy@37
|
106 lastError() << " in output number: " << i;
|
fazekasgy@37
|
107 }
|
fazekasgy@37
|
108 }
|
fazekasgy@37
|
109 if (!m_errorQueue.empty()) m_error = true;
|
fazekasgy@37
|
110 return rFeatureSet;
|
fazekasgy@37
|
111 }
|
fazekasgy@37
|
112
|
fazekasgy@51
|
113 /// accept None return values
|
fazekasgy@37
|
114 if (pyValue == Py_None) return rFeatureSet;
|
fazekasgy@37
|
115
|
fazekasgy@37
|
116 /// give up
|
fazekasgy@37
|
117 std::string msg = "Unsupported return type. Expected list or vampy.FeatureSet(). ";
|
fazekasgy@37
|
118 setValueError(msg,m_strict);
|
fazekasgy@37
|
119 #ifdef _DEBUG
|
fazekasgy@37
|
120 cerr << "PyTypeInterface::PyValue_To_FeatureSet failed. Error: " << msg << endl;
|
fazekasgy@37
|
121 #endif
|
fazekasgy@37
|
122 return rFeatureSet;
|
fazekasgy@37
|
123 }
|
fazekasgy@37
|
124
|
fazekasgy@37
|
125 Vamp::RealTime
|
fazekasgy@37
|
126 PyTypeInterface::PyValue_To_RealTime(PyObject* pyValue) const
|
fazekasgy@37
|
127 {
|
fazekasgy@37
|
128 // We accept integer sample counts (for backward compatibility)
|
fazekasgy@37
|
129 // or PyRealTime objects and convert them to Vamp::RealTime
|
fazekasgy@37
|
130
|
fazekasgy@37
|
131 if (PyRealTime_CheckExact(pyValue))
|
fazekasgy@37
|
132 {
|
fazekasgy@37
|
133 /// just create a copy of the wrapped object
|
fazekasgy@37
|
134 return Vamp::RealTime(*PyRealTime_AS_REALTIME(pyValue));
|
fazekasgy@37
|
135 }
|
fazekasgy@37
|
136
|
fazekasgy@37
|
137 // assume integer sample count
|
Chris@71
|
138 long sampleCount = m_conv.PyValue_To_Long(pyValue);
|
Chris@71
|
139 if (m_conv.error) {
|
fazekasgy@37
|
140 std::string msg = "Unexpected value passed as RealTime.\nMust be vampy.RealTime type or integer sample count.";
|
fazekasgy@37
|
141 setValueError(msg,m_strict);
|
fazekasgy@37
|
142 #ifdef _DEBUG
|
fazekasgy@37
|
143 cerr << "PyTypeInterface::PyValue_To_RealTime failed. " << msg << endl;
|
fazekasgy@37
|
144 #endif
|
fazekasgy@37
|
145 return Vamp::RealTime();
|
fazekasgy@37
|
146 }
|
fazekasgy@37
|
147
|
fazekasgy@37
|
148 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
149 Vamp::RealTime rt =
|
fazekasgy@37
|
150 Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate );
|
fazekasgy@37
|
151 cerr << "RealTime: " << (long)sampleCount << ", ->" << rt.toString() << endl;
|
fazekasgy@37
|
152 return rt;
|
fazekasgy@37
|
153 #else
|
fazekasgy@37
|
154 return Vamp::RealTime::frame2RealTime(sampleCount,m_inputSampleRate );
|
fazekasgy@37
|
155 #endif
|
fazekasgy@37
|
156
|
fazekasgy@37
|
157 }
|
fazekasgy@37
|
158
|
fazekasgy@37
|
159 Vamp::Plugin::OutputDescriptor::SampleType
|
fazekasgy@37
|
160 PyTypeInterface::PyValue_To_SampleType(PyObject* pyValue) const
|
fazekasgy@37
|
161 {
|
fazekasgy@37
|
162 /// convert simulated enum values
|
fazekasgy@37
|
163 /// { OneSamplePerStep,FixedSampleRate,VariableSampleRate }
|
fazekasgy@37
|
164 if (PyInt_CheckExact(pyValue)) {
|
fazekasgy@37
|
165 long lst = PyInt_AS_LONG(pyValue);
|
fazekasgy@37
|
166 if (lst<0 || lst>2) {
|
fazekasgy@37
|
167 setValueError("Overflow error. SampleType has to be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type.",m_strict);
|
fazekasgy@37
|
168 return Vamp::Plugin::OutputDescriptor::SampleType();
|
fazekasgy@37
|
169 }
|
fazekasgy@37
|
170 return (Vamp::Plugin::OutputDescriptor::SampleType) lst;
|
fazekasgy@37
|
171 }
|
fazekasgy@37
|
172
|
fazekasgy@37
|
173 /// convert string (backward compatible)
|
fazekasgy@37
|
174 if (PyString_CheckExact(pyValue)) {
|
fazekasgy@37
|
175 Vamp::Plugin::OutputDescriptor::SampleType st;
|
Chris@71
|
176 st = (Vamp::Plugin::OutputDescriptor::SampleType) sampleKeys[m_conv.PyValue_To_String(pyValue)];
|
Chris@71
|
177 if (m_conv.error) {
|
fazekasgy@37
|
178 std::string msg = "Unexpected value passed as SampleType. Must be one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type.";
|
fazekasgy@37
|
179 setValueError(msg,m_strict);
|
fazekasgy@37
|
180 return Vamp::Plugin::OutputDescriptor::SampleType();
|
fazekasgy@37
|
181 }
|
fazekasgy@37
|
182 return st;
|
fazekasgy@37
|
183 }
|
fazekasgy@37
|
184
|
fazekasgy@37
|
185 /// give up
|
fazekasgy@37
|
186 std::string msg = "Unsupported return type. Expected one of { OneSamplePerStep,FixedSampleRate,VariableSampleRate }\n(an integer in the range of 0..2) or a string value naming the type.";
|
fazekasgy@37
|
187 setValueError(msg,m_strict);
|
fazekasgy@37
|
188 #ifdef _DEBUG
|
fazekasgy@37
|
189 cerr << "PyTypeInterface::PyValue_To_SampleType failed. Error: " << msg << endl;
|
fazekasgy@37
|
190 #endif
|
fazekasgy@37
|
191 return Vamp::Plugin::OutputDescriptor::SampleType();
|
fazekasgy@37
|
192 }
|
fazekasgy@37
|
193
|
fazekasgy@37
|
194 Vamp::Plugin::InputDomain
|
fazekasgy@37
|
195 PyTypeInterface::PyValue_To_InputDomain(PyObject* pyValue) const
|
fazekasgy@37
|
196 {
|
fazekasgy@37
|
197 /// convert simulated enum values { TimeDomain,FrequencyDomain }
|
fazekasgy@37
|
198 if (PyInt_CheckExact(pyValue)) {
|
fazekasgy@37
|
199 long lst = PyInt_AS_LONG(pyValue);
|
fazekasgy@37
|
200 if (lst!=0 && lst!=1) {
|
fazekasgy@37
|
201 setValueError("Overflow error. InputDomain has to be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type.",m_strict);
|
fazekasgy@37
|
202 return Vamp::Plugin::InputDomain();
|
fazekasgy@37
|
203 }
|
fazekasgy@37
|
204 return (Vamp::Plugin::InputDomain) lst;
|
fazekasgy@37
|
205 }
|
fazekasgy@37
|
206
|
fazekasgy@37
|
207 /// convert string (backward compatible)
|
fazekasgy@37
|
208 if (PyString_CheckExact(pyValue)) {
|
fazekasgy@37
|
209 Vamp::Plugin::InputDomain id;
|
Chris@71
|
210 id = (m_conv.PyValue_To_String(pyValue) == "FrequencyDomain")?Vamp::Plugin::FrequencyDomain:Vamp::Plugin::TimeDomain;
|
Chris@71
|
211 if (m_conv.error)
|
fazekasgy@37
|
212 {
|
fazekasgy@37
|
213 std::string msg = "Unexpected value passed as SampleType. Must be one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type.";
|
fazekasgy@37
|
214 setValueError(msg,m_strict);
|
fazekasgy@37
|
215 return Vamp::Plugin::InputDomain();
|
fazekasgy@37
|
216 }
|
fazekasgy@37
|
217 return id;
|
fazekasgy@37
|
218 }
|
fazekasgy@37
|
219
|
fazekasgy@37
|
220 /// give up
|
fazekasgy@37
|
221 std::string msg = "Unsupported return type. Expected one of { TimeDomain,FrequencyDomain }\n(an integer in the range of 0..1) or a string value naming the type.";
|
fazekasgy@37
|
222 setValueError(msg,m_strict);
|
fazekasgy@37
|
223 #ifdef _DEBUG
|
fazekasgy@37
|
224 cerr << "PyTypeInterface::PyValue_To_InputDomain failed. Error: " << msg << endl;
|
fazekasgy@37
|
225 #endif
|
fazekasgy@37
|
226 return Vamp::Plugin::InputDomain();
|
fazekasgy@37
|
227 }
|
fazekasgy@37
|
228
|
Chris@71
|
229 /* Convert Sample Buffers to Python */
|
Chris@71
|
230
|
Chris@71
|
231 /// passing the sample buffers as builtin python lists
|
Chris@71
|
232 /// Optimization: using fast sequence protocol
|
Chris@71
|
233 inline PyObject*
|
Chris@71
|
234 PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
Chris@71
|
235 {
|
Chris@71
|
236 //create a list of lists (new references)
|
Chris@71
|
237 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
Chris@71
|
238
|
Chris@71
|
239 // Pack samples into a Python List Object
|
Chris@71
|
240 // pyFloat/pyComplex types will always be new references,
|
Chris@71
|
241 // they will be freed when the lists are deallocated.
|
Chris@71
|
242
|
Chris@71
|
243 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
Chris@71
|
244 for (size_t i=0; i < channels; ++i) {
|
Chris@71
|
245
|
Chris@71
|
246 size_t arraySize;
|
Chris@71
|
247 if (dtype==Vamp::Plugin::FrequencyDomain)
|
Chris@71
|
248 arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used
|
Chris@71
|
249 else
|
Chris@71
|
250 arraySize = blockSize;
|
Chris@71
|
251
|
Chris@71
|
252 PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize);
|
Chris@71
|
253 PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList);
|
Chris@71
|
254
|
Chris@71
|
255 // Note: passing a complex list crashes the C-style plugin
|
Chris@71
|
256 // when it tries to convert it to a numpy array directly.
|
Chris@71
|
257 // This plugin will be obsolete, but we have to find a way
|
Chris@71
|
258 // to prevent such crash: possibly a numpy bug,
|
Chris@71
|
259 // works fine above 1.0.4
|
Chris@71
|
260
|
Chris@71
|
261 switch (dtype) //(Vamp::Plugin::TimeDomain)
|
Chris@71
|
262 {
|
Chris@71
|
263 case Vamp::Plugin::TimeDomain :
|
Chris@71
|
264
|
Chris@71
|
265 for (size_t j = 0; j < arraySize; ++j) {
|
Chris@71
|
266 PyObject *pyFloat=PyFloat_FromDouble(
|
Chris@71
|
267 (double) inputBuffers[i][j]);
|
Chris@71
|
268 pySampleListArray[j] = pyFloat;
|
Chris@71
|
269 }
|
Chris@71
|
270 break;
|
Chris@71
|
271
|
Chris@71
|
272 case Vamp::Plugin::FrequencyDomain :
|
Chris@71
|
273
|
Chris@71
|
274 size_t k = 0;
|
Chris@71
|
275 for (size_t j = 0; j < arraySize; ++j) {
|
Chris@71
|
276 PyObject *pyComplex=PyComplex_FromDoubles(
|
Chris@71
|
277 (double) inputBuffers[i][k],
|
Chris@71
|
278 (double) inputBuffers[i][k+1]);
|
Chris@71
|
279 pySampleListArray[j] = pyComplex;
|
Chris@71
|
280 k += 2;
|
Chris@71
|
281 }
|
Chris@71
|
282 break;
|
Chris@71
|
283
|
Chris@71
|
284 }
|
Chris@71
|
285 pyChannelListArray[i] = pySampleList;
|
Chris@71
|
286 }
|
Chris@71
|
287 return pyChannelList;
|
Chris@71
|
288 }
|
Chris@71
|
289
|
Chris@71
|
290 /// numpy buffer interface: passing the sample buffers as shared memory buffers
|
Chris@71
|
291 /// Optimization: using sequence protocol for creating the buffer list
|
Chris@71
|
292 inline PyObject*
|
Chris@71
|
293 PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
Chris@71
|
294 {
|
Chris@71
|
295 //create a list of buffers (returns new references)
|
Chris@71
|
296 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
Chris@71
|
297 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
Chris@71
|
298
|
Chris@71
|
299 // Expose memory using the Buffer Interface.
|
Chris@71
|
300 // This will pass a pointer which can be recasted in Python code
|
Chris@71
|
301 // as complex or float array using Numpy's frombuffer() method
|
Chris@71
|
302 // (this will not copy values just keep the starting adresses
|
Chris@71
|
303 // for each channel in a list)
|
Chris@71
|
304 Py_ssize_t bufferSize;
|
Chris@71
|
305
|
Chris@71
|
306 if (dtype==Vamp::Plugin::FrequencyDomain)
|
Chris@71
|
307 bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2);
|
Chris@71
|
308 else
|
Chris@71
|
309 bufferSize = (Py_ssize_t) sizeof(float) * blockSize;
|
Chris@71
|
310
|
Chris@71
|
311 for (size_t i=0; i < channels; ++i) {
|
Chris@71
|
312 PyObject *pyBuffer = PyBuffer_FromMemory
|
Chris@71
|
313 ((void *) (float *) inputBuffers[i],bufferSize);
|
Chris@71
|
314 pyChannelListArray[i] = pyBuffer;
|
Chris@71
|
315 }
|
Chris@71
|
316 return pyChannelList;
|
Chris@71
|
317 }
|
Chris@71
|
318
|
Chris@71
|
319
|
Chris@71
|
320 /// numpy array interface: passing the sample buffers as 2D numpy array
|
Chris@71
|
321 /// Optimization: using array API (needs numpy headers)
|
Chris@71
|
322 #ifdef HAVE_NUMPY
|
Chris@71
|
323 inline PyObject*
|
Chris@71
|
324 PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
Chris@71
|
325 {
|
Chris@71
|
326 /*
|
Chris@71
|
327 NOTE: We create a list of 1D Numpy arrays for each channel instead
|
Chris@71
|
328 of a matrix, because the address space of inputBuffers doesn't seem
|
Chris@71
|
329 to be continuous. Although the array strides could be calculated for
|
Chris@71
|
330 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure
|
Chris@71
|
331 if this can be trusted, especially for more than 2 channels.
|
Chris@71
|
332
|
Chris@71
|
333 cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl;
|
Chris@71
|
334 if (channels == 2)
|
Chris@71
|
335 cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl;
|
Chris@71
|
336
|
Chris@71
|
337 */
|
Chris@71
|
338
|
Chris@71
|
339 // create a list of arrays (returns new references)
|
Chris@71
|
340 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
Chris@71
|
341 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
Chris@71
|
342
|
Chris@71
|
343 // Expose memory using the Numpy Array Interface.
|
Chris@71
|
344 // This will wrap an array objects around the data.
|
Chris@71
|
345 // (will not copy values just steal the starting adresses)
|
Chris@71
|
346
|
Chris@71
|
347 int arraySize, typenum;
|
Chris@71
|
348
|
Chris@71
|
349 switch (dtype)
|
Chris@71
|
350 {
|
Chris@71
|
351 case Vamp::Plugin::TimeDomain :
|
Chris@71
|
352 typenum = dtype_float32; //NPY_FLOAT;
|
Chris@71
|
353 arraySize = (int) blockSize;
|
Chris@71
|
354 break;
|
Chris@71
|
355
|
Chris@71
|
356 case Vamp::Plugin::FrequencyDomain :
|
Chris@71
|
357 typenum = dtype_complex64; //NPY_CFLOAT;
|
Chris@71
|
358 arraySize = (int) (blockSize / 2) + 1;
|
Chris@71
|
359 break;
|
Chris@71
|
360
|
Chris@71
|
361 default :
|
Chris@71
|
362 cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl;
|
Chris@71
|
363 return pyChannelList;
|
Chris@71
|
364 }
|
Chris@71
|
365
|
Chris@71
|
366 // size for each dimension
|
Chris@71
|
367 npy_intp ndims[1]={arraySize};
|
Chris@71
|
368
|
Chris@71
|
369 for (size_t i=0; i < channels; ++i) {
|
Chris@71
|
370 PyObject *pyChannelArray =
|
Chris@71
|
371 //args: (dimensions, size in each dim, type kind, pointer to continuous array)
|
Chris@71
|
372 PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]);
|
Chris@71
|
373 // make it read-only: set all flags to false except NPY_C_CONTIGUOUS
|
Chris@71
|
374 //!!! what about NPY_ARRAY_OWNDATA?
|
Chris@71
|
375 PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff);
|
Chris@71
|
376 PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS);
|
Chris@71
|
377 pyChannelListArray[i] = pyChannelArray;
|
Chris@71
|
378 }
|
Chris@71
|
379 return pyChannelList;
|
Chris@71
|
380 }
|
Chris@71
|
381 #endif
|
fazekasgy@37
|
382
|
fazekasgy@37
|
383 /// OutputDescriptor
|
fazekasgy@37
|
384 void
|
fazekasgy@37
|
385 PyTypeInterface::SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const
|
fazekasgy@37
|
386 {
|
fazekasgy@37
|
387 switch (outKeys[key])
|
fazekasgy@37
|
388 {
|
fazekasgy@37
|
389 case o::not_found:
|
fazekasgy@37
|
390 setValueError("Unknown key in Vamp OutputDescriptor",m_strict);
|
fazekasgy@37
|
391 cerr << "Unknown key in Vamp OutputDescriptor: " << key << endl;
|
fazekasgy@37
|
392 break;
|
fazekasgy@37
|
393 case o::identifier:
|
fazekasgy@37
|
394 _convert(pyValue,od.identifier);
|
fazekasgy@37
|
395 break;
|
fazekasgy@37
|
396 case o::name:
|
fazekasgy@37
|
397 _convert(pyValue,od.name);
|
fazekasgy@37
|
398 break;
|
fazekasgy@37
|
399 case o::description:
|
fazekasgy@37
|
400 _convert(pyValue,od.description);
|
fazekasgy@37
|
401 break;
|
fazekasgy@37
|
402 case o::unit:
|
fazekasgy@37
|
403 _convert(pyValue,od.unit);
|
fazekasgy@37
|
404 break;
|
fazekasgy@37
|
405 case o::hasFixedBinCount:
|
fazekasgy@37
|
406 _convert(pyValue,od.hasFixedBinCount);
|
fazekasgy@37
|
407 break;
|
fazekasgy@37
|
408 case o::binCount:
|
fazekasgy@37
|
409 _convert(pyValue,od.binCount);
|
fazekasgy@37
|
410 break;
|
fazekasgy@37
|
411 case o::binNames:
|
fazekasgy@37
|
412 _convert(pyValue,od.binNames);
|
fazekasgy@37
|
413 break;
|
fazekasgy@37
|
414 case o::hasKnownExtents:
|
fazekasgy@37
|
415 _convert(pyValue,od.hasKnownExtents);
|
fazekasgy@37
|
416 break;
|
fazekasgy@37
|
417 case o::minValue:
|
fazekasgy@37
|
418 _convert(pyValue,od.minValue);
|
fazekasgy@37
|
419 break;
|
fazekasgy@37
|
420 case o::maxValue:
|
fazekasgy@37
|
421 _convert(pyValue,od.maxValue);
|
fazekasgy@37
|
422 break;
|
fazekasgy@37
|
423 case o::isQuantized:
|
fazekasgy@37
|
424 _convert(pyValue,od.isQuantized);
|
fazekasgy@37
|
425 break;
|
fazekasgy@37
|
426 case o::quantizeStep:
|
fazekasgy@37
|
427 _convert(pyValue,od.quantizeStep);
|
fazekasgy@37
|
428 break;
|
fazekasgy@37
|
429 case o::sampleType:
|
fazekasgy@37
|
430 _convert(pyValue,od.sampleType);
|
fazekasgy@37
|
431 break;
|
fazekasgy@37
|
432 case o::sampleRate:
|
fazekasgy@37
|
433 _convert(pyValue,od.sampleRate);
|
fazekasgy@37
|
434 break;
|
fazekasgy@37
|
435 case o::hasDuration:
|
fazekasgy@37
|
436 _convert(pyValue,od.hasDuration);
|
fazekasgy@37
|
437 break;
|
fazekasgy@37
|
438 default:
|
fazekasgy@37
|
439 setValueError("Unknown key in Vamp OutputDescriptor",m_strict);
|
fazekasgy@37
|
440 cerr << "Invalid key in Vamp OutputDescriptor: " << key << endl;
|
fazekasgy@37
|
441 }
|
fazekasgy@37
|
442 }
|
fazekasgy@37
|
443
|
fazekasgy@37
|
444 /// ParameterDescriptor
|
fazekasgy@37
|
445 void
|
fazekasgy@37
|
446 PyTypeInterface::SetValue(Vamp::Plugin::ParameterDescriptor& pd, std::string& key, PyObject* pyValue) const
|
fazekasgy@37
|
447 {
|
fazekasgy@37
|
448 switch (parmKeys[key])
|
fazekasgy@37
|
449 {
|
fazekasgy@37
|
450 case p::not_found :
|
fazekasgy@37
|
451 setValueError("Unknown key in Vamp ParameterDescriptor",m_strict);
|
fazekasgy@37
|
452 cerr << "Unknown key in Vamp ParameterDescriptor: " << key << endl;
|
fazekasgy@37
|
453 break;
|
fazekasgy@37
|
454 case p::identifier:
|
fazekasgy@37
|
455 _convert(pyValue,pd.identifier);
|
fazekasgy@37
|
456 break;
|
fazekasgy@37
|
457 case p::name:
|
fazekasgy@37
|
458 _convert(pyValue,pd.name);
|
fazekasgy@37
|
459 break;
|
fazekasgy@37
|
460 case p::description:
|
fazekasgy@37
|
461 _convert(pyValue,pd.description);
|
fazekasgy@37
|
462 break;
|
fazekasgy@37
|
463 case p::unit:
|
fazekasgy@37
|
464 _convert(pyValue,pd.unit);
|
fazekasgy@37
|
465 break;
|
fazekasgy@37
|
466 case p::minValue:
|
fazekasgy@37
|
467 _convert(pyValue,pd.minValue);
|
fazekasgy@37
|
468 break;
|
fazekasgy@37
|
469 case p::maxValue:
|
fazekasgy@37
|
470 _convert(pyValue,pd.maxValue);
|
fazekasgy@37
|
471 break;
|
fazekasgy@37
|
472 case p::defaultValue:
|
fazekasgy@37
|
473 _convert(pyValue,pd.defaultValue);
|
fazekasgy@37
|
474 break;
|
fazekasgy@37
|
475 case p::isQuantized:
|
fazekasgy@37
|
476 _convert(pyValue,pd.isQuantized);
|
fazekasgy@37
|
477 break;
|
fazekasgy@37
|
478 case p::quantizeStep:
|
fazekasgy@37
|
479 _convert(pyValue,pd.quantizeStep);
|
fazekasgy@37
|
480 break;
|
gyorgyf@62
|
481 case p::valueNames:
|
gyorgyf@62
|
482 _convert(pyValue,pd.valueNames);
|
gyorgyf@62
|
483 break;
|
fazekasgy@37
|
484 default :
|
fazekasgy@37
|
485 setValueError("Unknown key in Vamp ParameterDescriptor",m_strict);
|
fazekasgy@37
|
486 cerr << "Invalid key in Vamp ParameterDescriptor: " << key << endl;
|
fazekasgy@37
|
487 }
|
fazekasgy@37
|
488 }
|
fazekasgy@37
|
489
|
fazekasgy@37
|
490 /// Feature (it's like a Descriptor)
|
fazekasgy@37
|
491 bool
|
fazekasgy@37
|
492 PyTypeInterface::SetValue(Vamp::Plugin::Feature& feature, std::string& key, PyObject* pyValue) const
|
fazekasgy@37
|
493 {
|
fazekasgy@37
|
494 bool found = true;
|
fazekasgy@37
|
495 switch (ffKeys[key])
|
fazekasgy@37
|
496 {
|
fazekasgy@37
|
497 case unknown :
|
fazekasgy@37
|
498 setValueError("Unknown key in Vamp Feature",m_strict);
|
fazekasgy@37
|
499 cerr << "Unknown key in Vamp Feature: " << key << endl;
|
fazekasgy@37
|
500 found = false;
|
fazekasgy@37
|
501 break;
|
fazekasgy@37
|
502 case hasTimestamp:
|
fazekasgy@37
|
503 _convert(pyValue,feature.hasTimestamp);
|
fazekasgy@37
|
504 break;
|
fazekasgy@37
|
505 case timestamp:
|
fazekasgy@37
|
506 _convert(pyValue,feature.timestamp);
|
fazekasgy@37
|
507 break;
|
fazekasgy@37
|
508 case hasDuration:
|
fazekasgy@37
|
509 _convert(pyValue,feature.hasDuration);
|
fazekasgy@37
|
510 break;
|
fazekasgy@37
|
511 case duration:
|
fazekasgy@37
|
512 _convert(pyValue,feature.duration);
|
fazekasgy@37
|
513 break;
|
fazekasgy@37
|
514 case values:
|
fazekasgy@37
|
515 _convert(pyValue,feature.values);
|
fazekasgy@37
|
516 break;
|
fazekasgy@37
|
517 case label:
|
fazekasgy@37
|
518 _convert(pyValue,feature.label);
|
fazekasgy@37
|
519 break;
|
fazekasgy@37
|
520 default:
|
fazekasgy@37
|
521 setValueError("Unknown key in Vamp Feature",m_strict);
|
fazekasgy@37
|
522 found = false;
|
fazekasgy@37
|
523 }
|
fazekasgy@37
|
524 return found;
|
fazekasgy@37
|
525 }
|
fazekasgy@37
|
526
|
fazekasgy@37
|
527
|
Chris@71
|
528 /* Error handling */
|
fazekasgy@37
|
529
|
fazekasgy@37
|
530 void
|
fazekasgy@37
|
531 PyTypeInterface::setValueError (std::string message, bool strict) const
|
fazekasgy@37
|
532 {
|
fazekasgy@37
|
533 m_error = true;
|
fazekasgy@37
|
534 m_errorQueue.push(ValueError(message,strict));
|
fazekasgy@37
|
535 }
|
fazekasgy@37
|
536
|
fazekasgy@37
|
537 /// return a reference to the last error or creates a new one.
|
Chris@71
|
538 ValueError&
|
fazekasgy@37
|
539 PyTypeInterface::lastError() const
|
fazekasgy@37
|
540 {
|
fazekasgy@37
|
541 m_error = false;
|
fazekasgy@37
|
542 if (!m_errorQueue.empty()) return m_errorQueue.back();
|
fazekasgy@37
|
543 else {
|
fazekasgy@37
|
544 m_errorQueue.push(ValueError("Type conversion error.",m_strict));
|
fazekasgy@37
|
545 return m_errorQueue.back();
|
fazekasgy@37
|
546 }
|
fazekasgy@37
|
547 }
|
fazekasgy@37
|
548
|
fazekasgy@37
|
549 /// helper function to iterate over the error message queue:
|
fazekasgy@37
|
550 /// pops the oldest item
|
Chris@71
|
551 ValueError
|
fazekasgy@37
|
552 PyTypeInterface::getError() const
|
fazekasgy@37
|
553 {
|
fazekasgy@37
|
554 if (!m_errorQueue.empty()) {
|
Chris@71
|
555 ValueError e = m_errorQueue.front();
|
fazekasgy@37
|
556 m_errorQueue.pop();
|
fazekasgy@37
|
557 if (m_errorQueue.empty()) m_error = false;
|
fazekasgy@37
|
558 return e;
|
fazekasgy@37
|
559 }
|
fazekasgy@37
|
560 else {
|
fazekasgy@37
|
561 m_error = false;
|
Chris@71
|
562 return ValueError();
|
fazekasgy@37
|
563 }
|
fazekasgy@37
|
564 }
|
fazekasgy@37
|
565
|
Chris@71
|
566 /* Utilities */
|
fazekasgy@37
|
567
|
fazekasgy@37
|
568 bool
|
fazekasgy@37
|
569 PyTypeInterface::initMaps() const
|
fazekasgy@37
|
570 {
|
fazekasgy@37
|
571
|
fazekasgy@37
|
572 if (isMapInitialised) return true;
|
fazekasgy@37
|
573
|
fazekasgy@37
|
574 outKeys["identifier"] = o::identifier;
|
fazekasgy@37
|
575 outKeys["name"] = o::name;
|
fazekasgy@37
|
576 outKeys["description"] = o::description;
|
fazekasgy@37
|
577 outKeys["unit"] = o::unit;
|
fazekasgy@37
|
578 outKeys["hasFixedBinCount"] = o::hasFixedBinCount;
|
fazekasgy@37
|
579 outKeys["binCount"] = o::binCount;
|
fazekasgy@37
|
580 outKeys["binNames"] = o::binNames;
|
fazekasgy@37
|
581 outKeys["hasKnownExtents"] = o::hasKnownExtents;
|
fazekasgy@37
|
582 outKeys["minValue"] = o::minValue;
|
fazekasgy@37
|
583 outKeys["maxValue"] = o::maxValue;
|
fazekasgy@37
|
584 outKeys["isQuantized"] = o::isQuantized;
|
fazekasgy@37
|
585 outKeys["quantizeStep"] = o::quantizeStep;
|
fazekasgy@37
|
586 outKeys["sampleType"] = o::sampleType;
|
fazekasgy@37
|
587 outKeys["sampleRate"] = o::sampleRate;
|
fazekasgy@37
|
588 outKeys["hasDuration"] = o::hasDuration;
|
fazekasgy@37
|
589
|
fazekasgy@37
|
590 sampleKeys["OneSamplePerStep"] = OneSamplePerStep;
|
fazekasgy@37
|
591 sampleKeys["FixedSampleRate"] = FixedSampleRate;
|
fazekasgy@37
|
592 sampleKeys["VariableSampleRate"] = VariableSampleRate;
|
fazekasgy@37
|
593
|
fazekasgy@37
|
594 ffKeys["hasTimestamp"] = hasTimestamp;
|
fazekasgy@37
|
595 ffKeys["timestamp"] = timestamp; // this is the correct one
|
fazekasgy@37
|
596 ffKeys["timeStamp"] = timestamp; // backward compatible
|
fazekasgy@37
|
597 ffKeys["hasDuration"] = hasDuration;
|
fazekasgy@37
|
598 ffKeys["duration"] = duration;
|
fazekasgy@37
|
599 ffKeys["values"] = values;
|
fazekasgy@37
|
600 ffKeys["label"] = label;
|
fazekasgy@37
|
601
|
fazekasgy@37
|
602 parmKeys["identifier"] = p::identifier;
|
fazekasgy@37
|
603 parmKeys["name"] = p::name;
|
fazekasgy@37
|
604 parmKeys["description"] = p::description;
|
fazekasgy@37
|
605 parmKeys["unit"] = p::unit;
|
fazekasgy@37
|
606 parmKeys["minValue"] = p::minValue;
|
fazekasgy@37
|
607 parmKeys["maxValue"] = p::maxValue;
|
fazekasgy@37
|
608 parmKeys["defaultValue"] = p::defaultValue;
|
fazekasgy@37
|
609 parmKeys["isQuantized"] = p::isQuantized;
|
fazekasgy@37
|
610 parmKeys["quantizeStep"] = p::quantizeStep;
|
gyorgyf@62
|
611 parmKeys["valueNames"] = p::valueNames;
|
fazekasgy@37
|
612
|
fazekasgy@37
|
613 isMapInitialised = true;
|
fazekasgy@37
|
614 return true;
|
fazekasgy@37
|
615 }
|