changeset 32:a8231788216c vampy2

Vampy2: accept numpy array return types.
author fazekasgy
date Mon, 21 Sep 2009 13:56:28 +0000
parents 4f1894c7591b
children c4da8d559872
files Makefile PyFeatureSet.cpp PyFeatureSet.h PyPlugin.cpp PyTypeInterface.cpp PyTypeInterface.h
diffstat 6 files changed, 202 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sun Sep 20 17:31:20 2009 +0000
+++ b/Makefile	Mon Sep 21 13:56:28 2009 +0000
@@ -1,17 +1,20 @@
 
-CXXFLAGS	:= -I../vamp-plugin-sdk -O2 -Wall -I/usr/include/python2.5 #-I../host/pyRealTime.h #-fvisibility=hidden
+CXXFLAGS	:= -DHAVE_NUMPY -I../vamp-plugin-sdk -O2 -Wall -I/usr/include/python2.5 -I/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/numpy/core/include/numpy/  #-fvisibility=hidden
+#without numpy headers available:
+#CXXFLAGS	:= -I../vamp-plugin-sdk -O2 -Wall -I/usr/include/python2.5  #-I../host/pyRealTime.h #-fvisibility=hidden
 LDFLAGS		:= -L../vamp-plugin-sdk/vamp-sdk -lvamp-sdk -dynamiclib -lpython2.5 -lpthread
 
-all: vampy.dylib
+default: vampy.dylib 
+all: vampy.dylib vampymod.so
 
 PyExtensionModule.a: PyExtensionModule.o PyRealTime.o PyFeature.o PyParameterDescriptor.o PyOutputDescriptor.o PyFeatureSet.o 
 	libtool -static $^ -o $@ 
 
 # The standard python extension is .so (even on the Mac)
-PyExtensionModule.so: PyExtensionModule.o PyRealTime.o PyFeature.o PyParameterDescriptor.o PyOutputDescriptor.o PyFeatureSet.o 
+vampymod.so: PyExtensionModule.o PyRealTime.o PyFeature.o PyParameterDescriptor.o PyOutputDescriptor.o PyFeatureSet.o 
 	g++ -shared $^ -o $@ $(LDFLAGS) 
 
-vampy.dylib:	PyPlugin.o PyPlugScanner.o vampy-main.o Mutex.o PyTypeInterface.o PyExtensionModule.a
+vampy.dylib: PyPlugin.o PyPlugScanner.o vampy-main.o Mutex.o PyTypeInterface.o PyExtensionModule.a 
 	g++ -shared $^ -o $@ $(LDFLAGS) 
 
 # Install plugin
--- a/PyFeatureSet.cpp	Sun Sep 20 17:31:20 2009 +0000
+++ b/PyFeatureSet.cpp	Mon Sep 21 13:56:28 2009 +0000
@@ -9,8 +9,6 @@
 {
     if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
         return -1;
-    self->state = 0;
-	cerr << "FeatureSet initialised" << endl;
     return 0;
 }
 
@@ -20,7 +18,7 @@
 	// cerr << "called FeatureSetObject_ass_sub" << endl;
 	if (!PyInt_CheckExact(v)) {
 		/// TODO: Set ValueError here.
-		cerr << "Output index must be positive integer" << endl;
+		cerr << "ValueError: Output index must be positive integer" << endl;
 		return 0;
 	}
 	if (w == NULL)
--- a/PyFeatureSet.h	Sun Sep 20 17:31:20 2009 +0000
+++ b/PyFeatureSet.h	Mon Sep 21 13:56:28 2009 +0000
@@ -5,7 +5,6 @@
 
 typedef struct {
     PyDictObject dict;
-    int state;
 } FeatureSetObject;
 
 PyAPI_DATA(PyTypeObject) FeatureSet_Type;
@@ -14,7 +13,6 @@
 #define PyFeatureSet_Check(v) PyObject_TypeCheck(v, &FeatureSet_Type)
 // #define PyFeatureSet_CheckExact(v)	((v)->ob_type == &PyDict_Type)
 // #define PyFeatureSet_Check(v) PyObject_TypeCheck(v, &PyDict_Type)
-
 // #define PyFeature_AS_DICT(v) ((const FeatureObject* const) (v))->dict
 
 void initFeatureSetType(void);
