fazekasgy@31
|
1 /*
|
fazekasgy@31
|
2
|
fazekasgy@31
|
3 Type safe conversion utilities from Python types to C/C++ types,
|
fazekasgy@31
|
4 mainly using Py/C API macros.
|
fazekasgy@31
|
5
|
fazekasgy@31
|
6 */
|
fazekasgy@31
|
7
|
fazekasgy@31
|
8 #ifndef _PY_TYPE_INTERFACE_H_
|
fazekasgy@31
|
9 #define _PY_TYPE_INTERFACE_H_
|
fazekasgy@31
|
10
|
fazekasgy@31
|
11 #include "vamp-sdk/Plugin.h"
|
fazekasgy@31
|
12 #include <Python.h>
|
fazekasgy@31
|
13 #include "PyExtensionModule.h"
|
fazekasgy@31
|
14 #include <vector>
|
fazekasgy@31
|
15 #include <queue>
|
fazekasgy@31
|
16 #include <string>
|
fazekasgy@31
|
17 //#include <typeinfo>
|
fazekasgy@31
|
18
|
fazekasgy@31
|
19
|
fazekasgy@31
|
20 using std::cerr;
|
fazekasgy@31
|
21 using std::endl;
|
fazekasgy@31
|
22
|
fazekasgy@31
|
23 namespace o {
|
fazekasgy@31
|
24 enum eOutDescriptors {
|
fazekasgy@31
|
25 not_found,
|
fazekasgy@31
|
26 identifier,
|
fazekasgy@31
|
27 name,
|
fazekasgy@31
|
28 description,
|
fazekasgy@31
|
29 unit,
|
fazekasgy@31
|
30 hasFixedBinCount,
|
fazekasgy@31
|
31 binCount,
|
fazekasgy@31
|
32 binNames,
|
fazekasgy@31
|
33 hasKnownExtents,
|
fazekasgy@31
|
34 minValue,
|
fazekasgy@31
|
35 maxValue,
|
fazekasgy@31
|
36 isQuantized,
|
fazekasgy@31
|
37 quantizeStep,
|
fazekasgy@31
|
38 sampleType,
|
fazekasgy@31
|
39 sampleRate,
|
fazekasgy@31
|
40 hasDuration,
|
fazekasgy@31
|
41 endNode
|
fazekasgy@31
|
42 };
|
fazekasgy@31
|
43 }
|
fazekasgy@31
|
44
|
fazekasgy@31
|
45 namespace p {
|
fazekasgy@31
|
46 enum eParmDescriptors {
|
fazekasgy@31
|
47 not_found,
|
fazekasgy@31
|
48 identifier,
|
fazekasgy@31
|
49 name,
|
fazekasgy@31
|
50 description,
|
fazekasgy@31
|
51 unit,
|
fazekasgy@31
|
52 minValue,
|
fazekasgy@31
|
53 maxValue,
|
fazekasgy@31
|
54 defaultValue,
|
fazekasgy@31
|
55 isQuantized,
|
fazekasgy@31
|
56 quantizeStep
|
fazekasgy@31
|
57 };
|
fazekasgy@31
|
58 }
|
fazekasgy@31
|
59
|
fazekasgy@31
|
60 enum eSampleTypes {
|
fazekasgy@31
|
61 OneSamplePerStep,
|
fazekasgy@31
|
62 FixedSampleRate,
|
fazekasgy@31
|
63 VariableSampleRate
|
fazekasgy@31
|
64 };
|
fazekasgy@31
|
65
|
fazekasgy@31
|
66 enum eFeatureFields {
|
fazekasgy@31
|
67 unknown,
|
fazekasgy@31
|
68 hasTimestamp,
|
fazekasgy@31
|
69 timeStamp,
|
fazekasgy@31
|
70 hasDuration,
|
fazekasgy@31
|
71 duration,
|
fazekasgy@31
|
72 values,
|
fazekasgy@31
|
73 label
|
fazekasgy@31
|
74 };
|
fazekasgy@31
|
75
|
fazekasgy@31
|
76
|
fazekasgy@31
|
77 /// sutructure of NumPy array interface:
|
fazekasgy@31
|
78 /// this is all we need to support numpy without direct dependency
|
fazekasgy@31
|
79 typedef struct {
|
fazekasgy@31
|
80 int two; /* contains the integer 2 -- simple sanity check */
|
fazekasgy@31
|
81 int nd; /* number of dimensions */
|
fazekasgy@31
|
82 char typekind; /* kind in array --- character code of typestr */
|
fazekasgy@31
|
83 int itemsize; /* size of each element */
|
fazekasgy@31
|
84 int flags; /* flags indicating how the data should be interpreted */
|
fazekasgy@31
|
85 /* must set ARR_HAS_DESCR bit to validate descr */
|
fazekasgy@31
|
86 Py_intptr_t *shape; /* A length-nd array of shape information */
|
fazekasgy@31
|
87 Py_intptr_t *strides; /* A length-nd array of stride information */
|
fazekasgy@31
|
88 void *data; /* A pointer to the first element of the array */
|
fazekasgy@31
|
89 PyObject *descr; /* NULL or data-description (same as descr key */
|
fazekasgy@31
|
90 /* of __array_interface__) -- must set ARR_HAS_DESCR */
|
fazekasgy@31
|
91 /* flag or this will be ignored. */
|
fazekasgy@31
|
92 } PyArrayInterface;
|
fazekasgy@31
|
93
|
fazekasgy@31
|
94 /* C++ mapping of PyNone Type*/
|
fazekasgy@31
|
95 typedef struct NoneType {};
|
fazekasgy@31
|
96
|
fazekasgy@31
|
97 class PyTypeInterface
|
fazekasgy@31
|
98 {
|
fazekasgy@31
|
99 public:
|
fazekasgy@31
|
100 PyTypeInterface();
|
fazekasgy@31
|
101 ~PyTypeInterface();
|
fazekasgy@31
|
102
|
fazekasgy@31
|
103 // Data
|
fazekasgy@31
|
104 class ValueError
|
fazekasgy@31
|
105 {
|
fazekasgy@31
|
106 public:
|
fazekasgy@31
|
107 ValueError() {}
|
fazekasgy@31
|
108 ValueError(std::string m, bool s) : message(m),strict(s) {}
|
fazekasgy@31
|
109 std::string location;
|
fazekasgy@31
|
110 std::string message;
|
fazekasgy@31
|
111 bool strict;
|
fazekasgy@31
|
112 std::string get() const { return message + "\nLocation: " + location + "\n";}
|
fazekasgy@31
|
113 void print() const { cerr << get(); }
|
fazekasgy@31
|
114 };
|
fazekasgy@31
|
115
|
fazekasgy@31
|
116 // Utilities
|
fazekasgy@31
|
117 void setStrictTypingFlag(bool b) {m_strict = b;}
|
fazekasgy@31
|
118 const ValueError &lastError() const;
|
fazekasgy@31
|
119 ValueError getError() const;
|
fazekasgy@31
|
120 std::string PyValue_Get_TypeName(PyObject*) const;
|
fazekasgy@31
|
121 bool initMaps() const;
|
fazekasgy@31
|
122
|
fazekasgy@31
|
123 // Basic type conversion: Python to C++
|
fazekasgy@31
|
124 float PyValue_To_Float(PyObject*) const;
|
fazekasgy@31
|
125 size_t PyValue_To_Size_t(PyObject*) const;
|
fazekasgy@31
|
126 bool PyValue_To_Bool(PyObject*) const;
|
fazekasgy@31
|
127 std::string PyValue_To_String(PyObject*) const;
|
fazekasgy@31
|
128 // int PyValue_To_Int(PyObject*) const;
|
fazekasgy@31
|
129
|
fazekasgy@31
|
130 // C++ to Python
|
fazekasgy@31
|
131 PyObject *PyValue_From_CValue(const char*) const;
|
fazekasgy@31
|
132 PyObject *PyValue_From_CValue(const std::string& x) const { return PyValue_From_CValue(x.c_str()); }
|
fazekasgy@31
|
133 PyObject *PyValue_From_CValue(size_t) const;
|
fazekasgy@31
|
134 PyObject *PyValue_From_CValue(double) const;
|
fazekasgy@31
|
135 PyObject *PyValue_From_CValue(float x) const { return PyValue_From_CValue((double)x); }
|
fazekasgy@31
|
136 PyObject *PyValue_From_CValue(bool) const;
|
fazekasgy@31
|
137
|
fazekasgy@31
|
138 // Sequence types
|
fazekasgy@31
|
139 std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
|
fazekasgy@31
|
140 std::vector<float> PyValue_To_FloatVector (PyObject*) const;
|
fazekasgy@31
|
141
|
fazekasgy@31
|
142 // Numpy types
|
fazekasgy@31
|
143 float* getNumPyObjectData(PyObject *object, int &length) const;
|
fazekasgy@31
|
144
|
fazekasgy@31
|
145
|
fazekasgy@31
|
146 /* Template functions */
|
fazekasgy@31
|
147
|
fazekasgy@31
|
148
|
fazekasgy@31
|
149 /// Common wrappers to set a value in one of these structs. (to be used in template functions)
|
fazekasgy@31
|
150 void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@31
|
151 void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@31
|
152 bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
|
fazekasgy@31
|
153 PyObject* GetDescriptor_As_Dict(PyObject* pyValue) const
|
fazekasgy@31
|
154 {
|
fazekasgy@31
|
155 if PyFeature_CheckExact(pyValue) return PyFeature_AS_DICT(pyValue);
|
fazekasgy@31
|
156 if PyOutputDescriptor_CheckExact(pyValue) return PyOutputDescriptor_AS_DICT(pyValue);
|
fazekasgy@31
|
157 if PyParameterDescriptor_CheckExact(pyValue) return PyParameterDescriptor_AS_DICT(pyValue);
|
fazekasgy@31
|
158 return NULL;
|
fazekasgy@31
|
159 }
|
fazekasgy@31
|
160
|
fazekasgy@31
|
161
|
fazekasgy@31
|
162 template<typename RET>
|
fazekasgy@31
|
163 RET PyTypeInterface::PyValue_To_VampDescriptor(PyObject* pyValue) const
|
fazekasgy@31
|
164 //returns e.g. Vamp::Plugin::OutputDescriptor or Vamp::Plugin::Feature
|
fazekasgy@31
|
165 {
|
fazekasgy@31
|
166 PyObject* pyDict;
|
fazekasgy@31
|
167
|
fazekasgy@31
|
168 // Descriptors encoded as dicts
|
fazekasgy@31
|
169 pyDict = GetDescriptor_As_Dict(pyValue);
|
fazekasgy@31
|
170 if (!pyDict) pyDict = pyValue;
|
fazekasgy@31
|
171
|
fazekasgy@31
|
172 // TODO: support full mapping protocol as fallback.
|
fazekasgy@31
|
173 if (!PyDict_Check(pyDict)) {
|
fazekasgy@31
|
174 setValueError("Error while converting descriptor or feature object.\nThe value is neither a dictionary nor a Vamp Feature or Descriptor type.",m_strict);
|
fazekasgy@31
|
175 return RET();
|
fazekasgy@31
|
176 }
|
fazekasgy@31
|
177
|
fazekasgy@31
|
178 Py_ssize_t pyPos = 0;
|
fazekasgy@31
|
179 PyObject *pyKey, *pyDictValue;
|
fazekasgy@31
|
180 initMaps();
|
fazekasgy@31
|
181 RET rd;
|
fazekasgy@31
|
182
|
fazekasgy@31
|
183 //Python Dictionary Iterator:
|
fazekasgy@31
|
184 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyDictValue))
|
fazekasgy@31
|
185 {
|
fazekasgy@31
|
186 std::string key = PyValue_To_String(pyKey);
|
fazekasgy@31
|
187 SetValue(rd,key,pyDictValue);
|
fazekasgy@31
|
188 if (m_error) {
|
fazekasgy@31
|
189 _lastError().location += "parameter: '" + key + "'";//"' descriptor: '" + rd.identifier + "'";
|
fazekasgy@31
|
190 }
|
fazekasgy@31
|
191 }
|
fazekasgy@31
|
192 if (!m_errorQueue.empty()) m_error = true;
|
fazekasgy@31
|
193 return rd;
|
fazekasgy@31
|
194 }
|
fazekasgy@31
|
195
|
fazekasgy@31
|
196 /// Convert a sequence (tipically list) of PySomething to
|
fazekasgy@31
|
197 /// OutputList,ParameterList or FeatureList
|
fazekasgy@31
|
198 template<typename RET,typename ELEM> //<OutputList> <OutputDescriptor>
|
fazekasgy@31
|
199 RET PyTypeInterface::PyValue_To_VampList(PyObject* pyList) const
|
fazekasgy@31
|
200 {
|
fazekasgy@31
|
201 // Vamp::Plugin::OutputList list;
|
fazekasgy@31
|
202 // Vamp::Plugin::OutputDescriptor od;
|
fazekasgy@31
|
203 RET list;
|
fazekasgy@31
|
204 ELEM element;
|
fazekasgy@31
|
205
|
fazekasgy@31
|
206 // Type checking
|
fazekasgy@31
|
207 if (! PyList_Check(pyList) ) {
|
fazekasgy@31
|
208 Py_CLEAR(pyList);
|
fazekasgy@31
|
209 // cerr << "ERROR: In Python plugin [" << m_class << "::" << method
|
fazekasgy@31
|
210 // << "] Expected List return type." << endl;
|
fazekasgy@31
|
211 return list;
|
fazekasgy@31
|
212 }
|
fazekasgy@31
|
213
|
fazekasgy@31
|
214 //This reference will be borrowed
|
fazekasgy@31
|
215 PyObject *pyDict;
|
fazekasgy@31
|
216
|
fazekasgy@31
|
217 //Parse Output List
|
fazekasgy@31
|
218 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
|
fazekasgy@31
|
219 //Get i-th Vamp output descriptor (Borrowed Reference)
|
fazekasgy@31
|
220 pyDict = PyList_GET_ITEM(pyList,i);
|
fazekasgy@31
|
221 element = PyValue_To_VampDescriptor<ELEM>(pyDict);
|
fazekasgy@31
|
222 // Check for empty Feature/Descriptor as before?
|
fazekasgy@31
|
223 list.push_back(element);
|
fazekasgy@31
|
224 }
|
fazekasgy@31
|
225 return list;
|
fazekasgy@31
|
226 }
|
fazekasgy@31
|
227
|
fazekasgy@31
|
228
|
fazekasgy@31
|
229 //Vamp specific types
|
fazekasgy@31
|
230
|
fazekasgy@31
|
231 Vamp::Plugin::FeatureSet PyValue_To_FeatureSet(PyObject*) const;
|
fazekasgy@31
|
232 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureSet &r) const
|
fazekasgy@31
|
233 { r = this->PyValue_To_FeatureSet(pyValue); }
|
fazekasgy@31
|
234
|
fazekasgy@31
|
235 Vamp::RealTime::RealTime PyValue_To_RealTime(PyObject*) const;
|
fazekasgy@31
|
236 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::RealTime::RealTime &r) const
|
fazekasgy@31
|
237 { r = this->PyValue_To_RealTime(pyValue); }
|
fazekasgy@31
|
238
|
fazekasgy@31
|
239
|
fazekasgy@31
|
240 /* Overloaded PyValue_To_rValue() to support generic functions */
|
fazekasgy@31
|
241 inline void PyValue_To_rValue(PyObject *pyValue, float &defValue) const
|
fazekasgy@31
|
242 { float tmp = this->PyValue_To_Float(pyValue);
|
fazekasgy@31
|
243 if(!m_error) defValue = tmp; }
|
fazekasgy@31
|
244 inline void PyValue_To_rValue(PyObject *pyValue, size_t &defValue) const
|
fazekasgy@31
|
245 { size_t tmp = this->PyValue_To_Size_t(pyValue);
|
fazekasgy@31
|
246 if(!m_error) defValue = tmp; }
|
fazekasgy@31
|
247 inline void PyValue_To_rValue(PyObject *pyValue, bool &defValue) const
|
fazekasgy@31
|
248 { bool tmp = this->PyValue_To_Bool(pyValue);
|
fazekasgy@31
|
249 if(!m_error) defValue = tmp; }
|
fazekasgy@31
|
250 inline void PyValue_To_rValue(PyObject *pyValue, std::string &defValue) const
|
fazekasgy@31
|
251 { std::string tmp = this->PyValue_To_String(pyValue);
|
fazekasgy@31
|
252 if(!m_error) defValue = tmp; }
|
fazekasgy@31
|
253 /*used by templates where we expect no return value, if there is one it will be ignored*/
|
fazekasgy@31
|
254 inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
|
fazekasgy@31
|
255 { if (m_strict && pyValue != Py_None)
|
fazekasgy@31
|
256 setValueError("Strict conversion error: expected 'None' type.",m_strict);
|
fazekasgy@31
|
257 }
|
fazekasgy@31
|
258
|
fazekasgy@31
|
259 /* convert sequence types to Vamp List types */
|
fazekasgy@31
|
260 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::OutputList &r) const
|
fazekasgy@31
|
261 { r = this->PyValue_To_VampList<Vamp::Plugin::OutputList,Vamp::Plugin::OutputDescriptor>(pyValue); }
|
fazekasgy@31
|
262 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::ParameterList &r) const
|
fazekasgy@31
|
263 { r = this->PyValue_To_VampList<Vamp::Plugin::ParameterList,Vamp::Plugin::ParameterDescriptor>(pyValue); }
|
fazekasgy@31
|
264 inline void PyValue_To_rValue(PyObject *pyValue, Vamp::Plugin::FeatureList &r) const
|
fazekasgy@31
|
265 { r = this->PyValue_To_VampList<Vamp::Plugin::FeatureList,Vamp::Plugin::Feature>(pyValue); }
|
fazekasgy@31
|
266
|
fazekasgy@31
|
267 /// this is only needed for RealTime->Frame conversion
|
fazekasgy@31
|
268 void setInputSampleRate(float inputSampleRate)
|
fazekasgy@31
|
269 { m_inputSampleRate = (unsigned int) inputSampleRate; }
|
fazekasgy@31
|
270
|
fazekasgy@31
|
271 private:
|
fazekasgy@31
|
272 bool m_strict;
|
fazekasgy@31
|
273 ValueError m_noError;
|
fazekasgy@31
|
274 mutable bool m_error;
|
fazekasgy@31
|
275 mutable ValueError& m_lastError;
|
fazekasgy@31
|
276 mutable std::queue<ValueError> m_errorQueue;
|
fazekasgy@31
|
277 // we only use it for RealTime conversion which requires unsigned int
|
fazekasgy@31
|
278 unsigned int m_inputSampleRate;
|
fazekasgy@31
|
279
|
fazekasgy@31
|
280 void setValueError(std::string,bool) const;
|
fazekasgy@31
|
281 ValueError& _lastError() const;
|
fazekasgy@31
|
282
|
fazekasgy@31
|
283 /* Overloaded _convert(), bypasses error checking to avoid doing it twice in internals. */
|
fazekasgy@31
|
284 inline void _convert(PyObject *pyValue,float &r) const
|
fazekasgy@31
|
285 { r = PyValue_To_Float(pyValue); }
|
fazekasgy@31
|
286 inline void _convert(PyObject *pyValue,size_t &r) const
|
fazekasgy@31
|
287 { r = PyValue_To_Size_t(pyValue); }
|
fazekasgy@31
|
288 inline void _convert(PyObject *pyValue,bool &r) const
|
fazekasgy@31
|
289 { r = PyValue_To_Bool(pyValue); }
|
fazekasgy@31
|
290 inline void _convert(PyObject *pyValue,std::string &r) const
|
fazekasgy@31
|
291 { r = PyValue_To_String(pyValue); }
|
fazekasgy@31
|
292 inline void _convert(PyObject *pyValue,std::vector<std::string> &r) const
|
fazekasgy@31
|
293 { r = PyValue_To_StringVector(pyValue); }
|
fazekasgy@31
|
294 inline void _convert(PyObject *pyValue,std::vector<float> &r) const
|
fazekasgy@31
|
295 { r = PyValue_To_FloatVector(pyValue); }
|
fazekasgy@31
|
296 inline void _convert(PyObject *pyValue,Vamp::RealTime::RealTime &r) const
|
fazekasgy@31
|
297 { r = PyValue_To_RealTime(pyValue); }
|
fazekasgy@31
|
298
|
fazekasgy@31
|
299 public:
|
fazekasgy@31
|
300 const bool& error;
|
fazekasgy@31
|
301
|
fazekasgy@31
|
302 };
|
fazekasgy@31
|
303
|
fazekasgy@31
|
304 #endif |