source: python/lib/gen_pyobject.py @ 6a03729

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

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