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