ref: ccb9fb565a315f676cd028cde7fa2da4ac26879f
parent: fbd7c80e36a2b490316585eeb07e5e467056903d
author: Paul Brossier <piem@piem.org>
date: Mon Apr 18 21:36:13 EDT 2016
lib/gen_external.py: rewrote wrapper
--- /dev/null
+++ b/python/lib/gen_code.py
@@ -1,0 +1,487 @@
+aubiodefvalue = {
+ # we have some clean up to do
+ 'buf_size': 'Py_default_vector_length',
+ 'win_s': 'Py_default_vector_length',
+ # and here too
+ 'hop_size': 'Py_default_vector_length / 2',
+ 'hop_s': 'Py_default_vector_length / 2',
+ # these should be alright
+ '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',
+ 'method': '"default"',
+ 'uri': '"none"',
+ }
+
+member_types = {
+ 'name': 'type',
+ 'char_t*': 'T_STRING',
+ 'uint_t': 'T_INT',
+ 'smpl_t': 'T_FLOAT',
+ }
+
+pyfromtype_fn = {
+ 'smpl_t': 'PyFloat_FromDouble',
+ 'uint_t': 'PyLong_FromLong', # was: 'PyInt_FromLong',
+ 'fvec_t*': 'PyAubio_CFvecToArray',
+ 'fmat_t*': 'PyAubio_CFmatToArray',
+ }
+
+pytoaubio_fn = {
+ 'fvec_t*': 'PyAubio_ArrayToCFvec',
+ 'cvec_t*': 'PyAubio_ArrayToCCvec',
+ 'fmat_t*': 'PyAubio_ArrayToCFmat',
+ }
+
+pyfromaubio_fn = {
+ 'fvec_t*': 'PyAubio_CFvecToArray',
+ 'cvec_t*': 'PyAubio_CCvecToArray',
+ 'fmat_t*': 'PyAubio_CFmatToArray',
+ }
+
+newfromtype_fn = {
+ 'fvec_t*': 'new_fvec',
+ 'fmat_t*': 'new_fmat',
+ 'cvec_t*': 'new_cvec',
+ }
+
+delfromtype_fn = {
+ 'fvec_t*': 'del_fvec',
+ 'fmat_t*': 'del_fmat',
+ 'cvec_t*': 'del_cvec',
+ }
+
+param_init = {
+ 'char_t*': 'NULL',
+ 'uint_t': '0',
+ 'sint_t': 0,
+ 'smpl_t': 0.,
+ 'lsmp_t': 0.,
+ }
+
+pyargparse_chars = {
+ 'smpl_t': 'f',
+ 'uint_t': 'I',
+ 'sint_t': 'I',
+ 'char_t*': 's',
+ 'fmat_t*': 'O',
+ 'fvec_t*': 'O',
+ }
+
+objoutsize = {
+ 'onset': '1',
+ 'pitch': '1',
+ 'wavetable': 'self->hop_size',
+ 'sampler': 'self->hop_size',
+ 'mfcc': 'self->n_coeffs',
+ 'specdesc': '1',
+ 'tempo': '1',
+ 'filterbank': 'self->n_filters',
+ }
+
+def get_name(proto):
+ name = proto.replace(' *', '* ').split()[1].split('(')[0]
+ name = name.replace('*','')
+ if name == '': raise ValueError(proto + "gave empty name")
+ return name
+
+def get_return_type(proto):
+ import re
+ paramregex = re.compile('(\w+ ?\*?).*')
+ outputs = paramregex.findall(proto)
+ assert len(outputs) == 1
+ return outputs[0].replace(' ', '')
+
+def split_type(arg):
+ """ arg = 'foo *name'
+ return ['foo*', 'name'] """
+ l = arg.split()
+ type_arg = {'type': l[0], 'name': l[1]}
+ # ['foo', '*name'] -> ['foo*', 'name']
+ if l[-1].startswith('*'):
+ #return [l[0]+'*', l[1][1:]]
+ type_arg['type'] = l[0] + '*'
+ type_arg['name'] = l[1][1:]
+ # ['foo', '*', 'name'] -> ['foo*', 'name']
+ if len(l) == 3:
+ #return [l[0]+l[1], l[2]]
+ type_arg['type'] = l[0]+l[1]
+ type_arg['name'] = l[2]
+ else:
+ #return l
+ pass
+ return type_arg
+
+def get_params(proto):
+ """ get the list of parameters from a function prototype
+ example: proto = "int main (int argc, char ** argv)"
+ returns: ['int argc', 'char ** argv']
+ """
+ import re
+ paramregex = re.compile('[\(, ](\w+ \*?\*? ?\w+)[, \)]')
+ return paramregex.findall(proto)
+
+def get_params_types_names(proto):
+ """ get the list of parameters from a function prototype
+ example: proto = "int main (int argc, char ** argv)"
+ returns: [['int', 'argc'], ['char **','argv']]
+ """
+ return list(map(split_type, get_params(proto)))
+
+
+class MappedObject(object):
+
+ def __init__(self, prototypes):
+ self.prototypes = prototypes
+
+ self.shortname = prototypes['shortname']
+ self.longname = prototypes['longname']
+ self.new_proto = prototypes['new'][0]
+ self.del_proto = prototypes['del'][0]
+ self.do_proto = prototypes['do'][0]
+ self.input_params = get_params_types_names(self.new_proto)
+ self.input_params_list = "; ".join(get_params(self.new_proto))
+ self.outputs = get_params_types_names(self.do_proto)[2:]
+ self.outputs_flat = get_params(self.do_proto)[2:]
+ self.output_results = ", ".join(self.outputs_flat)
+
+ def gen_code(self):
+ out = ""
+ out += self.gen_struct()
+ out += self.gen_doc()
+ out += self.gen_new()
+ out += self.gen_init()
+ out += self.gen_del()
+ out += self.gen_do()
+ out += self.gen_memberdef()
+ out += self.gen_set()
+ out += self.gen_get()
+ out += self.gen_methodef()
+ out += self.gen_typeobject()
+ return out
+
+ def gen_struct(self):
+ out = """
+// {shortname} structure
+typedef struct{{
+ PyObject_HEAD
+ // pointer to aubio object
+ {longname} *o;
+ // input parameters
+ {input_params_list};
+ // output results
+ {output_results};
+}} Py_{shortname};
+"""
+ return out.format(**self.__dict__)
+
+ def gen_doc(self):
+ out = """
+// TODO: add documentation
+static char Py_{shortname}_doc[] = \"undefined\";
+ """
+ return out.format(**self.__dict__)
+
+ def gen_new(self):
+ out = """
+// new {shortname}
+static PyObject *
+Py_{shortname}_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
+{{
+ Py_{shortname} *self;
+""".format(**self.__dict__)
+ params = self.input_params
+ for p in params:
+ out += """
+ {type} {name} = {defval};""".format(defval = param_init[p['type']], **p)
+ plist = ", ".join(["\"%s\"" % p['name'] for p in params])
+ out += """
+ static char *kwlist[] = {{ {plist}, NULL }};""".format(plist = plist)
+ argchars = "".join([pyargparse_chars[p['type']] for p in params])
+ arglist = ", ".join(["&%s" % p['name'] for p in params])
+ out += """
+ if (!PyArg_ParseTupleAndKeywords (args, kwds, "|{argchars}", kwlist,
+ {arglist})) {{
+ return NULL;
+ }}
+""".format(argchars = argchars, arglist = arglist)
+ out += """
+ self = (Py_{shortname} *) pytype->tp_alloc (pytype, 0);
+ if (self == NULL) {{
+ return NULL;
+ }}
+""".format(**self.__dict__)
+ params = self.input_params
+ for p in params:
+ out += self.check_valid(p)
+ out += """
+ return (PyObject *)self;
+}
+"""
+ return out
+
+ def check_valid(self, p):
+ if p['type'] == 'uint_t':
+ return self.check_valid_uint(p)
+ if p['type'] == 'char_t*':
+ return self.check_valid_char(p)
+ else:
+ print ("ERROR, no idea how to check %s for validity" % p['type'])
+
+ def check_valid_uint(self, p):
+ name = p['name']
+ return """
+ self->{name} = {defval};
+ if ((sint_t){name} > 0) {{
+ self->{name} = {name};
+ }} else if ((sint_t){name} < 0) {{
+ PyErr_SetString (PyExc_ValueError, "can not use negative value for {name}");
+ return NULL;
+ }}
+""".format(defval = aubiodefvalue[name], name = name)
+
+ def check_valid_char(self, p):
+ name = p['name']
+ return """
+ self->{name} = {defval};
+ if ({name} != NULL) {{
+ self->{name} = {name};
+ }}
+""".format(defval = aubiodefvalue[name], name = name)
+
+ def gen_init(self):
+ out = """
+// init {shortname}
+static int
+Py_{shortname}_init (Py_{shortname} * self, PyObject * args, PyObject * kwds)
+{{
+""".format(**self.__dict__)
+ new_name = get_name(self.new_proto)
+ new_params = ", ".join(["self->%s" % s['name'] for s in self.input_params])
+ out += """
+ self->o = {new_name}({new_params});
+""".format(new_name = new_name, new_params = new_params)
+ paramchars = "%s"
+ paramvals = "self->method"
+ out += """
+ // return -1 and set error string on failure
+ if (self->o == NULL) {{
+ //char_t errstr[30 + strlen(self->uri)];
+ //sprintf(errstr, "error creating {shortname} with params {paramchars}", {paramvals});
+ char_t errstr[60];
+ sprintf(errstr, "error creating {shortname} with given params");
+ PyErr_SetString (PyExc_Exception, errstr);
+ return -1;
+ }}
+""".format(paramchars = paramchars, paramvals = paramvals, **self.__dict__)
+ output_create = ""
+ for o in self.outputs:
+ output_create += """
+ self->{name} = {create_fn}({output_size});""".format(name = o['name'], create_fn = newfromtype_fn[o['type']], output_size = objoutsize[self.shortname])
+ out += """
+ // TODO get internal params after actual object creation?
+"""
+ out += """
+ // create outputs{output_create}
+""".format(output_create = output_create)
+ out += """
+ return 0;
+}
+"""
+ return out
+
+ def gen_memberdef(self):
+ out = """
+static PyMemberDef Py_{shortname}_members[] = {{
+""".format(**self.__dict__)
+ for p in get_params_types_names(self.new_proto):
+ tmp = " {{\"{name}\", {ttype}, offsetof (Py_{shortname}, {name}), READONLY, \"TODO documentation\"}},\n"
+ pytype = member_types[p['type']]
+ out += tmp.format(name = p['name'], ttype = pytype, shortname = self.shortname)
+ out += """ {NULL}, // sentinel
+};
+"""
+ return out
+
+ def gen_del(self):
+ out = """
+// del {shortname}
+static void
+Py_{shortname}_del (Py_{shortname} * self, PyObject * unused)
+{{""".format(**self.__dict__)
+ for o in self.outputs:
+ name = o['name']
+ del_out = delfromtype_fn[o['type']]
+ out += """
+ {del_out}(self->{name});""".format(del_out = del_out, name = name)
+ del_fn = get_name(self.del_proto)
+ out += """
+ {del_fn}(self->o);
+ Py_TYPE(self)->tp_free((PyObject *) self);
+}}
+""".format(del_fn = del_fn)
+ return out
+
+ def gen_do(self):
+ do_fn = get_name(self.do_proto)
+ input_param = get_params_types_names(self.do_proto)[1];
+ pytoaubio = pytoaubio_fn[input_param['type']]
+ output = self.outputs[0]
+ out = """
+// do {shortname}
+static PyObject*
+Py_{shortname}_do (Py_{shortname} * self, PyObject * args)
+{{
+ PyObject * in_obj;
+ {input_type} {input_name};
+
+ if (!PyArg_ParseTuple (args, "O", &in_obj)) {{
+ return NULL;
+ }}
+ {input_name} = {pytoaubio} (in_obj);
+ if ({input_name} == NULL) {{
+ return NULL;
+ }}
+
+ {do_fn}(self->o, {input_name}, {outputs});
+
+ return (PyObject *) {aubiotonumpy} ({outputs});
+}}
+"""
+ return out.format(do_fn = do_fn,
+ shortname = self.prototypes['shortname'],
+ input_name = input_param['name'],
+ input_type= input_param['type'],
+ pytoaubio = pytoaubio,
+ outputs = ", ".join(["self->%s" % p['name'] for p in self.outputs]),
+ aubiotonumpy = pyfromaubio_fn[output['type']],
+ )
+
+ def gen_set(self):
+ out = """
+// {shortname} setters
+""".format(**self.__dict__)
+ for set_param in self.prototypes['set']:
+ params = get_params_types_names(set_param)[1]
+ paramtype = params['type']
+ method_name = get_name(set_param)
+ param = method_name.split('aubio_'+self.shortname+'_set_')[-1]
+ pyparamtype = pyargparse_chars[paramtype]
+ out += """
+static PyObject *
+Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)
+{{
+ uint_t err = 0;
+ {paramtype} {param};
+
+ if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{
+ return NULL;
+ }}
+ err = aubio_{shortname}_set_{param} (self->o, {param});
+
+ if (err > 0) {{
+ PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");
+ return NULL;
+ }}
+ Py_RETURN_NONE;
+}}
+""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__)
+ return out
+
+ def gen_get(self):
+ out = """
+// {shortname} getters
+""".format(**self.__dict__)
+ for method in self.prototypes['get']:
+ params = get_params_types_names(method)
+ method_name = get_name(method)
+ assert len(params) == 1, \
+ "get method has more than one parameter %s" % params
+ param = method_name.split('aubio_'+self.shortname+'_get_')[-1]
+ paramtype = get_return_type(method)
+ ptypeconv = pyfromtype_fn[paramtype]
+ out += """
+static PyObject *
+Pyaubio_{shortname}_get_{param} (Py_{shortname} *self, PyObject *unused)
+{{
+ {ptype} {param} = aubio_{shortname}_get_{param} (self->o);
+ return (PyObject *){ptypeconv} ({param});
+}}
+""".format(param = param, ptype = paramtype, ptypeconv = ptypeconv,
+ **self.__dict__)
+ return out
+
+ def gen_methodef(self):
+ out = """
+static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__)
+ for m in self.prototypes['set']:
+ name = get_name(m)
+ shortname = name.replace('aubio_%s_' % self.shortname, '')
+ out += """
+ {{"{shortname}", (PyCFunction) Py{name},
+ METH_VARARGS, ""}},""".format(name = name, shortname = shortname)
+ for m in self.prototypes['get']:
+ name = get_name(m)
+ shortname = name.replace('aubio_%s_' % self.shortname, '')
+ out += """
+ {{"{shortname}", (PyCFunction) Py{name},
+ METH_NOARGS, ""}},""".format(name = name, shortname = shortname)
+ out += """
+ {NULL} /* sentinel */
+};
+"""
+ return out
+
+ def gen_typeobject(self):
+ return """
+PyTypeObject Py_{shortname}Type = {{
+ //PyObject_HEAD_INIT (NULL)
+ //0,
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "aubio.{shortname}",
+ sizeof (Py_{shortname}),
+ 0,
+ (destructor) Py_{shortname}_del,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ (ternaryfunc)Py_{shortname}_do,
+ 0,
+ 0,
+ 0,
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ Py_{shortname}_doc,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ Py_{shortname}_methods,
+ Py_{shortname}_members,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ (initproc) Py_{shortname}_init,
+ 0,
+ Py_{shortname}_new,
+}};
+""".format(**self.__dict__)
--- /dev/null
+++ b/python/lib/gen_external.py
@@ -1,0 +1,182 @@
+import os
+
+header = """// this file is generated! do not modify
+#include "aubio-types.h"
+"""
+
+skip_objects = [
+ # already in ext/
+ 'fft',
+ 'pvoc',
+ 'filter',
+ 'filterbank',
+ #'resampler',
+ # AUBIO_UNSTABLE
+ 'hist',
+ 'parameter',
+ 'scale',
+ 'beattracking',
+ 'resampler',
+ 'peakpicker',
+ 'pitchfcomb',
+ 'pitchmcomb',
+ 'pitchschmitt',
+ 'pitchspecacf',
+ 'pitchyin',
+ 'pitchyinfft',
+ 'sink',
+ 'sink_apple_audio',
+ 'sink_sndfile',
+ 'sink_wavwrite',
+ #'mfcc',
+ 'source',
+ 'source_apple_audio',
+ 'source_sndfile',
+ 'source_avcodec',
+ 'source_wavread',
+ #'sampler',
+ 'audio_unit',
+
+ 'tss',
+ ]
+
+
+def get_cpp_objects():
+
+ cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/src ../src/aubio.h').readlines()]
+ #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/onset/onset.h').readlines()]
+ #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/pitch/pitch.h').readlines()]
+
+ cpp_output = filter(lambda y: len(y) > 1, cpp_output)
+ cpp_output = filter(lambda y: not y.startswith('#'), cpp_output)
+ cpp_output = list(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)
+
+ cpp_objects = [a.split()[3][:-1] for a in typedefs]
+
+ return cpp_output, cpp_objects
+
+def generate_external(output_path):
+ os.mkdir(output_path)
+ sources_list = []
+ cpp_output, cpp_objects = get_cpp_objects()
+ lib = {}
+
+ for o in cpp_objects:
+ if o[:6] != 'aubio_':
+ continue
+ shortname = o[6:-2]
+ if shortname in skip_objects:
+ continue
+ lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
+ lib[shortname]['longname'] = o
+ lib[shortname]['shortname'] = shortname
+ for fn in cpp_output:
+ if o[:-1] in fn:
+ #print "found", o[:-1], "in", fn
+ if 'typedef struct ' in fn:
+ lib[shortname]['struct'].append(fn)
+ elif '_do' in fn:
+ lib[shortname]['do'].append(fn)
+ elif 'new_' in fn:
+ lib[shortname]['new'].append(fn)
+ elif 'del_' in fn:
+ lib[shortname]['del'].append(fn)
+ elif '_get_' in fn:
+ lib[shortname]['get'].append(fn)
+ elif '_set_' in fn:
+ lib[shortname]['set'].append(fn)
+ else:
+ #print "no idea what to do about", fn
+ lib[shortname]['other'].append(fn)
+
+ """
+ for fn in cpp_output:
+ found = 0
+ for o in lib:
+ for family in lib[o]:
+ if fn in lib[o][family]:
+ found = 1
+ if found == 0:
+ print "missing", fn
+
+ for o in lib:
+ for family in lib[o]:
+ if type(lib[o][family]) == str:
+ print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
+ elif len(lib[o][family]) == 1:
+ print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) )
+ else:
+ print ( "{:15s} {:10s} {:d}".format(o, family, len(lib[o][family]) ) )
+ """
+
+ from .gen_code import MappedObject
+ for o in lib:
+ out = header
+ mapped = MappedObject(lib[o])
+ out += mapped.gen_code()
+ output_file = os.path.join(output_path, 'gen-%s.c' % o)
+ with open(output_file, 'w') as f:
+ f.write(out)
+ print ("wrote %s" % output_file )
+ sources_list.append(output_file)
+
+ out = header
+ out += "#include \"aubio-generated.h\""
+ check_types = "\n || ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
+ out += """
+
+int generated_types_ready (void)
+{{
+ return ({pycheck_types});
+}}
+""".format(pycheck_types = check_types)
+
+ add_types = "".join(["""
+ Py_INCREF (&Py_{name}Type);
+ PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib])
+ out += """
+
+void add_generated_objects ( PyObject *m )
+{{
+{add_types}
+}}
+""".format(add_types = add_types)
+
+ output_file = os.path.join(output_path, 'aubio-generated.c')
+ with open(output_file, 'w') as f:
+ f.write(out)
+ print ("wrote %s" % output_file )
+ sources_list.append(output_file)
+
+ objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
+ out = """
+// generated list of objects created with gen_external.py
+#include <Python.h>
+
+{objlist}
+int generated_objects ( void );
+void add_generated_objects( PyObject *m );
+""".format(objlist = objlist)
+
+ output_file = os.path.join(output_path, 'aubio-generated.h')
+ with open(output_file, 'w') as f:
+ f.write(out)
+ print ("wrote %s" % output_file )
+ # no need to add header to list of sources
+
+ return sources_list
+
+if __name__ == '__main__':
+ output_path = 'gen'
+ generate_external(output_path)
--- a/python/lib/gen_pyobject.py
+++ /dev/null
@@ -1,531 +1,0 @@
-#! /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.
-"""
-
-param_numbers = {
- 'source': [0, 2],
- 'sink': [2, 0],
- 'sampler': [1, 1],
-}
-
-# 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.
-
-def write_msg(*args):
- pass
- # uncomment out for debugging
- #print args
-
-def split_type(arg):
- """ arg = 'foo *name'
- return ['foo*', 'name'] """
- l = arg.split()
- type_arg = {'type': l[0], 'name': l[1]}
- # ['foo', '*name'] -> ['foo*', 'name']
- if l[-1].startswith('*'):
- #return [l[0]+'*', l[1][1:]]
- type_arg['type'] = l[0] + '*'
- type_arg['name'] = l[1][1:]
- # ['foo', '*', 'name'] -> ['foo*', 'name']
- if len(l) == 3:
- #return [l[0]+l[1], l[2]]
- type_arg['type'] = l[0]+l[1]
- type_arg['name'] = l[2]
- else:
- #return l
- pass
- return type_arg
-
-def get_params(proto):
- """ get the list of parameters from a function prototype
- example: proto = "int main (int argc, char ** argv)"
- returns: ['int argc', 'char ** argv']
- """
- import re
- paramregex = re.compile('[\(, ](\w+ \*?\*? ?\w+)[, \)]')
- return paramregex.findall(proto)
-
-def get_params_types_names(proto):
- """ get the list of parameters from a function prototype
- example: proto = "int main (int argc, char ** argv)"
- returns: [['int', 'argc'], ['char **','argv']]
- """
- return list(map(split_type, get_params(proto)))
-
-def get_return_type(proto):
- import re
- paramregex = re.compile('(\w+ ?\*?).*')
- outputs = paramregex.findall(proto)
- assert len(outputs) == 1
- return outputs[0].replace(' ', '')
-
-def get_name(proto):
- name = proto.split()[1].split('(')[0]
- return name.replace('*','')
-
-# 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'],
- 'specdesc': ['1'],
- 'onset': ['1'],
- 'pitchyin': ['1'],
- 'pitchyinfft': ['1'],
- 'pitchschmitt': ['1'],
- 'pitchmcomb': ['1'],
- 'pitchfcomb': ['1'],
- 'pitch': ['1'],
- 'tss': ['self->buf_size', 'self->buf_size'],
- 'mfcc': ['self->n_coeffs'],
- 'beattracking': ['self->hop_size'],
- 'tempo': ['1'],
- 'peakpicker': ['1'],
- 'source': ['self->hop_size', '1'],
- 'sampler': ['self->hop_size'],
- 'wavetable': ['self->hop_size'],
-}
-
-# default value for variables
-aubioinitvalue = {
- 'uint_t': 0,
- 'sint_t': 0,
- 'smpl_t': 0,
- 'lsmp_t': 0.,
- 'char_t*': 'NULL',
- }
-
-aubiodefvalue = {
- # we have some clean up to do
- 'buf_size': 'Py_default_vector_length',
- # and here too
- 'hop_size': 'Py_default_vector_length / 2',
- # these should be alright
- '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',
- 'method': '"default"',
- 'uri': '"none"',
- }
-
-# aubio to python
-aubio2pytypes = {
- 'uint_t': 'I',
- 'sint_t': 'I',
- 'smpl_t': 'f',
- 'lsmp_t': 'd',
- 'fvec_t*': 'O',
- 'cvec_t*': 'O',
- 'char_t*': 's',
-}
-
-# python to aubio
-aubiovecfrompyobj = {
- 'fvec_t*': 'PyAubio_ArrayToCFvec',
- 'cvec_t*': 'PyAubio_ArrayToCCvec',
- 'uint_t': '(uint_t)PyLong_AsLong',
-}
-
-# aubio to python
-aubiovectopyobj = {
- 'fvec_t*': 'PyAubio_CFvecToArray',
- 'cvec_t*': 'PyAubio_CCvecToPyCvec',
- 'smpl_t': 'PyFloat_FromDouble',
- 'uint_t*': 'PyLong_FromLong',
- 'uint_t': 'PyLong_FromLong',
-}
-
-def gen_new_init(newfunc, name):
- newparams = get_params_types_names(newfunc)
- # self->param1, self->param2, self->param3
- if len(newparams):
- selfparams = ', self->'+', self->'.join([p['name'] for p in newparams])
- else:
- selfparams = ''
- # "param1", "param2", "param3"
- paramnames = ", ".join(["\""+p['name']+"\"" for p in newparams])
- pyparams = "".join([aubio2pytypes[p['type']] for p in newparams])
- paramrefs = ", ".join(["&" + p['name'] for p in newparams])
- s = """\
-// WARNING: this file is generated, DO NOT EDIT
-
-// WARNING: if you haven't read the first line yet, please do so
-#include "aubiowraphell.h"
-
-typedef struct
-{
- PyObject_HEAD
- aubio_%(name)s_t * o;
-""" % locals()
- for p in newparams:
- ptype = p['type']
- pname = p['name']
- 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)
-{
- Py_%(name)s *self;
-""" % locals()
- for p in newparams:
- ptype = p['type']
- pname = p['name']
- initval = aubioinitvalue[ptype]
- s += """\
- %(ptype)s %(pname)s = %(initval)s;
-""" % locals()
- # now the actual PyArg_Parse
- if len(paramnames):
- s += """\
- static char *kwlist[] = { %(paramnames)s, NULL };
-
- if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
- %(paramrefs)s)) {
- return NULL;
- }
-""" % locals()
- s += """\
-
- self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
-
- if (self == NULL) {
- return NULL;
- }
-""" % locals()
- for p in newparams:
- ptype = p['type']
- pname = p['name']
- defval = aubiodefvalue[pname]
- if ptype == 'char_t*':
- s += """\
-
- self->%(pname)s = %(defval)s;
- if (%(pname)s != NULL) {
- self->%(pname)s = %(pname)s;
- }
-""" % locals()
- elif ptype == 'uint_t':
- s += """\
-
- self->%(pname)s = %(defval)s;
- if ((sint_t)%(pname)s > 0) {
- self->%(pname)s = %(pname)s;
- } else if ((sint_t)%(pname)s < 0) {
- PyErr_SetString (PyExc_ValueError,
- "can not use negative value for %(pname)s");
- return NULL;
- }
-""" % locals()
- elif ptype == 'smpl_t':
- s += """\
-
- self->%(pname)s = %(defval)s;
- if (%(pname)s != %(defval)s) {
- self->%(pname)s = %(pname)s;
- }
-""" % locals()
- else:
- write_msg ("ERROR, unknown type of parameter %s %s" % (ptype, pname) )
- s += """\
-
- return (PyObject *) self;
-}
-
-AUBIO_INIT(%(name)s %(selfparams)s)
-
-AUBIO_DEL(%(name)s)
-
-""" % locals()
- return s
-
-def gen_do_input_params(inputparams):
- inputdefs = ''
- parseinput = ''
- inputrefs = ''
- inputvecs = ''
- pytypes = ''
-
- if len(inputparams):
- # build the parsing string for PyArg_ParseTuple
- pytypes = "".join([aubio2pytypes[p['type']] for p in inputparams])
-
- inputdefs = " /* input vectors python prototypes */\n"
- for p in inputparams:
- if p['type'] != 'uint_t':
- inputdefs += " PyObject * " + p['name'] + "_obj;\n"
-
- inputvecs = " /* input vectors prototypes */\n "
- inputvecs += "\n ".join([ p['type'] + ' ' + p['name'] + ";" for p in inputparams])
-
- parseinput = " /* input vectors parsing */\n "
- for p in inputparams:
- inputvec = p['name']
- if p['type'] != 'uint_t':
- inputdef = p['name'] + "_obj"
- else:
- inputdef = p['name']
- converter = aubiovecfrompyobj[p['type']]
- if p['type'] != 'uint_t':
- parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
-
- if (%(inputvec)s == NULL) {
- return NULL;
- }
-
- """ % locals()
-
- # build the string for the input objects references
- inputreflist = []
- for p in inputparams:
- if p['type'] != 'uint_t':
- inputreflist += [ "&" + p['name'] + "_obj" ]
- else:
- inputreflist += [ "&" + p['name'] ]
- inputrefs = ", ".join(inputreflist)
- # end of inputs strings
- return inputdefs, parseinput, inputrefs, inputvecs, pytypes
-
-def gen_do_output_params(outputparams, name):
- outputvecs = ""
- outputcreate = ""
- if len(outputparams):
- outputvecs = " /* output vectors prototypes */\n"
- for p in outputparams:
- params = {
- 'name': p['name'], 'pytype': p['type'], 'autype': p['type'][:-3],
- 'length': defaultsizes[name].pop(0) }
- if (p['type'] == 'uint_t*'):
- outputvecs += ' uint_t' + ' ' + p['name'] + ";\n"
- outputcreate += " %(name)s = 0;\n" % params
- else:
- outputvecs += " " + p['type'] + ' ' + p['name'] + ";\n"
- outputcreate += " /* creating output %(name)s as a new_%(autype)s of length %(length)s */\n" % params
- outputcreate += " %(name)s = new_%(autype)s (%(length)s);\n" % params
-
- returnval = "";
- if len(outputparams) > 1:
- returnval += " PyObject *outputs = PyList_New(0);\n"
- for p in outputparams:
- returnval += " PyList_Append( outputs, (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")" +");\n"
- returnval += " return outputs;"
- elif len(outputparams) == 1:
- if defaultsizes[name] == '1':
- returnval += " return (PyObject *)PyFloat_FromDouble(" + p['name'] + "->data[0])"
- else:
- returnval += " return (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")"
- else:
- returnval += " Py_RETURN_NONE"
- # end of output strings
- return outputvecs, outputcreate, returnval
-
-def gen_do(dofunc, name):
- funcname = dofunc.split()[1].split('(')[0]
- doparams = get_params_types_names(dofunc)
- # make sure the first parameter is the object
- assert doparams[0]['type'] == "aubio_"+name+"_t*", \
- "method is not in 'aubio_<name>_t"
- # and remove it
- doparams = doparams[1:]
-
- n_param = len(doparams)
-
- if name in list(param_numbers.keys()):
- n_input_param, n_output_param = param_numbers[name]
- else:
- n_input_param, n_output_param = 1, n_param - 1
-
- assert n_output_param + n_input_param == n_param, "n_output_param + n_input_param != n_param for %s" % name
-
- inputparams = doparams[:n_input_param]
- outputparams = doparams[n_input_param:n_input_param + n_output_param]
-
- inputdefs, parseinput, inputrefs, inputvecs, pytypes = gen_do_input_params(inputparams);
- outputvecs, outputcreate, returnval = gen_do_output_params(outputparams, name)
-
- # build strings for outputs
- # build the parameters for the _do() call
- doparams_string = "self->o"
- for p in doparams:
- if p['type'] == 'uint_t*':
- doparams_string += ", &" + p['name']
- else:
- doparams_string += ", " + p['name']
-
- if n_input_param:
- arg_parse_tuple = """\
- if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
- return NULL;
- }
-""" % locals()
- else:
- arg_parse_tuple = ""
- # put it all together
- s = """\
-/* function Py_%(name)s_do */
-static PyObject *
-Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
-{
-%(inputdefs)s
-%(inputvecs)s
-%(outputvecs)s
-
-%(arg_parse_tuple)s
-
-%(parseinput)s
-
-%(outputcreate)s
-
- /* compute _do function */
- %(funcname)s (%(doparams_string)s);
-
-%(returnval)s;
-}
-""" % locals()
- return s
-
-def gen_members(new_method, name):
- newparams = get_params_types_names(new_method)
- s = """
-AUBIO_MEMBERS_START(%(name)s)""" % locals()
- for param in newparams:
- if param['type'] == 'char_t*':
- s += """
- {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
- % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
- elif param['type'] == 'uint_t':
- s += """
- {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
- % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
- elif param['type'] == 'smpl_t':
- s += """
- {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
- % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
- else:
- write_msg ("-- ERROR, unknown member type ", param )
- s += """
-AUBIO_MEMBERS_STOP(%(name)s)
-
-""" % locals()
- return s
-
-
-def gen_methods(get_methods, set_methods, name):
- s = ""
- method_defs = ""
- for method in set_methods:
- method_name = get_name(method)
- params = get_params_types_names(method)
- out_type = get_return_type(method)
- assert params[0]['type'] == "aubio_"+name+"_t*", \
- "get method is not in 'aubio_<name>_t"
- write_msg (method )
- write_msg (params[1:])
- setter_args = "self->o, " +",".join([p['name'] for p in params[1:]])
- parse_args = ""
- for p in params[1:]:
- parse_args += p['type'] + " " + p['name'] + ";\n"
- argmap = "".join([aubio2pytypes[p['type']] for p in params[1:]])
- arglist = ", ".join(["&"+p['name'] for p in params[1:]])
- parse_args += """
- if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) {
- return NULL;
- } """ % locals()
- s += """
-static PyObject *
-Py%(funcname)s (Py_%(objname)s *self, PyObject *args)
-{
- uint_t err = 0;
-
- %(parse_args)s
-
- err = %(funcname)s (%(setter_args)s);
-
- if (err > 0) {
- PyErr_SetString (PyExc_ValueError,
- "error running %(funcname)s");
- return NULL;
- }
- Py_RETURN_NONE;
-}
-""" % {'funcname': method_name, 'objname': name,
- 'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args }
- shortname = method_name.split('aubio_'+name+'_')[-1]
- method_defs += """\
- {"%(shortname)s", (PyCFunction) Py%(method_name)s,
- METH_VARARGS, ""},
-""" % locals()
-
- for method in get_methods:
- method_name = get_name(method)
- params = get_params_types_names(method)
- out_type = get_return_type(method)
- assert params[0]['type'] == "aubio_"+name+"_t*", \
- "get method is not in 'aubio_<name>_t %s" % params[0]['type']
- assert len(params) == 1, \
- "get method has more than one parameter %s" % params
- getter_args = "self->o"
- returnval = "(PyObject *)" + aubiovectopyobj[out_type] + " (tmp)"
- shortname = method_name.split('aubio_'+name+'_')[-1]
- method_defs += """\
- {"%(shortname)s", (PyCFunction) Py%(method_name)s,
- METH_NOARGS, ""},
-""" % locals()
- s += """
-static PyObject *
-Py%(funcname)s (Py_%(objname)s *self, PyObject *unused)
-{
- %(out_type)s tmp = %(funcname)s (%(getter_args)s);
- return %(returnval)s;
-}
-""" % {'funcname': method_name, 'objname': name,
- 'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval }
-
- s += """
-static PyMethodDef Py_%(name)s_methods[] = {
-""" % locals()
- s += method_defs
- s += """\
- {NULL} /* sentinel */
-};
-""" % locals()
- return s
-
-def gen_finish(name):
- s = """\
-
-AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
-""" % locals()
- return s
--- a/python/lib/generator.py
+++ /dev/null
@@ -1,233 +1,0 @@
-#! /usr/bin/python
-
-""" This file generates a c file from a list of cpp prototypes. """
-
-import os, sys, shutil
-from .gen_pyobject import write_msg, gen_new_init, gen_do, gen_members, gen_methods, gen_finish
-
-def get_cpp_objects():
-
- cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/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)
- cpp_output = list(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)
- typedefs = list(typedefs)
-
- cpp_objects = [a.split()[3][:-1] for a in typedefs]
-
- return cpp_output, cpp_objects
-
-def generate_object_files(output_path):
- if os.path.isdir(output_path): shutil.rmtree(output_path)
- os.mkdir(output_path)
-
- generated_objects = []
- cpp_output, cpp_objects = get_cpp_objects()
- skip_objects = [
- # already in ext/
- 'fft',
- 'pvoc',
- 'filter',
- 'filterbank',
- #'resampler',
- # AUBIO_UNSTABLE
- 'hist',
- 'parameter',
- 'scale',
- 'beattracking',
- 'resampler',
- 'sndfile',
- 'peakpicker',
- 'pitchfcomb',
- 'pitchmcomb',
- 'pitchschmitt',
- 'pitchspecacf',
- 'pitchyin',
- 'pitchyinfft',
- 'sink',
- 'sink_apple_audio',
- 'sink_sndfile',
- 'sink_wavwrite',
- 'source',
- 'source_apple_audio',
- 'source_sndfile',
- 'source_avcodec',
- 'source_wavread',
- #'sampler',
- 'audio_unit',
- ]
-
- write_msg("-- INFO: %d objects in total" % len(cpp_objects))
-
- for this_object in cpp_objects:
- lint = 0
-
- if this_object[-2:] == '_t':
- object_name = this_object[:-2]
- else:
- object_name = this_object
- write_msg("-- WARNING: %s does not end in _t" % this_object)
-
- if object_name[:len('aubio_')] != 'aubio_':
- write_msg("-- WARNING: %s does not start n aubio_" % this_object)
-
- write_msg("-- INFO: looking at", object_name)
- object_methods = filter(lambda x: this_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)
- object_methods = list(object_methods)
- #for method in object_methods:
- # write_msg(method)
- new_methods = list(filter(
- lambda x: 'new_'+object_name in x, object_methods))
- if len(new_methods) > 1:
- write_msg("-- WARNING: more than one new method for", object_name)
- for method in new_methods:
- write_msg(method)
- elif len(new_methods) < 1:
- write_msg("-- WARNING: no new method for", object_name)
- elif 0:
- for method in new_methods:
- write_msg(method)
-
- del_methods = list(filter(
- lambda x: 'del_'+object_name in x, object_methods))
- if len(del_methods) > 1:
- write_msg("-- WARNING: more than one del method for", object_name)
- for method in del_methods:
- write_msg(method)
- elif len(del_methods) < 1:
- write_msg("-- WARNING: no del method for", object_name)
-
- do_methods = list(filter(
- lambda x: object_name+'_do' in x, object_methods))
- if len(do_methods) > 1:
- pass
- #write_msg("-- WARNING: more than one do method for", object_name)
- #for method in do_methods:
- # write_msg(method)
- elif len(do_methods) < 1:
- write_msg("-- WARNING: no do method for", object_name)
- elif 0:
- for method in do_methods:
- write_msg(method)
-
- # check do methods return void
- for method in do_methods:
- if (method.split()[0] != 'void'):
- write_msg("-- 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'):
- write_msg("-- 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)
- other_methods = list(other_methods)
-
- if len(other_methods) > 0:
- write_msg("-- WARNING: some methods for", object_name, "were unidentified")
- for method in other_methods:
- write_msg(method)
-
-
- # generate this_object
- short_name = object_name[len('aubio_'):]
- if short_name in skip_objects:
- write_msg("-- 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)
- generated_filepath = os.path.join(output_path,'gen-'+short_name+'.c')
- fd = open(generated_filepath, 'w')
- fd.write(s)
- #except Exception, e:
- # write_msg("-- ERROR:", type(e), str(e), "in", short_name)
- # continue
- generated_objects += [this_object]
-
- s = """// generated list of objects created with generator.py
-
-"""
-
- types_ready = []
- for each in generated_objects:
- types_ready.append(" PyType_Ready (&Py_%sType) < 0" % \
- each.replace('aubio_','').replace('_t','') )
-
- s = """// generated list of objects created with generator.py
-
-#include "aubio-generated.h"
-"""
-
- s += """
-int generated_types_ready (void)
-{
- return (
-"""
- s += ('\n ||').join(types_ready)
- s += """);
-}
-"""
-
- s += """
-void add_generated_objects ( PyObject *m )
-{"""
- for each in generated_objects:
- s += """
- Py_INCREF (&Py_%(name)sType);
- PyModule_AddObject (m, "%(name)s", (PyObject *) & Py_%(name)sType);""" % \
- { 'name': ( each.replace('aubio_','').replace('_t','') ) }
-
- s += """
-}"""
-
- fd = open(os.path.join(output_path,'aubio-generated.c'), 'w')
- fd.write(s)
-
- s = """// generated list of objects created with generator.py
-
-#include <Python.h>
-
-"""
-
- for each in generated_objects:
- s += "extern PyTypeObject Py_%sType;\n" % \
- each.replace('aubio_','').replace('_t','')
-
- s+= "int generated_objects ( void );\n"
- s+= "void add_generated_objects( PyObject *m );\n"
-
- fd = open(os.path.join(output_path,'aubio-generated.h'), 'w')
- fd.write(s)
-
- from os import listdir
- generated_files = listdir(output_path)
- generated_files = filter(lambda x: x.endswith('.c'), generated_files)
- generated_files = [output_path+'/'+f for f in generated_files]
- return generated_files
-
-if __name__ == '__main__':
- generate_object_files('gen')