fazekasgy@31
|
1 #include <Python.h>
|
fazekasgy@31
|
2 #include "PyExtensionModule.h"
|
fazekasgy@31
|
3 #include "PyRealTime.h"
|
fazekasgy@31
|
4 #include "PyFeature.h"
|
fazekasgy@31
|
5 #include "PyFeatureSet.h"
|
fazekasgy@31
|
6 #include "PyParameterDescriptor.h"
|
fazekasgy@31
|
7 #include "PyOutputDescriptor.h"
|
fazekasgy@31
|
8 #include "vamp-sdk/Plugin.h"
|
fazekasgy@31
|
9
|
fazekasgy@31
|
10 using namespace std;
|
fazekasgy@31
|
11 using namespace Vamp;
|
fazekasgy@31
|
12 using Vamp::Plugin;
|
fazekasgy@31
|
13 using Vamp::RealTime;
|
fazekasgy@31
|
14
|
fazekasgy@31
|
15 /* Simple Example Function */
|
fazekasgy@31
|
16
|
fazekasgy@31
|
17 static int five=5;
|
fazekasgy@31
|
18
|
fazekasgy@31
|
19 PyObject*
|
fazekasgy@31
|
20 get_five(PyObject *self, PyObject *args)
|
fazekasgy@31
|
21 {
|
fazekasgy@31
|
22 if(!PyArg_ParseTuple(args, ":five")) return NULL;
|
fazekasgy@31
|
23 return Py_BuildValue("i", five);
|
fazekasgy@31
|
24 }
|
fazekasgy@31
|
25
|
fazekasgy@31
|
26
|
fazekasgy@31
|
27 /* Functions Exposed by Vampy */
|
fazekasgy@31
|
28
|
fazekasgy@31
|
29 /* Creating PyRealTime Objects */
|
fazekasgy@31
|
30
|
fazekasgy@31
|
31
|
fazekasgy@31
|
32 /* New RealTime object from Frame (with given samplerate) */
|
fazekasgy@31
|
33 static PyObject *
|
fazekasgy@31
|
34 RealTime_frame2RealTime(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
35 {
|
fazekasgy@31
|
36 long frame;
|
fazekasgy@31
|
37 unsigned int sampleRate;
|
fazekasgy@31
|
38
|
fazekasgy@31
|
39 if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ",
|
fazekasgy@31
|
40 &frame,&sampleRate))
|
fazekasgy@31
|
41 return NULL;
|
fazekasgy@31
|
42
|
fazekasgy@31
|
43 RealTimeObject *self;
|
fazekasgy@31
|
44 self = PyObject_New(RealTimeObject, &RealTime_Type);
|
fazekasgy@31
|
45 if (self == NULL) return NULL;
|
fazekasgy@31
|
46
|
cannam@34
|
47 self->rt = new RealTime(
|
fazekasgy@31
|
48 RealTime::frame2RealTime(frame,sampleRate));
|
fazekasgy@31
|
49
|
fazekasgy@31
|
50 return (PyObject *) self;
|
fazekasgy@31
|
51 }
|
fazekasgy@31
|
52
|
fazekasgy@31
|
53 /* Creating PyParameterDescriptor Objects */
|
fazekasgy@31
|
54
|
fazekasgy@31
|
55 /* New ParameterDescriptor object
|
fazekasgy@31
|
56 static PyObject *
|
fazekasgy@31
|
57 ParameterDescriptor_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
58 {
|
fazekasgy@31
|
59
|
fazekasgy@31
|
60 if (!PyArg_ParseTuple(args, ":ParameterDescriptor")) {
|
fazekasgy@31
|
61 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
62 "Error: ParameterDescriptor initialised with arguments.");
|
fazekasgy@31
|
63 return NULL;
|
fazekasgy@31
|
64 }
|
fazekasgy@31
|
65
|
fazekasgy@31
|
66 ParameterDescriptorObject *self =
|
fazekasgy@31
|
67 PyObject_New(ParameterDescriptorObject, &ParameterDescriptor_Type);
|
fazekasgy@31
|
68 if (self == NULL) return NULL;
|
fazekasgy@31
|
69 self->dict = PyDict_New();
|
fazekasgy@31
|
70 if (self->dict == NULL) return NULL;
|
fazekasgy@31
|
71 return (PyObject *) self;
|
fazekasgy@31
|
72 }
|
fazekasgy@31
|
73 */
|
fazekasgy@31
|
74
|
fazekasgy@31
|
75 /* Creating PyOutputDescriptor Objects */
|
fazekasgy@31
|
76
|
fazekasgy@31
|
77 /* New OutputDescriptor object
|
fazekasgy@31
|
78 static PyObject *
|
fazekasgy@31
|
79 OutputDescriptor_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
80 {
|
fazekasgy@31
|
81
|
fazekasgy@31
|
82 if (!PyArg_ParseTuple(args, ":OutputDescriptor")) {
|
fazekasgy@31
|
83 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
84 "Error: OutputDescriptor initialised with arguments.");
|
fazekasgy@31
|
85 return NULL;
|
fazekasgy@31
|
86 }
|
fazekasgy@31
|
87
|
fazekasgy@31
|
88 OutputDescriptorObject *self =
|
fazekasgy@31
|
89 PyObject_New(OutputDescriptorObject, &OutputDescriptor_Type);
|
fazekasgy@31
|
90 if (self == NULL) return NULL;
|
fazekasgy@31
|
91 self->dict = PyDict_New();
|
fazekasgy@31
|
92 if (self->dict == NULL) return NULL;
|
fazekasgy@31
|
93 return (PyObject *) self;
|
fazekasgy@31
|
94 }
|
fazekasgy@31
|
95 */
|
fazekasgy@31
|
96
|
fazekasgy@31
|
97 /* Creating PyOutputList Objects */
|
fazekasgy@31
|
98
|
fazekasgy@31
|
99 /* New OutputList object */
|
fazekasgy@31
|
100 static PyObject *
|
fazekasgy@31
|
101 OutputList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
102 {
|
fazekasgy@31
|
103 if (!PyArg_ParseTuple(args, ":OutputList")) {
|
fazekasgy@31
|
104 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
105 "Error: OutputList initialised with arguments.");
|
fazekasgy@31
|
106 return NULL;
|
fazekasgy@31
|
107 }
|
fazekasgy@31
|
108
|
fazekasgy@31
|
109 return (PyObject *) PyList_New(0);
|
fazekasgy@31
|
110 }
|
fazekasgy@31
|
111
|
fazekasgy@31
|
112
|
fazekasgy@31
|
113 /* Creating PyParameterList Objects */
|
fazekasgy@31
|
114
|
fazekasgy@31
|
115 /* New ParameterList object */
|
fazekasgy@31
|
116 static PyObject *
|
fazekasgy@31
|
117 ParameterList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
118 {
|
fazekasgy@31
|
119 if (!PyArg_ParseTuple(args, ":ParameterList")) {
|
fazekasgy@31
|
120 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
121 "Error: ParameterList initialised with arguments.");
|
fazekasgy@31
|
122 return NULL;
|
fazekasgy@31
|
123 }
|
fazekasgy@31
|
124 return (PyObject *) PyList_New(0);
|
fazekasgy@31
|
125 }
|
fazekasgy@31
|
126
|
fazekasgy@31
|
127
|
fazekasgy@31
|
128 /* Creating PyFeatureList Objects */
|
fazekasgy@31
|
129
|
fazekasgy@31
|
130 /* New FeatureList object
|
fazekasgy@31
|
131 static PyObject *
|
fazekasgy@31
|
132 FeatureList_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
133 {
|
fazekasgy@31
|
134 if (!PyArg_ParseTuple(args, ":FeatureList")) {
|
fazekasgy@31
|
135 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
136 "Error: FeatureList initialised with arguments.");
|
fazekasgy@31
|
137 return NULL;
|
fazekasgy@31
|
138 }
|
fazekasgy@31
|
139 return (PyObject *) PyList_New(0);
|
fazekasgy@31
|
140 }
|
fazekasgy@31
|
141 */
|
fazekasgy@31
|
142
|
fazekasgy@31
|
143 /* Creating PyFeatureSet Objects */
|
fazekasgy@31
|
144
|
fazekasgy@31
|
145 /* New FeatureSet object
|
fazekasgy@31
|
146 static PyObject *
|
fazekasgy@31
|
147 FeatureSet_new(PyObject *ignored, PyObject *args)
|
fazekasgy@31
|
148 {
|
fazekasgy@31
|
149 if (!PyArg_ParseTuple(args, ":FeatureSet")) {
|
fazekasgy@31
|
150 PyErr_SetString(PyExc_TypeError,
|
fazekasgy@31
|
151 "Error: FeatureSet initialised with arguments.");
|
fazekasgy@31
|
152 return NULL;
|
fazekasgy@31
|
153 }
|
fazekasgy@31
|
154 return (PyObject *) PyDict_New();
|
fazekasgy@31
|
155 }
|
fazekasgy@31
|
156 */
|
fazekasgy@31
|
157
|
fazekasgy@31
|
158
|
fazekasgy@31
|
159 /* Declare the methods exposed by the vampy module */
|
fazekasgy@31
|
160
|
fazekasgy@31
|
161
|
fazekasgy@31
|
162 PyMethodDef VampyMethods[] = {
|
fazekasgy@31
|
163 /*NOTE: This is conventionally static, but limiting the scope
|
fazekasgy@31
|
164 here will cause seg fault if the declared functions are
|
fazekasgy@31
|
165 called back from a Python function wrapped in a C++ class.*/
|
fazekasgy@31
|
166 {"five", get_five, METH_VARARGS, "Return a number."},
|
fazekasgy@31
|
167
|
fazekasgy@31
|
168 {"frame2RealTime", (PyCFunction)RealTime_frame2RealTime, METH_VARARGS,
|
fazekasgy@31
|
169 PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")},
|
fazekasgy@31
|
170
|
fazekasgy@31
|
171 /*{"RealTime", RealTime_new, METH_VARARGS,
|
fazekasgy@31
|
172 PyDoc_STR("RealTime() -> returns new RealTime object")},*/
|
fazekasgy@31
|
173
|
fazekasgy@31
|
174 /*{"Feature", Feature_new, METH_VARARGS,
|
fazekasgy@31
|
175 PyDoc_STR("Feature() -> returns new Feature object")},*/
|
fazekasgy@31
|
176
|
fazekasgy@31
|
177 /*{"ParameterDescriptor", ParameterDescriptor_new, METH_VARARGS,
|
fazekasgy@31
|
178 PyDoc_STR("ParameterDescriptor() -> returns new ParameterDescriptor object")},
|
fazekasgy@31
|
179
|
fazekasgy@31
|
180 {"OutputDescriptor", OutputDescriptor_new, METH_VARARGS,
|
fazekasgy@31
|
181 PyDoc_STR("OutputDescriptor() -> returns new OutputDescriptor object")},
|
fazekasgy@31
|
182
|
fazekasgy@31
|
183 {"FeatureList", FeatureList_new, METH_VARARGS,
|
fazekasgy@31
|
184 PyDoc_STR("FeatureList() -> returns new FeatureList object")},*/
|
fazekasgy@31
|
185
|
fazekasgy@31
|
186 {"OutputList", OutputList_new, METH_VARARGS,
|
fazekasgy@31
|
187 PyDoc_STR("OutputList() -> returns new OutputList object")},
|
fazekasgy@31
|
188
|
fazekasgy@31
|
189 {"ParameterList", ParameterList_new, METH_VARARGS,
|
fazekasgy@31
|
190 PyDoc_STR("ParameterList() -> returns new ParameterList object")},
|
fazekasgy@31
|
191
|
fazekasgy@31
|
192 {NULL, NULL, 0, NULL}
|
fazekasgy@31
|
193 };
|
fazekasgy@31
|
194
|
fazekasgy@31
|
195 /* Module Documentation */
|
fazekasgy@31
|
196 // PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers.");
|
fazekasgy@31
|
197
|
fazekasgy@31
|
198 PyMODINIT_FUNC
|
fazekasgy@31
|
199 initvampy(void)
|
fazekasgy@31
|
200 {
|
fazekasgy@31
|
201 PyObject* module;
|
fazekasgy@31
|
202
|
fazekasgy@31
|
203 // if (PyType_Ready(&Feature_Type) < 0) return;
|
fazekasgy@31
|
204 /// Why do we get a segfault if this is initialised here?
|
fazekasgy@31
|
205 /*PyType_Ready adds these object to the GC.
|
fazekasgy@31
|
206 This is OK for an extension module, but it is a mistake here,
|
fazekasgy@31
|
207 because the reference count will be decremented in the Vamp
|
fazekasgy@31
|
208 wrapper plugin outside the interpreter.
|
fazekasgy@31
|
209 When the GC tries to visit a deallocated object, it will throw up.*/
|
fazekasgy@31
|
210
|
fazekasgy@31
|
211 RealTime_Type.ob_type = &PyType_Type;
|
fazekasgy@31
|
212 Feature_Type.ob_type = &PyType_Type;
|
fazekasgy@31
|
213 OutputDescriptor_Type.ob_type = &PyType_Type;
|
fazekasgy@31
|
214 ParameterDescriptor_Type.ob_type = &PyType_Type;
|
fazekasgy@31
|
215 initFeatureSetType(); /// this is derived from the builtin dict
|
fazekasgy@31
|
216
|
fazekasgy@31
|
217 PyImport_AddModule("vampy");
|
fazekasgy@31
|
218 module = Py_InitModule("vampy", VampyMethods);
|
fazekasgy@31
|
219 if (!module) return;
|
fazekasgy@31
|
220
|
fazekasgy@31
|
221 Py_INCREF(&RealTime_Type);
|
fazekasgy@31
|
222 PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type);
|
fazekasgy@31
|
223 // Py_INCREF(&RealTime_Type);
|
fazekasgy@31
|
224
|
fazekasgy@31
|
225 Py_INCREF((PyObject*)&Feature_Type);
|
fazekasgy@31
|
226 PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type);
|
fazekasgy@31
|
227 // Py_INCREF((PyObject*)&Feature_Type);
|
fazekasgy@31
|
228
|
fazekasgy@31
|
229 Py_INCREF((PyObject*)&FeatureSet_Type);
|
fazekasgy@31
|
230 PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type);
|
fazekasgy@31
|
231 // Py_INCREF((PyObject*)&FeatureSet_Type);
|
fazekasgy@31
|
232
|
fazekasgy@31
|
233 Py_INCREF((PyObject*)&OutputDescriptor_Type);
|
fazekasgy@31
|
234 PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type);
|
fazekasgy@31
|
235
|
fazekasgy@31
|
236 Py_INCREF((PyObject*)&ParameterDescriptor_Type);
|
fazekasgy@31
|
237 PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type);
|
fazekasgy@31
|
238
|
fazekasgy@31
|
239 cerr << "Vampy: extension module initialised." << endl;
|
fazekasgy@31
|
240 }
|
fazekasgy@31
|
241
|
fazekasgy@31
|
242 /*
|
fazekasgy@31
|
243 NOTE: Why do we need to clean up the module?
|
fazekasgy@31
|
244
|
fazekasgy@31
|
245 The module exposed by Vampy to the embedded interpreter
|
fazekasgy@31
|
246 contains callback functions. These functions are accessed
|
fazekasgy@31
|
247 via function pointers stored in the extension module dictionary.
|
fazekasgy@31
|
248
|
fazekasgy@31
|
249 Unfortunately, when the Vampy shared library is unloaded and
|
fazekasgy@31
|
250 reloaded again during a host session, these addresses might
|
fazekasgy@31
|
251 change. Therefore, we reinitialise the module dict before
|
fazekasgy@31
|
252 each use. However, this will cause garbage collection errors
|
fazekasgy@31
|
253 or segmentation faults, when elements of the dict of the
|
fazekasgy@31
|
254 previous session are attempted to free. Therefore, we hold
|
fazekasgy@31
|
255 a global reference count to all initialised Vampy plugins,
|
fazekasgy@31
|
256 and when this reaches zero, we clean up the module dict.
|
fazekasgy@31
|
257
|
fazekasgy@31
|
258 This is an attempt to catch the moment when the shared lib
|
fazekasgy@31
|
259 is finally unloaded and the references are still point to valid
|
fazekasgy@31
|
260 memory addresses.
|
fazekasgy@31
|
261
|
fazekasgy@31
|
262 Why doesn't the GC clean this up correctly?
|
fazekasgy@31
|
263
|
fazekasgy@31
|
264 In a normal Python session the GC would deallocate the module
|
fazekasgy@31
|
265 dict at the end. In embedded python, although the GC appears
|
fazekasgy@31
|
266 to be called when the shared lib is unloaded, the interpreter
|
fazekasgy@31
|
267 is reused. Since there is no C/API call to unload modules,
|
fazekasgy@31
|
268 and at the time of unloading vampy the wrapped function pointers
|
fazekasgy@31
|
269 are still valid, the GC doesn't collect them, nor are they freed
|
fazekasgy@31
|
270 by the interpreter. When vampy is reloaded however, the module
|
fazekasgy@31
|
271 dict will contain invalid addresses. The above procedure solves
|
fazekasgy@31
|
272 this problem.
|
fazekasgy@31
|
273
|
fazekasgy@31
|
274
|
fazekasgy@31
|
275 */
|
fazekasgy@31
|
276
|
fazekasgy@31
|
277 void cleanModule(void)
|
fazekasgy@31
|
278 {
|
fazekasgy@31
|
279 PyObject *m = PyImport_AddModule("vampy");
|
fazekasgy@31
|
280 if (!m) cerr << "Destr: PyImport_AddModule returned NULL!" << endl;
|
fazekasgy@31
|
281 else {
|
fazekasgy@31
|
282 // cerr << "Destr: Add module found existing." << endl;
|
fazekasgy@31
|
283 PyObject *dict = PyModule_GetDict(m);
|
fazekasgy@31
|
284 Py_ssize_t ln = PyDict_Size(dict);
|
fazekasgy@31
|
285 cerr << "Destr: Size of module dict = " << (int) ln << endl;
|
fazekasgy@31
|
286 /// Clean the module dictionary.
|
fazekasgy@31
|
287 PyDict_Clear(dict);
|
fazekasgy@31
|
288 ln = PyDict_Size(dict);
|
fazekasgy@31
|
289 cerr << "Destr: Cleaned size of module dict = " << (int) ln << endl;
|
fazekasgy@31
|
290 }
|
fazekasgy@31
|
291 }
|
fazekasgy@31
|
292
|