--- /dev/null
+This module wraps the aubio library for python using the numpy module.
+
+http://docs.python.org/c-api/index.html
+
+http://docs.scipy.org/doc/numpy/reference/c-api.html
--- /dev/null
+#include <Python.h>
+#include <structmember.h>
+#define NO_IMPORT_ARRAY
+#include <numpy/arrayobject.h>
+#include <aubio.h>
+
+typedef struct
+{
+ PyObject_HEAD fvec_t * o;
+ uint_t length;
+ uint_t channels;
+} Py_fvec;
+extern PyTypeObject Py_fvecType;
#include <Python.h>
-#include <structmember.h>
+#define PY_ARRAY_UNIQUE_SYMBOL PyArray_API
#include <numpy/arrayobject.h>
-#include <aubio.h>
-static char aubio_module_doc[] = "Python module for the aubio library";
-
-/* fvec type definition
-
-class fvec():
- def __init__(self, length = 1024, channels = 1):
- self.length = length
- self.channels = channels
- self.data = array(length, channels)
-
-*/
+#include "aubio-types.h"
-#define Py_fvec_default_length 1024
-#define Py_fvec_default_channels 1
-
-static char Py_fvec_doc[] = "fvec object";
-
-typedef struct
-{
- PyObject_HEAD fvec_t * o;
- uint_t length;
- uint_t channels;
-} Py_fvec;
+static char Py_alpha_norm_doc[] = "compute alpha normalisation factor";
static PyObject *
-Py_fvec_new (PyTypeObject * type, PyObject * args, PyObject * kwds)
+Py_alpha_norm (PyObject * self, PyObject * args)
{
- int length = 0, channels = 0;
- Py_fvec *self;
- static char *kwlist[] = { "length", "channels", NULL };
-
- if (!PyArg_ParseTupleAndKeywords (args, kwds, "|II", kwlist,
- &length, &channels)) {
- return NULL;
- }
-
-
- self = (Py_fvec *) type->tp_alloc (type, 0);
-
- self->length = Py_fvec_default_length;
- self->channels = Py_fvec_default_channels;
-
- if (self == NULL) {
- return NULL;
- }
+ PyObject *input;
+ Py_fvec *vec;
+ smpl_t alpha;
+ PyObject *result;
+ PyObject *array;
+ uint_t i;
- if (length > 0) {
- self->length = length;
- } else if (length < 0) {
- PyErr_SetString (PyExc_ValueError,
- "can not use negative number of elements");
+ if (!PyArg_ParseTuple (args, "Of:alpha_norm", &input, &alpha)) {
return NULL;
}
- if (channels > 0) {
- self->channels = channels;
- } else if (channels < 0) {
- PyErr_SetString (PyExc_ValueError,
- "can not use negative number of channels");
+ if (input == NULL) {
return NULL;
}
+ // parsing input object into a Py_fvec
+ if (PyObject_TypeCheck (input, &Py_fvecType)) {
+ // input is an fvec, nothing else to do
+ vec = (Py_fvec *) input;
+ } else if (PyArray_Check(input)) {
- return (PyObject *) self;
-}
-
-static int
-Py_fvec_init (Py_fvec * self, PyObject * args, PyObject * kwds)
-{
- self->o = new_fvec (self->length, self->channels);
- if (self->o == NULL) {
- return -1;
- }
-
- return 0;
-}
-
-static void
-Py_fvec_del (Py_fvec * self)
-{
- del_fvec (self->o);
- self->ob_type->tp_free ((PyObject *) self);
-}
-
-static PyObject *
-Py_fvec_repr (Py_fvec * self, PyObject * unused)
-{
- PyObject *format = NULL;
- PyObject *args = NULL;
- PyObject *result = NULL;
-
- format = PyString_FromString ("aubio fvec of %d elements with %d channels");
- if (format == NULL) {
- goto fail;
- }
-
- args = Py_BuildValue ("II", self->length, self->channels);
- if (args == NULL) {
- goto fail;
- }
-
- result = PyString_Format (format, args);
-
-fail:
- Py_XDECREF (format);
- Py_XDECREF (args);
-
- return result;
-}
-
-static PyObject *
-Py_fvec_print (Py_fvec * self, PyObject * unused)
-{
- fvec_print (self->o);
- return Py_None;
-}
-
-static PyObject *
-Py_fvec_array (Py_fvec * self)
-{
- PyObject *array = NULL;
- if (self->channels == 1) {
- npy_intp dims[] = { self->length, 1 };
- array = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[0]);
- } else {
- uint_t i;
- npy_intp dims[] = { self->length, 1 };
- PyObject *concat = PyList_New (0), *tmp = NULL;
- for (i = 0; i < self->channels; i++) {
- tmp = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[i]);
- PyList_Append (concat, tmp);
- Py_DECREF (tmp);
+ // we got an array, convert it to an fvec
+ if (PyArray_NDIM (input) == 0) {
+ PyErr_SetString (PyExc_ValueError, "input array is a scalar");
+ goto fail;
+ } else if (PyArray_NDIM (input) > 2) {
+ PyErr_SetString (PyExc_ValueError, "input array has more than two dimensions");
+ goto fail;
}
- array = PyArray_FromObject (concat, NPY_FLOAT, 2, 2);
- Py_DECREF (concat);
- }
- return array;
-}
-static Py_ssize_t
-Py_fvec_getchannels (Py_fvec * self)
-{
- return self->channels;
-}
-
-static PyObject *
-Py_fvec_getitem (Py_fvec * self, Py_ssize_t index)
-{
- PyObject *array;
-
- if (index < 0 || index >= self->channels) {
- PyErr_SetString (PyExc_IndexError, "no such channel");
- return NULL;
- }
-
- npy_intp dims[] = { self->length, 1 };
- array = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[index]);
- return array;
-}
-
-static int
-Py_fvec_setitem (Py_fvec * self, Py_ssize_t index, PyObject * o)
-{
- PyObject *array;
-
- if (index < 0 || index >= self->channels) {
- PyErr_SetString (PyExc_IndexError, "no such channel");
- return -1;
- }
-
- array = PyArray_FROM_OT (o, NPY_FLOAT);
- if (array == NULL) {
- PyErr_SetString (PyExc_ValueError, "should be an array of float");
- goto fail;
- }
-
- if (PyArray_NDIM (array) != 1) {
- PyErr_SetString (PyExc_ValueError, "should be a one-dimensional array");
- goto fail;
- }
-
- if (PyArray_SIZE (array) != self->length) {
- PyErr_SetString (PyExc_ValueError,
- "should be an array of same length as target fvec");
- goto fail;
- }
-
- self->o->data[index] = (smpl_t *) PyArray_GETPTR1 (array, 0);
-
- return 0;
-
-fail:
- return -1;
-}
-
-static PyMemberDef Py_fvec_members[] = {
- // TODO remove READONLY flag and define getter/setter
- {"length", T_INT, offsetof (Py_fvec, length), READONLY,
- "length attribute"},
- {"channels", T_INT, offsetof (Py_fvec, channels), READONLY,
- "channels attribute"},
- {NULL} /* Sentinel */
-};
-
-static PyMethodDef Py_fvec_methods[] = {
- {"dump", (PyCFunction) Py_fvec_print, METH_NOARGS,
- "Dumps the contents of the vector to stdout."},
- {"__array__", (PyCFunction) Py_fvec_array, METH_NOARGS,
- "Returns the first channel as a numpy array."},
- {NULL}
-};
-
-static PySequenceMethods Py_fvec_tp_as_sequence = {
- (lenfunc) Py_fvec_getchannels, /* sq_length */
- 0, /* sq_concat */
- 0, /* sq_repeat */
- (ssizeargfunc) Py_fvec_getitem, /* sq_item */
- 0, /* sq_slice */
- (ssizeobjargproc) Py_fvec_setitem, /* sq_ass_item */
- 0, /* sq_ass_slice */
- 0, /* sq_contains */
- 0, /* sq_inplace_concat */
- 0, /* sq_inplace_repeat */
-};
-
-
-static PyTypeObject Py_fvecType = {
- PyObject_HEAD_INIT (NULL)
- 0, /* ob_size */
- "fvec", /* tp_name */
- sizeof (Py_fvec), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor) Py_fvec_del, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- (reprfunc) Py_fvec_repr, /* tp_repr */
- 0, /* tp_as_number */
- &Py_fvec_tp_as_sequence, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- Py_fvec_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- Py_fvec_methods, /* tp_methods */
- Py_fvec_members, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- (initproc) Py_fvec_init, /* tp_init */
- 0, /* tp_alloc */
- Py_fvec_new, /* tp_new */
-};
-
-/* end of fvec type definition */
+ if (!PyArray_ISFLOAT (input)) {
+ PyErr_SetString (PyExc_ValueError, "input array should be float");
+ goto fail;
+ } else if (PyArray_TYPE (input) != NPY_FLOAT) {
+ // input data type is not float32, casting
+ array = PyArray_Cast ( (PyArrayObject*) input, NPY_FLOAT);
+ if (array == NULL) {
+ PyErr_SetString (PyExc_IndexError, "failed converting to NPY_FLOAT");
+ goto fail;
+ }
+ } else {
+ // input data type is float32, nothing else to do
+ array = input;
+ }
-static PyObject *
-Py_alpha_norm (PyObject * self, PyObject * args)
-{
- Py_fvec *vec;
- smpl_t alpha;
- PyObject *result;
+ // create a new fvec object
+ vec = (Py_fvec*) PyObject_New (Py_fvec, &Py_fvecType);
+ if (PyArray_NDIM (array) == 1) {
+ vec->channels = 1;
+ vec->length = PyArray_SIZE (array);
+ } else {
+ vec->channels = PyArray_DIM (array, 0);
+ vec->length = PyArray_DIM (array, 1);
+ }
- if (!PyArg_ParseTuple (args, "Of:alpha_norm", &vec, &alpha)) {
- return NULL;
- }
+ // FIXME should not need to allocate fvec
+ vec->o = new_fvec (vec->length, vec->channels);
+ for (i = 0; i < vec->channels; i++) {
+ vec->o->data[i] = (smpl_t *) PyArray_GETPTR1 (array, i);
+ }
- if (vec == NULL) {
+ } else {
+ PyErr_SetString (PyExc_ValueError, "can only accept array or fvec as input");
return NULL;
}
+ // compute the function
result = Py_BuildValue ("f", vec_alpha_norm (vec->o, alpha));
if (result == NULL) {
return NULL;
return result;
+fail:
+ return NULL;
}
-static char Py_alpha_norm_doc[] = "compute alpha normalisation factor";
-
static PyMethodDef aubio_methods[] = {
{"alpha_norm", Py_alpha_norm, METH_VARARGS, Py_alpha_norm_doc},
{NULL, NULL} /* Sentinel */
};
+static char aubio_module_doc[] = "Python module for the aubio library";
+
PyMODINIT_FUNC
init_aubio (void)
{
--- /dev/null
+#include "aubio-types.h"
+
+/* fvec type definition
+
+class fvec():
+ def __init__(self, length = 1024, channels = 1):
+ self.length = length
+ self.channels = channels
+ self.data = array(length, channels)
+
+*/
+
+#define Py_fvec_default_length 1024
+#define Py_fvec_default_channels 1
+
+static char Py_fvec_doc[] = "fvec object";
+
+static PyObject *
+Py_fvec_new (PyTypeObject * type, PyObject * args, PyObject * kwds)
+{
+ int length= 0, channels = 0;
+ Py_fvec *self;
+ static char *kwlist[] = { "length", "channels", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwds, "|II", kwlist,
+ &length, &channels)) {
+ return NULL;
+ }
+
+
+ self = (Py_fvec *) type->tp_alloc (type, 0);
+
+ self->length = Py_fvec_default_length;
+ self->channels = Py_fvec_default_channels;
+
+ if (self == NULL) {
+ return NULL;
+ }
+
+ if (length > 0) {
+ self->length = length;
+ } else if (length < 0) {
+ PyErr_SetString (PyExc_ValueError,
+ "can not use negative number of elements");
+ return NULL;
+ }
+
+ if (channels > 0) {
+ self->channels = channels;
+ } else if (channels < 0) {
+ PyErr_SetString (PyExc_ValueError,
+ "can not use negative number of channels");
+ return NULL;
+ }
+
+
+ return (PyObject *) self;
+}
+
+static int
+Py_fvec_init (Py_fvec * self, PyObject * args, PyObject * kwds)
+{
+ self->o = new_fvec (self->length, self->channels);
+ if (self->o == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+Py_fvec_del (Py_fvec * self)
+{
+ del_fvec (self->o);
+ self->ob_type->tp_free ((PyObject *) self);
+}
+
+static PyObject *
+Py_fvec_repr (Py_fvec * self, PyObject * unused)
+{
+ PyObject *format = NULL;
+ PyObject *args = NULL;
+ PyObject *result = NULL;
+
+ format = PyString_FromString ("aubio fvec of %d elements with %d channels");
+ if (format == NULL) {
+ goto fail;
+ }
+
+ args = Py_BuildValue ("II", self->length, self->channels);
+ if (args == NULL) {
+ goto fail;
+ }
+
+ result = PyString_Format (format, args);
+
+fail:
+ Py_XDECREF (format);
+ Py_XDECREF (args);
+
+ return result;
+}
+
+static PyObject *
+Py_fvec_print (Py_fvec * self, PyObject * unused)
+{
+ fvec_print (self->o);
+ return Py_None;
+}
+
+static PyObject *
+Py_fvec_array (Py_fvec * self)
+{
+ PyObject *array = NULL;
+ if (self->channels == 1) {
+ npy_intp dims[] = { self->length, 1 };
+ array = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[0]);
+ } else {
+ uint_t i;
+ npy_intp dims[] = { self->length, 1 };
+ PyObject *concat = PyList_New (0), *tmp = NULL;
+ for (i = 0; i < self->channels; i++) {
+ tmp = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[i]);
+ PyList_Append (concat, tmp);
+ Py_DECREF (tmp);
+ }
+ array = PyArray_FromObject (concat, NPY_FLOAT, 2, 2);
+ Py_DECREF (concat);
+ }
+ return array;
+}
+
+static Py_ssize_t
+Py_fvec_getchannels (Py_fvec * self)
+{
+ return self->channels;
+}
+
+static PyObject *
+Py_fvec_getitem (Py_fvec * self, Py_ssize_t index)
+{
+ PyObject *array;
+
+ if (index < 0 || index >= self->channels) {
+ PyErr_SetString (PyExc_IndexError, "no such channel");
+ return NULL;
+ }
+
+ npy_intp dims[] = { self->length, 1 };
+ array = PyArray_SimpleNewFromData (1, dims, NPY_FLOAT, self->o->data[index]);
+ return array;
+}
+
+static int
+Py_fvec_setitem (Py_fvec * self, Py_ssize_t index, PyObject * o)
+{
+ PyObject *array;
+
+ if (index < 0 || index >= self->channels) {
+ PyErr_SetString (PyExc_IndexError, "no such channel");
+ return -1;
+ }
+
+ array = PyArray_FROM_OT (o, NPY_FLOAT);
+ if (array == NULL) {
+ PyErr_SetString (PyExc_ValueError, "should be an array of float");
+ goto fail;
+ }
+
+ if (PyArray_NDIM (array) != 1) {
+ PyErr_SetString (PyExc_ValueError, "should be a one-dimensional array");
+ goto fail;
+ }
+
+ if (PyArray_SIZE (array) != self->length) {
+ PyErr_SetString (PyExc_ValueError,
+ "should be an array of same length as target fvec");
+ goto fail;
+ }
+
+ self->o->data[index] = (smpl_t *) PyArray_GETPTR1 (array, 0);
+
+ return 0;
+
+fail:
+ return -1;
+}
+
+static PyMemberDef Py_fvec_members[] = {
+ // TODO remove READONLY flag and define getter/setter
+ {"length", T_INT, offsetof (Py_fvec, length), READONLY,
+ "length attribute"},
+ {"channels", T_INT, offsetof (Py_fvec, channels), READONLY,
+ "channels attribute"},
+ {NULL} /* Sentinel */
+};
+
+static PyMethodDef Py_fvec_methods[] = {
+ {"dump", (PyCFunction) Py_fvec_print, METH_NOARGS,
+ "Dumps the contents of the vector to stdout."},
+ {"__array__", (PyCFunction) Py_fvec_array, METH_NOARGS,
+ "Returns the first channel as a numpy array."},
+ {NULL}
+};
+
+static PySequenceMethods Py_fvec_tp_as_sequence = {
+ (lenfunc) Py_fvec_getchannels, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (ssizeargfunc) Py_fvec_getitem, /* sq_item */
+ 0, /* sq_slice */
+ (ssizeobjargproc) Py_fvec_setitem, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ 0, /* sq_contains */
+ 0, /* sq_inplace_concat */
+ 0, /* sq_inplace_repeat */
+};
+
+
+PyTypeObject Py_fvecType = {
+ PyObject_HEAD_INIT (NULL)
+ 0, /* ob_size */
+ "fvec", /* tp_name */
+ sizeof (Py_fvec), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) Py_fvec_del, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) Py_fvec_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ &Py_fvec_tp_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ Py_fvec_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Py_fvec_methods, /* tp_methods */
+ Py_fvec_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc) Py_fvec_init, /* tp_init */
+ 0, /* tp_alloc */
+ Py_fvec_new, /* tp_new */
+};
setup(name="_aubio", version="1.0",
ext_modules = [
Extension("_aubio",
- ["aubiomodule.c"],
+ ["aubiomodule.c", "py-fvec.c"],
include_dirs=['../../build/default/src', '../../src' ],
library_dirs=['../../build/default/src', '../../src/.libs' ],
libraries=['aubio'])])
--- /dev/null
+import unittest
+from _aubio import *
+from numpy import array
+
+class aubiomodule_test_case(unittest.TestCase):
+
+ def setUp(self):
+ """ try importing aubio """
+
+ def test_vector(self):
+ a = fvec()
+ a.length, a.channels
+ a[0]
+ array(a)
+ a = fvec(10)
+ a = fvec(1, 2)
+ array(a).T
+ a[0] = range(a.length)
+ a[1][0] = 2
+
+ def test_wrong_values(self):
+ self.assertRaises (ValueError, fvec, -10)
+ self.assertRaises (ValueError, fvec, 1, -1)
+
+ a = fvec(2, 3)
+ self.assertRaises (IndexError, a.__getitem__, 3)
+ self.assertRaises (IndexError, a[0].__getitem__, 2)
+
+ def test_alpha_norm_of_fvec(self):
+ a = fvec(2, 2)
+ self.assertEquals (alpha_norm(a, 1), 0)
+ a[0] = [1, 2]
+ self.assertEquals (alpha_norm(a, 1), 1.5)
+ a[1] = [1, 2]
+ self.assertEquals (alpha_norm(a, 1), 3)
+ a[0] = [0, 1]; a[1] = [1, 0]
+ self.assertEquals (alpha_norm(a, 2), 1)
+
+ def test_alpha_norm_of_array_of_float32(self):
+ a = array(1, dtype = 'float32')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array([[[1,2],[3,4]]], dtype = 'float32')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array(range(10), dtype = 'float32')
+ self.assertEquals (alpha_norm(a, 1), 4.5)
+ a = array([range(10), range(10)], dtype = 'float32')
+ self.assertEquals (alpha_norm(a, 1), 9)
+
+ def test_alpha_norm_of_array_of_float64(self):
+ a = array(1, dtype = 'float64')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array([[[1,2],[3,4]]], dtype = 'float64')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array(range(10), dtype = 'float64')
+ self.assertEquals (alpha_norm(a, 1), 4.5)
+ a = array([range(10), range(10)], dtype = 'float64')
+ self.assertEquals (alpha_norm(a, 1), 9)
+
+ def test_alpha_norm_of_array_of_int(self):
+ a = array(1, dtype = 'int')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array([[[1,2],[3,4]]], dtype = 'int')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+ a = array(range(10), dtype = 'int')
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+
+ def test_alpha_norm_of_array_of_string (self):
+ a = "hello"
+ self.assertRaises (ValueError, alpha_norm, a, 1)
+
+if __name__ == '__main__':
+ unittest.main()