From ccb9fb565a315f676cd028cde7fa2da4ac26879f Mon Sep 17 00:00:00 2001 From: Paul Brossier Date: Tue, 19 Apr 2016 01:36:13 +0200 Subject: [PATCH] lib/gen_external.py: rewrote wrapper --- python/lib/gen_code.py | 487 +++++++++++++++++++++++++++++++++++++++++ python/lib/gen_external.py | 182 ++++++++++++++++ python/lib/gen_pyobject.py | 531 --------------------------------------------- python/lib/generator.py | 233 -------------------- 4 files changed, 669 insertions(+), 764 deletions(-) create mode 100644 python/lib/gen_code.py create mode 100644 python/lib/gen_external.py delete mode 100644 python/lib/gen_pyobject.py delete mode 100755 python/lib/generator.py diff --git a/python/lib/gen_code.py b/python/lib/gen_code.py new file mode 100644 index 00000000..620644a7 --- /dev/null +++ b/python/lib/gen_code.py @@ -0,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__) diff --git a/python/lib/gen_external.py b/python/lib/gen_external.py new file mode 100644 index 00000000..735cfefa --- /dev/null +++ b/python/lib/gen_external.py @@ -0,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 + +{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) diff --git a/python/lib/gen_pyobject.py b/python/lib/gen_pyobject.py deleted file mode 100644 index ca6a8700..00000000 --- a/python/lib/gen_pyobject.py +++ /dev/null @@ -1,531 +0,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__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__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__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__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 diff --git a/python/lib/generator.py b/python/lib/generator.py deleted file mode 100755 index cf164a9e..00000000 --- a/python/lib/generator.py +++ /dev/null @@ -1,233 +0,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 - -""" - - 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') -- 2.11.0