source: interfaces/python/gen_pyobject.py @ 09b1777

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

gen_pyobject.py: fix initialisation of strings and tempo output size

  • Property mode set to 100644
File size: 9.6 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
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
28# the important bits: the size of the output for each objects. this data should
29# move into the C library at some point.
30defaultsizes = {
31    'resampler':    ('input->length * self->ratio', 'input->channels'),
32    'onsetdetection': ('1', 'self->channels'),
33    'onset':        ('1', 'self->channels'),
34    'pitchyin':     ('1', 'in->channels'),
35    'pitchyinfft':  ('1', 'in->channels'),
36    'pitchschmitt': ('1', 'in->channels'),
37    'pitchmcomb':   ('1', 'self->channels'),
38    'pitchfcomb':   ('1', 'self->channels'),
39    'pitch':        ('1', 'self->channels'),
40    'tss':          ('self->hop_s', 'self->channels'),
41    'mfcc':         ('self->n_coeffs', 'in->channels'),
42    'beattracking': ('self->winlen', 'self->channels'),
43    'tempo':        ('1', 'self->channels'),
44    'peakpicker':   ('1', 'self->channels'),
45}
46
47# default value for variables
48aubioinitvalue = {
49    'uint_t': 0,
50    'smpl_t': 0,
51    'lsmp_t': 0.,
52    'char_t*': 'NULL',
53    }
54
55aubiodefvalue = {
56    # we have some clean up to do
57    'win_s': 'Py_default_vector_length', 
58    'bufsize': 'Py_default_vector_length', 
59    'buf_size': 'Py_default_vector_length', 
60    'winlen': 'Py_default_vector_length', 
61    # and here too
62    'hop_s': 'Py_default_vector_length / 2', 
63    'hopsize': 'Py_default_vector_length / 2', 
64    'hop_size': 'Py_default_vector_length / 2', 
65    # these should be alright
66    'channels': 'Py_default_vector_channels', 
67    'samplerate': 'Py_aubio_default_samplerate', 
68    # now for the non obvious ones
69    'n_filters': '40', 
70    'n_coeffs': '13', 
71    'nelems': '10',
72    'flow': '0.', 
73    'fhig': '1.', 
74    'ilow': '0.', 
75    'ihig': '1.', 
76    'thrs': '0.5',
77    'ratio': '0.5',
78    'threshold': '0.5',
79    'mode': '"default"',
80    'onset_mode': '"default"',
81    'type': '0',
82    }
83
84# aubio to python
85aubio2pytypes = {
86    'uint_t': 'I',
87    'smpl_t': 'I',
88    'lsmp_t': 'I',
89    'fvec_t': 'O',
90    'cvec_t': 'O',
91    'char_t*': 's',
92}
93
94# aubio to pyaubio
95aubio2pyaubio = {
96    'fvec_t': 'Py_fvec',
97    'cvec_t': 'Py_cvec',
98}
99
100# array to aubio
101aubiovecfrompyobj = {
102    'fvec_t': 'PyAubio_ArrayToFvec',
103    'cvec_t': 'PyAubio_ArrayToCvec',
104}
105
106# aubio to array
107aubiovectopyobj = {
108    'fvec_t': 'PyAubio_FvecToArray',
109    'cvec_t': 'PyAubio_CvecToArray',
110}
111
112def get_newparams(newfunc):
113    newparams = [[p.split()[0], p.split()[-1]]
114            for p in newfunc.split('(')[1].split(')')[0].split(',')]
115    # make char_t a pointer
116    return map(lambda x: [x[0].replace('char_t', 'char_t*'), x[1]], newparams)
117
118def gen_new_init(newfunc, name):
119    newparams = get_newparams(newfunc)
120    # self->param1, self->param2, self->param3
121    selfparams = ', self->'.join([p[1] for p in newparams])
122    # "param1", "param2", "param3"
123    paramnames = ", ".join(["\""+p[1]+"\"" for p in newparams])
124    pyparams = "".join(map(lambda p: aubio2pytypes[p[0]], newparams))
125    paramrefs = ", ".join(["&" + p[1] for p in newparams])
126    s = """\
127// WARNING: this file is generated, DO NOT EDIT
128#include "aubiowraphell.h"
129
130typedef struct
131{
132  PyObject_HEAD
133  aubio_%(name)s_t * o;
134""" % locals()
135    for ptype, pname in newparams:
136        s += """\
137  %(ptype)s %(pname)s;
138""" % locals()
139    s += """\
140} Py_%(name)s;
141
142static char Py_%(name)s_doc[] = "%(name)s object";
143
144static PyObject *
145Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
146{
147""" % locals()
148    for ptype, pname in newparams:
149        initval = aubioinitvalue[ptype]
150        s += """\
151  %(ptype)s %(pname)s = %(initval)s;
152""" % locals()
153    # now the actual PyArg_Parse
154    s += """\
155  Py_%(name)s *self;
156  static char *kwlist[] = { %(paramnames)s, NULL };
157
158  if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
159          %(paramrefs)s)) {
160    return NULL;
161  }
162
163  self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
164
165  if (self == NULL) {
166    return NULL;
167  }
168""" % locals()
169    # TODO add parameters default values
170    for ptype, pname in newparams:
171        defval = aubiodefvalue[pname]
172        if ptype == 'char_t*':
173            s += """\
174
175  self->%(pname)s = %(defval)s;
176  if (%(pname)s != NULL) {
177    self->%(pname)s = %(pname)s;
178  }
179""" % locals()
180        elif ptype == 'uint_t':
181            s += """\
182
183  self->%(pname)s = %(defval)s;
184  if (%(pname)s > 0) {
185    self->%(pname)s = %(pname)s;
186  } else if (%(pname)s < 0) {
187    PyErr_SetString (PyExc_ValueError,
188        "can not use negative value for %(pname)s");
189    return NULL;
190  }
191""" % locals()
192        elif ptype == 'smpl_t':
193            s += """\
194
195  self->%(pname)s = %(defval)s;
196  if (%(pname)s != %(defval)s) {
197    self->%(pname)s = %(pname)s;
198  }
199""" % locals()
200        else:
201            print "ERROR, unknown type of parameter %s %s" % (ptype, pname)
202    s += """\
203
204  return (PyObject *) self;
205}
206
207AUBIO_INIT(%(name)s, self->%(selfparams)s)
208
209AUBIO_DEL(%(name)s)
210
211""" % locals()
212    return s
213
214def gen_do(dofunc, name):
215    funcname = dofunc.split()[1].split('(')[0]
216    doparams = [p.split() for p in dofunc.split('(')[1].split(')')[0].split(',')]
217    # make sure the first parameter is the object
218    assert doparams[0][0] == "aubio_"+name+"_t", \
219        "method is not in 'aubio_<name>_t"
220    # and remove it
221    doparams = doparams[1:]
222    # guess the input/output params, assuming we have less than 3
223    assert len(doparams) > 0, \
224        "no parameters for function do in object %s" % name
225    #assert (len(doparams) <= 2), \
226    #    "more than 3 parameters for do in object %s" % name
227
228    # build strings for inputs, assuming there is only one input
229    inputparams = [doparams[0]]
230    # build the parsing string for PyArg_ParseTuple
231    pytypes = "".join([aubio2pytypes[p[0]] for p in doparams[0:1]])
232    inputdefs = "\n  ".join(["PyObject * " + p[-1] + "_obj;" for p in inputparams])
233    inputvecs = "\n  ".join(map(lambda p: \
234                aubio2pyaubio[p[0]]+" * " + p[-1] + ";", inputparams))
235    parseinput = ""
236    for p in inputparams:
237        inputvec = p[-1]
238        inputdef = p[-1] + "_obj"
239        converter = aubiovecfrompyobj[p[0]]
240        parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
241
242  if (%(inputvec)s == NULL) {
243    return NULL;
244  }""" % locals()
245    # build the string for the input objects references
246    inputrefs = ", ".join(["&" + p[-1] + "_obj" for p in inputparams])
247    # end of inputs strings
248
249    # build strings for outputs
250    outputparams = doparams[1:]
251    if len(doparams) > 1:
252        #assert len(outputparams) == 1, \
253        #    "too many output parameters"
254        outputvecs = "\n  ".join([aubio2pyaubio[p[0]]+" * " + p[-1] + ";" for p in outputparams])
255        outputcreate = """\
256AUBIO_NEW_VEC(%(name)s, %(pytype)s, %(length)s, %(channels)s)
257  %(name)s->o = new_%(autype)s (%(length)s, %(channels)s);""" % \
258    {'name': p[-1], 'pytype': aubio2pyaubio[p[0]], 'autype': p[0][:-2],
259        'length': defaultsizes[name][0], 'channels': defaultsizes[name][1]}
260        returnval = "(PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
261    else:
262        # no output
263        outputvecs = ""
264        outputcreate = ""
265        #returnval = "Py_None";
266        returnval = "(PyObject *)" + aubiovectopyobj[p[0]] + " (" + p[-1] + ")"
267    # end of output strings
268
269    # build the parameters for the  _do() call
270    doparams_string = "self->o, " + ", ".join([p[-1]+"->o" for p in doparams])
271
272    # put it all together
273    s = """\
274static PyObject *
275Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
276{
277  %(inputdefs)s
278  %(inputvecs)s
279  %(outputvecs)s
280
281  if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
282    return NULL;
283  }
284
285  %(parseinput)s
286 
287  %(outputcreate)s
288
289  /* compute _do function */
290  %(funcname)s (%(doparams_string)s);
291
292  return %(returnval)s;
293}
294""" % locals()
295    return s
296
297def gen_members(new_method, name):
298    newparams = get_newparams(new_method)
299    s = """
300AUBIO_MEMBERS_START(%(name)s)""" % locals()
301    for param in newparams:
302        if param[0] == 'char_t*':
303            s += """
304  {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
305        % { 'pname': param[1], 'ptype': param[0], 'name': name}
306        elif param[0] == 'uint_t':
307            s += """
308  {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
309        % { 'pname': param[1], 'ptype': param[0], 'name': name}
310        else:
311            print "-- ERROR, unknown member type ", param
312    s += """
313AUBIO_MEMBERS_STOP(%(name)s)
314
315""" % locals()
316    return s
317
318def gen_methods(get_methods, set_methods, name):
319    # TODO add methods
320    s = """\
321static PyMethodDef Py_%(name)s_methods[] = {
322""" % locals() 
323    # TODO add PyMethodDefs
324    s += """\
325  {NULL} /* sentinel */
326};
327""" % locals() 
328    return s
329
330def gen_finish(name):
331    s = """\
332
333AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
334""" % locals()
335    return s
Note: See TracBrowser for help on using the repository browser.