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 /*
|
fazekasgy@37
|
14 PyTypeInterface: Type safe conversion utilities between Python types
|
fazekasgy@37
|
15 and basic C/C++ types and Vamp API types.
|
fazekasgy@37
|
16 */
|
fazekasgy@37
|
17
|
fazekasgy@37
|
18 #ifndef _PY_TYPE_INTERFACE_H_
|
fazekasgy@37
|
19 #define _PY_TYPE_INTERFACE_H_
|
fazekasgy@37
|
20 #include <Python.h>
|
fazekasgy@37
|
21 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
22 #define PY_ARRAY_UNIQUE_SYMBOL VAMPY_ARRAY_API
|
fazekasgy@37
|
23 #define NO_IMPORT_ARRAY
|
Chris@66
|
24 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
fazekasgy@37
|
25 #include "numpy/arrayobject.h"
|
fazekasgy@37
|
26 #endif
|
fazekasgy@37
|
27 #include "PyExtensionModule.h"
|
fazekasgy@37
|
28 #include <vector>
|
fazekasgy@37
|
29 #include <queue>
|
fazekasgy@37
|
30 #include <string>
|
fazekasgy@37
|
31 #include <sstream>
|
fazekasgy@37
|
32 #include "vamp-sdk/Plugin.h"
|
fazekasgy@37
|
33
|
fazekasgy@37
|
34 using std::cerr;
|
fazekasgy@37
|
35 using std::endl;
|
fazekasgy@37
|
36
|
fazekasgy@37
|
37 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
38 enum eArrayDataType {
|
fazekasgy@37
|
39 dtype_float32 = (int) NPY_FLOAT,
|
fazekasgy@37
|
40 dtype_complex64 = (int) NPY_CFLOAT
|
fazekasgy@37
|
41 };
|
fazekasgy@37
|
42 #endif
|
fazekasgy@37
|
43
|
fazekasgy@37
|
44 namespace o {
|
fazekasgy@37
|
45 enum eOutDescriptors {
|
fazekasgy@37
|
46 not_found,
|
fazekasgy@37
|
47 identifier,
|
fazekasgy@37
|
48 name,
|
fazekasgy@37
|
49 description,
|
fazekasgy@37
|
50 unit,
|
fazekasgy@37
|
51 hasFixedBinCount,
|
fazekasgy@37
|
52 binCount,
|
fazekasgy@37
|
53 binNames,
|
fazekasgy@37
|
54 hasKnownExtents,
|
fazekasgy@37
|
55 minValue,
|
fazekasgy@37
|
56 maxValue,
|
fazekasgy@37
|
57 isQuantized,
|
fazekasgy@37
|
58 quantizeStep,
|
fazekasgy@37
|
59 sampleType,
|
fazekasgy@37
|
60 sampleRate,
|
fazekasgy@37
|
61 hasDuration,
|
fazekasgy@37
|
62 endNode
|
fazekasgy@37
|
63 };
|
fazekasgy@37
|
64 }
|
fazekasgy@37
|
65
|
fazekasgy@37
|
66 namespace p {
|
fazekasgy@37
|
67 enum eParmDescriptors {
|
fazekasgy@37
|
68 not_found,
|
fazekasgy@37
|
69 identifier,
|
fazekasgy@37
|
70 name,
|
fazekasgy@37
|
71 description,
|
fazekasgy@37
|
72 unit,
|
fazekasgy@37
|
73 minValue,
|
fazekasgy@37
|
74 maxValue,
|
fazekasgy@37
|
75 defaultValue,
|
fazekasgy@37
|
76 isQuantized,
|
gyorgyf@62
|
77 quantizeStep,
|
gyorgyf@62
|
78 valueNames
|
fazekasgy@37
|
79 };
|
fazekasgy@37
|
80 }
|
fazekasgy@37
|
81
|
fazekasgy@37
|
82 enum eSampleTypes {
|
fazekasgy@37
|
83 OneSamplePerStep,
|
fazekasgy@37
|
84 FixedSampleRate,
|
fazekasgy@37
|
85 VariableSampleRate
|
fazekasgy@37
|
86 };
|
fazekasgy@37
|
87
|
fazekasgy@37
|
88 enum eFeatureFields {
|
fazekasgy@37
|
89 unknown,
|
fazekasgy@37
|
90 hasTimestamp,
|
fazekasgy@37
|
91 timestamp,
|
fazekasgy@37
|
92 hasDuration,
|
fazekasgy@37
|
93 duration,
|
fazekasgy@37
|
94 values,
|
fazekasgy@37
|
95 label
|
fazekasgy@37
|
96 };
|
fazekasgy@37
|
97
|
fazekasgy@37
|
98 /* C++ mapping of PyNone Type */
|
fazekasgy@37
|
99 struct NoneType {};
|
fazekasgy@37
|
100
|
fazekasgy@37
|
101 class PyTypeInterface
|
fazekasgy@37
|
102 {
|
fazekasgy@37
|
103 public:
|
fazekasgy@37
|
104 PyTypeInterface();
|
fazekasgy@37
|
105 ~PyTypeInterface();
|
fazekasgy@37
|
106
|
fazekasgy@37
|
107 // Data
|
fazekasgy@37
|
108 class ValueError
|
fazekasgy@37
|
109 {
|
fazekasgy@37
|
110 public:
|
fazekasgy@37
|
111 ValueError() {}
|
fazekasgy@37
|
112 ValueError(std::string m, bool s) : message(m),strict(s) {}
|
fazekasgy@37
|
113 std::string location;
|
fazekasgy@37
|
114 std::string message;
|
fazekasgy@37
|
115 bool strict;
|
fazekasgy@37
|
116 std::string str() const {
|
fazekasgy@37
|
117 return (location.empty()) ? message : message + "\nLocation: " + location;}
|
fazekasgy@37
|
118 void print() const { cerr << str() << endl; }
|
fazekasgy@37
|
119 template<typename V> ValueError &operator<< (const V& v)
|
fazekasgy@37
|
120 {
|
fazekasgy@37
|
121 std::ostringstream ss;
|
fazekasgy@37
|
122 ss << v;
|
fazekasgy@37
|
123 location += ss.str();
|
fazekasgy@37
|
124 return *this;
|
fazekasgy@37
|
125 }
|
fazekasgy@37
|
126 };
|
fazekasgy@37
|
127
|
fazekasgy@37
|
128 // Utilities
|
fazekasgy@37
|
129 void setStrictTypingFlag(bool b) {m_strict = b;}
|
fazekasgy@51
|
130 void setNumpyInstalled(bool b) {m_numpyInstalled = b;}
|
fazekasgy@37
|
131 ValueError getError() const;
|
fazekasgy@37
|
132 std::string PyValue_Get_TypeName(PyObject*) const;
|
fazekasgy@37
|
133 bool initMaps() const;
|
fazekasgy@37
|
134
|
fazekasgy@37
|
135 // Basic type conversion: Python to C++
|
fazekasgy@37
|
136 float PyValue_To_Float(PyObject*) const;
|
fazekasgy@37
|
137 size_t PyValue_To_Size_t(PyObject*) const;
|
fazekasgy@37
|
138 bool PyValue_To_Bool(PyObject*) const;
|
fazekasgy@37
|
139 std::string PyValue_To_String(PyObject*) const;
|
fazekasgy@37
|
140 long PyValue_To_Long(PyObject*) const;
|
fazekasgy@37
|
141 // int PyValue_To_Int(PyObject* pyValue) const;
|
fazekasgy@37
|
142
|
fazekasgy@37
|
143
|
fazekasgy@37
|
144 // C++ to Python
|
fazekasgy@37
|
145 PyObject *PyValue_From_CValue(const char*) const;
|
fazekasgy@37
|
146 PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
|
fazekasgy@37
|
147 PyObject *PyValue_From_CValue(size_t) const;
|
fazekasgy@37
|
148 PyObject *PyValue_From_CValue(double) const;
|
fazekasgy@37
|
149 PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
|
fazekasgy@37
|
150 PyObject *PyValue_From_CValue(bool) const;
|
fazekasgy@37
|
151
|
fazekasgy@37
|
152 // Sequence types
|
fazekasgy@37
|
153 std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
|
fazekasgy@37
|
154 std::vector<float> PyValue_To_FloatVector (PyObject*) const;
|
fazekasgy@37
|
155 std::vector<float> PyList_To_FloatVector (PyObject*) const;
|
fazekasgy@37
|
156
|
fazekasgy@37
|
157 // Input buffers to Python
|
fazekasgy@37
|
158 PyObject* InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
|
fazekasgy@47
|
159 PyObject* InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype);
|
fazekasgy@37
|
160
|
fazekasgy@37
|
161 // Numpy types
|
fazekasgy@37
|
162 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
163 std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const;
|
fazekasgy@37
|
164 PyObject* InputBuffers_As_NumpyArray(const float *const *inputBuffers, const size_t&, const size_t&, const Vamp::Plugin::InputDomain& dtype);
|
fazekasgy@37
|
165 #endif
|
fazekasgy@37
|
166
|
fazekasgy@37
|
167
|
fazekasgy@37
|
168
|
fazekasgy@37
|
169
|
fazekasgy@37
|
170 /* Template functions */
|
fazekasgy@37
|
171
|
fazekasgy@37
|
172
|
fazekasgy@37
|
173 /// Common wrappers to set values in Vamp API structs. (to be used in template functions)
|
fazekasgy@37
|
174 void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@37
|
175 void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@37
|
176 bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@37
|
177 PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const
|
fazekasgy@37
|
178 {
|
fazekasgy@37
|
179 if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
|
fazekasgy@37
|
180 if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
|
fazekasgy@37
|
181 if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
|
fazekasgy@37
|
182 return NULL;
|
fazekasgy@37
|
183 }
|
fazekasgy@37
|
184
|
fazekasgy@37
|
185 //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
|
fazekasgy@37
|
186 template<typename RET>
|
fazekasgy@37
|
187 RET PyValue_To_VampDescriptor(PyObject* pyValue) const
|
fazekasgy@37
|
188 {
|
fazekasgy@37
|
189 PyObject* pyDict;
|
fazekasgy@37
|
190
|
fazekasgy@37
|
191 // Descriptors encoded as dicts
|
fazekasgy@37
|
192 pyDict = GetDescriptor_As_Dict(pyValue);
|
fazekasgy@37
|
193 if (!pyDict) pyDict = pyValue;
|
fazekasgy@37
|
194
|
fazekasgy@37
|
195 // TODO: support full mapping protocol as fallback.
|
fazekasgy@37
|
196 if (!PyDict_Check(pyDict)) {
|
fazekasgy@37
|
197 setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
|
fazekasgy@37
|
198 #ifdef _DEBUG
|
fazekasgy@37
|
199 cerr << "PyTypeInterface::PyValue_To_VampDescriptor failed. Error: Unexpected return type." << endl;
|
fazekasgy@37
|
200 #endif
|
fazekasgy@37
|
201 return RET();
|
fazekasgy@37
|
202 }
|
fazekasgy@37
|
203
|
fazekasgy@37
|
204 Py_ssize_t pyPos = 0;
|
fazekasgy@37
|
205 PyObject *pyKey, *pyDictValue;
|
fazekasgy@37
|
206 initMaps();
|
fazekasgy@37
|
207 int errors = 0;
|
fazekasgy@37
|
208 m_error = false;
|
fazekasgy@37
|
209 RET rd;
|
fazekasgy@37
|
210
|
fazekasgy@37
|
211 //Python Dictionary Iterator:
|
fazekasgy@37
|
212 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
|
fazekasgy@37
|
213 {
|
fazekasgy@37
|
214 std::string key = PyValue_To_String(pyKey);
|
fazekasgy@37
|
215 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
216 cerr << "key: '" << key << "' value: '" << PyValue_To_String(pyDictValue) << "' " << endl;
|
fazekasgy@37
|
217 #endif
|
fazekasgy@37
|
218 SetValue(rd,key,pyDictValue);
|
fazekasgy@37
|
219 if (m_error) {
|
fazekasgy@37
|
220 errors++;
|
fazekasgy@37
|
221 lastError() << "attribute '" << key << "'";// << " of " << getDescriptorId(rd);
|
fazekasgy@37
|
222 }
|
fazekasgy@37
|
223 }
|
fazekasgy@37
|
224 if (errors) {
|
fazekasgy@37
|
225 lastError() << " of " << getDescriptorId(rd);
|
fazekasgy@37
|
226 m_error = true;
|
fazekasgy@37
|
227 #ifdef _DEBUG
|
fazekasgy@37
|
228 cerr << "PyTypeInterface::PyValue_To_VampDescriptor: Warning: Value error in descriptor." << endl;
|
fazekasgy@37
|
229 #endif
|
fazekasgy@37
|
230 }
|
fazekasgy@37
|
231 return rd;
|
fazekasgy@37
|
232 }
|
fazekasgy@37
|
233
|
fazekasgy@37
|
234 /// Convert a sequence (tipically list) of PySomething to
|
fazekasgy@37
|
235 /// OutputList,ParameterList or FeatureList
|
fazekasgy@37
|
236 /// <OutputList> <OutputDescriptor>
|
fazekasgy@37
|
237 template<typename RET,typename ELEM>
|
fazekasgy@37
|
238 RET PyValue_To_VampList(PyObject* pyValue) const
|
fazekasgy@37
|
239 {
|
fazekasgy@37
|
240 RET list; // e.g. Vamp::Plugin::OutputList
|
fazekasgy@37
|
241 ELEM element; // e.g. Vamp::Plugin::OutputDescriptor
|
fazekasgy@37
|
242
|
fazekasgy@37
|
243 /// convert lists (ParameterList, OutputList, FeatureList)
|
fazekasgy@37
|
244 if (PyList_Check(pyValue)) {
|
fazekasgy@37
|
245 PyObject *pyDict; //This reference will be borrowed
|
fazekasgy@37
|
246 m_error = false; int errors = 0;
|
fazekasgy@37
|
247 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyValue); ++i) {
|
fazekasgy@37
|
248 //Get i-th Vamp output descriptor (Borrowed Reference)
|
fazekasgy@37
|
249 pyDict = PyList_GET_ITEM(pyValue,i);
|
fazekasgy@37
|
250 element = PyValue_To_VampDescriptor<ELEM>(pyDict);
|
fazekasgy@37
|
251 if (m_error) errors++;
|
fazekasgy@37
|
252 // Check for empty Feature/Descriptor as before?
|
fazekasgy@37
|
253 list.push_back(element);
|
fazekasgy@37
|
254 }
|
fazekasgy@37
|
255 if (errors) m_error=true;
|
fazekasgy@37
|
256 return list;
|
fazekasgy@37
|
257 }
|
fazekasgy@37
|
258
|
fazekasgy@37
|
259 /// convert other types implementing the sequence protocol
|
fazekasgy@37
|
260 if (PySequence_Check(pyValue)) {
|
fazekasgy@37
|
261 PyObject *pySequence = PySequence_Fast(pyValue,"Returned value can not be converted to list or tuple.");
|
fazekasgy@37
|
262 PyObject **pyElements = PySequence_Fast_ITEMS(pySequence);
|
fazekasgy@37
|
263 m_error = false; int errors = 0;
|
fazekasgy@37
|
264 for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(pySequence); ++i)
|
fazekasgy@37
|
265 {
|
fazekasgy@37
|
266 element = PyValue_To_VampDescriptor<ELEM>(pyElements[i]);
|
fazekasgy@37
|
267 if (m_error) errors++;
|
fazekasgy@37
|
268 list.push_back(element);
|
fazekasgy@37
|
269 }
|
fazekasgy@37
|
270 if (errors) m_error=true;
|
fazekasgy@37
|
271 Py_XDECREF(pySequence);
|
fazekasgy@37
|
272 return list;
|
fazekasgy@37
|
273 }
|
fazekasgy@37
|
274
|
fazekasgy@37
|
275 // accept None as an empty list
|
fazekasgy@37
|
276 if (pyValue == Py_None) return list;
|
fazekasgy@37
|
277
|
fazekasgy@37
|
278 // in strict mode, returning a single value is not allowed
|
fazekasgy@37
|
279 if (m_strict) {
|
fazekasgy@37
|
280 setValueError("Strict conversion error: object is not list or iterable sequence.",m_strict);
|
fazekasgy@37
|
281 return list;
|
fazekasgy@37
|
282 }
|
fazekasgy@37
|
283
|
fazekasgy@37
|
284 /// try to insert single, non-iterable values. i.e. feature <- [feature]
|
fazekasgy@37
|
285 element = PyValue_To_VampDescriptor<ELEM>(pyValue);
|
fazekasgy@37
|
286 if (m_error) {
|
fazekasgy@37
|
287 setValueError("Could not insert returned value to Vamp List.",m_strict);
|
fazekasgy@37
|
288 return list;
|
fazekasgy@37
|
289 }
|
fazekasgy@37
|
290 list.push_back(element);
|
fazekasgy@37
|
291 return list;
|
fazekasgy@37
|
292
|
fazekasgy@37
|
293 #ifdef _DEBUG
|
fazekasgy@37
|
294 cerr << "PyTypeInterface::PyValue_To_VampList failed. Expected iterable return type." << endl;
|
fazekasgy@37
|
295 #endif
|
fazekasgy@37
|
296
|
fazekasgy@37
|
297 }
|
fazekasgy@37
|
298
|
fazekasgy@37
|
299 /// Convert DTYPE type 1D NumpyArray to std::vector<RET>
|
fazekasgy@37
|
300 template<typename RET, typename DTYPE>
|
Chris@66
|
301 std::vector<RET> PyArray_Convert(void* raw_data_ptr, long length, size_t strides) const
|
fazekasgy@37
|
302 {
|
fazekasgy@37
|
303 std::vector<RET> rValue;
|
fazekasgy@37
|
304
|
fazekasgy@37
|
305 /// check if the array is continuous, if not use strides info
|
fazekasgy@37
|
306 if (sizeof(DTYPE)!=strides) {
|
fazekasgy@37
|
307 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
308 cerr << "Warning: discontinuous numpy array. Strides: " << strides << " bytes. sizeof(dtype): " << sizeof(DTYPE) << endl;
|
fazekasgy@37
|
309 #endif
|
fazekasgy@37
|
310 char* data = (char*) raw_data_ptr;
|
fazekasgy@37
|
311 for (long i = 0; i<length; ++i){
|
fazekasgy@37
|
312 rValue.push_back((RET)(*((DTYPE*)data)));
|
fazekasgy@37
|
313 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
314 cerr << "value: " << (RET)(*((DTYPE*)data)) << endl;
|
fazekasgy@37
|
315 #endif
|
fazekasgy@37
|
316 data+=strides;
|
fazekasgy@37
|
317 }
|
fazekasgy@37
|
318 return rValue;
|
fazekasgy@37
|
319 }
|
fazekasgy@37
|
320
|
fazekasgy@37
|
321 DTYPE* data = (DTYPE*) raw_data_ptr;
|
fazekasgy@37
|
322 for (long i = 0; i<length; ++i){
|
fazekasgy@37
|
323 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
324 cerr << "value: " << (RET)data[i] << endl;
|
fazekasgy@37
|
325 #endif
|
fazekasgy@37
|
326 rValue.push_back((RET)data[i]);
|
fazekasgy@37
|
327 }
|
fazekasgy@37
|
328 return rValue;
|
fazekasgy@37
|
329 }
|
fazekasgy@37
|
330
|
fazekasgy@37
|
331 /// this is a special case. numpy.float64 has an array interface but no array descriptor
|
fazekasgy@37
|
332 inline std::vector<float> PyArray0D_Convert(PyArrayInterface *ai) const
|
fazekasgy@37
|
333 {
|
fazekasgy@37
|
334 std::vector<float> rValue;
|
fazekasgy@37
|
335 if ((ai->typekind) == *"f")
|
fazekasgy@37
|
336 rValue.push_back((float)*(double*)(ai->data));
|
fazekasgy@37
|
337 else {
|
fazekasgy@37
|
338 setValueError("Unsupported NumPy data type.",m_strict);
|
fazekasgy@37
|
339 return rValue;
|
fazekasgy@37
|
340 }
|
fazekasgy@37
|
341 #ifdef _DEBUG_VALUES
|
fazekasgy@37
|
342 cerr << "value: " << rValue[0] << endl;
|
fazekasgy@37
|
343 #endif
|
fazekasgy@37
|
344 return rValue;
|
fazekasgy@37
|
345 }
|
fazekasgy@37
|
346
|
fazekasgy@37
|
347 //Vamp specific types
|
fazekasgy@37
|
348 Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
|
fazekasgy@37
|
349 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
|
fazekasgy@37
|
350 { r = this->PyValue_To_FeatureSet(pyValue); }
|
fazekasgy@37
|
351
|
fazekasgy@37
|
352 Vamp::RealTime PyValue_To_RealTime(PyObject*) const;
|
fazekasgy@37
|
353 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime &r) const
|
fazekasgy@37
|
354 { r = this->PyValue_To_RealTime(pyValue); }
|
fazekasgy@37
|
355
|
fazekasgy@37
|
356 Vamp::Plugin::OutputDescriptor::SampleType PyValue_To_SampleType(PyObject*) const;
|
fazekasgy@37
|
357
|
fazekasgy@37
|
358 Vamp::Plugin::InputDomain PyValue_To_InputDomain(PyObject*) const;
|
fazekasgy@37
|
359 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::InputDomain &r) const
|
fazekasgy@37
|
360 { r = this->PyValue_To_InputDomain(pyValue); }
|
fazekasgy@37
|
361
|
fazekasgy@37
|
362
|
fazekasgy@37
|
363 /* Overloaded PyValue_To_rValue() to support generic functions */
|
fazekasgy@37
|
364 inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const
|
fazekasgy@37
|
365 { float tmp = this->PyValue_To_Float(pyValue);
|
fazekasgy@37
|
366 if(!m_error) defValue = tmp; }
|
fazekasgy@37
|
367 inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
|
fazekasgy@37
|
368 { size_t tmp = this->PyValue_To_Size_t(pyValue);
|
fazekasgy@37
|
369 if(!m_error) defValue = tmp; }
|
fazekasgy@37
|
370 inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
|
fazekasgy@37
|
371 { bool tmp = this->PyValue_To_Bool(pyValue);
|
fazekasgy@37
|
372 if(!m_error) defValue = tmp; }
|
fazekasgy@37
|
373 inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
|
fazekasgy@37
|
374 { std::string tmp = this->PyValue_To_String(pyValue);
|
fazekasgy@37
|
375 if(!m_error) defValue = tmp; }
|
fazekasgy@37
|
376 /*used by templates where we expect no return value, if there is one it will be ignored*/
|
fazekasgy@37
|
377 inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
|
fazekasgy@37
|
378 { if (m_strict && pyValue != Py_None)
|
fazekasgy@37
|
379 setValueError("Strict conversion error: Expected 'None' type.",m_strict);
|
fazekasgy@37
|
380 }
|
fazekasgy@37
|
381
|
fazekasgy@37
|
382 /* convert sequence types to Vamp List types */
|
fazekasgy@37
|
383 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
|
fazekasgy@37
|
384 { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
|
fazekasgy@37
|
385 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
|
fazekasgy@37
|
386 { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
|
fazekasgy@37
|
387 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
|
fazekasgy@37
|
388 { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
|
fazekasgy@37
|
389
|
fazekasgy@37
|
390 /// this is only needed for RealTime->Frame conversion
|
fazekasgy@37
|
391 void setInputSampleRate(float inputSampleRate)
|
fazekasgy@37
|
392 { m_inputSampleRate = (unsigned int) inputSampleRate; }
|
fazekasgy@37
|
393
|
fazekasgy@37
|
394 private:
|
fazekasgy@37
|
395 bool m_strict;
|
fazekasgy@37
|
396 mutable bool m_error;
|
fazekasgy@37
|
397 mutable std::queue<ValueError> m_errorQueue;
|
fazekasgy@37
|
398 unsigned int m_inputSampleRate;
|
fazekasgy@51
|
399 bool m_numpyInstalled;
|
fazekasgy@37
|
400
|
fazekasgy@37
|
401 void setValueError(std::string,bool) const;
|
fazekasgy@37
|
402 ValueError& lastError() const;
|
fazekasgy@37
|
403
|
fazekasgy@37
|
404 /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
|
fazekasgy@37
|
405 inline void _convert(PyObject *pyValue,float &r) const
|
fazekasgy@37
|
406 { r = PyValue_To_Float(pyValue); }
|
fazekasgy@37
|
407 inline void _convert(PyObject *pyValue,size_t &r) const
|
fazekasgy@37
|
408 { r = PyValue_To_Size_t(pyValue); }
|
fazekasgy@37
|
409 inline void _convert(PyObject *pyValue,bool &r) const
|
fazekasgy@37
|
410 { r = PyValue_To_Bool(pyValue); }
|
fazekasgy@37
|
411 inline void _convert(PyObject *pyValue,std::string &r) const
|
fazekasgy@37
|
412 { r = PyValue_To_String(pyValue); }
|
fazekasgy@37
|
413 inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
|
fazekasgy@37
|
414 { r = PyValue_To_StringVector(pyValue); }
|
fazekasgy@37
|
415 inline void _convert(PyObject *pyValue,std::vector<float> &r) const
|
fazekasgy@37
|
416 { r = PyValue_To_FloatVector(pyValue); }
|
fazekasgy@37
|
417 inline void _convert(PyObject *pyValue,Vamp::RealTime &r) const
|
fazekasgy@37
|
418 { r = PyValue_To_RealTime(pyValue); }
|
fazekasgy@37
|
419 inline void _convert(PyObject *pyValue,Vamp::Plugin::OutputDescriptor::SampleType &r) const
|
fazekasgy@37
|
420 { r = PyValue_To_SampleType(pyValue); }
|
fazekasgy@37
|
421 // inline void _convert(PyObject *pyValue,Vamp::Plugin::InputDomain &r) const
|
fazekasgy@37
|
422 // { r = PyValue_To_InputDomain(pyValue); }
|
fazekasgy@37
|
423
|
fazekasgy@37
|
424
|
fazekasgy@37
|
425 /* Identify descriptors for error reporting */
|
fazekasgy@37
|
426 inline std::string getDescriptorId(Vamp::Plugin::OutputDescriptor d) const
|
fazekasgy@37
|
427 {return std::string("Output Descriptor '") + d.identifier +"' ";}
|
fazekasgy@37
|
428 inline std::string getDescriptorId(Vamp::Plugin::ParameterDescriptor d) const
|
fazekasgy@37
|
429 {return std::string("Parameter Descriptor '") + d.identifier +"' ";}
|
fazekasgy@37
|
430 inline std::string getDescriptorId(Vamp::Plugin::Feature f) const
|
fazekasgy@37
|
431 {return std::string("Feature (") + f.label + ")"; }
|
fazekasgy@37
|
432
|
fazekasgy@37
|
433 public:
|
fazekasgy@37
|
434 const bool& error;
|
fazekasgy@37
|
435
|
fazekasgy@37
|
436 };
|
fazekasgy@37
|
437
|
fazekasgy@37
|
438 /* Convert Sample Buffers to Python */
|
fazekasgy@37
|
439
|
fazekasgy@51
|
440 /// passing the sample buffers as builtin python lists
|
fazekasgy@37
|
441 /// Optimization: using fast sequence protocol
|
fazekasgy@37
|
442 inline PyObject*
|
fazekasgy@37
|
443 PyTypeInterface::InputBuffers_As_PythonLists(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
fazekasgy@37
|
444 {
|
fazekasgy@37
|
445 //create a list of lists (new references)
|
fazekasgy@37
|
446 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
fazekasgy@37
|
447
|
fazekasgy@37
|
448 // Pack samples into a Python List Object
|
fazekasgy@37
|
449 // pyFloat/pyComplex types will always be new references,
|
fazekasgy@37
|
450 // they will be freed when the lists are deallocated.
|
fazekasgy@37
|
451
|
fazekasgy@37
|
452 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
fazekasgy@37
|
453 for (size_t i=0; i < channels; ++i) {
|
fazekasgy@51
|
454
|
fazekasgy@51
|
455 size_t arraySize;
|
fazekasgy@47
|
456 if (dtype==Vamp::Plugin::FrequencyDomain)
|
fazekasgy@51
|
457 arraySize = (blockSize / 2) + 1; //blockSize + 2; if cplx list isn't used
|
fazekasgy@47
|
458 else
|
fazekasgy@47
|
459 arraySize = blockSize;
|
fazekasgy@51
|
460
|
fazekasgy@51
|
461 PyObject *pySampleList = PyList_New((Py_ssize_t) arraySize);
|
fazekasgy@51
|
462 PyObject **pySampleListArray = PySequence_Fast_ITEMS(pySampleList);
|
fazekasgy@37
|
463
|
fazekasgy@37
|
464 // Note: passing a complex list crashes the C-style plugin
|
fazekasgy@37
|
465 // when it tries to convert it to a numpy array directly.
|
fazekasgy@37
|
466 // This plugin will be obsolete, but we have to find a way
|
fazekasgy@51
|
467 // to prevent such crash: possibly a numpy bug,
|
fazekasgy@51
|
468 // works fine above 1.0.4
|
fazekasgy@37
|
469
|
fazekasgy@51
|
470 switch (dtype) //(Vamp::Plugin::TimeDomain)
|
fazekasgy@37
|
471 {
|
fazekasgy@37
|
472 case Vamp::Plugin::TimeDomain :
|
fazekasgy@37
|
473
|
fazekasgy@47
|
474 for (size_t j = 0; j < arraySize; ++j) {
|
fazekasgy@37
|
475 PyObject *pyFloat=PyFloat_FromDouble(
|
fazekasgy@37
|
476 (double) inputBuffers[i][j]);
|
fazekasgy@37
|
477 pySampleListArray[j] = pyFloat;
|
fazekasgy@37
|
478 }
|
fazekasgy@37
|
479 break;
|
fazekasgy@37
|
480
|
fazekasgy@37
|
481 case Vamp::Plugin::FrequencyDomain :
|
fazekasgy@37
|
482
|
fazekasgy@37
|
483 size_t k = 0;
|
fazekasgy@51
|
484 for (size_t j = 0; j < arraySize; ++j) {
|
fazekasgy@37
|
485 PyObject *pyComplex=PyComplex_FromDoubles(
|
fazekasgy@37
|
486 (double) inputBuffers[i][k],
|
fazekasgy@37
|
487 (double) inputBuffers[i][k+1]);
|
fazekasgy@37
|
488 pySampleListArray[j] = pyComplex;
|
fazekasgy@37
|
489 k += 2;
|
fazekasgy@37
|
490 }
|
fazekasgy@37
|
491 break;
|
fazekasgy@37
|
492
|
fazekasgy@37
|
493 }
|
fazekasgy@37
|
494 pyChannelListArray[i] = pySampleList;
|
fazekasgy@37
|
495 }
|
fazekasgy@37
|
496 return pyChannelList;
|
fazekasgy@37
|
497 }
|
fazekasgy@37
|
498
|
fazekasgy@37
|
499 /// numpy buffer interface: passing the sample buffers as shared memory buffers
|
fazekasgy@37
|
500 /// Optimization: using sequence protocol for creating the buffer list
|
fazekasgy@37
|
501 inline PyObject*
|
fazekasgy@47
|
502 PyTypeInterface::InputBuffers_As_SharedMemoryList(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
fazekasgy@37
|
503 {
|
fazekasgy@37
|
504 //create a list of buffers (returns new references)
|
fazekasgy@37
|
505 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
fazekasgy@37
|
506 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
fazekasgy@37
|
507
|
fazekasgy@37
|
508 // Expose memory using the Buffer Interface.
|
fazekasgy@37
|
509 // This will pass a pointer which can be recasted in Python code
|
fazekasgy@37
|
510 // as complex or float array using Numpy's frombuffer() method
|
fazekasgy@37
|
511 // (this will not copy values just keep the starting adresses
|
fazekasgy@37
|
512 // for each channel in a list)
|
fazekasgy@47
|
513 Py_ssize_t bufferSize;
|
fazekasgy@47
|
514
|
fazekasgy@47
|
515 if (dtype==Vamp::Plugin::FrequencyDomain)
|
fazekasgy@47
|
516 bufferSize = (Py_ssize_t) sizeof(float) * (blockSize+2);
|
fazekasgy@47
|
517 else
|
fazekasgy@47
|
518 bufferSize = (Py_ssize_t) sizeof(float) * blockSize;
|
fazekasgy@47
|
519
|
fazekasgy@37
|
520 for (size_t i=0; i < channels; ++i) {
|
fazekasgy@37
|
521 PyObject *pyBuffer = PyBuffer_FromMemory
|
fazekasgy@37
|
522 ((void *) (float *) inputBuffers[i],bufferSize);
|
fazekasgy@37
|
523 pyChannelListArray[i] = pyBuffer;
|
fazekasgy@37
|
524 }
|
fazekasgy@37
|
525 return pyChannelList;
|
fazekasgy@37
|
526 }
|
fazekasgy@37
|
527
|
fazekasgy@37
|
528
|
fazekasgy@37
|
529 /// numpy array interface: passing the sample buffers as 2D numpy array
|
fazekasgy@37
|
530 /// Optimization: using array API (needs numpy headers)
|
fazekasgy@37
|
531 #ifdef HAVE_NUMPY
|
fazekasgy@37
|
532 inline PyObject*
|
fazekasgy@37
|
533 PyTypeInterface::InputBuffers_As_NumpyArray(const float *const *inputBuffers,const size_t& channels, const size_t& blockSize, const Vamp::Plugin::InputDomain& dtype)
|
fazekasgy@37
|
534 {
|
fazekasgy@37
|
535 /*
|
fazekasgy@37
|
536 NOTE: We create a list of 1D Numpy arrays for each channel instead
|
fazekasgy@37
|
537 of a matrix, because the address space of inputBuffers doesn't seem
|
fazekasgy@37
|
538 to be continuous. Although the array strides could be calculated for
|
fazekasgy@37
|
539 2 channels (i.e. inputBuffers[1] - inputBuffers[0]) i'm not sure
|
fazekasgy@37
|
540 if this can be trusted, especially for more than 2 channels.
|
fazekasgy@37
|
541
|
fazekasgy@37
|
542 cerr << "First channel: " << inputBuffers[0][0] << " address: " << inputBuffers[0] << endl;
|
fazekasgy@37
|
543 if (channels == 2)
|
fazekasgy@37
|
544 cerr << "Second channel: " << inputBuffers[1][0] << " address: " << inputBuffers[1] << endl;
|
fazekasgy@37
|
545
|
fazekasgy@37
|
546 */
|
fazekasgy@37
|
547
|
fazekasgy@37
|
548 // create a list of arrays (returns new references)
|
fazekasgy@37
|
549 PyObject *pyChannelList = PyList_New((Py_ssize_t) channels);
|
fazekasgy@37
|
550 PyObject **pyChannelListArray = PySequence_Fast_ITEMS(pyChannelList);
|
fazekasgy@37
|
551
|
fazekasgy@37
|
552 // Expose memory using the Numpy Array Interface.
|
fazekasgy@37
|
553 // This will wrap an array objects around the data.
|
fazekasgy@37
|
554 // (will not copy values just steal the starting adresses)
|
fazekasgy@37
|
555
|
fazekasgy@37
|
556 int arraySize, typenum;
|
fazekasgy@37
|
557
|
fazekasgy@37
|
558 switch (dtype)
|
fazekasgy@37
|
559 {
|
fazekasgy@37
|
560 case Vamp::Plugin::TimeDomain :
|
fazekasgy@37
|
561 typenum = dtype_float32; //NPY_FLOAT;
|
fazekasgy@37
|
562 arraySize = (int) blockSize;
|
fazekasgy@37
|
563 break;
|
fazekasgy@37
|
564
|
fazekasgy@37
|
565 case Vamp::Plugin::FrequencyDomain :
|
fazekasgy@37
|
566 typenum = dtype_complex64; //NPY_CFLOAT;
|
fazekasgy@47
|
567 arraySize = (int) (blockSize / 2) + 1;
|
fazekasgy@37
|
568 break;
|
fazekasgy@37
|
569
|
fazekasgy@37
|
570 default :
|
fazekasgy@37
|
571 cerr << "PyTypeInterface::InputBuffers_As_NumpyArray: Error: Unsupported numpy array data type." << endl;
|
fazekasgy@37
|
572 return pyChannelList;
|
fazekasgy@37
|
573 }
|
fazekasgy@37
|
574
|
fazekasgy@37
|
575 // size for each dimension
|
fazekasgy@37
|
576 npy_intp ndims[1]={arraySize};
|
fazekasgy@37
|
577
|
fazekasgy@37
|
578 for (size_t i=0; i < channels; ++i) {
|
fazekasgy@37
|
579 PyObject *pyChannelArray =
|
fazekasgy@37
|
580 //args: (dimensions, size in each dim, type kind, pointer to continuous array)
|
fazekasgy@37
|
581 PyArray_SimpleNewFromData(1, ndims, typenum, (void*) inputBuffers[i]);
|
Chris@66
|
582 // make it read-only: set all flags to false except NPY_C_CONTIGUOUS
|
Chris@66
|
583 //!!! what about NPY_ARRAY_OWNDATA?
|
Chris@66
|
584 PyArray_CLEARFLAGS((PyArrayObject *)pyChannelArray, 0xff);
|
Chris@66
|
585 PyArray_ENABLEFLAGS((PyArrayObject *)pyChannelArray, NPY_ARRAY_C_CONTIGUOUS);
|
fazekasgy@37
|
586 pyChannelListArray[i] = pyChannelArray;
|
fazekasgy@37
|
587 }
|
fazekasgy@37
|
588 return pyChannelList;
|
fazekasgy@37
|
589 }
|
fazekasgy@37
|
590 #endif
|
fazekasgy@37
|
591
|
fazekasgy@37
|
592
|
fazekasgy@37
|
593
|
fazekasgy@37
|
594 #ifdef NUMPY_REFERENCE
|
fazekasgy@37
|
595 /// This should be all we need to compile without direct dependency,
|
fazekasgy@37
|
596 /// but we don't do that. (it may not work on some platforms)
|
fazekasgy@37
|
597 typedef struct {
|
fazekasgy@37
|
598 int two; /* contains the integer 2 -- simple sanity check */
|
fazekasgy@37
|
599 int nd; /* number of dimensions */
|
fazekasgy@37
|
600 char typekind; /* kind in array --- character code of typestr */
|
fazekasgy@37
|
601 int itemsize; /* size of each element */
|
fazekasgy@37
|
602 int flags; /* flags indicating how the data should be interpreted */
|
fazekasgy@37
|
603 /* must set ARR_HAS_DESCR bit to validate descr */
|
fazekasgy@37
|
604 Py_intptr_t *shape; /* A length-nd array of shape information */
|
fazekasgy@37
|
605 Py_intptr_t *strides; /* A length-nd array of stride information */
|
fazekasgy@37
|
606 void *data; /* A pointer to the first element of the array */
|
fazekasgy@37
|
607 PyObject *descr; /* NULL or data-description (same as descr key */
|
fazekasgy@37
|
608 /* of __array_interface__) -- must set ARR_HAS_DESCR */
|
fazekasgy@37
|
609 /* flag or this will be ignored. */
|
fazekasgy@37
|
610 } PyArrayInterface;
|
fazekasgy@37
|
611
|
fazekasgy@37
|
612 typedef struct PyArrayObject {
|
fazekasgy@37
|
613 PyObject_HEAD
|
fazekasgy@37
|
614 char *data; /* pointer to raw data buffer */
|
fazekasgy@37
|
615 int nd; /* number of dimensions, also called ndim */
|
fazekasgy@37
|
616 npy_intp *dimensions; /* size in each dimension */
|
fazekasgy@37
|
617 npy_intp *strides; /* bytes to jump to get to the
|
fazekasgy@37
|
618 next element in each dimension */
|
fazekasgy@37
|
619 PyObject *base; /* This object should be decref'd
|
fazekasgy@37
|
620 upon deletion of array */
|
fazekasgy@37
|
621 /* For views it points to the original array */
|
fazekasgy@37
|
622 /* For creation from buffer object it points
|
fazekasgy@37
|
623 to an object that shold be decref'd on
|
fazekasgy@37
|
624 deletion */
|
fazekasgy@37
|
625 /* For UPDATEIFCOPY flag this is an array
|
fazekasgy@37
|
626 to-be-updated upon deletion of this one */
|
fazekasgy@37
|
627 PyArray_Descr *descr; /* Pointer to type structure */
|
fazekasgy@37
|
628 int flags; /* Flags describing array -- see below*/
|
fazekasgy@37
|
629 PyObject *weakreflist; /* For weakreferences */
|
fazekasgy@37
|
630 } PyArrayObject;
|
fazekasgy@37
|
631
|
fazekasgy@37
|
632 typedef struct _PyArray_Descr {
|
fazekasgy@37
|
633 PyObject_HEAD
|
fazekasgy@37
|
634 PyTypeObject *typeobj; /* the type object representing an
|
fazekasgy@37
|
635 instance of this type -- should not
|
fazekasgy@37
|
636 be two type_numbers with the same type
|
fazekasgy@37
|
637 object. */
|
fazekasgy@37
|
638 char kind; /* kind for this type */
|
fazekasgy@37
|
639 char type; /* unique-character representing this type */
|
fazekasgy@37
|
640 char byteorder; /* '>' (big), '<' (little), '|'
|
fazekasgy@37
|
641 (not-applicable), or '=' (native). */
|
fazekasgy@37
|
642 char hasobject; /* non-zero if it has object arrays
|
fazekasgy@37
|
643 in fields */
|
fazekasgy@37
|
644 int type_num; /* number representing this type */
|
fazekasgy@37
|
645 int elsize; /* element size for this type */
|
fazekasgy@37
|
646 int alignment; /* alignment needed for this type */
|
fazekasgy@37
|
647 struct _arr_descr \
|
fazekasgy@37
|
648 *subarray; /* Non-NULL if this type is
|
fazekasgy@37
|
649 is an array (C-contiguous)
|
fazekasgy@37
|
650 of some other type
|
fazekasgy@37
|
651 */
|
fazekasgy@37
|
652 PyObject *fields; /* The fields dictionary for this type */
|
fazekasgy@37
|
653 /* For statically defined descr this
|
fazekasgy@37
|
654 is always Py_None */
|
fazekasgy@37
|
655
|
fazekasgy@37
|
656 PyObject *names; /* An ordered tuple of field names or NULL
|
fazekasgy@37
|
657 if no fields are defined */
|
fazekasgy@37
|
658
|
fazekasgy@37
|
659 PyArray_ArrFuncs *f; /* a table of functions specific for each
|
fazekasgy@37
|
660 basic data descriptor */
|
fazekasgy@37
|
661 } PyArray_Descr;
|
fazekasgy@37
|
662
|
fazekasgy@37
|
663 enum NPY_TYPES { NPY_BOOL=0,
|
fazekasgy@37
|
664 NPY_BYTE, NPY_UBYTE,
|
fazekasgy@37
|
665 NPY_SHORT, NPY_USHORT,
|
fazekasgy@37
|
666 NPY_INT, NPY_UINT,
|
fazekasgy@37
|
667 NPY_LONG, NPY_ULONG,
|
fazekasgy@37
|
668 NPY_LONGLONG, NPY_ULONGLONG,
|
fazekasgy@37
|
669 NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE,
|
fazekasgy@37
|
670 NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE,
|
fazekasgy@37
|
671 NPY_OBJECT=17,
|
fazekasgy@37
|
672 NPY_STRING, NPY_UNICODE,
|
fazekasgy@37
|
673 NPY_VOID,
|
fazekasgy@37
|
674 NPY_NTYPES,
|
fazekasgy@37
|
675 NPY_NOTYPE,
|
fazekasgy@37
|
676 NPY_CHAR, /* special flag */
|
fazekasgy@37
|
677 NPY_USERDEF=256 /* leave room for characters */
|
fazekasgy@37
|
678 };
|
fazekasgy@37
|
679 #endif /*NUMPY_REFERENCE*/
|
fazekasgy@37
|
680 #endif
|