source: interfaces/python/gen_pyobject.py @ 7db3477

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

gen_pyobject.py: update, add smpl_t

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