--- a/PyPlugin.cpp	Sun Sep 20 17:31:20 2009 +0000
+++ b/PyPlugin.cpp	Mon Sep 21 13:56:28 2009 +0000
@@ -364,8 +364,8 @@
 	PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
 	for (size_t i=0; i < m_channels; ++i) {
 		//Expose memory using the Buffer Interface of C/API		
-		//This will virtually pass a pointer which can be 
-		//recasted in Python code as float or complex array             
+		//This will pass a pointer which can be recasted 
+		//in Python code as float or complex array             
 		PyObject *pyBuffer = PyBuffer_FromMemory
 		((void *) (float *) inputBuffers[i], 
 		(Py_ssize_t) sizeof(float) * m_blockSize);
--- a/PyTypeInterface.cpp	Sun Sep 20 17:31:20 2009 +0000
+++ b/PyTypeInterface.cpp	Mon Sep 21 13:56:28 2009 +0000
@@ -2,6 +2,9 @@
 */
 
 #include <Python.h>
+#ifdef HAVE_NUMPY
+#include "arrayobject.h"
+#endif
 #include "PyTypeInterface.h"
 #include "PyRealTime.h"
 #include "PyExtensionModule.h"
@@ -25,7 +28,7 @@
 static bool isMapInitialised = false;
 
 /*  Note: NO FUNCTION IN THIS CLASS SHOULD ALTER REFERENCE COUNTS
-	(EXCEPT FOR TEMPORARY PYOBJECTS)!  						   		*/
+	(EXCEPT FOR TEMPORARY PYTHON OBJECTS)!  						 */
 
 PyTypeInterface::PyTypeInterface() : 
 	m_strict(false),
@@ -405,29 +408,51 @@
 	return Output;
 }
 
-//convert Python list to C++ vector of floats
+//convert PyFeature.value (typically a list or numpy array) to C++ vector of floats
 std::vector<float> 
