source: interfaces/python/gen_pyobject.py @ c71e405

feature/autosinkfeature/constantqfeature/pitchshiftfeature/pydocstringsfeature/timestretchpitchshiftsamplertimestretchyinfft+
Last change on this file since c71e405 was c71e405, checked in by Paul Brossier <piem@piem.org>, 8 years ago

updated setup to generate files

  • Property mode set to 100644
File size: 12.8 KB
Line 
1#! /usr/bin/python
2
3""" This madness of code is used to generate the C code of the python interface
4to aubio. Don't try this at home.
5
6The list of typedefs and functions is obtained from the command line 'cpp
7aubio.h'. This list is then used to parse all the functions about this object.
8
9I hear the ones asking "why not use swig, or cython, or something like that?"
10
11The requirements for this extension are the following:
12
13    - aubio vectors can be viewed as numpy arrays, and vice versa
14    - aubio 'object' should be python classes, not just a bunch of functions
15
16I haven't met any python interface generator that can meet both these
17requirements. If you know of one, please let me know, it will spare me
18maintaining this bizarre file.
19"""
20
21# TODO
22# do function: for now, only the following pattern is supported:
23# void aubio_<foo>_do (aubio_foo_t * o,
24#       [input1_t * input, [output1_t * output, ..., output3_t * output]]);
25# There is no way of knowing that output1 is actually input2. In the future,
26# const could be used for the inputs in the C prototypes.
27
28def write_msg(*args):
29  pass
30  # uncomment out for debugging
31  #print args
32
33def split_type(arg):
34    """ arg = 'foo *name'
35        return ['foo*', 'name'] """
36    l = arg.split()
37    # ['foo', '*name'] -> ['foo*', 'name']
38    if l[-1].startswith('*'):
39        return [l[0]+'*', l[1][1:]]
40    # ['foo', '*', 'name'] -> ['foo*', 'name']
41    if len(l) == 3:
42        return [l[0]+l[1], l[2]]
43    else:
44        return l
45
46def get_params(proto):
47    """ get the list of parameters from a function prototype
48    example: proto = "int main (int argc, char ** argv)"
49    returns: ['int argc', 'char ** argv']
50    """
51    import re
52    paramregex = re.compile('[\(, ](\w+ \*?\*? ?\w+)[, \)]')
53    return paramregex.findall(proto)
54
55def get_params_types_names(proto):
56    """ get the list of parameters from a function prototype
57    example: proto = "int main (int argc, char ** argv)"
58    returns: [['int', 'argc'], ['char **','argv']]
59    """
60    return map(split_type, get_params(proto)) 
61
62def get_return_type(proto):
63    import re
64    paramregex = re.compile('(\w+ ?\*?).*')
65    outputs = paramregex.findall(proto)
66    assert len(outputs) == 1
67    return outputs[0].replace(' ', '')
68
69def get_name(proto):
70    name = proto.split()[1].split('(')[0]
71    return name.replace('*','')
72
73# the important bits: the size of the output for each objects. this data should
74# move into the C library at some point.
75defaultsizes = {
76    'resampler':    'input->length * self->ratio',
77    'specdesc':     '1',
78    'onset':        '1',
79    'pitchyin':     '1',
80    'pitchyinfft':  '1',
81    'pitchschmitt': '1',
82    'pitchmcomb':   '1',
83    'pitchfcomb':   '1',
84    'pitch':        '1',
85    'tss':          'self->hop_size',
86    'mfcc':         'self->n_coeffs',
87    'beattracking': 'self->hop_size',
88    'tempo':        '1',
89    'peakpicker':   '1',
90}
91
92# default value for variables
93aubioinitvalue = {
94    'uint_t': 0,
95    'smpl_t': 0,
96    'lsmp_t': 0.,
97    'char_t*': 'NULL',
98    }
99
100aubiodefvalue = {
101    # we have some clean up to do
102    'buf_size': 'Py_default_vector_length', 
103    # and here too
104    'hop_size': 'Py_default_vector_length / 2', 
105    # these should be alright
106    'samplerate': 'Py_aubio_default_samplerate', 
107    # now for the non obvious ones
108    'n_filters': '40', 
109    'n_coeffs': '13', 
110    'nelems': '10',
111    'flow': '0.', 
112    'fhig': '1.', 
113    'ilow': '0.', 
114    'ihig': '1.', 
115    'thrs': '0.5',
116    'ratio': '0.5',
117    'method': '"default"',
118    }
119
120# aubio to python
121aubio2pytypes = {
122    'uint_t': 'I',
123    'smpl_t': 'f',
124    'lsmp_t': 'd',
125    'fvec_t*': 'O',
126    'cvec_t*': 'O',
127    'char_t*': 's',
128}
129
130# python to aubio
131aubiovecfrompyobj = {
132    'fvec_t*': 'PyAubio_ArrayToCFvec',
133    'cvec_t*': 'PyAubio_ArrayToCCvec',
134}
135
136# aubio to python
137aubiovectopyobj = {
138    'fvec_t*': 'PyAubio_CFvecToArray',
139    'cvec_t*': 'PyAubio_CCvecToPyCvec',
140    'smpl_t': 'PyFloat_FromDouble',
141}
142
143def gen_new_init(newfunc, name):
144    newparams = get_params_types_names(newfunc)
145    # self->param1, self->param2, self->param3
146    if len(newparams):
147        selfparams = ', self->'+', self->'.join([p[1] for p in newparams])
148    else:
149        selfparams = '' 
150    # "param1", "param2", "param3"
151    paramnames = ", ".join(["\""+p[1]+"\"" for p in newparams])
152    pyparams = "".join(map(lambda p: aubio2pytypes[p[0]], newparams))
153    paramrefs = ", ".join(["&" + p[1] for p in newparams])
154    s = """\
155// WARNING: this file is generated, DO NOT EDIT
156
157// WARNING: if you haven't read the first line yet, please do so
158#include "aubiowraphell.h"
159
160typedef struct
161{
162  PyObject_HEAD
163  aubio_%(name)s_t * o;
164""" % locals()
165    for ptype, pname in newparams:
166        s += """\
167  %(ptype)s %(pname)s;
168""" % locals()
169    s += """\
170} Py_%(name)s;
171
172static char Py_%(name)s_doc[] = "%(name)s object";
173
174static PyObject *
175Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
176{
177  Py_%(name)s *self;
178""" % locals()
179    for ptype, pname in newparams:
180        initval = aubioinitvalue[ptype]
181        s += """\
182  %(ptype)s %(pname)s = %(initval)s;
183""" % locals()
184    # now the actual PyArg_Parse
185    if len(paramnames):
186        s += """\
187  static char *kwlist[] = { %(paramnames)s, NULL };
188
189  if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
190          %(paramrefs)s)) {
191    return NULL;
192  }
193""" % locals()
194    s += """\
195
196  self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
197
198  if (self == NULL) {
199    return NULL;
200  }
201""" % locals()
202    for ptype, pname in newparams:
203        defval = aubiodefvalue[pname]
204        if ptype == 'char_t*':
205            s += """\
206
207  self->%(pname)s = %(defval)s;
208  if (%(pname)s != NULL) {
209    self->%(pname)s = %(pname)s;
210  }
211""" % locals()
212        elif ptype == 'uint_t':
213            s += """\
214
215  self->%(pname)s = %(defval)s;
216  if (%(pname)s > 0) {
217    self->%(pname)s = %(pname)s;
218  } else if (%(pname)s < 0) {
219    PyErr_SetString (PyExc_ValueError,
220        "can not use negative value for %(pname)s");
221    return NULL;
222  }
223""" % locals()
224        elif ptype == 'smpl_t':
225            s += """\
226
227  self->%(pname)s = %(defval)s;
228  if (%(pname)s != %(defval)s) {
229    self->%(pname)s = %(pname)s;
230  }
231""" % locals()
232        else:
233            write_msg ("ERROR, unknown type of parameter %s %s" % (ptype, pname) )
234    s += """\
235
236  return (PyObject *) self;
237}
238
239AUBIO_INIT(%(name)s %(selfparams)s)
240
241AUBIO_DEL(%(name)s)
242
243""" % locals()
244    return s
245
246def gen_do(dofunc, name):
247    funcname = dofunc.split()[1].split('(')[0]
248    doparams = get_params_types_names(dofunc) 
249    # make sure the first parameter is the object
250    assert doparams[0][0] == "aubio_"+name+"_t*", \
251        "method is not in 'aubio_<name>_t"
252    # and remove it
253    doparams = doparams[1:]
254    # guess the input/output params, assuming we have less than 3
255    assert len(doparams) > 0, \
256        "no parameters for function do in object %s" % name
257    #assert (len(doparams) <= 2), \
258    #    "more than 3 parameters for do in object %s" % name
259
260    # build strings for inputs, assuming there is only one input
261    inputparams = [doparams[0]]
262    # build the parsing string for PyArg_ParseTuple
263    pytypes = "".join([aubio2pytypes[p[0]] for p in doparams[0:1]])
264    inputdefs = "\n  ".join(["PyObject * " + p[-1] + "_obj;" for p in inputparams])
265    inputvecs = "\n  ".join(map(lambda p: \
266                p[0] + p[-1] + ";", inputparams))
267    parseinput = ""
268    for p in inputparams:
269        inputvec = p[-1]
270        inputdef = p[-1] + "_obj"
271        converter = aubiovecfrompyobj[p[0]]
272        parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
273
274  if (%(inputvec)s == NULL) {
275    return NULL;
276  }""" % locals()
277    # build the string for the input objects references
278    inputrefs = ", ".join(["&" + p[-1] + "_obj" for p in inputparams])
279    # end of inputs strings
280
281    # build strings for outputs
282    outputparams = doparams[1:]
283    if len(outputparams) >= 1:
284        #assert len(outputparams) == 1, \
285        #    "too many output parameters"
286        outputvecs = "\n  ".join([p[0] + p[-1] + ";" for p in outputparams])
287        outputcreate = "\n  ".join(["""\
288  %(name)s = new_%(autype)s (%(length)s);""" % \
289    {'name': p[-1], 'pytype': p[0], 'autype': p[0][:-3],
290        'length': defaultsizes[name]} \
291        for p in outputparams]) 
292        if len(outputparams) > 1:
293            returnval = "PyObject *outputs = PyList_New(0);\n"
294            for p in outputparams:
295                returnval += "  PyList_Append( outputs, (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")" +");\n"
296            returnval += "  return outputs;"
297        else:
298            returnval = "return (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
299    else:
300        # no output
301        outputvecs = ""
302        outputcreate = ""
303        #returnval = "Py_None";
304        returnval = "return (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
305    # end of output strings
306
307    # build the parameters for the  _do() call
308    doparams_string = "self->o, " + ", ".join([p[-1] for p in doparams])
309
310    # put it all together
311    s = """\
312static PyObject *
313Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
314{
315  %(inputdefs)s
316  %(inputvecs)s
317  %(outputvecs)s
318
319  if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
320    return NULL;
321  }
322
323  %(parseinput)s
324 
325  %(outputcreate)s
326
327  /* compute _do function */
328  %(funcname)s (%(doparams_string)s);
329
330  %(returnval)s;
331}
332""" % locals()
333    return s
334
335def gen_members(new_method, name):
336    newparams = get_params_types_names(new_method)
337    s = """
338AUBIO_MEMBERS_START(%(name)s)""" % locals()
339    for param in newparams:
340        if param[0] == 'char_t*':
341            s += """
342  {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
343        % { 'pname': param[1], 'ptype': param[0], 'name': name}
344        elif param[0] == 'uint_t':
345            s += """
346  {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
347        % { 'pname': param[1], 'ptype': param[0], 'name': name}
348        elif param[0] == 'smpl_t':
349            s += """
350  {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
351        % { 'pname': param[1], 'ptype': param[0], 'name': name}
352        else:
353            write_msg ("-- ERROR, unknown member type ", param )
354    s += """
355AUBIO_MEMBERS_STOP(%(name)s)
356
357""" % locals()
358    return s
359
360
361def gen_methods(get_methods, set_methods, name):
362    s = ""
363    method_defs = ""
364    for method in set_methods:
365        method_name = get_name(method)
366        params = get_params_types_names(method)
367        out_type = get_return_type(method)
368        assert params[0][0] == "aubio_"+name+"_t*", \
369            "get method is not in 'aubio_<name>_t"
370        write_msg (method )
371        write_msg (params[1:])
372        setter_args = "self->o, " +",".join([p[1] for p in params[1:]])
373        parse_args = ""
374        for p in params[1:]:
375            parse_args += p[0] + " " + p[1] + ";\n"
376        argmap = "".join([aubio2pytypes[p[0]] for p in params[1:]])
377        arglist = ", ".join(["&"+p[1] for p in params[1:]])
378        parse_args += """
379  if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) {
380    return NULL;
381  } """ % locals()
382        s += """
383static PyObject *
384Py%(funcname)s (Py_%(objname)s *self, PyObject *args)
385{
386  uint_t err = 0;
387
388  %(parse_args)s
389
390  err = %(funcname)s (%(setter_args)s);
391
392  if (err > 0) {
393    PyErr_SetString (PyExc_ValueError,
394        "error running %(funcname)s");
395    return NULL;
396  }
397  return Py_None;
398}
399""" % {'funcname': method_name, 'objname': name, 
400        'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args }
401        shortname = method_name.split(name+'_')[-1]
402        method_defs += """\
403  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
404    METH_VARARGS, ""},
405""" % locals()
406
407    for method in get_methods:
408        method_name = get_name(method)
409        params = get_params_types_names(method)
410        out_type = get_return_type(method)
411        assert params[0][0] == "aubio_"+name+"_t*", \
412            "get method is not in 'aubio_<name>_t %s" % params[0][0]
413        assert len(params) == 1, \
414            "get method has more than one parameter %s" % params
415        getter_args = "self->o" 
416        returnval = "(PyObject *)" + aubiovectopyobj[out_type] + " (tmp)"
417        shortname = method_name.split(name+'_')[-1]
418        method_defs += """\
419  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
420    METH_NOARGS, ""},
421""" % locals()
422        s += """
423static PyObject *
424Py%(funcname)s (Py_%(objname)s *self, PyObject *unused)
425{
426  %(out_type)s tmp = %(funcname)s (%(getter_args)s);
427  return %(returnval)s;
428}
429""" % {'funcname': method_name, 'objname': name, 
430        'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval }
431
432    s += """
433static PyMethodDef Py_%(name)s_methods[] = {
434""" % locals() 
435    s += method_defs
436    s += """\
437  {NULL} /* sentinel */
438};
439""" % locals() 
440    return s
441
442def gen_finish(name):
443    s = """\
444
445AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
446""" % locals()
447    return s
Note: See TracBrowser for help on using the repository browser.