source: interfaces/python/gen_pyobject.py @ b8aaaff

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

gen_pyobject.py: simplify with proxy functions

  • Property mode set to 100644
File size: 12.7 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# python to aubio
126aubiovecfrompyobj = {
127    'fvec_t*': 'PyAubio_ArrayToCFvec',
128    'cvec_t*': 'PyAubio_ArrayToCCvec',
129}
130
131# aubio to python
132aubiovectopyobj = {
133    'fvec_t*': 'PyAubio_CFvecToArray',
134    'cvec_t*': 'PyAubio_CCvecToPyCvec',
135    'smpl_t': 'PyFloat_FromDouble',
136}
137
138def gen_new_init(newfunc, name):
139    newparams = get_params_types_names(newfunc)
140    # self->param1, self->param2, self->param3
141    if len(newparams):
142        selfparams = ', self->'+', self->'.join([p[1] for p in newparams])
143    else:
144        selfparams = '' 
145    # "param1", "param2", "param3"
146    paramnames = ", ".join(["\""+p[1]+"\"" for p in newparams])
147    pyparams = "".join(map(lambda p: aubio2pytypes[p[0]], newparams))
148    paramrefs = ", ".join(["&" + p[1] for p in newparams])
149    s = """\
150// WARNING: this file is generated, DO NOT EDIT
151
152// WARNING: if you haven't read the first line yet, please do so
153#include "aubiowraphell.h"
154
155typedef struct
156{
157  PyObject_HEAD
158  aubio_%(name)s_t * o;
159""" % locals()
160    for ptype, pname in newparams:
161        s += """\
162  %(ptype)s %(pname)s;
163""" % locals()
164    s += """\
165} Py_%(name)s;
166
167static char Py_%(name)s_doc[] = "%(name)s object";
168
169static PyObject *
170Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
171{
172  Py_%(name)s *self;
173""" % locals()
174    for ptype, pname in newparams:
175        initval = aubioinitvalue[ptype]
176        s += """\
177  %(ptype)s %(pname)s = %(initval)s;
178""" % locals()
179    # now the actual PyArg_Parse
180    if len(paramnames):
181        s += """\
182  static char *kwlist[] = { %(paramnames)s, NULL };
183
184  if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
185          %(paramrefs)s)) {
186    return NULL;
187  }
188""" % locals()
189    s += """\
190
191  self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
192
193  if (self == NULL) {
194    return NULL;
195  }
196""" % locals()
197    for ptype, pname in newparams:
198        defval = aubiodefvalue[pname]
199        if ptype == 'char_t*':
200            s += """\
201
202  self->%(pname)s = %(defval)s;
203  if (%(pname)s != NULL) {
204    self->%(pname)s = %(pname)s;
205  }
206""" % locals()
207        elif ptype == 'uint_t':
208            s += """\
209
210  self->%(pname)s = %(defval)s;
211  if (%(pname)s > 0) {
212    self->%(pname)s = %(pname)s;
213  } else if (%(pname)s < 0) {
214    PyErr_SetString (PyExc_ValueError,
215        "can not use negative value for %(pname)s");
216    return NULL;
217  }
218""" % locals()
219        elif ptype == 'smpl_t':
220            s += """\
221
222  self->%(pname)s = %(defval)s;
223  if (%(pname)s != %(defval)s) {
224    self->%(pname)s = %(pname)s;
225  }
226""" % locals()
227        else:
228            print "ERROR, unknown type of parameter %s %s" % (ptype, pname)
229    s += """\
230
231  return (PyObject *) self;
232}
233
234AUBIO_INIT(%(name)s %(selfparams)s)
235
236AUBIO_DEL(%(name)s)
237
238""" % locals()
239    return s
240
241def gen_do(dofunc, name):
242    funcname = dofunc.split()[1].split('(')[0]
243    doparams = get_params_types_names(dofunc) 
244    # make sure the first parameter is the object
245    assert doparams[0][0] == "aubio_"+name+"_t*", \
246        "method is not in 'aubio_<name>_t"
247    # and remove it
248    doparams = doparams[1:]
249    # guess the input/output params, assuming we have less than 3
250    assert len(doparams) > 0, \
251        "no parameters for function do in object %s" % name
252    #assert (len(doparams) <= 2), \
253    #    "more than 3 parameters for do in object %s" % name
254
255    # build strings for inputs, assuming there is only one input
256    inputparams = [doparams[0]]
257    # build the parsing string for PyArg_ParseTuple
258    pytypes = "".join([aubio2pytypes[p[0]] for p in doparams[0:1]])
259    inputdefs = "\n  ".join(["PyObject * " + p[-1] + "_obj;" for p in inputparams])
260    inputvecs = "\n  ".join(map(lambda p: \
261                p[0] + p[-1] + ";", inputparams))
262    parseinput = ""
263    for p in inputparams:
264        inputvec = p[-1]
265        inputdef = p[-1] + "_obj"
266        converter = aubiovecfrompyobj[p[0]]
267        parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
268
269  if (%(inputvec)s == NULL) {
270    return NULL;
271  }""" % locals()
272    # build the string for the input objects references
273    inputrefs = ", ".join(["&" + p[-1] + "_obj" for p in inputparams])
274    # end of inputs strings
275
276    # build strings for outputs
277    outputparams = doparams[1:]
278    if len(outputparams) >= 1:
279        #assert len(outputparams) == 1, \
280        #    "too many output parameters"
281        outputvecs = "\n  ".join([p[0] + p[-1] + ";" for p in outputparams])
282        outputcreate = "\n  ".join(["""\
283  %(name)s = new_%(autype)s (%(length)s);""" % \
284    {'name': p[-1], 'pytype': p[0], 'autype': p[0][:-3],
285        'length': defaultsizes[name]} \
286        for p in outputparams]) 
287        if len(outputparams) > 1:
288            returnval = "PyObject *outputs = PyList_New(0);\n"
289            for p in outputparams:
290                returnval += "  PyList_Append( outputs, (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")" +");\n"
291            returnval += "  return outputs;"
292        else:
293            returnval = "return (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
294    else:
295        # no output
296        outputvecs = ""
297        outputcreate = ""
298        #returnval = "Py_None";
299        returnval = "return (PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
300    # end of output strings
301
302    # build the parameters for the  _do() call
303    doparams_string = "self->o, " + ", ".join([p[-1] for p in doparams])
304
305    # put it all together
306    s = """\
307static PyObject *
308Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
309{
310  %(inputdefs)s
311  %(inputvecs)s
312  %(outputvecs)s
313
314  if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
315    return NULL;
316  }
317
318  %(parseinput)s
319 
320  %(outputcreate)s
321
322  /* compute _do function */
323  %(funcname)s (%(doparams_string)s);
324
325  %(returnval)s;
326}
327""" % locals()
328    return s
329
330def gen_members(new_method, name):
331    newparams = get_params_types_names(new_method)
332    s = """
333AUBIO_MEMBERS_START(%(name)s)""" % locals()
334    for param in newparams:
335        if param[0] == 'char_t*':
336            s += """
337  {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
338        % { 'pname': param[1], 'ptype': param[0], 'name': name}
339        elif param[0] == 'uint_t':
340            s += """
341  {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
342        % { 'pname': param[1], 'ptype': param[0], 'name': name}
343        elif param[0] == 'smpl_t':
344            s += """
345  {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
346        % { 'pname': param[1], 'ptype': param[0], 'name': name}
347        else:
348            print "-- ERROR, unknown member type ", param
349    s += """
350AUBIO_MEMBERS_STOP(%(name)s)
351
352""" % locals()
353    return s
354
355
356def gen_methods(get_methods, set_methods, name):
357    s = ""
358    method_defs = ""
359    for method in set_methods:
360        method_name = get_name(method)
361        params = get_params_types_names(method)
362        out_type = get_return_type(method)
363        assert params[0][0] == "aubio_"+name+"_t*", \
364            "get method is not in 'aubio_<name>_t"
365        print method
366        print params[1:]
367        setter_args = "self->o, " +",".join([p[1] for p in params[1:]])
368        parse_args = ""
369        for p in params[1:]:
370            parse_args += p[0] + " " + p[1] + ";\n"
371        argmap = "".join([aubio2pytypes[p[0]] for p in params[1:]])
372        arglist = ", ".join(["&"+p[1] for p in params[1:]])
373        parse_args += """
374  if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) {
375    return NULL;
376  } """ % locals()
377        s += """
378static PyObject *
379Py%(funcname)s (Py_%(objname)s *self, PyObject *args)
380{
381  uint_t err = 0;
382
383  %(parse_args)s
384
385  err = %(funcname)s (%(setter_args)s);
386
387  if (err > 0) {
388    PyErr_SetString (PyExc_ValueError,
389        "error running %(funcname)s");
390    return NULL;
391  }
392  return Py_None;
393}
394""" % {'funcname': method_name, 'objname': name, 
395        'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args }
396        shortname = method_name.split(name+'_')[-1]
397        method_defs += """\
398  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
399    METH_VARARGS, ""},
400""" % locals()
401
402    for method in get_methods:
403        method_name = get_name(method)
404        params = get_params_types_names(method)
405        out_type = get_return_type(method)
406        assert params[0][0] == "aubio_"+name+"_t*", \
407            "get method is not in 'aubio_<name>_t %s" % params[0][0]
408        assert len(params) == 1, \
409            "get method has more than one parameter %s" % params
410        getter_args = "self->o" 
411        returnval = "(PyObject *)" + aubiovectopyobj[out_type] + " (tmp)"
412        shortname = method_name.split(name+'_')[-1]
413        method_defs += """\
414  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
415    METH_NOARGS, ""},
416""" % locals()
417        s += """
418static PyObject *
419Py%(funcname)s (Py_%(objname)s *self, PyObject *unused)
420{
421  %(out_type)s tmp = %(funcname)s (%(getter_args)s);
422  return %(returnval)s;
423}
424""" % {'funcname': method_name, 'objname': name, 
425        'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval }
426
427    s += """
428static PyMethodDef Py_%(name)s_methods[] = {
429""" % locals() 
430    s += method_defs
431    s += """\
432  {NULL} /* sentinel */
433};
434""" % locals() 
435    return s
436
437def gen_finish(name):
438    s = """\
439
440AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
441""" % locals()
442    return s
Note: See TracBrowser for help on using the repository browser.