-PyTypeInterface::PyValue_To_FloatVector (PyObject *inputList) const 
+PyTypeInterface::PyValue_To_FloatVector (PyObject *pyValue) const 
 {
-	typedef std::vector<float> floatVector;
 	std::vector<float> Output;
+
+#ifdef HAVE_NUMPY
+	/// Check for NumPy Array: this requires linking with numpy
+	/// but, we don't really need this macro
+	// if (PyArray_CheckExact(inputList)) cerr << "PyPyArray_CheckExact OK" << endl;
 	
-	/// Check for NumPy Array
-	if (PyObject_HasAttrString(inputList,"__array_struct__")) {
-		int vectorLength;
-		float *dataptr = getNumPyObjectData(inputList,vectorLength);
-		if (dataptr != 0) cerr << "Numpy array found: " << vectorLength << endl;
-		// Output = *dataptr;
+	/// numpy array
+	if (PyObject_HasAttrString(pyValue,"__array_struct__")) {
+		return PyArray_To_FloatVector(pyValue);
 	}
+#endif
+
+	/// python list
+	if (PyList_Check(pyValue)) {
+		return PyList_To_FloatVector(pyValue);
+	}
+	
+	/// assume a single number 
+	/// this allows to write e.g. Feature.values = 5 instead of [5.00]
+	Output.push_back(PyValue_To_Float(pyValue));
+	return Output;
+	
+	/// TODO : set error
+	
+}
+
+//convert a list of python floats
+std::vector<float> 
+PyTypeInterface::PyList_To_FloatVector (PyObject *inputList) const 
+{
+	std::vector<float> Output;
 	
 	float ListElement;
 	PyObject *pyFloat = NULL;
 	
 	if (!PyList_Check(inputList)) return Output; 
 
-	for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) {
+	for (Py_ssize_t i = 0; i < PyList_GET_SIZE(inputList); ++i) {
 		//Get next list item (Borrowed Reference)
-		pyFloat =  PyList_GET_ITEM(inputList,k);
+		pyFloat = PyList_GET_ITEM(inputList,i);
 		ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
 #ifdef _DEBUG
 		cerr << "value: " << ListElement << endl;
@@ -438,6 +463,50 @@
 	return Output;
 }
 
+#ifdef HAVE_NUMPY
+std::vector<float> 
+PyTypeInterface::PyArray_To_FloatVector (PyObject *pyValue) const 
+{
+	std::vector<float> Output;
+	 
+	/// we don't verify the array here as it'd be duplicated mostly
+	// if (!PyObject_HasAttrString(pyValue,"__array_struct__")) {
+	// 	return Output;
+	// }
+
+	PyArrayObject* pyArray = (PyArrayObject*) pyValue;
+	PyArray_Descr* descr = pyArray->descr;
+	
+	/// check raw data pointer
+	if (pyArray->data == 0) return Output;
+
+	/// check dimensions
+	if (pyArray->nd != 1) {
+		cerr << "Error: array must be 1D" << endl;
+		return Output;
+	}
+
+#ifdef _DEBUG
+	cerr << "Numpy array verified." << endl;
+#endif	
+
+	switch (descr->type_num)
+	{
+		case NPY_FLOAT :
+			return PyArray_Convert<float,float>(pyArray->data,pyArray->dimensions[0]);
+		case NPY_DOUBLE :
+			return PyArray_Convert<float,double>(pyArray->data,pyArray->dimensions[0]);
+		case NPY_INT :
+			return PyArray_Convert<float,int>(pyArray->data,pyArray->dimensions[0]);
+		case NPY_LONG :
+			return PyArray_Convert<float,long>(pyArray->data,pyArray->dimensions[0]);
+		
+		default :
+		cerr << "Error. Unsupported element type in NumPy array object." << endl;
+			return Output;
+	}
+}
+#endif
 
 /*			 			Vamp API Specific Types				  		
 
@@ -580,7 +649,7 @@
 Vamp::Plugin::FeatureSet
 PyTypeInterface::PyValue_To_FeatureSet(PyObject* pyValue) const
 {
-	Vamp::Plugin::FeatureSet rFeatureSet; /// PyFeatureSet is an int map
+	Vamp::Plugin::FeatureSet rFeatureSet; /// map<int>
 	if (pyValue == NULL) {
 		cerr << "NULL FeatureSet" << endl;
 		return rFeatureSet;
@@ -893,77 +962,6 @@
 
 /*			   			  	Utilities						  		*/
 
-//return a pointer to the data in the numPy array
-float* 
-PyTypeInterface::getNumPyObjectData(PyObject *object, int &length) const
-{
-
-	char attr_name[]="__array_struct__";
-
-	//check if we passed in a NumPy array object
-	if (!PyObject_HasAttrString(object,attr_name)) {
-		// PyErr_SetString(PyExc_TypeError,
-		// "Input object has no __array_struct__ attribute. NumPy array required.");
-		return NULL;		
-	}
-
-	//retrieve __array_struct__ interface
-	object = PyObject_GetAttrString(object,attr_name);	
-
-	//check whether we found CObjects
-	if (!PyCObject_Check(object)) {
-		PyErr_SetString(PyExc_TypeError,
-		"The passed __array_struct__ interface is not a valid C Object.");
-		return NULL; 
-	}
-
-
-	//check if the pointers directed to the integer '2'
-	int *check = (int *) PyCObject_AsVoidPtr (object);
-
-	if (*check != 2 ) {
-		PyErr_SetString(PyExc_TypeError,
-		"A C Object __array_struct__ required as inputs");
-		return NULL; 
-	}
-
-	//convert CObjects to Array interfaces
-	PyArrayInterface *arrayInterface = 
-	(PyArrayInterface *) PyCObject_AsVoidPtr (object);
-
-	//check array dimension: should be 1
-	int inputDim = arrayInterface->nd;
-	
-	if (inputDim > 1 ) {
-		PyErr_SetString(PyExc_TypeError,
-		"Array dimensions must not exceed one.");
-		return NULL;		
-	}
-
-	// check if vector size is sane	
-	Py_intptr_t arrayLength = arrayInterface->shape[0];
-	length = (int) arrayLength;
-	
-	// if (arrayLength < 8 || arrayLength > 65536 ) {
-	// 	PyErr_SetString(PyExc_TypeError,
-	// 	"Array length is out of bounds.");
-	// 	return NULL;		
-	// 	}
-
-	//check type; must be float32
-	char arrayType = arrayInterface->typekind;
-
-	if (arrayType != 'f' ) {
-		PyErr_SetString(PyExc_TypeError,
-		"Floating point arrays required.");
-		return NULL;		
-	}
-
-	//return data vector address
-	return (float*) arrayInterface->data;
-	
-}
-
 /// get the type name of an object
 std::string
 PyTypeInterface::PyValue_Get_TypeName(PyObject* pyValue) const
--- a/PyTypeInterface.h	Sun Sep 20 17:31:20 2009 +0000
+++ b/PyTypeInterface.h	Mon Sep 21 13:56:28 2009 +0000
@@ -7,13 +7,15 @@
 
 #ifndef _PY_TYPE_INTERFACE_H_
 #define _PY_TYPE_INTERFACE_H_
-
-#include "vamp-sdk/Plugin.h"
 #include <Python.h>
+#ifdef HAVE_NUMPY
+#include "arrayobject.h"
+#endif
 #include "PyExtensionModule.h"
 #include <vector>
 #include <queue>
 #include <string>
+#include "vamp-sdk/Plugin.h"
 //#include <typeinfo>
 
 
@@ -73,24 +75,6 @@
 	label
 	};
 
-
-/// sutructure of NumPy array interface:
-/// this is all we need to support numpy without direct dependency
-typedef struct {
-    int two;              /* contains the integer 2 -- simple sanity check */
-    int nd;               /* number of dimensions */
-    char typekind;        /* kind in array --- character code of typestr */
-    int itemsize;         /* size of each element */
-    int flags;            /* flags indicating how the data should be interpreted */
-                          /*   must set ARR_HAS_DESCR bit to validate descr */
-    Py_intptr_t *shape;   /* A length-nd array of shape information */
-    Py_intptr_t *strides; /* A length-nd array of stride information */
-    void *data;           /* A pointer to the first element of the array */
-    PyObject *descr;      /* NULL or data-description (same as descr key */
-                          /*        of __array_interface__) -- must set ARR_HAS_DESCR */
-                          /*        flag or this will be ignored. */
-} PyArrayInterface;
-
 /* C++ mapping of PyNone Type*/
 typedef struct NoneType {};
 
@@ -138,15 +122,18 @@
 	// Sequence types
 	std::vector<std::string> PyValue_To_StringVector (PyObject*) const;
 	std::vector<float> PyValue_To_FloatVector (PyObject*) const;
+	std::vector<float> PyList_To_FloatVector (PyObject*) const;
 
 	// Numpy types
-	float* getNumPyObjectData(PyObject *object, int &length) const; 
+#ifdef HAVE_NUMPY
+	std::vector<float> PyArray_To_FloatVector (PyObject *pyValue) const;
+#endif	
 	
 
 /* 						Template functions 							*/
 
 
-	/// Common wrappers to set a value in one of these structs. (to be used in template functions)
+	/// Common wrappers to set values in Vamp API structs. (to be used in template functions)
 	void SetValue(Vamp::Plugin::OutputDescriptor& od, std::string& key, PyObject* pyValue) const;
 	void SetValue(Vamp::Plugin::ParameterDescriptor& od, std::string& key, PyObject* pyValue) const;
 	bool SetValue(Vamp::Plugin::Feature& od, std::string& key, PyObject* pyValue) const;
@@ -225,6 +212,20 @@
 		return list;
 	}
 
