cannam@18
|
1 /* -*- c-basic-offset: 8 indent-tabs-mode: t -*- */
|
fazekasgy@0
|
2 /*
|
fazekasgy@0
|
3 Vamp
|
fazekasgy@0
|
4
|
fazekasgy@0
|
5 An API for audio analysis and feature extraction plugins.
|
fazekasgy@0
|
6
|
fazekasgy@0
|
7 Centre for Digital Music, Queen Mary, University of London.
|
fazekasgy@0
|
8 Copyright 2006 Chris Cannam.
|
fazekasgy@0
|
9
|
fazekasgy@0
|
10 Permission is hereby granted, free of charge, to any person
|
fazekasgy@0
|
11 obtaining a copy of this software and associated documentation
|
fazekasgy@0
|
12 files (the "Software"), to deal in the Software without
|
fazekasgy@0
|
13 restriction, including without limitation the rights to use, copy,
|
fazekasgy@0
|
14 modify, merge, publish, distribute, sublicense, and/or sell copies
|
fazekasgy@0
|
15 of the Software, and to permit persons to whom the Software is
|
fazekasgy@0
|
16 furnished to do so, subject to the following conditions:
|
fazekasgy@0
|
17
|
fazekasgy@0
|
18 The above copyright notice and this permission notice shall be
|
fazekasgy@0
|
19 included in all copies or substantial portions of the Software.
|
fazekasgy@0
|
20
|
fazekasgy@0
|
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
fazekasgy@0
|
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
fazekasgy@0
|
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
fazekasgy@0
|
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
fazekasgy@0
|
25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
fazekasgy@0
|
26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
fazekasgy@0
|
27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
fazekasgy@0
|
28
|
fazekasgy@0
|
29 Except as contained in this notice, the names of the Centre for
|
fazekasgy@0
|
30 Digital Music; Queen Mary, University of London; and Chris Cannam
|
fazekasgy@0
|
31 shall not be used in advertising or otherwise to promote the sale,
|
fazekasgy@0
|
32 use or other dealings in this Software without prior written
|
fazekasgy@0
|
33 authorization.
|
fazekasgy@0
|
34 */
|
fazekasgy@0
|
35
|
fazekasgy@0
|
36 #ifndef _PYTHON_WRAPPER_PLUGIN_H_
|
fazekasgy@0
|
37 #define _PYTHON_WRAPPER_PLUGIN_H_
|
fazekasgy@0
|
38
|
fazekasgy@31
|
39 #define _CLASS_METHOD_ m_class << "::" << method
|
fazekasgy@31
|
40 #define PLUGIN_ERROR "ERROR: In Vampy plugin [" << _CLASS_METHOD_ << "]" << endl << "Cause: "
|
fazekasgy@31
|
41 #define DEBUG_NAME "[Vampy::call] " << _CLASS_METHOD_ << " "
|
fazekasgy@31
|
42 #define DEAFULT_RETURN "Method [" << _CLASS_METHOD_ << "] is not implemented." << endl << "Returning default value: " << rValue
|
fazekasgy@31
|
43 #define FLAG_VALUE "Flag: " << flagName << ": " << ((rValue==0)?"False":"True")
|
fazekasgy@31
|
44
|
fazekasgy@0
|
45 #include "vamp-sdk/Plugin.h"
|
cannam@3
|
46 #include <Python.h>
|
fazekasgy@31
|
47 // #include <typeinfo>
|
fazekasgy@31
|
48 // #include <stdarg.h>
|
fazekasgy@31
|
49 #include "PyTypeInterface.h"
|
cannam@3
|
50
|
cannam@3
|
51 #include "Mutex.h"
|
fazekasgy@0
|
52
|
fazekasgy@31
|
53 using std::string;
|
fazekasgy@31
|
54 using std::cerr;
|
fazekasgy@31
|
55 using std::endl;
|
fazekasgy@0
|
56
|
fazekasgy@6
|
57 enum eProcessType {
|
fazekasgy@6
|
58 not_implemented,
|
fazekasgy@6
|
59 legacyProcess,
|
fazekasgy@6
|
60 numpyProcess
|
fazekasgy@6
|
61 };
|
fazekasgy@0
|
62
|
fazekasgy@0
|
63 class PyPlugin : public Vamp::Plugin
|
fazekasgy@0
|
64 {
|
fazekasgy@0
|
65 public:
|
fazekasgy@31
|
66 PyPlugin(std::string plugin,float inputSampleRate, PyObject *pyClass, int &instcount);
|
cannam@18
|
67 virtual ~PyPlugin();
|
fazekasgy@0
|
68
|
cannam@18
|
69 bool initialise(size_t channels, size_t stepSize, size_t blockSize);
|
cannam@18
|
70 void reset();
|
fazekasgy@6
|
71
|
fazekasgy@0
|
72 InputDomain getInputDomain() const;
|
fazekasgy@0
|
73 size_t getPreferredBlockSize() const;
|
fazekasgy@0
|
74 size_t getPreferredStepSize() const;
|
fazekasgy@0
|
75 size_t getMinChannelCount() const;
|
fazekasgy@0
|
76 size_t getMaxChannelCount() const;
|
fazekasgy@0
|
77
|
cannam@18
|
78 std::string getIdentifier() const;
|
cannam@18
|
79 std::string getName() const;
|
cannam@18
|
80 std::string getDescription() const;
|
cannam@18
|
81 std::string getMaker() const;
|
cannam@18
|
82 int getPluginVersion() const;
|
cannam@18
|
83 std::string getCopyright() const;
|
cannam@18
|
84
|
cannam@18
|
85 OutputList getOutputDescriptors() const;
|
cannam@18
|
86 ParameterList getParameterDescriptors() const;
|
fazekasgy@0
|
87 float getParameter(std::string paramid) const;
|
fazekasgy@0
|
88 void setParameter(std::string paramid, float newval);
|
fazekasgy@0
|
89
|
cannam@18
|
90 FeatureSet process(const float *const *inputBuffers,
|
cannam@18
|
91 Vamp::RealTime timestamp);
|
fazekasgy@0
|
92
|
cannam@18
|
93 FeatureSet getRemainingFeatures();
|
fazekasgy@31
|
94
|
fazekasgy@0
|
95 protected:
|
fazekasgy@31
|
96 static Mutex m_pythonInterpreterMutex;
|
cannam@24
|
97 PyObject *m_pyClass;
|
fazekasgy@0
|
98 PyObject *m_pyInstance;
|
fazekasgy@31
|
99 int &m_instcount;
|
cannam@18
|
100 size_t m_stepSize;
|
cannam@18
|
101 size_t m_blockSize;
|
cannam@18
|
102 size_t m_channels;
|
fazekasgy@0
|
103 std::string m_plugin;
|
fazekasgy@0
|
104 std::string m_class;
|
fazekasgy@0
|
105 std::string m_path;
|
fazekasgy@6
|
106 int m_processType;
|
fazekasgy@6
|
107 PyObject *m_pyProcess;
|
fazekasgy@6
|
108 InputDomain m_inputDomain;
|
fazekasgy@31
|
109 PyTypeInterface m_ti;
|
fazekasgy@31
|
110 bool m_quitOnErrorFlag;
|
fazekasgy@31
|
111 bool m_debugFlag;
|
fazekasgy@31
|
112
|
fazekasgy@31
|
113 void setProcessType();
|
fazekasgy@6
|
114
|
fazekasgy@31
|
115 PyObject* numpyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp);
|
fazekasgy@31
|
116 PyObject* legacyProcessCall(const float *const *inputBuffers, Vamp::RealTime timestamp);
|
fazekasgy@31
|
117
|
fazekasgy@31
|
118 bool getBooleanFlag(char flagName[],bool) const;
|
fazekasgy@31
|
119 /*
|
fazekasgy@31
|
120 Flags may be used to control the behaviour of the interface.
|
fazekasgy@31
|
121 Flags can be set in any Vampy plugin's __init__() function.
|
fazekasgy@31
|
122 Their scope is limited to an instance.
|
fazekasgy@31
|
123 Default values for all flags are False.
|
fazekasgy@31
|
124 Python Example:
|
fazekasgy@31
|
125 def __init__(self,inputSampleRate):
|
fazekasgy@31
|
126 self.use_strict_type_conversion = True
|
fazekasgy@31
|
127 self.vampy_debug_messages = True
|
fazekasgy@31
|
128 self.use_realtime_timestamp = False
|
fazekasgy@31
|
129 self.use_numpy_interface = False
|
fazekasgy@31
|
130 self.quit_on_type_error = False
|
fazekasgy@31
|
131
|
fazekasgy@31
|
132 */
|
fazekasgy@31
|
133
|
fazekasgy@31
|
134 void genericMethodCall(char *method) const
|
fazekasgy@31
|
135 {
|
fazekasgy@31
|
136 if (m_debugFlag) cerr << DEBUG_NAME << endl;
|
fazekasgy@31
|
137 if ( PyObject_HasAttrString(m_pyInstance,method) )
|
fazekasgy@31
|
138 {
|
fazekasgy@31
|
139 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL);
|
fazekasgy@31
|
140 if (!pyValue) {
|
fazekasgy@31
|
141 cerr << PLUGIN_ERROR << "Failed to call method." << endl;
|
fazekasgy@31
|
142 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
143 }
|
fazekasgy@31
|
144 }
|
fazekasgy@31
|
145 }
|
fazekasgy@31
|
146
|
fazekasgy@31
|
147 template<typename RET>
|
fazekasgy@31
|
148 RET &genericMethodCall(char *method, RET &rValue) const
|
fazekasgy@31
|
149 {
|
fazekasgy@31
|
150 if (m_debugFlag) cerr << DEBUG_NAME << endl;
|
fazekasgy@31
|
151 if ( PyObject_HasAttrString(m_pyInstance,method) )
|
fazekasgy@31
|
152 {
|
fazekasgy@31
|
153 PyObject *pyValue = PyObject_CallMethod(m_pyInstance, method, NULL);
|
fazekasgy@31
|
154 if (pyValue) {
|
fazekasgy@31
|
155 m_ti.PyValue_To_rValue(pyValue,rValue);
|
fazekasgy@31
|
156 if (!m_ti.error) {
|
fazekasgy@31
|
157 Py_DECREF(pyValue);
|
fazekasgy@31
|
158 return rValue;
|
fazekasgy@31
|
159 } else {
|
fazekasgy@31
|
160 cerr << PLUGIN_ERROR << m_ti.lastError().message << endl;
|
fazekasgy@31
|
161 Py_CLEAR(pyValue);
|
fazekasgy@31
|
162 if (m_quitOnErrorFlag) exit(EXIT_FAILURE);
|
fazekasgy@31
|
163 return rValue;
|
fazekasgy@31
|
164 }
|
fazekasgy@31
|
165 } else {
|
fazekasgy@31
|
166 cerr << PLUGIN_ERROR << "Failed to call method." << endl;
|
fazekasgy@31
|
167 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
168 return rValue;
|
fazekasgy@31
|
169 }
|
fazekasgy@31
|
170 }
|
fazekasgy@31
|
171 // TODO: this fails to generalise because the << operator
|
fazekasgy@31
|
172 // doesn't accept all types.
|
fazekasgy@31
|
173 // if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
|
fazekasgy@31
|
174 return rValue;
|
fazekasgy@31
|
175 }
|
cannam@3
|
176
|
fazekasgy@31
|
177 template<typename RET,typename A1>
|
fazekasgy@31
|
178 RET genericMethodCallArgs(char *method, A1 arg1) const
|
fazekasgy@31
|
179 {
|
fazekasgy@31
|
180 RET rValue = RET();
|
fazekasgy@31
|
181 if (m_debugFlag) cerr << DEBUG_NAME << endl;
|
fazekasgy@31
|
182 if (!PyObject_HasAttrString(m_pyInstance,method)) {
|
fazekasgy@31
|
183 // if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
|
fazekasgy@31
|
184 return rValue;
|
fazekasgy@31
|
185 }
|
fazekasgy@31
|
186
|
fazekasgy@31
|
187 // These functions always return valid PyObjects
|
fazekasgy@31
|
188 PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
|
fazekasgy@31
|
189 PyObject* pyTuple = PyTuple_New(3);
|
fazekasgy@31
|
190 if (!pyTuple) return rValue;
|
fazekasgy@31
|
191
|
fazekasgy@31
|
192 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
|
fazekasgy@31
|
193
|
fazekasgy@31
|
194 PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,NULL);
|
fazekasgy@31
|
195 if (!pyValue)
|
fazekasgy@31
|
196 {
|
fazekasgy@31
|
197 cerr << PLUGIN_ERROR << "Failed to call method." << endl;
|
fazekasgy@31
|
198 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
199 }
|
fazekasgy@31
|
200
|
fazekasgy@31
|
201 Py_DECREF(pyMethod);
|
fazekasgy@31
|
202 Py_DECREF(pyArg1);
|
fazekasgy@31
|
203
|
fazekasgy@31
|
204 m_ti.PyValue_To_rValue(pyValue,rValue);
|
fazekasgy@31
|
205 if (!m_ti.error) {
|
fazekasgy@31
|
206 Py_DECREF(pyValue);
|
fazekasgy@31
|
207 } else {
|
fazekasgy@31
|
208 cerr << PLUGIN_ERROR << m_ti.lastError().message << endl;
|
fazekasgy@31
|
209 Py_CLEAR(pyValue);
|
fazekasgy@31
|
210 if (m_quitOnErrorFlag) exit(EXIT_FAILURE);
|
fazekasgy@31
|
211 }
|
fazekasgy@31
|
212 return rValue;
|
fazekasgy@31
|
213 }
|
fazekasgy@31
|
214
|
fazekasgy@31
|
215 template<typename RET,typename A1,typename A2>
|
fazekasgy@31
|
216 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2)
|
fazekasgy@31
|
217 {
|
fazekasgy@31
|
218 RET rValue = RET();
|
fazekasgy@31
|
219 if (m_debugFlag) cerr << DEBUG_NAME << endl;
|
fazekasgy@31
|
220 if (!PyObject_HasAttrString(m_pyInstance,method)) {
|
fazekasgy@31
|
221 // if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
|
fazekasgy@31
|
222 return rValue;
|
fazekasgy@31
|
223 }
|
fazekasgy@31
|
224
|
fazekasgy@31
|
225 // These functions always return valid PyObjects
|
fazekasgy@31
|
226 PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
|
fazekasgy@31
|
227 PyObject* pyTuple = PyTuple_New(3);
|
fazekasgy@31
|
228 if (!pyTuple) return rValue;
|
fazekasgy@31
|
229
|
fazekasgy@31
|
230 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
|
fazekasgy@31
|
231 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2);
|
fazekasgy@31
|
232
|
fazekasgy@31
|
233 PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,NULL);
|
fazekasgy@31
|
234 if (!pyValue)
|
fazekasgy@31
|
235 {
|
fazekasgy@31
|
236 cerr << PLUGIN_ERROR << "Failed to call method." << endl;
|
fazekasgy@31
|
237 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
238 }
|
fazekasgy@31
|
239
|
fazekasgy@31
|
240 Py_DECREF(pyMethod);
|
fazekasgy@31
|
241 Py_DECREF(pyArg1);
|
fazekasgy@31
|
242 Py_DECREF(pyArg2);
|
fazekasgy@31
|
243
|
fazekasgy@31
|
244 m_ti.PyValue_To_rValue(pyValue,rValue);
|
fazekasgy@31
|
245 if (!m_ti.error) {
|
fazekasgy@31
|
246 Py_DECREF(pyValue);
|
fazekasgy@31
|
247 } else {
|
fazekasgy@31
|
248 cerr << PLUGIN_ERROR << m_ti.lastError().message << endl;
|
fazekasgy@31
|
249 Py_CLEAR(pyValue);
|
fazekasgy@31
|
250 if (m_quitOnErrorFlag) exit(EXIT_FAILURE);
|
fazekasgy@31
|
251 }
|
fazekasgy@31
|
252 return rValue;
|
fazekasgy@31
|
253 }
|
fazekasgy@31
|
254
|
fazekasgy@31
|
255
|
fazekasgy@31
|
256 template<typename RET,typename A1,typename A2,typename A3>
|
fazekasgy@31
|
257 RET genericMethodCallArgs(char *method, A1 arg1, A2 arg2, A3 arg3)
|
fazekasgy@31
|
258 {
|
fazekasgy@31
|
259 RET rValue = RET();
|
fazekasgy@31
|
260 if (m_debugFlag) cerr << DEBUG_NAME << endl;
|
fazekasgy@31
|
261 if (!PyObject_HasAttrString(m_pyInstance,method)) {
|
fazekasgy@31
|
262 if (m_debugFlag) cerr << DEAFULT_RETURN << endl;
|
fazekasgy@31
|
263 return rValue;
|
fazekasgy@31
|
264 }
|
fazekasgy@31
|
265
|
fazekasgy@31
|
266 // These functions always return valid PyObjects
|
fazekasgy@31
|
267 PyObject *pyMethod = m_ti.PyValue_From_CValue(method);
|
fazekasgy@31
|
268 PyObject* pyTuple = PyTuple_New(3);
|
fazekasgy@31
|
269 if (!pyTuple) return rValue;
|
fazekasgy@31
|
270
|
fazekasgy@31
|
271 PyObject *pyArg1 = m_ti.PyValue_From_CValue(arg1);
|
fazekasgy@31
|
272 PyObject *pyArg2 = m_ti.PyValue_From_CValue(arg2);
|
fazekasgy@31
|
273 PyObject *pyArg3 = m_ti.PyValue_From_CValue(arg3);
|
fazekasgy@31
|
274
|
fazekasgy@31
|
275 // TODO: Pack it in a tuple to avoid va_list parsing!
|
fazekasgy@31
|
276
|
fazekasgy@31
|
277 // callable = PyObject_GetAttr(callable, name);
|
fazekasgy@31
|
278 // if (callable == NULL)
|
fazekasgy@31
|
279 // return NULL;
|
fazekasgy@31
|
280 // PyObject* args; // pyTuple of input arguments
|
fazekasgy@31
|
281 //tmp = PyObject_Call(callable, args, NULL);
|
fazekasgy@31
|
282
|
fazekasgy@31
|
283
|
fazekasgy@31
|
284 PyObject *pyValue = PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyArg1,pyArg2,pyArg3,NULL);
|
fazekasgy@31
|
285 if (!pyValue)
|
fazekasgy@31
|
286 {
|
fazekasgy@31
|
287 cerr << PLUGIN_ERROR << "Failed to call method." << endl;
|
fazekasgy@31
|
288 if (PyErr_Occurred()) {PyErr_Print(); PyErr_Clear();}
|
fazekasgy@31
|
289 }
|
fazekasgy@31
|
290
|
fazekasgy@31
|
291 Py_DECREF(pyMethod);
|
fazekasgy@31
|
292 Py_DECREF(pyArg1);
|
fazekasgy@31
|
293 Py_DECREF(pyArg2);
|
fazekasgy@31
|
294 Py_DECREF(pyArg3);
|
fazekasgy@31
|
295
|
fazekasgy@31
|
296 m_ti.PyValue_To_rValue(pyValue,rValue);
|
fazekasgy@31
|
297 if (!m_ti.error) {
|
fazekasgy@31
|
298 Py_DECREF(pyValue);
|
fazekasgy@31
|
299 } else {
|
fazekasgy@31
|
300 cerr << PLUGIN_ERROR << m_ti.lastError().message << endl;
|
fazekasgy@31
|
301 Py_CLEAR(pyValue);
|
fazekasgy@31
|
302 if (m_quitOnErrorFlag) exit(EXIT_FAILURE);
|
fazekasgy@31
|
303 }
|
fazekasgy@31
|
304 return rValue;
|
fazekasgy@31
|
305 }
|
fazekasgy@31
|
306
|
fazekasgy@0
|
307 };
|
fazekasgy@0
|
308
|
fazekasgy@0
|
309
|
fazekasgy@0
|
310 #endif
|