History

lib/gen_external.py: rewrote wrapper

Paul Brossier committed 10 years ago

Commit ccb9fb51 parent fbd7c804 files · +669 −764Browse files

Showing the changes to python (4 of 4 files). Show all

Unified · Split

4 files
  1. python/lib/gen_code.py +487 −0
  2. python/lib/gen_external.py +182 −0
  3. python/lib/gen_pyobject.py +0 −531
  4. python/lib/generator.py +0 −233
python/lib/gen_code.py+487−0
@@ -0,0 +1,487 @@
1aubiodefvalue = {
2 # we have some clean up to do
3 'buf_size': 'Py_default_vector_length',
4 'win_s': 'Py_default_vector_length',
5 # and here too
6 'hop_size': 'Py_default_vector_length / 2',
7 'hop_s': 'Py_default_vector_length / 2',
8 # these should be alright
9 'samplerate': 'Py_aubio_default_samplerate',
10 # now for the non obvious ones
11 'n_filters': '40',
12 'n_coeffs': '13',
13 'nelems': '10',
14 'flow': '0.',
15 'fhig': '1.',
16 'ilow': '0.',
17 'ihig': '1.',
18 'thrs': '0.5',
19 'ratio': '0.5',
20 'method': '"default"',
21 'uri': '"none"',
22 }
23
24member_types = {
25 'name': 'type',
26 'char_t*': 'T_STRING',
27 'uint_t': 'T_INT',
28 'smpl_t': 'T_FLOAT',
29 }
30
31pyfromtype_fn = {
32 'smpl_t': 'PyFloat_FromDouble',
33 'uint_t': 'PyLong_FromLong', # was: 'PyInt_FromLong',
34 'fvec_t*': 'PyAubio_CFvecToArray',
35 'fmat_t*': 'PyAubio_CFmatToArray',
36 }
37
38pytoaubio_fn = {
39 'fvec_t*': 'PyAubio_ArrayToCFvec',
40 'cvec_t*': 'PyAubio_ArrayToCCvec',
41 'fmat_t*': 'PyAubio_ArrayToCFmat',
42 }
43
44pyfromaubio_fn = {
45 'fvec_t*': 'PyAubio_CFvecToArray',
46 'cvec_t*': 'PyAubio_CCvecToArray',
47 'fmat_t*': 'PyAubio_CFmatToArray',
48 }
49
50newfromtype_fn = {
51 'fvec_t*': 'new_fvec',
52 'fmat_t*': 'new_fmat',
53 'cvec_t*': 'new_cvec',
54 }
55
56delfromtype_fn = {
57 'fvec_t*': 'del_fvec',
58 'fmat_t*': 'del_fmat',
59 'cvec_t*': 'del_cvec',
60 }
61
62param_init = {
63 'char_t*': 'NULL',
64 'uint_t': '0',
65 'sint_t': 0,
66 'smpl_t': 0.,
67 'lsmp_t': 0.,
68 }
69
70pyargparse_chars = {
71 'smpl_t': 'f',
72 'uint_t': 'I',
73 'sint_t': 'I',
74 'char_t*': 's',
75 'fmat_t*': 'O',
76 'fvec_t*': 'O',
77 }
78
79objoutsize = {
80 'onset': '1',
81 'pitch': '1',
82 'wavetable': 'self->hop_size',
83 'sampler': 'self->hop_size',
84 'mfcc': 'self->n_coeffs',
85 'specdesc': '1',
86 'tempo': '1',
87 'filterbank': 'self->n_filters',
88 }
89
90def get_name(proto):
91 name = proto.replace(' *', '* ').split()[1].split('(')[0]
92 name = name.replace('*','')
93 if name == '': raise ValueError(proto + "gave empty name")
94 return name
95
96def get_return_type(proto):
97 import re
98 paramregex = re.compile('(\w+ ?\*?).*')
99 outputs = paramregex.findall(proto)
100 assert len(outputs) == 1
101 return outputs[0].replace(' ', '')
102
103def split_type(arg):
104 """ arg = 'foo *name'
105 return ['foo*', 'name'] """
106 l = arg.split()
107 type_arg = {'type': l[0], 'name': l[1]}
108 # ['foo', '*name'] -> ['foo*', 'name']
109 if l[-1].startswith('*'):
110 #return [l[0]+'*', l[1][1:]]
111 type_arg['type'] = l[0] + '*'
112 type_arg['name'] = l[1][1:]
113 # ['foo', '*', 'name'] -> ['foo*', 'name']
114 if len(l) == 3:
115 #return [l[0]+l[1], l[2]]
116 type_arg['type'] = l[0]+l[1]
117 type_arg['name'] = l[2]
118 else:
119 #return l
120 pass
121 return type_arg
122
123def get_params(proto):
124 """ get the list of parameters from a function prototype
125 example: proto = "int main (int argc, char ** argv)"
126 returns: ['int argc', 'char ** argv']
127 """
128 import re
129 paramregex = re.compile('[\(, ](\w+ \*?\*? ?\w+)[, \)]')
130 return paramregex.findall(proto)
131
132def get_params_types_names(proto):
133 """ get the list of parameters from a function prototype
134 example: proto = "int main (int argc, char ** argv)"
135 returns: [['int', 'argc'], ['char **','argv']]
136 """
137 return list(map(split_type, get_params(proto)))
138
139
140class MappedObject(object):
141
142 def __init__(self, prototypes):
143 self.prototypes = prototypes
144
145 self.shortname = prototypes['shortname']
146 self.longname = prototypes['longname']
147 self.new_proto = prototypes['new'][0]
148 self.del_proto = prototypes['del'][0]
149 self.do_proto = prototypes['do'][0]
150 self.input_params = get_params_types_names(self.new_proto)
151 self.input_params_list = "; ".join(get_params(self.new_proto))
152 self.outputs = get_params_types_names(self.do_proto)[2:]
153 self.outputs_flat = get_params(self.do_proto)[2:]
154 self.output_results = ", ".join(self.outputs_flat)
155
156 def gen_code(self):
157 out = ""
158 out += self.gen_struct()
159 out += self.gen_doc()
160 out += self.gen_new()
161 out += self.gen_init()
162 out += self.gen_del()
163 out += self.gen_do()
164 out += self.gen_memberdef()
165 out += self.gen_set()
166 out += self.gen_get()
167 out += self.gen_methodef()
168 out += self.gen_typeobject()
169 return out
170
171 def gen_struct(self):
172 out = """
173// {shortname} structure
174typedef struct{{
175 PyObject_HEAD
176 // pointer to aubio object
177 {longname} *o;
178 // input parameters
179 {input_params_list};
180 // output results
181 {output_results};
182}} Py_{shortname};
183"""
184 return out.format(**self.__dict__)
185
186 def gen_doc(self):
187 out = """
188// TODO: add documentation
189static char Py_{shortname}_doc[] = \"undefined\";
190 """
191 return out.format(**self.__dict__)
192
193 def gen_new(self):
194 out = """
195// new {shortname}
196static PyObject *
197Py_{shortname}_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
198{{
199 Py_{shortname} *self;
200""".format(**self.__dict__)
201 params = self.input_params
202 for p in params:
203 out += """
204 {type} {name} = {defval};""".format(defval = param_init[p['type']], **p)
205 plist = ", ".join(["\"%s\"" % p['name'] for p in params])
206 out += """
207 static char *kwlist[] = {{ {plist}, NULL }};""".format(plist = plist)
208 argchars = "".join([pyargparse_chars[p['type']] for p in params])
209 arglist = ", ".join(["&%s" % p['name'] for p in params])
210 out += """
211 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|{argchars}", kwlist,
212 {arglist})) {{
213 return NULL;
214 }}
215""".format(argchars = argchars, arglist = arglist)
216 out += """
217 self = (Py_{shortname} *) pytype->tp_alloc (pytype, 0);
218 if (self == NULL) {{
219 return NULL;
220 }}
221""".format(**self.__dict__)
222 params = self.input_params
223 for p in params:
224 out += self.check_valid(p)
225 out += """
226 return (PyObject *)self;
227}
228"""
229 return out
230
231 def check_valid(self, p):
232 if p['type'] == 'uint_t':
233 return self.check_valid_uint(p)
234 if p['type'] == 'char_t*':
235 return self.check_valid_char(p)
236 else:
237 print ("ERROR, no idea how to check %s for validity" % p['type'])
238
239 def check_valid_uint(self, p):
240 name = p['name']
241 return """
242 self->{name} = {defval};
243 if ((sint_t){name} > 0) {{
244 self->{name} = {name};
245 }} else if ((sint_t){name} < 0) {{
246 PyErr_SetString (PyExc_ValueError, "can not use negative value for {name}");
247 return NULL;
248 }}
249""".format(defval = aubiodefvalue[name], name = name)
250
251 def check_valid_char(self, p):
252 name = p['name']
253 return """
254 self->{name} = {defval};
255 if ({name} != NULL) {{
256 self->{name} = {name};
257 }}
258""".format(defval = aubiodefvalue[name], name = name)
259
260 def gen_init(self):
261 out = """
262// init {shortname}
263static int
264Py_{shortname}_init (Py_{shortname} * self, PyObject * args, PyObject * kwds)
265{{
266""".format(**self.__dict__)
267 new_name = get_name(self.new_proto)
268 new_params = ", ".join(["self->%s" % s['name'] for s in self.input_params])
269 out += """
270 self->o = {new_name}({new_params});
271""".format(new_name = new_name, new_params = new_params)
272 paramchars = "%s"
273 paramvals = "self->method"
274 out += """
275 // return -1 and set error string on failure
276 if (self->o == NULL) {{
277 //char_t errstr[30 + strlen(self->uri)];
278 //sprintf(errstr, "error creating {shortname} with params {paramchars}", {paramvals});
279 char_t errstr[60];
280 sprintf(errstr, "error creating {shortname} with given params");
281 PyErr_SetString (PyExc_Exception, errstr);
282 return -1;
283 }}
284""".format(paramchars = paramchars, paramvals = paramvals, **self.__dict__)
285 output_create = ""
286 for o in self.outputs:
287 output_create += """
288 self->{name} = {create_fn}({output_size});""".format(name = o['name'], create_fn = newfromtype_fn[o['type']], output_size = objoutsize[self.shortname])
289 out += """
290 // TODO get internal params after actual object creation?
291"""
292 out += """
293 // create outputs{output_create}
294""".format(output_create = output_create)
295 out += """
296 return 0;
297}
298"""
299 return out
300
301 def gen_memberdef(self):
302 out = """
303static PyMemberDef Py_{shortname}_members[] = {{
304""".format(**self.__dict__)
305 for p in get_params_types_names(self.new_proto):
306 tmp = " {{\"{name}\", {ttype}, offsetof (Py_{shortname}, {name}), READONLY, \"TODO documentation\"}},\n"
307 pytype = member_types[p['type']]
308 out += tmp.format(name = p['name'], ttype = pytype, shortname = self.shortname)
309 out += """ {NULL}, // sentinel
310};
311"""
312 return out
313
314 def gen_del(self):
315 out = """
316// del {shortname}
317static void
318Py_{shortname}_del (Py_{shortname} * self, PyObject * unused)
319{{""".format(**self.__dict__)
320 for o in self.outputs:
321 name = o['name']
322 del_out = delfromtype_fn[o['type']]
323 out += """
324 {del_out}(self->{name});""".format(del_out = del_out, name = name)
325 del_fn = get_name(self.del_proto)
326 out += """
327 {del_fn}(self->o);
328 Py_TYPE(self)->tp_free((PyObject *) self);
329}}
330""".format(del_fn = del_fn)
331 return out
332
333 def gen_do(self):
334 do_fn = get_name(self.do_proto)
335 input_param = get_params_types_names(self.do_proto)[1];
336 pytoaubio = pytoaubio_fn[input_param['type']]
337 output = self.outputs[0]
338 out = """
339// do {shortname}
340static PyObject*
341Py_{shortname}_do (Py_{shortname} * self, PyObject * args)
342{{
343 PyObject * in_obj;
344 {input_type} {input_name};
345
346 if (!PyArg_ParseTuple (args, "O", &in_obj)) {{
347 return NULL;
348 }}
349 {input_name} = {pytoaubio} (in_obj);
350 if ({input_name} == NULL) {{
351 return NULL;
352 }}
353
354 {do_fn}(self->o, {input_name}, {outputs});
355
356 return (PyObject *) {aubiotonumpy} ({outputs});
357}}
358"""
359 return out.format(do_fn = do_fn,
360 shortname = self.prototypes['shortname'],
361 input_name = input_param['name'],
362 input_type= input_param['type'],
363 pytoaubio = pytoaubio,
364 outputs = ", ".join(["self->%s" % p['name'] for p in self.outputs]),
365 aubiotonumpy = pyfromaubio_fn[output['type']],
366 )
367
368 def gen_set(self):
369 out = """
370// {shortname} setters
371""".format(**self.__dict__)
372 for set_param in self.prototypes['set']:
373 params = get_params_types_names(set_param)[1]
374 paramtype = params['type']
375 method_name = get_name(set_param)
376 param = method_name.split('aubio_'+self.shortname+'_set_')[-1]
377 pyparamtype = pyargparse_chars[paramtype]
378 out += """
379static PyObject *
380Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)
381{{
382 uint_t err = 0;
383 {paramtype} {param};
384
385 if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{
386 return NULL;
387 }}
388 err = aubio_{shortname}_set_{param} (self->o, {param});
389
390 if (err > 0) {{
391 PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");
392 return NULL;
393 }}
394 Py_RETURN_NONE;
395}}
396""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__)
397 return out
398
399 def gen_get(self):
400 out = """
401// {shortname} getters
402""".format(**self.__dict__)
403 for method in self.prototypes['get']:
404 params = get_params_types_names(method)
405 method_name = get_name(method)
406 assert len(params) == 1, \
407 "get method has more than one parameter %s" % params
408 param = method_name.split('aubio_'+self.shortname+'_get_')[-1]
409 paramtype = get_return_type(method)
410 ptypeconv = pyfromtype_fn[paramtype]
411 out += """
412static PyObject *
413Pyaubio_{shortname}_get_{param} (Py_{shortname} *self, PyObject *unused)
414{{
415 {ptype} {param} = aubio_{shortname}_get_{param} (self->o);
416 return (PyObject *){ptypeconv} ({param});
417}}
418""".format(param = param, ptype = paramtype, ptypeconv = ptypeconv,
419 **self.__dict__)
420 return out
421
422 def gen_methodef(self):
423 out = """
424static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__)
425 for m in self.prototypes['set']:
426 name = get_name(m)
427 shortname = name.replace('aubio_%s_' % self.shortname, '')
428 out += """
429 {{"{shortname}", (PyCFunction) Py{name},
430 METH_VARARGS, ""}},""".format(name = name, shortname = shortname)
431 for m in self.prototypes['get']:
432 name = get_name(m)
433 shortname = name.replace('aubio_%s_' % self.shortname, '')
434 out += """
435 {{"{shortname}", (PyCFunction) Py{name},
436 METH_NOARGS, ""}},""".format(name = name, shortname = shortname)
437 out += """
438 {NULL} /* sentinel */
439};
440"""
441 return out
442
443 def gen_typeobject(self):
444 return """
445PyTypeObject Py_{shortname}Type = {{
446 //PyObject_HEAD_INIT (NULL)
447 //0,
448 PyVarObject_HEAD_INIT (NULL, 0)
449 "aubio.{shortname}",
450 sizeof (Py_{shortname}),
451 0,
452 (destructor) Py_{shortname}_del,
453 0,
454 0,
455 0,
456 0,
457 0,
458 0,
459 0,
460 0,
461 0,
462 (ternaryfunc)Py_{shortname}_do,
463 0,
464 0,
465 0,
466 0,
467 Py_TPFLAGS_DEFAULT,
468 Py_{shortname}_doc,
469 0,
470 0,
471 0,
472 0,
473 0,
474 0,
475 Py_{shortname}_methods,
476 Py_{shortname}_members,
477 0,
478 0,
479 0,
480 0,
481 0,
482 0,
483 (initproc) Py_{shortname}_init,
484 0,
485 Py_{shortname}_new,
486}};
487""".format(**self.__dict__)
python/lib/gen_external.py+182−0
@@ -0,0 +1,182 @@
1import os
2
3header = """// this file is generated! do not modify
4#include "aubio-types.h"
5"""
6
7skip_objects = [
8 # already in ext/
9 'fft',
10 'pvoc',
11 'filter',
12 'filterbank',
13 #'resampler',
14 # AUBIO_UNSTABLE
15 'hist',
16 'parameter',
17 'scale',
18 'beattracking',
19 'resampler',
20 'peakpicker',
21 'pitchfcomb',
22 'pitchmcomb',
23 'pitchschmitt',
24 'pitchspecacf',
25 'pitchyin',
26 'pitchyinfft',
27 'sink',
28 'sink_apple_audio',
29 'sink_sndfile',
30 'sink_wavwrite',
31 #'mfcc',
32 'source',
33 'source_apple_audio',
34 'source_sndfile',
35 'source_avcodec',
36 'source_wavread',
37 #'sampler',
38 'audio_unit',
39
40 'tss',
41 ]
42
43
44def get_cpp_objects():
45
46 cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/src ../src/aubio.h').readlines()]
47 #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/onset/onset.h').readlines()]
48 #cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=0 -I../build/src ../src/pitch/pitch.h').readlines()]
49
50 cpp_output = filter(lambda y: len(y) > 1, cpp_output)
51 cpp_output = filter(lambda y: not y.startswith('#'), cpp_output)
52 cpp_output = list(cpp_output)
53
54 i = 1
55 while 1:
56 if i >= len(cpp_output): break
57 if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'):
58 cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i]
59 cpp_output.pop(i-1)
60 else:
61 i += 1
62
63 typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output)
64
65 cpp_objects = [a.split()[3][:-1] for a in typedefs]
66
67 return cpp_output, cpp_objects
68
69def generate_external(output_path):
70 os.mkdir(output_path)
71 sources_list = []
72 cpp_output, cpp_objects = get_cpp_objects()
73 lib = {}
74
75 for o in cpp_objects:
76 if o[:6] != 'aubio_':
77 continue
78 shortname = o[6:-2]
79 if shortname in skip_objects:
80 continue
81 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
82 lib[shortname]['longname'] = o
83 lib[shortname]['shortname'] = shortname
84 for fn in cpp_output:
85 if o[:-1] in fn:
86 #print "found", o[:-1], "in", fn
87 if 'typedef struct ' in fn:
88 lib[shortname]['struct'].append(fn)
89 elif '_do' in fn:
90 lib[shortname]['do'].append(fn)
91 elif 'new_' in fn:
92 lib[shortname]['new'].append(fn)
93 elif 'del_' in fn:
94 lib[shortname]['del'].append(fn)
95 elif '_get_' in fn:
96 lib[shortname]['get'].append(fn)
97 elif '_set_' in fn:
98 lib[shortname]['set'].append(fn)
99 else:
100 #print "no idea what to do about", fn
101 lib[shortname]['other'].append(fn)
102
103 """
104 for fn in cpp_output:
105 found = 0
106 for o in lib:
107 for family in lib[o]:
108 if fn in lib[o][family]:
109 found = 1
110 if found == 0:
111 print "missing", fn
112
113 for o in lib:
114 for family in lib[o]:
115 if type(lib[o][family]) == str:
116 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
117 elif len(lib[o][family]) == 1:
118 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) )
119 else:
120 print ( "{:15s} {:10s} {:d}".format(o, family, len(lib[o][family]) ) )
121 """
122
123 from .gen_code import MappedObject
124 for o in lib:
125 out = header
126 mapped = MappedObject(lib[o])
127 out += mapped.gen_code()
128 output_file = os.path.join(output_path, 'gen-%s.c' % o)
129 with open(output_file, 'w') as f:
130 f.write(out)
131 print ("wrote %s" % output_file )
132 sources_list.append(output_file)
133
134 out = header
135 out += "#include \"aubio-generated.h\""
136 check_types = "\n || ".join(["PyType_Ready(&Py_%sType) < 0" % o for o in lib])
137 out += """
138
139int generated_types_ready (void)
140{{
141 return ({pycheck_types});
142}}
143""".format(pycheck_types = check_types)
144
145 add_types = "".join(["""
146 Py_INCREF (&Py_{name}Type);
147 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib])
148 out += """
149
150void add_generated_objects ( PyObject *m )
151{{
152{add_types}
153}}
154""".format(add_types = add_types)
155
156 output_file = os.path.join(output_path, 'aubio-generated.c')
157 with open(output_file, 'w') as f:
158 f.write(out)
159 print ("wrote %s" % output_file )
160 sources_list.append(output_file)
161
162 objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
163 out = """
164// generated list of objects created with gen_external.py
165#include <Python.h>
166
167{objlist}
168int generated_objects ( void );
169void add_generated_objects( PyObject *m );
170""".format(objlist = objlist)
171
172 output_file = os.path.join(output_path, 'aubio-generated.h')
173 with open(output_file, 'w') as f:
174 f.write(out)
175 print ("wrote %s" % output_file )
176 # no need to add header to list of sources
177
178 return sources_list
179
180if __name__ == '__main__':
181 output_path = 'gen'
182 generate_external(output_path)
python/lib/gen_pyobject.py+0−531
@@ -1,531 +0,0 @@
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 list(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 'wavetable': ['self->hop_size'],
106}
107
108# default value for variables
109aubioinitvalue = {
110 'uint_t': 0,
111 'sint_t': 0,
112 'smpl_t': 0,
113 'lsmp_t': 0.,
114 'char_t*': 'NULL',
115 }
116
117aubiodefvalue = {
118 # we have some clean up to do
119 'buf_size': 'Py_default_vector_length',
120 # and here too
121 'hop_size': 'Py_default_vector_length / 2',
122 # these should be alright
123 'samplerate': 'Py_aubio_default_samplerate',
124 # now for the non obvious ones
125 'n_filters': '40',
126 'n_coeffs': '13',
127 'nelems': '10',
128 'flow': '0.',
129 'fhig': '1.',
130 'ilow': '0.',
131 'ihig': '1.',
132 'thrs': '0.5',
133 'ratio': '0.5',
134 'method': '"default"',
135 'uri': '"none"',
136 }
137
138# aubio to python
139aubio2pytypes = {
140 'uint_t': 'I',
141 'sint_t': 'I',
142 'smpl_t': 'f',
143 'lsmp_t': 'd',
144 'fvec_t*': 'O',
145 'cvec_t*': 'O',
146 'char_t*': 's',
147}
148
149# python to aubio
150aubiovecfrompyobj = {
151 'fvec_t*': 'PyAubio_ArrayToCFvec',
152 'cvec_t*': 'PyAubio_ArrayToCCvec',
153 'uint_t': '(uint_t)PyLong_AsLong',
154}
155
156# aubio to python
157aubiovectopyobj = {
158 'fvec_t*': 'PyAubio_CFvecToArray',
159 'cvec_t*': 'PyAubio_CCvecToPyCvec',
160 'smpl_t': 'PyFloat_FromDouble',
161 'uint_t*': 'PyLong_FromLong',
162 'uint_t': 'PyLong_FromLong',
163}
164
165def gen_new_init(newfunc, name):
166 newparams = get_params_types_names(newfunc)
167 # self->param1, self->param2, self->param3
168 if len(newparams):
169 selfparams = ', self->'+', self->'.join([p['name'] for p in newparams])
170 else:
171 selfparams = ''
172 # "param1", "param2", "param3"
173 paramnames = ", ".join(["\""+p['name']+"\"" for p in newparams])
174 pyparams = "".join([aubio2pytypes[p['type']] for p in newparams])
175 paramrefs = ", ".join(["&" + p['name'] for p in newparams])
176 s = """\
177// WARNING: this file is generated, DO NOT EDIT
178
179// WARNING: if you haven't read the first line yet, please do so
180#include "aubiowraphell.h"
181
182typedef struct
183{
184 PyObject_HEAD
185 aubio_%(name)s_t * o;
186""" % locals()
187 for p in newparams:
188 ptype = p['type']
189 pname = p['name']
190 s += """\
191 %(ptype)s %(pname)s;
192""" % locals()
193 s += """\
194} Py_%(name)s;
195
196static char Py_%(name)s_doc[] = "%(name)s object";
197
198static PyObject *
199Py_%(name)s_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
200{
201 Py_%(name)s *self;
202""" % locals()
203 for p in newparams:
204 ptype = p['type']
205 pname = p['name']
206 initval = aubioinitvalue[ptype]
207 s += """\
208 %(ptype)s %(pname)s = %(initval)s;
209""" % locals()
210 # now the actual PyArg_Parse
211 if len(paramnames):
212 s += """\
213 static char *kwlist[] = { %(paramnames)s, NULL };
214
215 if (!PyArg_ParseTupleAndKeywords (args, kwds, "|%(pyparams)s", kwlist,
216 %(paramrefs)s)) {
217 return NULL;
218 }
219""" % locals()
220 s += """\
221
222 self = (Py_%(name)s *) pytype->tp_alloc (pytype, 0);
223
224 if (self == NULL) {
225 return NULL;
226 }
227""" % locals()
228 for p in newparams:
229 ptype = p['type']
230 pname = p['name']
231 defval = aubiodefvalue[pname]
232 if ptype == 'char_t*':
233 s += """\
234
235 self->%(pname)s = %(defval)s;
236 if (%(pname)s != NULL) {
237 self->%(pname)s = %(pname)s;
238 }
239""" % locals()
240 elif ptype == 'uint_t':
241 s += """\
242
243 self->%(pname)s = %(defval)s;
244 if ((sint_t)%(pname)s > 0) {
245 self->%(pname)s = %(pname)s;
246 } else if ((sint_t)%(pname)s < 0) {
247 PyErr_SetString (PyExc_ValueError,
248 "can not use negative value for %(pname)s");
249 return NULL;
250 }
251""" % locals()
252 elif ptype == 'smpl_t':
253 s += """\
254
255 self->%(pname)s = %(defval)s;
256 if (%(pname)s != %(defval)s) {
257 self->%(pname)s = %(pname)s;
258 }
259""" % locals()
260 else:
261 write_msg ("ERROR, unknown type of parameter %s %s" % (ptype, pname) )
262 s += """\
263
264 return (PyObject *) self;
265}
266
267AUBIO_INIT(%(name)s %(selfparams)s)
268
269AUBIO_DEL(%(name)s)
270
271""" % locals()
272 return s
273
274def gen_do_input_params(inputparams):
275 inputdefs = ''
276 parseinput = ''
277 inputrefs = ''
278 inputvecs = ''
279 pytypes = ''
280
281 if len(inputparams):
282 # build the parsing string for PyArg_ParseTuple
283 pytypes = "".join([aubio2pytypes[p['type']] for p in inputparams])
284
285 inputdefs = " /* input vectors python prototypes */\n"
286 for p in inputparams:
287 if p['type'] != 'uint_t':
288 inputdefs += " PyObject * " + p['name'] + "_obj;\n"
289
290 inputvecs = " /* input vectors prototypes */\n "
291 inputvecs += "\n ".join([ p['type'] + ' ' + p['name'] + ";" for p in inputparams])
292
293 parseinput = " /* input vectors parsing */\n "
294 for p in inputparams:
295 inputvec = p['name']
296 if p['type'] != 'uint_t':
297 inputdef = p['name'] + "_obj"
298 else:
299 inputdef = p['name']
300 converter = aubiovecfrompyobj[p['type']]
301 if p['type'] != 'uint_t':
302 parseinput += """%(inputvec)s = %(converter)s (%(inputdef)s);
303
304 if (%(inputvec)s == NULL) {
305 return NULL;
306 }
307
308 """ % locals()
309
310 # build the string for the input objects references
311 inputreflist = []
312 for p in inputparams:
313 if p['type'] != 'uint_t':
314 inputreflist += [ "&" + p['name'] + "_obj" ]
315 else:
316 inputreflist += [ "&" + p['name'] ]
317 inputrefs = ", ".join(inputreflist)
318 # end of inputs strings
319 return inputdefs, parseinput, inputrefs, inputvecs, pytypes
320
321def gen_do_output_params(outputparams, name):
322 outputvecs = ""
323 outputcreate = ""
324 if len(outputparams):
325 outputvecs = " /* output vectors prototypes */\n"
326 for p in outputparams:
327 params = {
328 'name': p['name'], 'pytype': p['type'], 'autype': p['type'][:-3],
329 'length': defaultsizes[name].pop(0) }
330 if (p['type'] == 'uint_t*'):
331 outputvecs += ' uint_t' + ' ' + p['name'] + ";\n"
332 outputcreate += " %(name)s = 0;\n" % params
333 else:
334 outputvecs += " " + p['type'] + ' ' + p['name'] + ";\n"
335 outputcreate += " /* creating output %(name)s as a new_%(autype)s of length %(length)s */\n" % params
336 outputcreate += " %(name)s = new_%(autype)s (%(length)s);\n" % params
337
338 returnval = "";
339 if len(outputparams) > 1:
340 returnval += " PyObject *outputs = PyList_New(0);\n"
341 for p in outputparams:
342 returnval += " PyList_Append( outputs, (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")" +");\n"
343 returnval += " return outputs;"
344 elif len(outputparams) == 1:
345 if defaultsizes[name] == '1':
346 returnval += " return (PyObject *)PyFloat_FromDouble(" + p['name'] + "->data[0])"
347 else:
348 returnval += " return (PyObject *)" + aubiovectopyobj[p['type']] + " (" + p['name'] + ")"
349 else:
350 returnval += " Py_RETURN_NONE"
351 # end of output strings
352 return outputvecs, outputcreate, returnval
353
354def gen_do(dofunc, name):
355 funcname = dofunc.split()[1].split('(')[0]
356 doparams = get_params_types_names(dofunc)
357 # make sure the first parameter is the object
358 assert doparams[0]['type'] == "aubio_"+name+"_t*", \
359 "method is not in 'aubio_<name>_t"
360 # and remove it
361 doparams = doparams[1:]
362
363 n_param = len(doparams)
364
365 if name in list(param_numbers.keys()):
366 n_input_param, n_output_param = param_numbers[name]
367 else:
368 n_input_param, n_output_param = 1, n_param - 1
369
370 assert n_output_param + n_input_param == n_param, "n_output_param + n_input_param != n_param for %s" % name
371
372 inputparams = doparams[:n_input_param]
373 outputparams = doparams[n_input_param:n_input_param + n_output_param]
374
375 inputdefs, parseinput, inputrefs, inputvecs, pytypes = gen_do_input_params(inputparams);
376 outputvecs, outputcreate, returnval = gen_do_output_params(outputparams, name)
377
378 # build strings for outputs
379 # build the parameters for the _do() call
380 doparams_string = "self->o"
381 for p in doparams:
382 if p['type'] == 'uint_t*':
383 doparams_string += ", &" + p['name']
384 else:
385 doparams_string += ", " + p['name']
386
387 if n_input_param:
388 arg_parse_tuple = """\
389 if (!PyArg_ParseTuple (args, "%(pytypes)s", %(inputrefs)s)) {
390 return NULL;
391 }
392""" % locals()
393 else:
394 arg_parse_tuple = ""
395 # put it all together
396 s = """\
397/* function Py_%(name)s_do */
398static PyObject *
399Py_%(name)s_do(Py_%(name)s * self, PyObject * args)
400{
401%(inputdefs)s
402%(inputvecs)s
403%(outputvecs)s
404
405%(arg_parse_tuple)s
406
407%(parseinput)s
408
409%(outputcreate)s
410
411 /* compute _do function */
412 %(funcname)s (%(doparams_string)s);
413
414%(returnval)s;
415}
416""" % locals()
417 return s
418
419def gen_members(new_method, name):
420 newparams = get_params_types_names(new_method)
421 s = """
422AUBIO_MEMBERS_START(%(name)s)""" % locals()
423 for param in newparams:
424 if param['type'] == 'char_t*':
425 s += """
426 {"%(pname)s", T_STRING, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
427 % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
428 elif param['type'] == 'uint_t':
429 s += """
430 {"%(pname)s", T_INT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
431 % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
432 elif param['type'] == 'smpl_t':
433 s += """
434 {"%(pname)s", T_FLOAT, offsetof (Py_%(name)s, %(pname)s), READONLY, ""},""" \
435 % { 'pname': param['name'], 'ptype': param['type'], 'name': name}
436 else:
437 write_msg ("-- ERROR, unknown member type ", param )
438 s += """
439AUBIO_MEMBERS_STOP(%(name)s)
440
441""" % locals()
442 return s
443
444
445def gen_methods(get_methods, set_methods, name):
446 s = ""
447 method_defs = ""
448 for method in set_methods:
449 method_name = get_name(method)
450 params = get_params_types_names(method)
451 out_type = get_return_type(method)
452 assert params[0]['type'] == "aubio_"+name+"_t*", \
453 "get method is not in 'aubio_<name>_t"
454 write_msg (method )
455 write_msg (params[1:])
456 setter_args = "self->o, " +",".join([p['name'] for p in params[1:]])
457 parse_args = ""
458 for p in params[1:]:
459 parse_args += p['type'] + " " + p['name'] + ";\n"
460 argmap = "".join([aubio2pytypes[p['type']] for p in params[1:]])
461 arglist = ", ".join(["&"+p['name'] for p in params[1:]])
462 parse_args += """
463 if (!PyArg_ParseTuple (args, "%(argmap)s", %(arglist)s)) {
464 return NULL;
465 } """ % locals()
466 s += """
467static PyObject *
468Py%(funcname)s (Py_%(objname)s *self, PyObject *args)
469{
470 uint_t err = 0;
471
472 %(parse_args)s
473
474 err = %(funcname)s (%(setter_args)s);
475
476 if (err > 0) {
477 PyErr_SetString (PyExc_ValueError,
478 "error running %(funcname)s");
479 return NULL;
480 }
481 Py_RETURN_NONE;
482}
483""" % {'funcname': method_name, 'objname': name,
484 'out_type': out_type, 'setter_args': setter_args, 'parse_args': parse_args }
485 shortname = method_name.split('aubio_'+name+'_')[-1]
486 method_defs += """\
487 {"%(shortname)s", (PyCFunction) Py%(method_name)s,
488 METH_VARARGS, ""},
489""" % locals()
490
491 for method in get_methods:
492 method_name = get_name(method)
493 params = get_params_types_names(method)
494 out_type = get_return_type(method)
495 assert params[0]['type'] == "aubio_"+name+"_t*", \
496 "get method is not in 'aubio_<name>_t %s" % params[0]['type']
497 assert len(params) == 1, \
498 "get method has more than one parameter %s" % params
499 getter_args = "self->o"
500 returnval = "(PyObject *)" + aubiovectopyobj[out_type] + " (tmp)"
501 shortname = method_name.split('aubio_'+name+'_')[-1]
502 method_defs += """\
503 {"%(shortname)s", (PyCFunction) Py%(method_name)s,
504 METH_NOARGS, ""},
505""" % locals()
506 s += """
507static PyObject *
508Py%(funcname)s (Py_%(objname)s *self, PyObject *unused)
509{
510 %(out_type)s tmp = %(funcname)s (%(getter_args)s);
511 return %(returnval)s;
512}
513""" % {'funcname': method_name, 'objname': name,
514 'out_type': out_type, 'getter_args': getter_args, 'returnval': returnval }
515
516 s += """
517static PyMethodDef Py_%(name)s_methods[] = {
518""" % locals()
519 s += method_defs
520 s += """\
521 {NULL} /* sentinel */
522};
523""" % locals()
524 return s
525
526def gen_finish(name):
527 s = """\
528
529AUBIO_TYPEOBJECT(%(name)s, "aubio.%(name)s")
530""" % locals()
531 return s
python/lib/generator.py+0−233
@@ -1,233 +0,0 @@
1#! /usr/bin/python
2
3""" This file generates a c file from a list of cpp prototypes. """
4
5import os, sys, shutil
6from .gen_pyobject import write_msg, gen_new_init, gen_do, gen_members, gen_methods, gen_finish
7
8def get_cpp_objects():
9
10 cpp_output = [l.strip() for l in os.popen('cpp -DAUBIO_UNSTABLE=1 -I../build/src ../src/aubio.h').readlines()]
11
12 cpp_output = filter(lambda y: len(y) > 1, cpp_output)
13 cpp_output = filter(lambda y: not y.startswith('#'), cpp_output)
14 cpp_output = list(cpp_output)
15
16 i = 1
17 while 1:
18 if i >= len(cpp_output): break
19 if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'):
20 cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i]
21 cpp_output.pop(i-1)
22 else:
23 i += 1
24
25 typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output)
26 typedefs = list(typedefs)
27
28 cpp_objects = [a.split()[3][:-1] for a in typedefs]
29
30 return cpp_output, cpp_objects
31
32def generate_object_files(output_path):
33 if os.path.isdir(output_path): shutil.rmtree(output_path)
34 os.mkdir(output_path)
35
36 generated_objects = []
37 cpp_output, cpp_objects = get_cpp_objects()
38 skip_objects = [
39 # already in ext/
40 'fft',
41 'pvoc',
42 'filter',
43 'filterbank',
44 #'resampler',
45 # AUBIO_UNSTABLE
46 'hist',
47 'parameter',
48 'scale',
49 'beattracking',
50 'resampler',
51 'sndfile',
52 'peakpicker',
53 'pitchfcomb',
54 'pitchmcomb',
55 'pitchschmitt',
56 'pitchspecacf',
57 'pitchyin',
58 'pitchyinfft',
59 'sink',
60 'sink_apple_audio',
61 'sink_sndfile',
62 'sink_wavwrite',
63 'source',
64 'source_apple_audio',
65 'source_sndfile',
66 'source_avcodec',
67 'source_wavread',
68 #'sampler',
69 'audio_unit',
70 ]
71
72 write_msg("-- INFO: %d objects in total" % len(cpp_objects))
73
74 for this_object in cpp_objects:
75 lint = 0
76
77 if this_object[-2:] == '_t':
78 object_name = this_object[:-2]
79 else:
80 object_name = this_object
81 write_msg("-- WARNING: %s does not end in _t" % this_object)
82
83 if object_name[:len('aubio_')] != 'aubio_':
84 write_msg("-- WARNING: %s does not start n aubio_" % this_object)
85
86 write_msg("-- INFO: looking at", object_name)
87 object_methods = filter(lambda x: this_object in x, cpp_output)
88 object_methods = [a.strip() for a in object_methods]
89 object_methods = filter(lambda x: not x.startswith('typedef'), object_methods)
90 object_methods = list(object_methods)
91 #for method in object_methods:
92 # write_msg(method)
93 new_methods = list(filter(
94 lambda x: 'new_'+object_name in x, object_methods))
95 if len(new_methods) > 1:
96 write_msg("-- WARNING: more than one new method for", object_name)
97 for method in new_methods:
98 write_msg(method)
99 elif len(new_methods) < 1:
100 write_msg("-- WARNING: no new method for", object_name)
101 elif 0:
102 for method in new_methods:
103 write_msg(method)
104
105 del_methods = list(filter(
106 lambda x: 'del_'+object_name in x, object_methods))
107 if len(del_methods) > 1:
108 write_msg("-- WARNING: more than one del method for", object_name)
109 for method in del_methods:
110 write_msg(method)
111 elif len(del_methods) < 1:
112 write_msg("-- WARNING: no del method for", object_name)
113
114 do_methods = list(filter(
115 lambda x: object_name+'_do' in x, object_methods))
116 if len(do_methods) > 1:
117 pass
118 #write_msg("-- WARNING: more than one do method for", object_name)
119 #for method in do_methods:
120 # write_msg(method)
121 elif len(do_methods) < 1:
122 write_msg("-- WARNING: no do method for", object_name)
123 elif 0:
124 for method in do_methods:
125 write_msg(method)
126
127 # check do methods return void
128 for method in do_methods:
129 if (method.split()[0] != 'void'):
130 write_msg("-- ERROR: _do method does not return void:", method )
131
132 get_methods = filter(lambda x: object_name+'_get_' in x, object_methods)
133
134 set_methods = filter(lambda x: object_name+'_set_' in x, object_methods)
135 for method in set_methods:
136 if (method.split()[0] != 'uint_t'):
137 write_msg("-- ERROR: _set method does not return uint_t:", method )
138
139 other_methods = filter(lambda x: x not in new_methods, object_methods)
140 other_methods = filter(lambda x: x not in del_methods, other_methods)
141 other_methods = filter(lambda x: x not in do_methods, other_methods)
142 other_methods = filter(lambda x: x not in get_methods, other_methods)
143 other_methods = filter(lambda x: x not in set_methods, other_methods)
144 other_methods = list(other_methods)
145
146 if len(other_methods) > 0:
147 write_msg("-- WARNING: some methods for", object_name, "were unidentified")
148 for method in other_methods:
149 write_msg(method)
150
151
152 # generate this_object
153 short_name = object_name[len('aubio_'):]
154 if short_name in skip_objects:
155 write_msg("-- INFO: skipping object", short_name )
156 continue
157 if 1: #try:
158 s = gen_new_init(new_methods[0], short_name)
159 s += gen_do(do_methods[0], short_name)
160 s += gen_members(new_methods[0], short_name)
161 s += gen_methods(get_methods, set_methods, short_name)
162 s += gen_finish(short_name)
163 generated_filepath = os.path.join(output_path,'gen-'+short_name+'.c')
164 fd = open(generated_filepath, 'w')
165 fd.write(s)
166 #except Exception, e:
167 # write_msg("-- ERROR:", type(e), str(e), "in", short_name)
168 # continue
169 generated_objects += [this_object]
170
171 s = """// generated list of objects created with generator.py
172
173"""
174
175 types_ready = []
176 for each in generated_objects:
177 types_ready.append(" PyType_Ready (&Py_%sType) < 0" % \
178 each.replace('aubio_','').replace('_t','') )
179
180 s = """// generated list of objects created with generator.py
181
182#include "aubio-generated.h"
183"""
184
185 s += """
186int generated_types_ready (void)
187{
188 return (
189"""
190 s += ('\n ||').join(types_ready)
191 s += """);
192}
193"""
194
195 s += """
196void add_generated_objects ( PyObject *m )
197{"""
198 for each in generated_objects:
199 s += """
200 Py_INCREF (&Py_%(name)sType);
201 PyModule_AddObject (m, "%(name)s", (PyObject *) & Py_%(name)sType);""" % \
202 { 'name': ( each.replace('aubio_','').replace('_t','') ) }
203
204 s += """
205}"""
206
207 fd = open(os.path.join(output_path,'aubio-generated.c'), 'w')
208 fd.write(s)
209
210 s = """// generated list of objects created with generator.py
211
212#include <Python.h>
213
214"""
215
216 for each in generated_objects:
217 s += "extern PyTypeObject Py_%sType;\n" % \
218 each.replace('aubio_','').replace('_t','')
219
220 s+= "int generated_objects ( void );\n"
221 s+= "void add_generated_objects( PyObject *m );\n"
222
223 fd = open(os.path.join(output_path,'aubio-generated.h'), 'w')
224 fd.write(s)
225
226 from os import listdir
227 generated_files = listdir(output_path)
228 generated_files = filter(lambda x: x.endswith('.c'), generated_files)
229 generated_files = [output_path+'/'+f for f in generated_files]
230 return generated_files
231
232if __name__ == '__main__':
233 generate_object_files('gen')

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t