comparison PyExtensionModule.cpp @ 31:4f1894c7591b vampy2

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