Chris@26
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@26
|
2
|
Chris@26
|
3 /*
|
Chris@28
|
4 VampyHost
|
Chris@26
|
5
|
Chris@28
|
6 Use Vamp audio analysis plugins in Python
|
Chris@26
|
7
|
Chris@28
|
8 Gyorgy Fazekas and Chris Cannam
|
Chris@28
|
9 Centre for Digital Music, Queen Mary, University of London
|
Chris@28
|
10 Copyright 2008-2014 Queen Mary, University of London
|
Chris@26
|
11
|
Chris@28
|
12 Permission is hereby granted, free of charge, to any person
|
Chris@28
|
13 obtaining a copy of this software and associated documentation
|
Chris@28
|
14 files (the "Software"), to deal in the Software without
|
Chris@28
|
15 restriction, including without limitation the rights to use, copy,
|
Chris@28
|
16 modify, merge, publish, distribute, sublicense, and/or sell copies
|
Chris@28
|
17 of the Software, and to permit persons to whom the Software is
|
Chris@28
|
18 furnished to do so, subject to the following conditions:
|
Chris@26
|
19
|
Chris@28
|
20 The above copyright notice and this permission notice shall be
|
Chris@28
|
21 included in all copies or substantial portions of the Software.
|
Chris@26
|
22
|
Chris@28
|
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
Chris@28
|
24 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
Chris@28
|
25 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
Chris@28
|
26 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
Chris@28
|
27 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
Chris@28
|
28 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
Chris@28
|
29 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Chris@26
|
30
|
Chris@28
|
31 Except as contained in this notice, the names of the Centre for
|
Chris@28
|
32 Digital Music; Queen Mary, University of London; and the authors
|
Chris@28
|
33 shall not be used in advertising or otherwise to promote the sale,
|
Chris@28
|
34 use or other dealings in this Software without prior written
|
Chris@28
|
35 authorization.
|
Chris@26
|
36 */
|
Chris@26
|
37
|
Chris@26
|
38 #include <Python.h>
|
Chris@26
|
39
|
Chris@29
|
40 #include "VectorConversion.h"
|
Chris@26
|
41
|
Chris@26
|
42 #include <math.h>
|
Chris@26
|
43 #include <float.h>
|
Chris@26
|
44
|
Chris@28
|
45 using namespace std;
|
Chris@26
|
46
|
Chris@26
|
47 /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
|
Chris@28
|
48 (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */
|
Chris@26
|
49
|
Chris@29
|
50 VectorConversion::VectorConversion() :
|
Chris@28
|
51 m_error(false),
|
Chris@28
|
52 error(m_error) // const public reference for easy access
|
Chris@26
|
53 {
|
Chris@26
|
54 }
|
Chris@26
|
55
|
Chris@29
|
56 VectorConversion::~VectorConversion()
|
Chris@26
|
57 {
|
Chris@26
|
58 }
|
Chris@26
|
59
|
Chris@26
|
60 /// floating point numbers (TODO: check numpy.float128)
|
Chris@26
|
61 float
|
Chris@29
|
62 VectorConversion::PyValue_To_Float(PyObject* pyValue) const
|
Chris@26
|
63 {
|
Chris@28
|
64 // convert float
|
Chris@28
|
65 if (pyValue && PyFloat_Check(pyValue))
|
Chris@28
|
66 //TODO: check for limits here (same on most systems)
|
Chris@28
|
67 return (float) PyFloat_AS_DOUBLE(pyValue);
|
Chris@26
|
68
|
Chris@28
|
69 if (pyValue == NULL)
|
Chris@28
|
70 {
|
Chris@28
|
71 setValueError("Error while converting object " + PyValue_Get_TypeName(pyValue) + " to float. ");
|
Chris@28
|
72 return 0.0;
|
Chris@28
|
73 }
|
Chris@26
|
74
|
Chris@28
|
75 setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float.");
|
Chris@28
|
76 return 0.0;
|
Chris@26
|
77 }
|
Chris@26
|
78
|
Chris@28
|
79 vector<float>
|
Chris@29
|
80 VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const
|
Chris@26
|
81 {
|
Chris@28
|
82 /// numpy array
|
Chris@28
|
83 if (PyArray_CheckExact(pyValue))
|
Chris@28
|
84 return PyArray_To_FloatVector(pyValue);
|
Chris@26
|
85
|
Chris@28
|
86 /// python list of floats (backward compatible)
|
Chris@28
|
87 if (PyList_Check(pyValue)) {
|
Chris@28
|
88 return PyList_To_FloatVector(pyValue);
|
Chris@28
|
89 }
|
Chris@26
|
90
|
Chris@28
|
91 string msg = "Value is not list or array of floats";
|
Chris@28
|
92 setValueError(msg);
|
Chris@26
|
93 #ifdef _DEBUG
|
Chris@29
|
94 cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl;
|
Chris@26
|
95 #endif
|
Chris@28
|
96 return vector<float>();
|
Chris@26
|
97 }
|
Chris@26
|
98
|
Chris@28
|
99 vector<float>
|
Chris@29
|
100 VectorConversion::PyList_To_FloatVector (PyObject *inputList) const
|
Chris@26
|
101 {
|
Chris@28
|
102 vector<float> v;
|
Chris@26
|
103
|
Chris@28
|
104 if (!PyList_Check(inputList)) {
|
Chris@28
|
105 setValueError("Value is not a list");
|
Chris@28
|
106 return v;
|
Chris@28
|
107 }
|
Chris@26
|
108
|
Chris@28
|
109 PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList);
|
Chris@28
|
110 int n = PyList_GET_SIZE(inputList);
|
Chris@26
|
111
|
Chris@28
|
112 for (int i = 0; i < n; ++i) {
|
Chris@28
|
113 v.push_back(PyValue_To_Float(pyObjectArray[i]));
|
Chris@28
|
114 }
|
Chris@28
|
115
|
Chris@28
|
116 return v;
|
Chris@26
|
117 }
|
Chris@26
|
118
|
Chris@28
|
119 vector<float>
|
Chris@29
|
120 VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const
|
Chris@26
|
121 {
|
Chris@28
|
122 vector<float> v;
|
Chris@26
|
123
|
Chris@28
|
124 if (!PyArray_Check(pyValue)) {
|
Chris@28
|
125 setValueError("Value is not an array");
|
Chris@28
|
126 return v;
|
Chris@28
|
127 }
|
Chris@28
|
128
|
Chris@28
|
129 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
|
Chris@28
|
130 PyArray_Descr* descr = PyArray_DESCR(pyArray);
|
Chris@28
|
131
|
Chris@28
|
132 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
|
Chris@28
|
133 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
|
Chris@28
|
134 setValueError(msg);
|
Chris@28
|
135 return v;
|
Chris@28
|
136 }
|
Chris@28
|
137
|
Chris@28
|
138 if (PyArray_NDIM(pyArray) != 1) {
|
Chris@40
|
139 string msg = "NumPy array must be a one-dimensional vector.";
|
Chris@28
|
140 setValueError(msg);
|
Chris@28
|
141 return v;
|
Chris@28
|
142 }
|
Chris@28
|
143
|
Chris@28
|
144 /// check strides (useful if array is not continuous)
|
Chris@28
|
145 size_t strides = *((size_t*) PyArray_STRIDES(pyArray));
|
Chris@28
|
146
|
Chris@28
|
147 /// convert the array
|
Chris@28
|
148 switch (descr->type_num) {
|
Chris@28
|
149
|
Chris@28
|
150 case NPY_FLOAT : // dtype='float32'
|
Chris@28
|
151 return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
152 case NPY_DOUBLE : // dtype='float64'
|
Chris@28
|
153 return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
154 case NPY_INT : // dtype='int'
|
Chris@28
|
155 return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
156 case NPY_LONG : // dtype='long'
|
Chris@28
|
157 return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
158 default :
|
Chris@28
|
159 string msg = "Unsupported value type in NumPy array object.";
|
Chris@28
|
160 setValueError(msg);
|
Chris@26
|
161 #ifdef _DEBUG
|
Chris@29
|
162 cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl;
|
Chris@26
|
163 #endif
|
Chris@28
|
164 return v;
|
Chris@28
|
165 }
|
Chris@26
|
166 }
|
Chris@26
|
167
|
Chris@40
|
168 vector<vector<float> >
|
Chris@40
|
169 VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const
|
Chris@40
|
170 {
|
Chris@40
|
171 vector<vector<float> > v;
|
Chris@40
|
172
|
Chris@40
|
173 if (!PyArray_Check(pyValue)) {
|
Chris@40
|
174 setValueError("Value is not an array");
|
Chris@40
|
175 return v;
|
Chris@40
|
176 }
|
Chris@40
|
177
|
Chris@40
|
178 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
|
Chris@40
|
179 PyArray_Descr* descr = PyArray_DESCR(pyArray);
|
Chris@40
|
180
|
Chris@40
|
181 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
|
Chris@40
|
182 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
|
Chris@40
|
183 setValueError(msg);
|
Chris@40
|
184 return v;
|
Chris@40
|
185 }
|
Chris@40
|
186
|
Chris@40
|
187 if (PyArray_NDIM(pyArray) != 2) {
|
Chris@40
|
188 string msg = "NumPy array must be a two-dimensional matrix.";
|
Chris@40
|
189 setValueError(msg);
|
Chris@40
|
190 return v;
|
Chris@40
|
191 }
|
Chris@40
|
192
|
Chris@40
|
193 /// check strides (useful if array is not continuous)
|
Chris@41
|
194 size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray);
|
Chris@40
|
195
|
Chris@40
|
196 /// convert the array
|
Chris@40
|
197 for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) {
|
Chris@40
|
198
|
Chris@40
|
199 vector<float> vv;
|
Chris@40
|
200
|
Chris@40
|
201 switch (descr->type_num) {
|
Chris@40
|
202
|
Chris@40
|
203 case NPY_FLOAT : // dtype='float32'
|
Chris@41
|
204 vv = PyArray_Convert<float,float>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
205 break;
|
Chris@40
|
206 case NPY_DOUBLE : // dtype='float64'
|
Chris@41
|
207 vv = PyArray_Convert<float,double>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
208 break;
|
Chris@40
|
209 case NPY_INT : // dtype='int'
|
Chris@41
|
210 vv = PyArray_Convert<float,int>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
211 break;
|
Chris@40
|
212 case NPY_LONG : // dtype='long'
|
Chris@41
|
213 vv = PyArray_Convert<float,long>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
214 break;
|
Chris@40
|
215 default :
|
Chris@40
|
216 string msg = "Unsupported value type in NumPy array object.";
|
Chris@40
|
217 cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl;
|
Chris@40
|
218 setValueError(msg);
|
Chris@40
|
219 return v;
|
Chris@40
|
220 }
|
Chris@40
|
221
|
Chris@40
|
222 v.push_back(vv);
|
Chris@40
|
223 }
|
Chris@40
|
224
|
Chris@40
|
225 return v;
|
Chris@40
|
226 }
|
Chris@40
|
227
|
Chris@26
|
228 PyObject *
|
Chris@29
|
229 VectorConversion::PyArray_From_FloatVector(const vector<float> &v) const
|
Chris@26
|
230 {
|
Chris@28
|
231 npy_intp ndims[1];
|
Chris@28
|
232 ndims[0] = (int)v.size();
|
Chris@28
|
233 PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT);
|
Chris@28
|
234 float *data = (float *)PyArray_DATA((PyArrayObject *)arr);
|
Chris@28
|
235 for (int i = 0; i < ndims[0]; ++i) {
|
Chris@28
|
236 data[i] = v[i];
|
Chris@28
|
237 }
|
Chris@28
|
238 return arr;
|
Chris@26
|
239 }
|
Chris@26
|
240
|
Chris@26
|
241 PyObject *
|
Chris@29
|
242 VectorConversion::PyValue_From_StringVector(const vector<string> &v) const
|
Chris@26
|
243 {
|
Chris@28
|
244 PyObject *pyList = PyList_New(v.size());
|
Chris@28
|
245 for (size_t i = 0; i < v.size(); ++i) {
|
Chris@28
|
246 PyObject *pyStr = PyString_FromString(v[i].c_str());
|
Chris@28
|
247 PyList_SET_ITEM(pyList, i, pyStr);
|
Chris@28
|
248 }
|
Chris@28
|
249 return pyList;
|
Chris@26
|
250 }
|
Chris@26
|
251
|
Chris@26
|
252
|
Chris@28
|
253 /* Error handling */
|
Chris@26
|
254
|
Chris@26
|
255 void
|
Chris@29
|
256 VectorConversion::setValueError (string message) const
|
Chris@26
|
257 {
|
Chris@28
|
258 m_error = true;
|
Chris@28
|
259 m_errorQueue.push(ValueError(message));
|
Chris@26
|
260 }
|
Chris@26
|
261
|
Chris@26
|
262 /// return a reference to the last error or creates a new one.
|
Chris@26
|
263 ValueError&
|
Chris@29
|
264 VectorConversion::lastError() const
|
Chris@26
|
265 {
|
Chris@28
|
266 m_error = false;
|
Chris@28
|
267 if (!m_errorQueue.empty()) return m_errorQueue.back();
|
Chris@28
|
268 else {
|
Chris@28
|
269 m_errorQueue.push(ValueError("Type conversion error."));
|
Chris@28
|
270 return m_errorQueue.back();
|
Chris@28
|
271 }
|
Chris@26
|
272 }
|
Chris@26
|
273
|
Chris@26
|
274 /// helper function to iterate over the error message queue:
|
Chris@26
|
275 /// pops the oldest item
|
Chris@26
|
276 ValueError
|
Chris@29
|
277 VectorConversion::getError() const
|
Chris@26
|
278 {
|
Chris@28
|
279 if (!m_errorQueue.empty()) {
|
Chris@28
|
280 ValueError e = m_errorQueue.front();
|
Chris@28
|
281 m_errorQueue.pop();
|
Chris@28
|
282 if (m_errorQueue.empty()) m_error = false;
|
Chris@28
|
283 return e;
|
Chris@28
|
284 }
|
Chris@28
|
285 else {
|
Chris@28
|
286 m_error = false;
|
Chris@28
|
287 return ValueError();
|
Chris@28
|
288 }
|
Chris@26
|
289 }
|
Chris@26
|
290
|
Chris@28
|
291 /* Utilities */
|
Chris@26
|
292
|
Chris@26
|
293 /// get the type name of an object
|
Chris@28
|
294 string
|
Chris@29
|
295 VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const
|
Chris@26
|
296 {
|
Chris@28
|
297 PyObject *pyType = PyObject_Type(pyValue);
|
Chris@28
|
298 if (!pyType)
|
Chris@28
|
299 {
|
Chris@28
|
300 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
301 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
302 return string ("< unknown type >");
|
Chris@28
|
303 }
|
Chris@28
|
304 PyObject *pyString = PyObject_Str(pyType);
|
Chris@28
|
305 if (!pyString)
|
Chris@28
|
306 {
|
Chris@28
|
307 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
308 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
309 Py_CLEAR(pyType);
|
Chris@28
|
310 return string ("< unknown type >");
|
Chris@28
|
311 }
|
Chris@28
|
312 char *cstr = PyString_AS_STRING(pyString);
|
Chris@28
|
313 if (!cstr)
|
Chris@28
|
314 {
|
Chris@28
|
315 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
316 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
317 Py_DECREF(pyType);
|
Chris@28
|
318 Py_CLEAR(pyString);
|
Chris@28
|
319 return string("< unknown type >");
|
Chris@28
|
320 }
|
Chris@28
|
321 Py_DECREF(pyType);
|
Chris@28
|
322 Py_DECREF(pyString);
|
Chris@28
|
323 return string(cstr);
|
Chris@26
|
324 }
|