Mercurial > hg > vampy
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 |