--- /dev/null
+#! /usr/bin/python
+
+""" This madness of code is used to generate the C code of the python interface
+to aubio. Don't try this at home.
+
+The list of typedefs and functions is obtained from the command line 'cpp
+aubio.h'. This list is then used to parse all the functions about this object.
+
+I hear the ones asking "why not use swig, or cython, or something like that?"
+
+The requirements for this extension are the following:
+
+ - aubio vectors can be viewed as numpy arrays, and vice versa
+ - aubio 'object' should be python classes, not just a bunch of functions
+
+I haven't met any python interface generator that can meet both these
+requirements. If you know of one, please let me know, it will spare me
+maintaining this bizarre file.
+"""
+
+# TODO
+# do function: for now, only the following pattern is supported:
+# void aubio_<foo>_do (aubio_foo_t * o,
+# [input1_t * input, [output1_t * output, ..., output3_t * output]]);
+# There is no way of knowing that output1 is actually input2. In the future,
+# const could be used for the inputs in the C prototypes.
+
+# the important bits: the size of the output for each objects. this data should
+# move into the C library at some point.
+defaultsizes = {
+ 'resampler': ('input->length * self->ratio', 'input->channels'),
+ 'onsetdetection': ('1', 'self->channels'),
+ 'onset': ('1', 'self->channels'),
+ 'pitchyin': ('1', 'in->channels'),
+ 'pitchyinfft': ('1', 'in->channels'),
+ 'pitchschmitt': ('1', 'in->channels'),
+ 'pitchmcomb': ('1', 'self->channels'),
+ 'pitchfcomb': ('1', 'self->channels'),
+ 'pitch': ('1', 'self->channels'),
+ 'tss': ('self->hop_s', 'self->channels'),
+ 'mfcc': ('self->n_coeffs', 'in->channels'),
+ 'beattracking': ('self->winlen', 'self->channels'),
+ 'tempo': ('self->buf_size', 'self->channels'),
+ 'peakpicker': ('1', 'self->channels'),
+}
+
+# default value for variables
+aubioinitvalue = {
+ 'uint_t': 0,
+ 'smpl_t': 0,
+ 'lsmp_t': 0.,
+ 'char_t*': 'NULL',
+ }
+
+aubiodefvalue = {
+ # we have some clean up to do
+ 'win_s': 'Py_default_vector_length',
+ 'bufsize': 'Py_default_vector_length',
+ 'buf_size': 'Py_default_vector_length',
+ 'winlen': 'Py_default_vector_length',
+ # and here too
+ 'hop_s': 'Py_default_vector_length / 2',
+ 'hopsize': 'Py_default_vector_length / 2',
+ 'hop_size': 'Py_default_vector_length / 2',
+ # these should be alright
+ 'channels': 'Py_default_vector_channels',
+ 'samplerate': 'Py_aubio_default_samplerate',
+ # now for the non obvious ones
+ 'n_filters': '40',
+ 'n_coeffs': '13',
+ 'nelems': '10',
+ 'flow': '0.',
+ 'fhig': '1.',
+ 'ilow': '0.',
+ 'ihig': '1.',
+ 'thrs': '0.5',
+ 'ratio': '0.5',
+ 'threshold': '0.5',
+ 'mode': '"default"',
+ 'onset_mode': '"default"',
+ 'type': '0',
+ }
+
+# aubio to python
+aubio2pytypes = {
+ 'uint_t': 'I',
+ 'smpl_t': 'I',
+ 'lsmp_t': 'I',
+ 'fvec_t': 'O',
+ 'cvec_t': 'O',
+ 'char_t*': 's',
+}
+
+# aubio to pyaubio
+aubio2pyaubio = {
+ 'fvec_t': 'Py_fvec',
+ 'cvec_t': 'Py_cvec',
+}
+
+# array to aubio
+aubiovecfrompyobj = {
+ 'fvec_t': 'PyAubio_ArrayToFvec',
+ 'cvec_t': 'PyAubio_ArrayToCvec',
+}
+
+# aubio to array
+aubiovectopyobj = {
+ 'fvec_t': 'PyAubio_FvecToArray',
+ 'cvec_t': 'PyAubio_CvecToArray',
+}
+
+def get_newparams(newfunc):
+ newparams = [[p.split()[0], p.split()[-1]]
+ for p in newfunc.split('(')[1].split(')')[0].split(',')]
+ # make char_t a pointer
+ return map(lambda x: [x[0].replace('char_t', 'char_t*'), x[1]], newparams)
+
+def gen_new_init(newfunc, name):
+ newparams = get_newparams(newfunc)
+ # self->param1, self->param2, self->param3
+ selfparams = ', self->'.join([p[1] for p in newparams])
+ # "param1", "param2", "param3"
+ paramnames = ", ".join(["\""+p[1]+"\"" for p in newparams])
+ pyparams = "".join(map(lambda p: aubio2pytypes[p[0]], newparams))
+ paramrefs = ", ".join(["&" + p[1] for p in newparams])
+ s = """\
+// WARNING: this file is generated, DO NOT EDIT
+#include "aubiowraphell.h"
+
+typedef struct
+{
+ PyObject_HEAD
+ aubio_%(name)s_t * o;
+""" % locals()
+ for ptype, pname in newparams:
+ s += """\
+ %(ptype)s %(pname)s;
+""" % locals()
+ s += """\
+} Py_%(name)s;
+
+static char Py_%(name)s_doc[] = "%(name)s object";
+
+static PyObject *
+Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
+{
+""" % locals()
+ for ptype, pname in newparams:
+ defval = aubioinitvalue[ptype]
+ s += """\
+ %(ptype)s %(pname)s = %(defval)s;
+""" % locals()
+ s += """\
+ Py_%(name)s *self;
+ static char *kwlist[] = { %(paramnames)s, NULL };
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
+ %(paramrefs)s)) {
+ return NULL;
+ }
+
+ self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
+
+ if (self == NULL) {
+ return NULL;
+ }
+""" % locals()
+ # TODO add parameters default values
+ for ptype, pname in newparams:
+ defval = aubiodefvalue[pname]
+ s += """\
+
+ self->%(pname)s = %(defval)s;
+ if (%(pname)s > 0) {
+ self->%(pname)s = %(pname)s;
+ } else if (%(pname)s < 0) {
+ PyErr_SetString (PyExc_ValueError,
+ "can not use negative window size");
+ return NULL;
+ }
+""" % locals()
+ s += """\
+
+ return (PyObject *) self;
+}
+
+AUBIO_INIT(%(name)s, self->%(selfparams)s)
+
+AUBIO_DEL(%(name)s)
+
+""" % locals()
+ return s
+
+def gen_do(dofunc, name):
+ funcname = dofunc.split()[1].split('(')[0]
+ doparams = [p.split() for p in dofunc.split('(')[1].split(')')[0].split(',')]
+ # make sure the first parameter is the object
+ assert doparams[0][0] == "aubio_"+name+"_t", \
+ "method is not in 'aubio_<name>_t"
+ # and remove it
+ doparams = doparams[1:]
+ # guess the input/output params, assuming we have less than 3
+ assert len(doparams) > 0, \
+ "no parameters for function do in object %s" % name
+ #assert (len(doparams) <= 2), \
+ # "more than 3 parameters for do in object %s" % name
+
+ # build strings for inputs, assuming there is only one input
+ inputparams = [doparams[0]]
+ # build the parsing string for PyArg_ParseTuple
+ pytypes = "".join([aubio2pytypes[p[0]] for p in doparams[0:1]])
+ inputdefs = "\n ".join(["PyObject * " + p[-1] + "_obj;" for p in inputparams])
+ inputvecs = "\n ".join(map(lambda p: \
+ aubio2pyaubio[p[0]]+" * " + p[-1] + ";", inputparams))
+ parseinput = ""
+ for p in inputparams:
+ inputvec = p[-1]
+ inputdef = p[-1] + "_obj"
+ converter = aubiovecfrompyobj[p[0]]
+ parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
+
+ if (%(inputvec)s == NULL) {
+ return NULL;
+ }""" % locals()
+ # build the string for the input objects references
+ inputrefs = ", ".join(["&" + p[-1] + "_obj" for p in inputparams])
+ # end of inputs strings
+
+ # build strings for outputs
+ outputparams = doparams[1:]
+ if len(doparams) > 1:
+ #assert len(outputparams) == 1, \
+ # "too many output parameters"
+ outputvecs = "\n ".join([aubio2pyaubio[p[0]]+" * " + p[-1] + ";" for p in outputparams])
+ outputcreate = """\
+AUBIO_NEW_VEC(%(name)s, %(pytype)s, %(length)s, %(channels)s)
+ %(name)s->o = new_%(autype)s (%(length)s, %(channels)s);""" % \
+ {'name': p[-1], 'pytype': aubio2pyaubio[p[0]], 'autype': p[0][:-2],
+ 'length': defaultsizes[name][0], 'channels': defaultsizes[name][1]}
+ returnval = "(PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
+ else:
+ # no output
+ outputvecs = ""
+ outputcreate = ""
+ #returnval = "Py_None";
+ returnval = "(PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
+ # end of output strings
+
+ # build the parameters for the _do() call
+ doparams_string = "self->o, " + ", ".join([p[-1]+"->o" for p in doparams])
+
+ # put it all together
+ s = """\
+static PyObject *
+Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
+{
+ %(inputdefs)s
+ %(inputvecs)s
+ %(outputvecs)s
+
+ if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
+ return NULL;
+ }
+
+ %(parseinput)s
+
+ %(outputcreate)s
+
+ /* compute _do function */
+ %(funcname)s (%(doparams_string)s);
+
+ return %(returnval)s;
+}
+""" % locals()
+ return s
+
+def gen_members(new_method, name):
+ newparams = get_newparams(new_method)
+ s = """
+AUBIO_MEMBERS_START(%(name)s)""" % locals()
+ for param in newparams:
+ s += """
+ {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
+ % { 'pname': param[1], 'ptype': param[0], 'name': name}
+ s += """
+AUBIO_MEMBERS_STOP(%(name)s)
+
+""" % locals()
+ return s
+
+def gen_methods(get_methods, set_methods, name):
+ # TODO add methods
+ s = """\
+static PyMethodDef Py_%(name)s_methods[] = {
+""" % locals()
+ # TODO add PyMethodDefs
+ s += """\
+ {NULL} /* sentinel */
+};
+""" % locals()
+ return s
+
+def gen_finish(name):
+ s = """\
+
+AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
+""" % locals()
+ return s
--- /dev/null
+#! /usr/bin/python
+
+""" This file generates a c file from a list of cpp prototypes. """
+
+import os, sys
+
+skip_objects = ['fft', 'pvoc', 'filter', 'filterbank', 'biquad']
+
+cpp_output = [l.strip() for l in os.popen('cpp -I ../../build/default/src ../../src/aubio.h').readlines()]
+
+cpp_output = filter(lambda y: len(y) > 1, cpp_output)
+cpp_output = filter(lambda y: not y.startswith('#'), cpp_output)
+
+i = 1
+while 1:
+ if i >= len(cpp_output): break
+ if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'):
+ cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i]
+ cpp_output.pop(i-1)
+ else:
+ i += 1
+
+typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output)
+
+objects = [a.split()[3][:-1] for a in typedefs]
+
+print "-- INFO: %d objects in total" % len(objects)
+
+for object in objects:
+ lint = 0
+
+ if object[-2:] == '_t':
+ object_name = object[:-2]
+ else:
+ object_name = object
+ print "-- WARNING: %s does not end in _t" % object
+
+ if object_name[:len('aubio_')] != 'aubio_':
+ print "-- WARNING: %s does not start n aubio_" % object
+
+ print "-- INFO: looking at", object_name
+ object_methods = filter(lambda x: object in x, cpp_output)
+ object_methods = [a.strip() for a in object_methods]
+ object_methods = filter(lambda x: not x.startswith('typedef'), object_methods)
+ #for method in object_methods:
+ # print method
+
+ new_methods = filter(lambda x: 'new_'+object_name in x, object_methods)
+ if len(new_methods) > 1:
+ print "-- WARNING: more than one new method for", object_name
+ for method in new_methods:
+ print method
+ elif len(new_methods) < 1:
+ print "-- WARNING: no new method for", object_name
+ elif 0:
+ for method in new_methods:
+ print method
+
+ del_methods = filter(lambda x: 'del_'+object_name in x, object_methods)
+ if len(del_methods) > 1:
+ print "-- WARNING: more than one del method for", object_name
+ for method in del_methods:
+ print method
+ elif len(del_methods) < 1:
+ print "-- WARNING: no del method for", object_name
+
+ do_methods = filter(lambda x: object_name+'_do' in x, object_methods)
+ if len(do_methods) > 1:
+ pass
+ #print "-- WARNING: more than one do method for", object_name
+ #for method in do_methods:
+ # print method
+ elif len(do_methods) < 1:
+ print "-- WARNING: no do method for", object_name
+ elif 0:
+ for method in do_methods:
+ print method
+
+ # check do methods return void
+ for method in do_methods:
+ if (method.split()[0] != 'void'):
+ print "-- ERROR: _do method does not return void:", method
+
+ get_methods = filter(lambda x: object_name+'_get_' in x, object_methods)
+
+ set_methods = filter(lambda x: object_name+'_set_' in x, object_methods)
+ for method in set_methods:
+ if (method.split()[0] != 'uint_t'):
+ print "-- ERROR: _set method does not return uint_t:", method
+
+ other_methods = filter(lambda x: x not in new_methods, object_methods)
+ other_methods = filter(lambda x: x not in del_methods, other_methods)
+ other_methods = filter(lambda x: x not in do_methods, other_methods)
+ other_methods = filter(lambda x: x not in get_methods, other_methods)
+ other_methods = filter(lambda x: x not in set_methods, other_methods)
+
+ if len(other_methods) > 0:
+ print "-- WARNING: some methods for", object_name, "were unidentified"
+ for method in other_methods:
+ print method
+
+ # generate object
+ if not os.path.isdir('generated'): os.mkdir('generated')
+ from gen_pyobject import *
+ short_name = object_name[len('aubio_'):]
+ if short_name in skip_objects:
+ print "-- INFO: skipping object", short_name
+ continue
+ if 1: #try:
+ s = gen_new_init(new_methods[0], short_name)
+ s += gen_do(do_methods[0], short_name)
+ s += gen_members(new_methods[0], short_name)
+ s += gen_methods(get_methods, set_methods, short_name)
+ s += gen_finish(short_name)
+ fd = open('generated/gen-'+short_name+'.c', 'w')
+ fd.write(s)
+ #except Exception, e:
+ # print "-- ERROR:", type(e), str(e), "in", short_name
+ # continue