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@48
|
40 #include "FloatConversion.h"
|
Chris@29
|
41 #include "VectorConversion.h"
|
Chris@26
|
42
|
Chris@26
|
43 #include <math.h>
|
Chris@26
|
44 #include <float.h>
|
Chris@26
|
45
|
Chris@28
|
46 using namespace std;
|
Chris@26
|
47
|
Chris@26
|
48 /* Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
|
Chris@28
|
49 (EXCEPT FOR TEMPORARY PYTHON OBJECTS)! */
|
Chris@26
|
50
|
Chris@29
|
51 VectorConversion::VectorConversion() :
|
Chris@28
|
52 m_error(false),
|
Chris@28
|
53 error(m_error) // const public reference for easy access
|
Chris@26
|
54 {
|
Chris@26
|
55 }
|
Chris@26
|
56
|
Chris@29
|
57 VectorConversion::~VectorConversion()
|
Chris@26
|
58 {
|
Chris@26
|
59 }
|
Chris@26
|
60
|
Chris@26
|
61 /// floating point numbers (TODO: check numpy.float128)
|
Chris@26
|
62 float
|
Chris@29
|
63 VectorConversion::PyValue_To_Float(PyObject* pyValue) const
|
Chris@26
|
64 {
|
Chris@48
|
65 if (FloatConversion::check(pyValue)) {
|
Chris@48
|
66 return FloatConversion::convert(pyValue);
|
Chris@43
|
67 }
|
Chris@43
|
68
|
Chris@43
|
69 setValueError("Conversion error: object" + PyValue_Get_TypeName(pyValue) +" is not float, int, or long.");
|
Chris@28
|
70 return 0.0;
|
Chris@26
|
71 }
|
Chris@26
|
72
|
Chris@28
|
73 vector<float>
|
Chris@29
|
74 VectorConversion::PyValue_To_FloatVector (PyObject *pyValue) const
|
Chris@26
|
75 {
|
Chris@28
|
76 /// numpy array
|
Chris@28
|
77 if (PyArray_CheckExact(pyValue))
|
Chris@28
|
78 return PyArray_To_FloatVector(pyValue);
|
Chris@26
|
79
|
Chris@28
|
80 /// python list of floats (backward compatible)
|
Chris@28
|
81 if (PyList_Check(pyValue)) {
|
Chris@28
|
82 return PyList_To_FloatVector(pyValue);
|
Chris@28
|
83 }
|
Chris@26
|
84
|
Chris@28
|
85 string msg = "Value is not list or array of floats";
|
Chris@28
|
86 setValueError(msg);
|
Chris@26
|
87 #ifdef _DEBUG
|
Chris@29
|
88 cerr << "VectorConversion::PyValue_To_FloatVector failed. " << msg << endl;
|
Chris@26
|
89 #endif
|
Chris@28
|
90 return vector<float>();
|
Chris@26
|
91 }
|
Chris@26
|
92
|
Chris@28
|
93 vector<float>
|
Chris@29
|
94 VectorConversion::PyList_To_FloatVector (PyObject *inputList) const
|
Chris@26
|
95 {
|
Chris@28
|
96 vector<float> v;
|
Chris@26
|
97
|
Chris@28
|
98 if (!PyList_Check(inputList)) {
|
Chris@28
|
99 setValueError("Value is not a list");
|
Chris@28
|
100 return v;
|
Chris@28
|
101 }
|
Chris@26
|
102
|
Chris@28
|
103 PyObject **pyObjectArray = PySequence_Fast_ITEMS(inputList);
|
Chris@28
|
104 int n = PyList_GET_SIZE(inputList);
|
Chris@26
|
105
|
Chris@28
|
106 for (int i = 0; i < n; ++i) {
|
Chris@28
|
107 v.push_back(PyValue_To_Float(pyObjectArray[i]));
|
Chris@28
|
108 }
|
Chris@28
|
109
|
Chris@28
|
110 return v;
|
Chris@26
|
111 }
|
Chris@26
|
112
|
Chris@28
|
113 vector<float>
|
Chris@29
|
114 VectorConversion::PyArray_To_FloatVector (PyObject *pyValue) const
|
Chris@26
|
115 {
|
Chris@28
|
116 vector<float> v;
|
Chris@26
|
117
|
Chris@28
|
118 if (!PyArray_Check(pyValue)) {
|
Chris@28
|
119 setValueError("Value is not an array");
|
Chris@28
|
120 return v;
|
Chris@28
|
121 }
|
Chris@28
|
122
|
Chris@28
|
123 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
|
Chris@28
|
124 PyArray_Descr* descr = PyArray_DESCR(pyArray);
|
Chris@28
|
125
|
Chris@28
|
126 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
|
Chris@28
|
127 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
|
Chris@28
|
128 setValueError(msg);
|
Chris@28
|
129 return v;
|
Chris@28
|
130 }
|
Chris@28
|
131
|
Chris@28
|
132 if (PyArray_NDIM(pyArray) != 1) {
|
Chris@40
|
133 string msg = "NumPy array must be a one-dimensional vector.";
|
Chris@28
|
134 setValueError(msg);
|
Chris@28
|
135 return v;
|
Chris@28
|
136 }
|
Chris@28
|
137
|
Chris@28
|
138 /// check strides (useful if array is not continuous)
|
Chris@28
|
139 size_t strides = *((size_t*) PyArray_STRIDES(pyArray));
|
Chris@28
|
140
|
Chris@28
|
141 /// convert the array
|
Chris@28
|
142 switch (descr->type_num) {
|
Chris@28
|
143
|
Chris@28
|
144 case NPY_FLOAT : // dtype='float32'
|
Chris@28
|
145 return PyArray_Convert<float,float>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
146 case NPY_DOUBLE : // dtype='float64'
|
Chris@28
|
147 return PyArray_Convert<float,double>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
148 case NPY_INT : // dtype='int'
|
Chris@28
|
149 return PyArray_Convert<float,int>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
150 case NPY_LONG : // dtype='long'
|
Chris@28
|
151 return PyArray_Convert<float,long>(PyArray_DATA(pyArray),PyArray_DIMS(pyArray)[0],strides);
|
Chris@28
|
152 default :
|
Chris@28
|
153 string msg = "Unsupported value type in NumPy array object.";
|
Chris@28
|
154 setValueError(msg);
|
Chris@26
|
155 #ifdef _DEBUG
|
Chris@29
|
156 cerr << "VectorConversion::PyArray_To_FloatVector failed. Error: " << msg << endl;
|
Chris@26
|
157 #endif
|
Chris@28
|
158 return v;
|
Chris@28
|
159 }
|
Chris@26
|
160 }
|
Chris@26
|
161
|
Chris@40
|
162 vector<vector<float> >
|
Chris@40
|
163 VectorConversion::Py2DArray_To_FloatVector (PyObject *pyValue) const
|
Chris@40
|
164 {
|
Chris@40
|
165 vector<vector<float> > v;
|
Chris@40
|
166
|
Chris@40
|
167 if (!PyArray_Check(pyValue)) {
|
Chris@40
|
168 setValueError("Value is not an array");
|
Chris@40
|
169 return v;
|
Chris@40
|
170 }
|
Chris@40
|
171
|
Chris@40
|
172 PyArrayObject* pyArray = (PyArrayObject*) pyValue;
|
Chris@40
|
173 PyArray_Descr* descr = PyArray_DESCR(pyArray);
|
Chris@40
|
174
|
Chris@40
|
175 if (PyArray_DATA(pyArray) == 0 || descr == 0) {
|
Chris@40
|
176 string msg = "NumPy array with NULL data or descriptor pointer encountered.";
|
Chris@40
|
177 setValueError(msg);
|
Chris@40
|
178 return v;
|
Chris@40
|
179 }
|
Chris@40
|
180
|
Chris@40
|
181 if (PyArray_NDIM(pyArray) != 2) {
|
Chris@40
|
182 string msg = "NumPy array must be a two-dimensional matrix.";
|
Chris@40
|
183 setValueError(msg);
|
Chris@40
|
184 return v;
|
Chris@40
|
185 }
|
Chris@40
|
186
|
Chris@40
|
187 /// check strides (useful if array is not continuous)
|
Chris@41
|
188 size_t *strideptr = (size_t*) PyArray_STRIDES(pyArray);
|
Chris@40
|
189
|
Chris@40
|
190 /// convert the array
|
Chris@40
|
191 for (int i = 0; i < PyArray_DIMS(pyArray)[0]; ++i) {
|
Chris@40
|
192
|
Chris@40
|
193 vector<float> vv;
|
Chris@40
|
194
|
Chris@40
|
195 switch (descr->type_num) {
|
Chris@40
|
196
|
Chris@40
|
197 case NPY_FLOAT : // dtype='float32'
|
Chris@41
|
198 vv = PyArray_Convert<float,float>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
199 break;
|
Chris@40
|
200 case NPY_DOUBLE : // dtype='float64'
|
Chris@41
|
201 vv = PyArray_Convert<float,double>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
202 break;
|
Chris@40
|
203 case NPY_INT : // dtype='int'
|
Chris@41
|
204 vv = PyArray_Convert<float,int>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
205 break;
|
Chris@40
|
206 case NPY_LONG : // dtype='long'
|
Chris@41
|
207 vv = PyArray_Convert<float,long>(PyArray_GETPTR2(pyArray, i, 0),PyArray_DIMS(pyArray)[1],strideptr[1]);
|
Chris@40
|
208 break;
|
Chris@40
|
209 default :
|
Chris@40
|
210 string msg = "Unsupported value type in NumPy array object.";
|
Chris@40
|
211 cerr << "VectorConversion::PyArray_To_FloatVector failed (value type = " << descr->type_num << "). Error: " << msg << endl;
|
Chris@40
|
212 setValueError(msg);
|
Chris@40
|
213 return v;
|
Chris@40
|
214 }
|
Chris@40
|
215
|
Chris@40
|
216 v.push_back(vv);
|
Chris@40
|
217 }
|
Chris@40
|
218
|
Chris@40
|
219 return v;
|
Chris@40
|
220 }
|
Chris@40
|
221
|
Chris@26
|
222 PyObject *
|
Chris@29
|
223 VectorConversion::PyArray_From_FloatVector(const vector<float> &v) const
|
Chris@26
|
224 {
|
Chris@28
|
225 npy_intp ndims[1];
|
Chris@28
|
226 ndims[0] = (int)v.size();
|
Chris@28
|
227 PyObject *arr = PyArray_SimpleNew(1, ndims, NPY_FLOAT);
|
Chris@28
|
228 float *data = (float *)PyArray_DATA((PyArrayObject *)arr);
|
Chris@28
|
229 for (int i = 0; i < ndims[0]; ++i) {
|
Chris@28
|
230 data[i] = v[i];
|
Chris@28
|
231 }
|
Chris@28
|
232 return arr;
|
Chris@26
|
233 }
|
Chris@26
|
234
|
Chris@26
|
235 PyObject *
|
Chris@29
|
236 VectorConversion::PyValue_From_StringVector(const vector<string> &v) const
|
Chris@26
|
237 {
|
Chris@28
|
238 PyObject *pyList = PyList_New(v.size());
|
Chris@28
|
239 for (size_t i = 0; i < v.size(); ++i) {
|
Chris@28
|
240 PyObject *pyStr = PyString_FromString(v[i].c_str());
|
Chris@28
|
241 PyList_SET_ITEM(pyList, i, pyStr);
|
Chris@28
|
242 }
|
Chris@28
|
243 return pyList;
|
Chris@26
|
244 }
|
Chris@26
|
245
|
Chris@26
|
246
|
Chris@28
|
247 /* Error handling */
|
Chris@26
|
248
|
Chris@26
|
249 void
|
Chris@29
|
250 VectorConversion::setValueError (string message) const
|
Chris@26
|
251 {
|
Chris@28
|
252 m_error = true;
|
Chris@28
|
253 m_errorQueue.push(ValueError(message));
|
Chris@26
|
254 }
|
Chris@26
|
255
|
Chris@26
|
256 /// return a reference to the last error or creates a new one.
|
Chris@26
|
257 ValueError&
|
Chris@29
|
258 VectorConversion::lastError() const
|
Chris@26
|
259 {
|
Chris@28
|
260 m_error = false;
|
Chris@28
|
261 if (!m_errorQueue.empty()) return m_errorQueue.back();
|
Chris@28
|
262 else {
|
Chris@28
|
263 m_errorQueue.push(ValueError("Type conversion error."));
|
Chris@28
|
264 return m_errorQueue.back();
|
Chris@28
|
265 }
|
Chris@26
|
266 }
|
Chris@26
|
267
|
Chris@26
|
268 /// helper function to iterate over the error message queue:
|
Chris@26
|
269 /// pops the oldest item
|
Chris@26
|
270 ValueError
|
Chris@29
|
271 VectorConversion::getError() const
|
Chris@26
|
272 {
|
Chris@28
|
273 if (!m_errorQueue.empty()) {
|
Chris@28
|
274 ValueError e = m_errorQueue.front();
|
Chris@28
|
275 m_errorQueue.pop();
|
Chris@28
|
276 if (m_errorQueue.empty()) m_error = false;
|
Chris@28
|
277 return e;
|
Chris@28
|
278 }
|
Chris@28
|
279 else {
|
Chris@28
|
280 m_error = false;
|
Chris@28
|
281 return ValueError();
|
Chris@28
|
282 }
|
Chris@26
|
283 }
|
Chris@26
|
284
|
Chris@28
|
285 /* Utilities */
|
Chris@26
|
286
|
Chris@26
|
287 /// get the type name of an object
|
Chris@28
|
288 string
|
Chris@29
|
289 VectorConversion::PyValue_Get_TypeName(PyObject* pyValue) const
|
Chris@26
|
290 {
|
Chris@28
|
291 PyObject *pyType = PyObject_Type(pyValue);
|
Chris@28
|
292 if (!pyType)
|
Chris@28
|
293 {
|
Chris@28
|
294 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
295 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
296 return string ("< unknown type >");
|
Chris@28
|
297 }
|
Chris@28
|
298 PyObject *pyString = PyObject_Str(pyType);
|
Chris@28
|
299 if (!pyString)
|
Chris@28
|
300 {
|
Chris@28
|
301 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
302 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
303 Py_CLEAR(pyType);
|
Chris@28
|
304 return string ("< unknown type >");
|
Chris@28
|
305 }
|
Chris@28
|
306 char *cstr = PyString_AS_STRING(pyString);
|
Chris@28
|
307 if (!cstr)
|
Chris@28
|
308 {
|
Chris@28
|
309 cerr << "Warning: Object type name could not be found." << endl;
|
Chris@28
|
310 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
Chris@28
|
311 Py_DECREF(pyType);
|
Chris@28
|
312 Py_CLEAR(pyString);
|
Chris@28
|
313 return string("< unknown type >");
|
Chris@28
|
314 }
|
Chris@28
|
315 Py_DECREF(pyType);
|
Chris@28
|
316 Py_DECREF(pyString);
|
Chris@28
|
317 return string(cstr);
|
Chris@26
|
318 }
|