source: interfaces/python/gen_pyobject.py @ ba00fd7

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

gen_pyobject.py: fix tss default size

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