+	/// Convert DTYPE type 1D NumpyArray to std::vector<RET>
+	template<typename RET, typename DTYPE>
+	std::vector<RET> PyTypeInterface::PyArray_Convert(char* raw_data_ptr, long length) const
+	{
+		std::vector<RET> rValue;
+		DTYPE* data = (DTYPE*) raw_data_ptr;
+		for (long i = 0; i<length; ++i){
+#ifdef _DEBUG
+			cerr << "value: " << (RET)data[i] << endl;
+#endif
+			rValue.push_back((RET)data[i]);
+		}
+		return rValue;
+	}
 	
 	//Vamp specific types
 
@@ -253,7 +254,7 @@
 	/*used by templates where we expect no return value, if there is one it will be ignored*/			
 	inline void PyValue_To_rValue(PyObject *pyValue, NoneType &defValue) const
 		{ if (m_strict && pyValue != Py_None) 
-				setValueError("Strict conversion error: expected 'None' type.",m_strict); 
+				setValueError("Strict conversion error: Expected 'None' type.",m_strict); 
 		}
 
 	/* convert sequence types to Vamp List types */			
@@ -301,4 +302,90 @@
 
 };
 
+#ifdef NUMPY_REFERENCE
+/// This should be all we need to compile without direct dependency,
+/// but we don't do that. (it may not work on some platforms)
+typedef struct {
+    int two;              /* contains the integer 2 -- simple sanity check */
+    int nd;               /* number of dimensions */
+    char typekind;        /* kind in array --- character code of typestr */
+    int itemsize;         /* size of each element */
+    int flags;            /* flags indicating how the data should be interpreted */
+                          /*   must set ARR_HAS_DESCR bit to validate descr */
+    Py_intptr_t *shape;   /* A length-nd array of shape information */
+    Py_intptr_t *strides; /* A length-nd array of stride information */
+    void *data;           /* A pointer to the first element of the array */
+    PyObject *descr;      /* NULL or data-description (same as descr key */
+                          /*        of __array_interface__) -- must set ARR_HAS_DESCR */
+                          /*        flag or this will be ignored. */
+} PyArrayInterface;
+
+typedef struct PyArrayObject {
+        PyObject_HEAD
+        char *data;             /* pointer to raw data buffer */
+        int nd;                 /* number of dimensions, also called ndim */
+        npy_intp *dimensions;       /* size in each dimension */
+        npy_intp *strides;          /* bytes to jump to get to the
+                                   next element in each dimension */
+        PyObject *base;         /* This object should be decref'd
+                                   upon deletion of array */
+                                /* For views it points to the original array */
+                                /* For creation from buffer object it points
+                                   to an object that shold be decref'd on
+                                   deletion */
+                                /* For UPDATEIFCOPY flag this is an array
+                                   to-be-updated upon deletion of this one */
+        PyArray_Descr *descr;   /* Pointer to type structure */
+        int flags;              /* Flags describing array -- see below*/
+        PyObject *weakreflist;  /* For weakreferences */
+} PyArrayObject;
+
+typedef struct _PyArray_Descr {
+        PyObject_HEAD
+        PyTypeObject *typeobj;  /* the type object representing an
+                                   instance of this type -- should not
+                                   be two type_numbers with the same type
+                                   object. */
+        char kind;              /* kind for this type */
+        char type;              /* unique-character representing this type */
+        char byteorder;         /* '>' (big), '<' (little), '|'
+                                   (not-applicable), or '=' (native). */
+        char hasobject;         /* non-zero if it has object arrays
+                                   in fields */
+        int type_num;          /* number representing this type */
+        int elsize;             /* element size for this type */
+        int alignment;          /* alignment needed for this type */
+        struct _arr_descr                                       \
+        *subarray;              /* Non-NULL if this type is
+                                   is an array (C-contiguous)
+                                   of some other type
+                                */
+        PyObject *fields;       /* The fields dictionary for this type */
+                                /* For statically defined descr this
+                                   is always Py_None */
+
+        PyObject *names;        /* An ordered tuple of field names or NULL
+                                   if no fields are defined */
+
+        PyArray_ArrFuncs *f;     /* a table of functions specific for each
+                                    basic data descriptor */
+} PyArray_Descr;
+
+enum NPY_TYPES {    NPY_BOOL=0,
+                    NPY_BYTE, NPY_UBYTE,
+                    NPY_SHORT, NPY_USHORT,
+                    NPY_INT, NPY_UINT,
+                    NPY_LONG, NPY_ULONG,
+                    NPY_LONGLONG, NPY_ULONGLONG,
+                    NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE,
+                    NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE,
+                    NPY_OBJECT=17,
+                    NPY_STRING, NPY_UNICODE,
+                    NPY_VOID,
+                    NPY_NTYPES,
+                    NPY_NOTYPE,
+                    NPY_CHAR,      /* special flag */
+                    NPY_USERDEF=256  /* leave room for characters */
+};
+#endif /*NUMPY_REFERENCE*/
 #endif
\ No newline at end of file