diff PyExtensionModule.cpp @ 31:4f1894c7591b vampy2

Created Vampy2 branch
author fazekasgy
date Sun, 20 Sep 2009 17:31:20 +0000
parents
children c905122f79e7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PyExtensionModule.cpp	Sun Sep 20 17:31:20 2009 +0000
@@ -0,0 +1,292 @@
+#include <Python.h>
+#include "PyExtensionModule.h"
+#include "PyRealTime.h"
+#include "PyFeature.h"
+#include "PyFeatureSet.h"
+#include "PyParameterDescriptor.h"
+#include "PyOutputDescriptor.h"
+#include "vamp-sdk/Plugin.h"
+
+using namespace std;
+using namespace Vamp;
+using Vamp::Plugin;
+using Vamp::RealTime;
+
+/* Simple Example Function */
+
+static int five=5;
+
+PyObject*
+get_five(PyObject *self, PyObject *args)
+{
+    if(!PyArg_ParseTuple(args, ":five")) return NULL;
+    return Py_BuildValue("i", five);
+}
+
+
+/*			 Functions Exposed by Vampy 					*/
+
+/*			 Creating PyRealTime Objects 					*/
+
+
+/* New RealTime object from Frame (with given samplerate) */
+static PyObject *
+RealTime_frame2RealTime(PyObject *ignored, PyObject *args)
+{
+	long frame;
+	unsigned int sampleRate;
+
+    if (!PyArg_ParseTuple(args, "lI:realtime.frame2RealTime ", 
+	&frame,&sampleRate))
+		return NULL;
+
+	RealTimeObject *self;
+	self = PyObject_New(RealTimeObject, &RealTime_Type); 
+	if (self == NULL) return NULL;
+
+	self->rt = new RealTime::RealTime(
+	RealTime::frame2RealTime(frame,sampleRate));
+
+	return (PyObject *) self;
+}
+
+/*			 Creating PyParameterDescriptor Objects 			  	*/
+
+/* New ParameterDescriptor object 
+static PyObject *
+ParameterDescriptor_new(PyObject *ignored, PyObject *args)
+{
+
+    if (!PyArg_ParseTuple(args, ":ParameterDescriptor")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: ParameterDescriptor initialised with arguments.");
+		return NULL; 
+	  }
+
+	ParameterDescriptorObject *self = 
+	PyObject_New(ParameterDescriptorObject, &ParameterDescriptor_Type); 
+	if (self == NULL) return NULL;
+    self->dict = PyDict_New();
+	if (self->dict == NULL) return NULL;
+	return (PyObject *) self;
+}
+*/
+
+/*			 Creating PyOutputDescriptor Objects 					*/
+
+/* New OutputDescriptor object 
+static PyObject *
+OutputDescriptor_new(PyObject *ignored, PyObject *args)
+{
+
+	if (!PyArg_ParseTuple(args, ":OutputDescriptor")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: OutputDescriptor initialised with arguments.");
+		return NULL; 
+	  }
+
+	OutputDescriptorObject *self = 
+	PyObject_New(OutputDescriptorObject, &OutputDescriptor_Type); 
+	if (self == NULL) return NULL;
+    self->dict = PyDict_New();
+	if (self->dict == NULL) return NULL;
+	return (PyObject *) self;
+}
+*/
+
+/*			 Creating PyOutputList Objects 					*/
+
+/* New OutputList object */
+static PyObject *
+OutputList_new(PyObject *ignored, PyObject *args)
+{
+	if (!PyArg_ParseTuple(args, ":OutputList")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: OutputList initialised with arguments.");
+		return NULL; 
+	  }
+
+	return (PyObject *) PyList_New(0);
+}
+
+
+/*			 Creating PyParameterList Objects 					*/
+
+/* New ParameterList object */
+static PyObject *
+ParameterList_new(PyObject *ignored, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":ParameterList")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: ParameterList initialised with arguments.");
+		return NULL; 
+	  }
+	return (PyObject *) PyList_New(0);
+}
+
+
+/*			 Creating PyFeatureList Objects 					*/
+
+/* New FeatureList object 
+static PyObject *
+FeatureList_new(PyObject *ignored, PyObject *args)
+{
+	if (!PyArg_ParseTuple(args, ":FeatureList")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: FeatureList initialised with arguments.");
+		return NULL; 
+	  }
+	return (PyObject *) PyList_New(0);
+}
+*/
+
+/*			 Creating PyFeatureSet Objects 					*/
+
+/* New FeatureSet object 
+static PyObject *
+FeatureSet_new(PyObject *ignored, PyObject *args)
+{
+	if (!PyArg_ParseTuple(args, ":FeatureSet")) { 
+		PyErr_SetString(PyExc_TypeError, 
+		"Error: FeatureSet initialised with arguments.");
+		return NULL; 
+	  }
+	return (PyObject *) PyDict_New();
+}
+*/
+
+
+/*		 	Declare the methods exposed by the vampy module 		*/
+
+
+PyMethodDef VampyMethods[] = {
+/*NOTE: This is conventionally static, but limiting the scope
+	here will cause seg fault if the declared functions are 
+	called back from a Python function wrapped in a C++ class.*/
+    {"five", get_five, METH_VARARGS, "Return a number."},
+
+	{"frame2RealTime",	(PyCFunction)RealTime_frame2RealTime,	METH_VARARGS,
+		PyDoc_STR("frame2RealTime((int64)frame, (uint32)sampleRate ) -> returns new RealTime object from frame.")},
+
+	/*{"RealTime",	RealTime_new,		METH_VARARGS,
+		PyDoc_STR("RealTime() -> returns new RealTime object")},*/
+
+	/*{"Feature",	Feature_new,		METH_VARARGS,
+		PyDoc_STR("Feature() -> returns new Feature object")},*/
+
+	/*{"ParameterDescriptor",	ParameterDescriptor_new,		METH_VARARGS,
+		PyDoc_STR("ParameterDescriptor() -> returns new ParameterDescriptor object")},
+
+	{"OutputDescriptor",	OutputDescriptor_new,		METH_VARARGS,
+		PyDoc_STR("OutputDescriptor() -> returns new OutputDescriptor object")},
+
+	{"FeatureList",	FeatureList_new,		METH_VARARGS,
+		PyDoc_STR("FeatureList() -> returns new FeatureList object")},*/
+
+	{"OutputList",	OutputList_new,		METH_VARARGS,
+		PyDoc_STR("OutputList() -> returns new OutputList object")},
+
+	{"ParameterList",	ParameterList_new,		METH_VARARGS,
+		PyDoc_STR("ParameterList() -> returns new ParameterList object")},
+
+    {NULL, NULL, 0, NULL} 
+};
+
+/* Module Documentation */
+// PyDoc_STRVAR(vampy_doc,"This module exposes Vamp plugin data type wrappers.");
+
+PyMODINIT_FUNC
+initvampy(void)
+{
+	PyObject* module;
+
+	// if (PyType_Ready(&Feature_Type) < 0) return;
+	/// Why do we get a segfault if this is initialised here?
+	/*PyType_Ready adds these object to the GC.
+	This is OK for an extension module, but it is a mistake here, 
+	because the reference count will be decremented in the Vamp 
+	wrapper plugin outside the interpreter.
+	When the GC tries to visit a deallocated object, it will throw up.*/
+	
+	RealTime_Type.ob_type = &PyType_Type;
+	Feature_Type.ob_type = &PyType_Type;
+	OutputDescriptor_Type.ob_type = &PyType_Type;
+	ParameterDescriptor_Type.ob_type = &PyType_Type;
+	initFeatureSetType(); /// this is derived from the builtin dict
+
+	PyImport_AddModule("vampy");
+	module = Py_InitModule("vampy", VampyMethods);
+	if (!module) return;
+	
+	Py_INCREF(&RealTime_Type);
+    PyModule_AddObject(module,"RealTime",(PyObject*)&RealTime_Type);
+	// Py_INCREF(&RealTime_Type);
+
+	Py_INCREF((PyObject*)&Feature_Type);
+	PyModule_AddObject(module,"Feature",(PyObject*)&Feature_Type);
+	// Py_INCREF((PyObject*)&Feature_Type);
+
+	Py_INCREF((PyObject*)&FeatureSet_Type);
+	PyModule_AddObject(module,"FeatureSet",(PyObject*)&FeatureSet_Type);
+	// Py_INCREF((PyObject*)&FeatureSet_Type);
+
+	Py_INCREF((PyObject*)&OutputDescriptor_Type);
+	PyModule_AddObject(module,"OutputDescriptor",(PyObject*)&OutputDescriptor_Type);
+
+	Py_INCREF((PyObject*)&ParameterDescriptor_Type);
+	PyModule_AddObject(module,"ParameterDescriptor",(PyObject*)&ParameterDescriptor_Type);
+	
+	cerr << "Vampy: extension module initialised." << endl;
+}
+
+/*
+NOTE: Why do we need to clean up the module?
+
+The module exposed by Vampy to the embedded interpreter
+contains callback functions. These functions are accessed
+via function pointers stored in the extension module dictionary.
+
+Unfortunately, when the Vampy shared library is unloaded and
+reloaded again during a host session, these addresses might
+change. Therefore, we reinitialise the module dict before 
+each use. However, this will cause garbage collection errors
+or segmentation faults, when elements of the dict of the 
+previous session are attempted to free. Therefore, we hold
+a global reference count to all initialised Vampy plugins,
+and when this reaches zero, we clean up the module dict.
+
+This is an attempt to catch the moment when the shared lib
+is finally unloaded and the references are still point to valid
+memory addresses.
+
+Why doesn't the GC clean this up correctly?
+
+In a normal Python session the GC would deallocate the module
+dict at the end. In embedded python, although the GC appears
+to be called when the shared lib is unloaded, the interpreter
+is reused. Since there is no C/API call to unload modules,
+and at the time of unloading vampy the wrapped function pointers
+are still valid, the GC doesn't collect them, nor are they freed
+by the interpreter. When vampy is reloaded however, the module
+dict will contain invalid addresses. The above procedure solves
+this problem.
+
+
+*/
+
+void cleanModule(void)
+{
+	PyObject *m = PyImport_AddModule("vampy");
+	if (!m) cerr << "Destr: PyImport_AddModule returned NULL!" << endl;
+	else {
+		// cerr << "Destr: Add module found existing." << endl;
+		PyObject *dict = PyModule_GetDict(m);
+		Py_ssize_t ln = PyDict_Size(dict);
+		cerr << "Destr: Size of module dict = " << (int) ln << endl;
+		/// Clean the module dictionary.
+		PyDict_Clear(dict);
+	    ln = PyDict_Size(dict);
+		cerr << "Destr: Cleaned size of module dict = " << (int) ln << endl;
+	}
+}
+