annotate PyExtensionModule.cpp @ 31:4f1894c7591b vampy2

Created Vampy2 branch
author fazekasgy
date Sun, 20 Sep 2009 17:31:20 +0000
parents
children c905122f79e7
rev   line source
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
fazekasgy@31 47 self->rt = new RealTime::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