source: python/lib/gen_pyobject.py @ 016dc3b

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

python/lib/gen_pyobject.py: add sampler

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