source: interfaces/python/gen_pyobject.py @ 1a6ef2c

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

gen_pyobject.py: updated to use proxy functions

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[ec1ce52]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
[90077e4]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
[ec1ce52]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 = {
[96fe713]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',
[ec1ce52]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',
[5c246b0]112    'method': '"default"',
[ec1ce52]113    }
114
115# aubio to python
116aubio2pytypes = {
117    'uint_t': 'I',
[75be651]118    'smpl_t': 'f',
119    'lsmp_t': 'd',
[90077e4]120    'fvec_t*': 'O',
121    'cvec_t*': 'O',
[ec1ce52]122    'char_t*': 's',
123}
124
125# aubio to pyaubio
126aubio2pyaubio = {
[91ce8d5]127    'fvec_t*': 'fvec_t',
128    'cvec_t*': 'cvec_t',
[ec1ce52]129}
130
131# array to aubio
132aubiovecfrompyobj = {
[91ce8d5]133    'fvec_t*': 'PyAubio_ArrayToCFvec',
134    'cvec_t*': 'PyAubio_ArrayToCCvec',
[ec1ce52]135}
136
137# aubio to array
138aubiovectopyobj = {
[90077e4]139    'fvec_t*': 'PyAubio_FvecToArray',
140    'cvec_t*': 'PyAubio_CvecToArray',
[ec1ce52]141}
142
[90077e4]143aubiovectopyobj_new = {
144    'fvec_t*': 'PyAubio_CFvecToArray',
[91ce8d5]145    'cvec_t*': 'PyAubio_CCvecToPyCvec',
[90077e4]146    'smpl_t': 'PyFloat_FromDouble',
147}
[ec1ce52]148
149def gen_new_init(newfunc, name):
[90077e4]150    newparams = get_params_types_names(newfunc)
[ec1ce52]151    # self->param1, self->param2, self->param3
[96fe713]152    if len(newparams):
153        selfparams = ', self->'+', self->'.join([p[1] for p in newparams])
154    else:
155        selfparams = '' 
[ec1ce52]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
[90077e4]162
163// WARNING: if you haven't read the first line yet, please do so
[ec1ce52]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{
[96fe713]183  Py_%(name)s *self;
[ec1ce52]184""" % locals()
185    for ptype, pname in newparams:
[09b1777]186        initval = aubioinitvalue[ptype]
[ec1ce52]187        s += """\
[09b1777]188  %(ptype)s %(pname)s = %(initval)s;
[ec1ce52]189""" % locals()
[09b1777]190    # now the actual PyArg_Parse
[96fe713]191    if len(paramnames):
192        s += """\
[ec1ce52]193  static char *kwlist[] = { %(paramnames)s, NULL };
194
195  if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
196          %(paramrefs)s)) {
197    return NULL;
198  }
[96fe713]199""" % locals()
200    s += """\
[ec1ce52]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]
[09b1777]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 += """\
[ec1ce52]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,
[09b1777]226        "can not use negative value for %(pname)s");
[ec1ce52]227    return NULL;
228  }
229""" % locals()
[09b1777]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)
[ec1ce52]240    s += """\
241
242  return (PyObject *) self;
243}
244
[96fe713]245AUBIO_INIT(%(name)s %(selfparams)s)
[ec1ce52]246
247AUBIO_DEL(%(name)s)
248
249""" % locals()
250    return s
251
252def gen_do(dofunc, name):
253    funcname = dofunc.split()[1].split('(')[0]
[90077e4]254    doparams = get_params_types_names(dofunc) 
[ec1ce52]255    # make sure the first parameter is the object
[90077e4]256    assert doparams[0][0] == "aubio_"+name+"_t*", \
[ec1ce52]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:]
[90077e4]289    if len(outputparams) >= 1:
[ec1ce52]290        #assert len(outputparams) == 1, \
291        #    "too many output parameters"
292        outputvecs = "\n  ".join([aubio2pyaubio[p[0]]+" * " + p[-1] + ";" for p in outputparams])
[90077e4]293        outputcreate = "\n  ".join(["""\
[91ce8d5]294  %(name)s = new_%(autype)s (%(length)s);""" % \
[90077e4]295    {'name': p[-1], 'pytype': aubio2pyaubio[p[0]], 'autype': p[0][:-3],
[96fe713]296        'length': defaultsizes[name]} \
[90077e4]297        for p in outputparams]) 
298        if len(outputparams) > 1:
299            returnval = "PyObject *outputs = PyList_New(0);\n"
300            for p in outputparams:
[91ce8d5]301                returnval += "  PyList_Append( outputs, (PyObject *)" + aubiovectopyobj_new[p[0]] + " (" + p[-1] + ")" +");\n"
[90077e4]302            returnval += "  return outputs;"
303        else:
[91ce8d5]304            returnval = "return (PyObject *)" + aubiovectopyobj_new[p[0]] + " (" + p[-1] + ")"
[ec1ce52]305    else:
306        # no output
307        outputvecs = ""
308        outputcreate = ""
309        #returnval = "Py_None";
[91ce8d5]310        returnval = "return (PyObject *)" + aubiovectopyobj_new[p[0]] + " (" + p[-1] + ")"
[ec1ce52]311    # end of output strings
312
313    # build the parameters for the  _do() call
[91ce8d5]314    doparams_string = "self->o, " + ", ".join([p[-1] for p in doparams])
[ec1ce52]315
316    # put it all together
317    s = """\
318static PyObject *
319Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
320{
321  %(inputdefs)s
322  %(inputvecs)s
323  %(outputvecs)s
324
325  if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
326    return NULL;
327  }
328
329  %(parseinput)s
330 
331  %(outputcreate)s
332
333  /* compute _do function */
334  %(funcname)s (%(doparams_string)s);
335
[90077e4]336  %(returnval)s;
[ec1ce52]337}
338""" % locals()
339    return s
340
341def gen_members(new_method, name):
[90077e4]342    newparams = get_params_types_names(new_method)
[ec1ce52]343    s = """
344AUBIO_MEMBERS_START(%(name)s)""" % locals()
345    for param in newparams:
[09b1777]346        if param[0] == 'char_t*':
347            s += """
348  {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
349        % { 'pname': param[1], 'ptype': param[0], 'name': name}
350        elif param[0] == 'uint_t':
351            s += """
[ec1ce52]352  {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
353        % { 'pname': param[1], 'ptype': param[0], 'name': name}
[5c246b0]354        elif param[0] == 'smpl_t':
355            s += """
356  {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
357        % { 'pname': param[1], 'ptype': param[0], 'name': name}
[09b1777]358        else:
359            print "-- ERROR, unknown member type ", param
[ec1ce52]360    s += """
361AUBIO_MEMBERS_STOP(%(name)s)
362
363""" % locals()
364    return s
365
[90077e4]366
[ec1ce52]367def gen_methods(get_methods, set_methods, name):
[90077e4]368    s = ""
369    method_defs = ""
370    for method in set_methods:
371        method_name = get_name(method)
372        params = get_params_types_names(method)
373        out_type = get_return_type(method)
374        assert params[0][0] == "aubio_"+name+"_t*", \
375            "get method is not in 'aubio_<name>_t"
376        print method
377        print params[1:]
378        setter_args = "self->o, " +",".join([p[1] for p in params[1:]])
379        parse_args = ""
380        for p in params[1:]:
381            parse_args += p[0] + " " + p[1] + ";\n"
382        argmap = "".join([aubio2pytypes[p[0]] for p in params[1:]])
383        arglist = ", ".join(["&"+p[1] for p in params[1:]])
384        parse_args += """
385  if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) {
386    return NULL;
387  } """ % locals()
388        s += """
389static PyObject *
390Py%(funcname)s (Py_%(objname)s *self, PyObject *args)
391{
392  uint_t err = 0;
393
394  %(parse_args)s
395
396  err = %(funcname)s (%(setter_args)s);
397
398  if (err > 0) {
399    PyErr_SetString (PyExc_ValueError,
400        "error running %(funcname)s");
401    return NULL;
402  }
403  return Py_None;
404}
405""" % {'funcname': method_name, 'objname': name, 
406        'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args }
407        shortname = method_name.split(name+'_')[-1]
408        method_defs += """\
409  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
410    METH_VARARGS, ""},
411""" % locals()
412
413    for method in get_methods:
414        method_name = get_name(method)
415        params = get_params_types_names(method)
416        out_type = get_return_type(method)
417        assert params[0][0] == "aubio_"+name+"_t*", \
418            "get method is not in 'aubio_<name>_t %s" % params[0][0]
419        assert len(params) == 1, \
420            "get method has more than one parameter %s" % params
421        getter_args = "self->o" 
422        returnval = "(PyObject *)" + aubiovectopyobj_new[out_type] + " (tmp)"
423        shortname = method_name.split(name+'_')[-1]
424        method_defs += """\
425  {"%(shortname)s", (PyCFunction) Py%(method_name)s,
426    METH_NOARGS, ""},
427""" % locals()
428        s += """
429static PyObject *
430Py%(funcname)s (Py_%(objname)s *self, PyObject *unused)
431{
432  %(out_type)s tmp = %(funcname)s (%(getter_args)s);
433  return %(returnval)s;
434}
435""" % {'funcname': method_name, 'objname': name, 
436        'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval }
437
438    s += """
[ec1ce52]439static PyMethodDef Py_%(name)s_methods[] = {
440""" % locals() 
[90077e4]441    s += method_defs
[ec1ce52]442    s += """\
443  {NULL} /* sentinel */
444};
445""" % locals() 
446    return s
447
448def gen_finish(name):
449    s = """\
450
451AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
452""" % locals()
453    return s
Note: See TracBrowser for help on using the repository browser.