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