source: interfaces/python/gen_pyobject.py @ 0178c65

feature/autosinkfeature/cnnfeature/cnn_orgfeature/constantqfeature/crepefeature/crepe_orgfeature/pitchshiftfeature/pydocstringsfeature/timestretchfix/ffmpeg5pitchshiftsamplertimestretchyinfft+
Last change on this file since 0178c65 was 96fe713, checked in by Paul Brossier <piem@piem.org>, 15 years ago

interfaces/python: towards mono

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