Changeset 633400d for python/lib
- Timestamp:
- Dec 5, 2018, 10:34:39 PM (6 years ago)
- Branches:
- feature/cnn, feature/crepe, feature/pitchshift, feature/timestretch, fix/ffmpeg5, master
- Children:
- 283a619a
- Parents:
- 5b46bc3 (diff), f19db54 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Location:
- python/lib
- Files:
-
- 2 added
- 1 deleted
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
python/lib/aubio/__init__.py
r5b46bc3 r633400d 1 1 #! /usr/bin/env python 2 # -*- coding: utf8 -*- 3 4 """ 5 aubio 6 ===== 7 8 Provides a number of classes and functions for music and audio signal 9 analysis. 10 11 How to use the documentation 12 ---------------------------- 13 14 Documentation of the python module is available as docstrings provided 15 within the code, and a reference guide available online from `the 16 aubio homepage <https://aubio.org/documentation>`_. 17 18 The docstrings examples are written assuming `aubio` and `numpy` have been 19 imported with: 20 21 >>> import aubio 22 >>> import numpy as np 23 """ 2 24 3 25 import numpy 26 from ._aubio import __version__ as version 27 from ._aubio import float_type 4 28 from ._aubio import * 5 from ._aubio import float_type6 29 from .midiconv import * 7 30 from .slicing import * 8 31 32 9 33 class fvec(numpy.ndarray): 10 """a numpy vector holding audio samples""" 34 """fvec(input_arg=1024) 35 A vector holding float samples. 11 36 12 def __new__(cls, input_arg=1024, **kwargs): 37 If `input_arg` is an `int`, a 1-dimensional vector of length `input_arg` 38 will be created and filled with zeros. Otherwise, if `input_arg` is an 39 `array_like` object, it will be converted to a 1-dimensional vector of 40 type :data:`float_type`. 41 42 Parameters 43 ---------- 44 input_arg : `int` or `array_like` 45 Can be a positive integer, or any object that can be converted to 46 a numpy array with :func:`numpy.array`. 47 48 Examples 49 -------- 50 >>> aubio.fvec(10) 51 array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32) 52 >>> aubio.fvec([0,1,2]) 53 array([0., 1., 2.], dtype=float32) 54 >>> a = np.arange(10); type(a), type(aubio.fvec(a)) 55 (<class 'numpy.ndarray'>, <class 'numpy.ndarray'>) 56 >>> a.dtype, aubio.fvec(a).dtype 57 (dtype('int64'), dtype('float32')) 58 59 Notes 60 ----- 61 62 In the Python world, `fvec` is simply a subclass of 63 :class:`numpy.ndarray`. In practice, any 1-dimensional `numpy.ndarray` of 64 `dtype` :data:`float_type` may be passed to methods accepting 65 `fvec` as parameter. For instance, `sink()` or `pvoc()`. 66 67 See Also 68 -------- 69 cvec : a container holding spectral data 70 numpy.ndarray : parent class of :class:`fvec` 71 numpy.zeros : create a numpy array filled with zeros 72 numpy.array : create a numpy array from an existing object 73 """ 74 def __new__(cls, input_arg=1024): 13 75 if isinstance(input_arg, int): 14 76 if input_arg == 0: 15 77 raise ValueError("vector length of 1 or more expected") 16 return numpy.zeros(input_arg, dtype=float_type, **kwargs)78 return numpy.zeros(input_arg, dtype=float_type, order='C') 17 79 else: 18 return numpy.array(input_arg, dtype=float_type, **kwargs) 80 np_input = numpy.array(input_arg, dtype=float_type, order='C') 81 if len(np_input.shape) != 1: 82 raise ValueError("input_arg should have shape (n,)") 83 if np_input.shape[0] == 0: 84 raise ValueError("vector length of 1 or more expected") 85 return np_input -
python/lib/aubio/midiconv.py
r5b46bc3 r633400d 2 2 """ utilities to convert midi note number to and from note names """ 3 3 4 __all__ = ['note2midi', 'midi2note', 'freq2note'] 4 import sys 5 from ._aubio import freqtomidi, miditofreq 5 6 6 import sys 7 __all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq'] 8 7 9 py3 = sys.version_info[0] == 3 8 10 if py3: … … 13 15 int_instances = (int, long) 14 16 17 15 18 def note2midi(note): 16 " convert note name to midi note number, e.g. [C-1, G9] -> [0, 127] " 17 _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11} 18 _valid_modifiers = {None: 0, u'♮': 0, '#': +1, u'♯': +1, u'\udd2a': +2, 19 'b': -1, u'♭': -1, u'\ufffd': -2} 19 """Convert note name to midi note number. 20 21 Input string `note` should be composed of one note root 22 and one octave, with optionally one modifier in between. 23 24 List of valid components: 25 26 - note roots: `C`, `D`, `E`, `F`, `G`, `A`, `B`, 27 - modifiers: `b`, `#`, as well as unicode characters 28 `𝄫`, `♭`, `♮`, `♯` and `𝄪`, 29 - octave numbers: `-1` -> `11`. 30 31 Parameters 32 ---------- 33 note : str 34 note name 35 36 Returns 37 ------- 38 int 39 corresponding midi note number 40 41 Examples 42 -------- 43 >>> aubio.note2midi('C#4') 44 61 45 >>> aubio.note2midi('B♭5') 46 82 47 48 Raises 49 ------ 50 TypeError 51 If `note` was not a string. 52 ValueError 53 If an error was found while converting `note`. 54 55 See Also 56 -------- 57 midi2note, freqtomidi, miditofreq 58 """ 59 _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 60 'A': 9, 'B': 11} 61 _valid_modifiers = { 62 u'𝄫': -2, # double flat 63 u'♭': -1, 'b': -1, '\u266d': -1, # simple flat 64 u'♮': 0, '\u266e': 0, None: 0, # natural 65 '#': +1, u'♯': +1, '\u266f': +1, # sharp 66 u'𝄪': +2, # double sharp 67 } 20 68 _valid_octaves = range(-1, 10) 21 69 if not isinstance(note, str_instances): 22 raise TypeError("a string is required, got %s (%s)" % (note, str(type(note)))) 70 msg = "a string is required, got {:s} ({:s})" 71 raise TypeError(msg.format(str(type(note)), repr(note))) 23 72 if len(note) not in range(2, 5): 24 raise ValueError("string of 2 to 4 characters expected, got %d (%s)" \25 %(len(note), note))26 notename, modifier, octave = [None] *373 msg = "string of 2 to 4 characters expected, got {:d} ({:s})" 74 raise ValueError(msg.format(len(note), note)) 75 notename, modifier, octave = [None] * 3 27 76 28 77 if len(note) == 4: … … 47 96 raise ValueError("%s is not a valid octave" % octave) 48 97 49 midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier] 98 midi = (octave + 1) * 12 + _valid_notenames[notename] \ 99 + _valid_modifiers[modifier] 50 100 if midi > 127: 51 101 raise ValueError("%s is outside of the range C-2 to G8" % note) 52 102 return midi 53 103 104 54 105 def midi2note(midi): 55 " convert midi note number to note name, e.g. [0, 127] -> [C-1, G9] " 106 """Convert midi note number to note name. 107 108 Parameters 109 ---------- 110 midi : int [0, 128] 111 input midi note number 112 113 Returns 114 ------- 115 str 116 note name 117 118 Examples 119 -------- 120 >>> aubio.midi2note(70) 121 'A#4' 122 >>> aubio.midi2note(59) 123 'B3' 124 125 Raises 126 ------ 127 TypeError 128 If `midi` was not an integer. 129 ValueError 130 If `midi` is out of the range `[0, 128]`. 131 132 See Also 133 -------- 134 note2midi, miditofreq, freqtomidi 135 """ 56 136 if not isinstance(midi, int_instances): 57 137 raise TypeError("an integer is required, got %s" % midi) 58 138 if midi not in range(0, 128): 59 raise ValueError("an integer between 0 and 127 is excepted, got %d" % midi) 60 _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] 139 msg = "an integer between 0 and 127 is excepted, got {:d}" 140 raise ValueError(msg.format(midi)) 141 _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 142 'A', 'A#', 'B'] 61 143 return _valid_notenames[midi % 12] + str(int(midi / 12) - 1) 62 144 145 63 146 def freq2note(freq): 64 " convert frequency in Hz to nearest note name, e.g. [0, 22050.] -> [C-1, G9] " 65 from aubio import freqtomidi 66 return midi2note(int(freqtomidi(freq))) 147 """Convert frequency in Hz to nearest note name. 148 149 Parameters 150 ---------- 151 freq : float [0, 23000[ 152 input frequency, in Hz 153 154 Returns 155 ------- 156 str 157 name of the nearest note 158 159 Example 160 ------- 161 >>> aubio.freq2note(440) 162 'A4' 163 >>> aubio.freq2note(220.1) 164 'A3' 165 """ 166 nearest_note = int(freqtomidi(freq) + .5) 167 return midi2note(nearest_note) 168 169 170 def note2freq(note): 171 """Convert note name to corresponding frequency, in Hz. 172 173 Parameters 174 ---------- 175 note : str 176 input note name 177 178 Returns 179 ------- 180 freq : float [0, 23000[ 181 frequency, in Hz 182 183 Example 184 ------- 185 >>> aubio.note2freq('A4') 186 440 187 >>> aubio.note2freq('A3') 188 220.1 189 """ 190 midi = note2midi(note) 191 return miditofreq(midi) -
python/lib/aubio/slicing.py
r5b46bc3 r633400d 6 6 _max_timestamp = 1e120 7 7 8 8 9 def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, 9 output_dir=None, samplerate=0, hopsize=256): 10 """ slice a sound file at given timestamps """ 10 output_dir=None, samplerate=0, hopsize=256, 11 create_first=False): 12 """Slice a sound file at given timestamps. 11 13 12 if timestamps is None or len(timestamps) == 0: 14 This function reads `source_file` and creates slices, new smaller 15 files each starting at `t` in `timestamps`, a list of integer 16 corresponding to time locations in `source_file`, in samples. 17 18 If `timestamps_end` is unspecified, the slices will end at 19 `timestamps_end[n] = timestamps[n+1]-1`, or the end of file. 20 Otherwise, `timestamps_end` should be a list with the same length 21 as `timestamps` containing the locations of the end of each slice. 22 23 If `output_dir` is unspecified, the new slices will be written in 24 the current directory. If `output_dir` is a string, new slices 25 will be written in `output_dir`, after creating the directory if 26 required. 27 28 The default `samplerate` is 0, meaning the original sampling rate 29 of `source_file` will be used. When using a sampling rate 30 different to the one of the original files, `timestamps` and 31 `timestamps_end` should be expressed in the re-sampled signal. 32 33 The `hopsize` parameter simply tells :class:`source` to use this 34 hopsize and does not change the output slices. 35 36 If `create_first` is True and `timestamps` does not start with `0`, the 37 first slice from `0` to `timestamps[0] - 1` will be automatically added. 38 39 Parameters 40 ---------- 41 source_file : str 42 path of the resource to slice 43 timestamps : :obj:`list` of :obj:`int` 44 time stamps at which to slice, in samples 45 timestamps_end : :obj:`list` of :obj:`int` (optional) 46 time stamps at which to end the slices 47 output_dir : str (optional) 48 output directory to write the slices to 49 samplerate : int (optional) 50 samplerate to read the file at 51 hopsize : int (optional) 52 number of samples read from source per iteration 53 create_first : bool (optional) 54 always create the slice at the start of the file 55 56 Examples 57 -------- 58 Create two slices: the first slice starts at the beginning of the 59 input file `loop.wav` and lasts exactly one second, starting at 60 sample `0` and ending at sample `44099`; the second slice starts 61 at sample `44100` and lasts until the end of the input file: 62 63 >>> aubio.slice_source_at_stamps('loop.wav', [0, 44100]) 64 65 Create one slice, from 1 second to 2 seconds: 66 67 >>> aubio.slice_source_at_stamps('loop.wav', [44100], [44100 * 2 - 1]) 68 69 Notes 70 ----- 71 Slices may be overlapping. If `timestamps_end` is `1` element 72 shorter than `timestamps`, the last slice will end at the end of 73 the file. 74 """ 75 76 if not timestamps: 13 77 raise ValueError("no timestamps given") 14 78 15 if timestamps[0] != 0 :79 if timestamps[0] != 0 and create_first: 16 80 timestamps = [0] + timestamps 17 81 if timestamps_end is not None: … … 19 83 20 84 if timestamps_end is not None: 21 if len(timestamps_end) != len(timestamps): 85 if len(timestamps_end) == len(timestamps) - 1: 86 timestamps_end = timestamps_end + [_max_timestamp] 87 elif len(timestamps_end) != len(timestamps): 22 88 raise ValueError("len(timestamps_end) != len(timestamps)") 23 89 else: … … 25 91 26 92 regions = list(zip(timestamps, timestamps_end)) 27 #print regions28 93 29 94 source_base_name, _ = os.path.splitext(os.path.basename(source_file)) … … 33 98 source_base_name = os.path.join(output_dir, source_base_name) 34 99 35 def new_sink_name(source_base_name, timestamp, samplerate):36 """ create a sink based on a timestamp in samples, converted in seconds """100 def _new_sink_name(source_base_name, timestamp, samplerate): 101 # create name based on a timestamp in samples, converted in seconds 37 102 timestamp_seconds = timestamp / float(samplerate) 38 103 return source_base_name + "_%011.6f" % timestamp_seconds + '.wav' … … 49 114 vec, read = _source.do_multi() 50 115 # if the total number of frames read will exceed the next region start 51 if len(regions) and total_frames + read >= regions[0][0]: 52 #print "getting", regions[0], "at", total_frames 116 while regions and total_frames + read >= regions[0][0]: 53 117 # get next region 54 118 start_stamp, end_stamp = regions.pop(0) 55 119 # create a name for the sink 56 new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate) 120 new_sink_path = _new_sink_name(source_base_name, start_stamp, 121 samplerate) 57 122 # create its sink 58 123 _sink = sink(new_sink_path, samplerate, _source.channels) 59 124 # create a dictionary containing all this 60 new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink} 125 new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 126 'sink': _sink} 61 127 # append the dictionary to the current list of slices 62 128 slices.append(new_slice) … … 70 136 # number of samples yet to written be until end of region 71 137 remaining = end_stamp - total_frames + 1 72 #print current_slice, remaining, start73 138 # not enough frames remaining, time to split 74 139 if remaining < read: … … 76 141 # write remaining samples from current region 77 142 _sink.do_multi(vec[:, start:remaining], remaining - start) 78 #print "closing region", "remaining", remaining79 143 # close this file 80 144 _sink.close() … … 83 147 _sink.do_multi(vec[:, start:read], read - start) 84 148 total_frames += read 149 # remove old slices 150 slices = list(filter(lambda s: s['end_stamp'] > total_frames, 151 slices)) 85 152 if read < hopsize: 86 153 break -
python/lib/gen_code.py
r5b46bc3 r633400d 3 3 'buf_size': 'Py_default_vector_length', 4 4 'win_s': 'Py_default_vector_length', 5 'size': 'Py_default_vector_length', 5 6 # and here too 6 7 'hop_size': 'Py_default_vector_length / 2', … … 85 86 'tss': 'self->buf_size', 86 87 'pitchshift': 'self->hop_size', 88 'dct': 'self->size', 87 89 } 88 90 … … 180 182 self.do_outputs = get_params_types_names(self.do_proto)[2:] 181 183 struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs] 184 if len(self.prototypes['rdo']): 185 rdo_outputs = get_params_types_names(prototypes['rdo'][0])[2:] 186 struct_output_str += ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in rdo_outputs] 187 self.outputs += rdo_outputs 182 188 self.struct_outputs = ";\n ".join(struct_output_str) 183 189 … … 187 193 def gen_code(self): 188 194 out = "" 189 out += self.gen_struct() 190 out += self.gen_doc() 191 out += self.gen_new() 192 out += self.gen_init() 193 out += self.gen_del() 194 out += self.gen_do() 195 out += self.gen_memberdef() 196 out += self.gen_set() 197 out += self.gen_get() 198 out += self.gen_methodef() 199 out += self.gen_typeobject() 195 try: 196 out += self.gen_struct() 197 out += self.gen_doc() 198 out += self.gen_new() 199 out += self.gen_init() 200 out += self.gen_del() 201 out += self.gen_do() 202 if len(self.prototypes['rdo']): 203 self.do_proto = self.prototypes['rdo'][0] 204 self.do_inputs = [get_params_types_names(self.do_proto)[1]] 205 self.do_outputs = get_params_types_names(self.do_proto)[2:] 206 out += self.gen_do(method='rdo') 207 out += self.gen_memberdef() 208 out += self.gen_set() 209 out += self.gen_get() 210 out += self.gen_methodef() 211 out += self.gen_typeobject() 212 except Exception as e: 213 print ("Failed generating code for", self.shortname) 214 raise 200 215 return out 201 216 … … 381 396 return out 382 397 383 def gen_do(self ):398 def gen_do(self, method = 'do'): 384 399 out = """ 385 400 // do {shortname} 386 401 static PyObject* 387 Py _{shortname}_do(Py_{shortname} * self, PyObject * args)388 {{""".format( **self.__dict__)402 Pyaubio_{shortname}_{method} (Py_{shortname} * self, PyObject * args) 403 {{""".format(method = method, **self.__dict__) 389 404 input_params = self.do_inputs 390 405 output_params = self.do_outputs … … 462 477 """.format(**self.__dict__) 463 478 for set_param in self.prototypes['set']: 464 params = get_params_types_names(set_param)[1] 465 paramtype = params['type'] 479 params = get_params_types_names(set_param)[1:] 480 param = self.shortname.split('_set_')[-1] 481 paramdecls = "".join([""" 482 {0} {1};""".format(p['type'], p['name']) for p in params]) 466 483 method_name = get_name(set_param) 467 484 param = method_name.split('aubio_'+self.shortname+'_set_')[-1] 468 pyparamtype = pyargparse_chars[paramtype] 485 refs = ", ".join(["&%s" % p['name'] for p in params]) 486 paramlist = ", ".join(["%s" % p['name'] for p in params]) 487 if len(params): 488 paramlist = "," + paramlist 489 pyparamtypes = ''.join([pyargparse_chars[p['type']] for p in params]) 469 490 out += """ 470 491 static PyObject * … … 472 493 {{ 473 494 uint_t err = 0; 474 {paramtype} {param}; 475 476 if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{ 495 {paramdecls} 496 """.format(param = param, paramdecls = paramdecls, **self.__dict__) 497 498 if len(refs) and len(pyparamtypes): 499 out += """ 500 501 if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{ 477 502 return NULL; 478 503 }} 479 err = aubio_{shortname}_set_{param} (self->o, {param}); 504 """.format(pyparamtypes = pyparamtypes, refs = refs) 505 506 out += """ 507 err = aubio_{shortname}_set_{param} (self->o {paramlist}); 480 508 481 509 if (err > 0) {{ 482 PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}"); 510 if (PyErr_Occurred() == NULL) {{ 511 PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}"); 512 }} else {{ 513 // change the RuntimeError into ValueError 514 PyObject *type, *value, *traceback; 515 PyErr_Fetch(&type, &value, &traceback); 516 PyErr_Restore(PyExc_ValueError, value, traceback); 517 }} 483 518 return NULL; 484 519 }} 485 520 Py_RETURN_NONE; 486 521 }} 487 """.format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__) 522 """.format(param = param, refs = refs, paramdecls = paramdecls, 523 pyparamtypes = pyparamtypes, paramlist = paramlist, **self.__dict__) 488 524 return out 489 525 … … 526 562 {{"{shortname}", (PyCFunction) Py{name}, 527 563 METH_NOARGS, ""}},""".format(name = name, shortname = shortname) 564 for m in self.prototypes['rdo']: 565 name = get_name(m) 566 shortname = name.replace('aubio_%s_' % self.shortname, '') 567 out += """ 568 {{"{shortname}", (PyCFunction) Py{name}, 569 METH_VARARGS, ""}},""".format(name = name, shortname = shortname) 528 570 out += """ 529 571 {NULL} /* sentinel */ … … 551 593 0, 552 594 0, 553 (ternaryfunc)Py _{shortname}_do,595 (ternaryfunc)Pyaubio_{shortname}_do, 554 596 0, 555 597 0, -
python/lib/gen_external.py
r5b46bc3 r633400d 1 1 import distutils.ccompiler 2 import sys, os, subprocess, glob 2 import sys 3 import os 4 import subprocess 5 import glob 6 from distutils.sysconfig import customize_compiler 7 from gen_code import MappedObject 3 8 4 9 header = os.path.join('src', 'aubio.h') … … 9 14 """ 10 15 11 skip_objects = [ 12 # already in ext/ 13 'fft', 14 'pvoc', 15 'filter', 16 'filterbank', 17 #'resampler', 18 # AUBIO_UNSTABLE 19 'hist', 20 'parameter', 21 'scale', 22 'beattracking', 23 'resampler', 24 'peakpicker', 25 'pitchfcomb', 26 'pitchmcomb', 27 'pitchschmitt', 28 'pitchspecacf', 29 'pitchyin', 30 'pitchyinfft', 31 'sink', 32 'sink_apple_audio', 33 'sink_sndfile', 34 'sink_wavwrite', 35 #'mfcc', 36 'source', 37 'source_apple_audio', 38 'source_sndfile', 39 'source_avcodec', 40 'source_wavread', 41 #'sampler', 42 'audio_unit', 43 ] 16 default_skip_objects = [ 17 # already in ext/ 18 'fft', 19 'pvoc', 20 'filter', 21 'filterbank', 22 # AUBIO_UNSTABLE 23 'hist', 24 'parameter', 25 'scale', 26 'beattracking', 27 'resampler', 28 'peakpicker', 29 'pitchfcomb', 30 'pitchmcomb', 31 'pitchschmitt', 32 'pitchspecacf', 33 'pitchyin', 34 'pitchyinfft', 35 'pitchyinfast', 36 'sink', 37 'sink_apple_audio', 38 'sink_sndfile', 39 'sink_wavwrite', 40 #'mfcc', 41 'source', 42 'source_apple_audio', 43 'source_sndfile', 44 'source_avcodec', 45 'source_wavread', 46 #'sampler', 47 'audio_unit', 48 'spectral_whitening', 49 ] 50 44 51 45 52 def get_preprocessor(): 46 53 # findout which compiler to use 47 from distutils.sysconfig import customize_compiler48 54 compiler_name = distutils.ccompiler.get_default_compiler() 49 55 compiler = distutils.ccompiler.new_compiler(compiler=compiler_name) … … 60 66 61 67 cpp_cmd = None 62 if hasattr(compiler, 'preprocessor'): # for unixccompiler68 if hasattr(compiler, 'preprocessor'): # for unixccompiler 63 69 cpp_cmd = compiler.preprocessor 64 elif hasattr(compiler, 'compiler'): # for ccompiler70 elif hasattr(compiler, 'compiler'): # for ccompiler 65 71 cpp_cmd = compiler.compiler.split() 66 72 cpp_cmd += ['-E'] 67 elif hasattr(compiler, 'cc'): # for msvccompiler73 elif hasattr(compiler, 'cc'): # for msvccompiler 68 74 cpp_cmd = compiler.cc.split() 69 75 cpp_cmd += ['-E'] 76 77 # On win-amd64 (py3.x), the default compiler is cross-compiling, from x86 78 # to amd64 with %WIN_SDK_ROOT%\x86_amd64\cl.exe, but using this binary as a 79 # pre-processor generates no output, so we use %WIN_SDK_ROOT%\cl.exe 80 # instead. 81 if len(cpp_cmd) > 1 and 'cl.exe' in cpp_cmd[-2]: 82 plat = os.path.basename(os.path.dirname(cpp_cmd[-2])) 83 if plat == 'x86_amd64': 84 print('workaround on win64 to avoid empty pre-processor output') 85 cpp_cmd[-2] = cpp_cmd[-2].replace('x86_amd64', '') 86 elif True in ['amd64' in f for f in cpp_cmd]: 87 print('warning: not using workaround for', cpp_cmd[0], plat) 70 88 71 89 if not cpp_cmd: … … 73 91 cpp_cmd = os.environ.get('CC', 'cc').split() 74 92 cpp_cmd += ['-E'] 75 93 if 'emcc' in cpp_cmd: 94 cpp_cmd += ['-x', 'c'] # emcc defaults to c++, force C language 76 95 return cpp_cmd 77 96 78 def get_cpp_objects(header=header): 97 98 def get_c_declarations(header=header, usedouble=False): 99 ''' return a dense and preprocessed string of all c declarations implied by aubio.h 100 ''' 101 cpp_output = get_cpp_output(header=header, usedouble=usedouble) 102 return filter_cpp_output (cpp_output) 103 104 105 def get_cpp_output(header=header, usedouble=False): 106 ''' find and run a C pre-processor on aubio.h ''' 79 107 cpp_cmd = get_preprocessor() 80 108 81 109 macros = [('AUBIO_UNSTABLE', 1)] 110 if usedouble: 111 macros += [('HAVE_AUBIO_DOUBLE', 1)] 82 112 83 113 if not os.path.isfile(header): … … 90 120 print("Running command: {:s}".format(" ".join(cpp_cmd))) 91 121 proc = subprocess.Popen(cpp_cmd, 92 stderr=subprocess.PIPE,93 stdout=subprocess.PIPE)122 stderr=subprocess.PIPE, 123 stdout=subprocess.PIPE) 94 124 assert proc, 'Proc was none' 95 125 cpp_output = proc.stdout.read() 96 126 err_output = proc.stderr.read() 127 if err_output: 128 print("Warning: preprocessor produced errors or warnings:\n%s" \ 129 % err_output.decode('utf8')) 97 130 if not cpp_output: 98 raise Exception("preprocessor output is empty:\n%s" % err_output) 99 elif err_output: 100 print ("Warning: preprocessor produced warnings:\n%s" % err_output) 131 raise_msg = "preprocessor output is empty! Running command " \ 132 + "\"%s\" failed" % " ".join(cpp_cmd) 133 if err_output: 134 raise_msg += " with stderr: \"%s\"" % err_output.decode('utf8') 135 else: 136 raise_msg += " with no stdout or stderr" 137 raise Exception(raise_msg) 101 138 if not isinstance(cpp_output, list): 102 139 cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')] 103 140 104 cpp_output = filter(lambda y: len(y) > 1, cpp_output) 141 return cpp_output 142 143 def filter_cpp_output(cpp_raw_output): 144 ''' prepare cpp-output for parsing ''' 145 cpp_output = filter(lambda y: len(y) > 1, cpp_raw_output) 105 146 cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output)) 106 147 107 148 i = 1 108 149 while 1: 109 if i >= len(cpp_output): break 110 if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'): 111 cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i] 112 cpp_output.pop(i-1) 150 if i >= len(cpp_output): 151 break 152 if ('{' in cpp_output[i - 1]) and ('}' not in cpp_output[i - 1]) or (';' not in cpp_output[i - 1]): 153 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i] 154 cpp_output.pop(i - 1) 155 elif ('}' in cpp_output[i]): 156 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i] 157 cpp_output.pop(i - 1) 113 158 else: 114 159 i += 1 115 160 116 typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output) 117 161 # clean pointer notations 162 tmp = [] 163 for l in cpp_output: 164 tmp += [l.replace(' *', ' * ')] 165 cpp_output = tmp 166 167 return cpp_output 168 169 170 def get_cpp_objects_from_c_declarations(c_declarations, skip_objects=None): 171 if skip_objects is None: 172 skip_objects = default_skip_objects 173 typedefs = filter(lambda y: y.startswith('typedef struct _aubio'), c_declarations) 118 174 cpp_objects = [a.split()[3][:-1] for a in typedefs] 119 120 return cpp_output, cpp_objects 121 122 123 def analyze_cpp_output(cpp_objects, cpp_output): 175 cpp_objects_filtered = filter(lambda y: not y[6:-2] in skip_objects, cpp_objects) 176 return cpp_objects_filtered 177 178 179 def get_all_func_names_from_lib(lib): 180 ''' return flat string of all function used in lib 181 ''' 182 res = [] 183 for _, v in lib.items(): 184 if isinstance(v, dict): 185 res += get_all_func_names_from_lib(v) 186 elif isinstance(v, list): 187 for elem in v: 188 e = elem.split('(') 189 if len(e) < 2: 190 continue # not a function 191 fname_part = e[0].strip().split(' ') 192 fname = fname_part[-1] 193 if fname: 194 res += [fname] 195 else: 196 raise NameError('gen_lib : weird function: ' + str(e)) 197 198 return res 199 200 201 def generate_lib_from_c_declarations(cpp_objects, c_declarations): 202 ''' returns a lib from given cpp_object names 203 204 a lib is a dict grouping functions by family (onset,pitch...) 205 each eement is itself a dict of functions grouped by puposes as : 206 struct, new, del, do, get, set and other 207 ''' 124 208 lib = {} 125 209 126 210 for o in cpp_objects: 127 if o[:6] != 'aubio_': 128 continue 129 shortname = o[6:-2] 130 if shortname in skip_objects: 131 continue 132 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []} 211 shortname = o 212 if o[:6] == 'aubio_': 213 shortname = o[6:-2] # without aubio_ prefix and _t suffix 214 215 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'rdo': [], 'get': [], 'set': [], 'other': []} 133 216 lib[shortname]['longname'] = o 134 217 lib[shortname]['shortname'] = shortname 135 for fn in cpp_output: 136 if o[:-1] in fn: 137 #print "found", o[:-1], "in", fn 218 219 fullshortname = o[:-2] # name without _t suffix 220 221 for fn in c_declarations: 222 func_name = fn.split('(')[0].strip().split(' ')[-1] 223 if func_name.startswith(fullshortname + '_') or func_name.endswith(fullshortname): 224 # print "found", shortname, "in", fn 138 225 if 'typedef struct ' in fn: 139 226 lib[shortname]['struct'].append(fn) 140 227 elif '_do' in fn: 141 228 lib[shortname]['do'].append(fn) 229 elif '_rdo' in fn: 230 lib[shortname]['rdo'].append(fn) 142 231 elif 'new_' in fn: 143 232 lib[shortname]['new'].append(fn) … … 149 238 lib[shortname]['set'].append(fn) 150 239 else: 151 # print "no idea what to do about", fn240 # print "no idea what to do about", fn 152 241 lib[shortname]['other'].append(fn) 153 242 return lib 154 243 155 def print_cpp_output_results(lib, cpp_output): 156 for fn in cpp_output: 244 245 def print_c_declarations_results(lib, c_declarations): 246 for fn in c_declarations: 157 247 found = 0 158 248 for o in lib: … … 161 251 found = 1 162 252 if found == 0: 163 print 253 print("missing", fn) 164 254 165 255 for o in lib: 166 256 for family in lib[o]: 167 257 if type(lib[o][family]) == str: 168 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ))258 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family])) 169 259 elif len(lib[o][family]) == 1: 170 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ))260 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family][0])) 171 261 else: 172 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ))262 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family])) 173 263 174 264 175 265 def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True): 176 if not os.path.isdir(output_path): os.mkdir(output_path) 177 elif not overwrite: return glob.glob(os.path.join(output_path, '*.c')) 178 179 cpp_output, cpp_objects = get_cpp_objects(header) 180 181 lib = analyze_cpp_output(cpp_objects, cpp_output) 182 # print_cpp_output_results(lib, cpp_output) 266 if not os.path.isdir(output_path): 267 os.mkdir(output_path) 268 elif not overwrite: 269 return sorted(glob.glob(os.path.join(output_path, '*.c'))) 270 271 c_declarations = get_c_declarations(header, usedouble=usedouble) 272 cpp_objects = get_cpp_objects_from_c_declarations(c_declarations) 273 274 lib = generate_lib_from_c_declarations(cpp_objects, c_declarations) 275 # print_c_declarations_results(lib, c_declarations) 183 276 184 277 sources_list = [] 185 try:186 from .gen_code import MappedObject187 except (SystemError, ValueError):188 from gen_code import MappedObject189 278 for o in lib: 190 279 out = source_header 191 mapped = MappedObject(lib[o], usedouble =usedouble)280 mapped = MappedObject(lib[o], usedouble=usedouble) 192 281 out += mapped.gen_code() 193 282 output_file = os.path.join(output_path, 'gen-%s.c' % o) 194 283 with open(output_file, 'w') as f: 195 284 f.write(out) 196 print ("wrote %s" % output_file)285 print("wrote %s" % output_file) 197 286 sources_list.append(output_file) 198 287 … … 206 295 return ({pycheck_types}); 207 296 }} 208 """.format(pycheck_types =check_types)297 """.format(pycheck_types=check_types) 209 298 210 299 add_types = "".join([""" 211 300 Py_INCREF (&Py_{name}Type); 212 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name =o) for o in lib])301 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name=o) for o in lib]) 213 302 out += """ 214 303 … … 217 306 {add_types} 218 307 }} 219 """.format(add_types =add_types)308 """.format(add_types=add_types) 220 309 221 310 output_file = os.path.join(output_path, 'aubio-generated.c') 222 311 with open(output_file, 'w') as f: 223 312 f.write(out) 224 print ("wrote %s" % output_file)313 print("wrote %s" % output_file) 225 314 sources_list.append(output_file) 226 315 … … 240 329 int generated_objects ( void ); 241 330 void add_generated_objects( PyObject *m ); 242 """.format(objlist =objlist)331 """.format(objlist=objlist) 243 332 244 333 output_file = os.path.join(output_path, 'aubio-generated.h') 245 334 with open(output_file, 'w') as f: 246 335 f.write(out) 247 print ("wrote %s" % output_file)336 print("wrote %s" % output_file) 248 337 # no need to add header to list of sources 249 338 250 return so urces_list339 return sorted(sources_list) 251 340 252 341 if __name__ == '__main__': 253 if len(sys.argv) > 1: header = sys.argv[1] 254 if len(sys.argv) > 2: output_path = sys.argv[2] 342 if len(sys.argv) > 1: 343 header = sys.argv[1] 344 if len(sys.argv) > 2: 345 output_path = sys.argv[2] 255 346 generate_external(header, output_path) -
python/lib/moresetuptools.py
r5b46bc3 r633400d 3 3 import sys, os, glob, subprocess 4 4 import distutils, distutils.command.clean, distutils.dir_util 5 from .gen_external import generate_external, header, output_path 5 from gen_external import generate_external, header, output_path 6 7 from this_version import get_aubio_version 6 8 7 9 # inspired from https://gist.github.com/abergmeier/9488990 … … 22 24 23 25 for package in packages: 26 print("checking for {:s}".format(package)) 24 27 cmd = ['pkg-config', '--libs', '--cflags', package] 25 28 try: … … 55 58 def add_local_aubio_sources(ext): 56 59 """ build aubio inside python module instead of linking against libaubio """ 57 print("Warning: libaubio was not built with waf, adding src/") 58 # create an empty header, macros will be passed on the command line 59 fake_config_header = os.path.join('python', 'ext', 'config.h') 60 distutils.file_util.write_file(fake_config_header, "") 61 aubio_sources = glob.glob(os.path.join('src', '**.c')) 62 aubio_sources += glob.glob(os.path.join('src', '*', '**.c')) 60 print("Info: libaubio was not installed or built locally with waf, adding src/") 61 aubio_sources = sorted(glob.glob(os.path.join('src', '**.c'))) 62 aubio_sources += sorted(glob.glob(os.path.join('src', '*', '**.c'))) 63 63 ext.sources += aubio_sources 64 65 def add_local_macros(ext, usedouble = False): 66 if usedouble: 67 ext.define_macros += [('HAVE_AUBIO_DOUBLE', 1)] 64 68 # define macros (waf puts them in build/src/config.h) 65 69 for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H', … … 70 74 ext.define_macros += [(define_macro, 1)] 71 75 76 def add_external_deps(ext, usedouble = False): 72 77 # loof for additional packages 73 78 print("Info: looking for *optional* additional packages") 74 packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample', 79 packages = ['libavcodec', 'libavformat', 'libavutil', 80 'libswresample', 'libavresample', 75 81 'jack', 76 'sndfile', 'samplerate',82 'sndfile', 77 83 'rubberband', 78 84 #'fftw3f', 79 85 ] 86 # samplerate only works with float 87 if usedouble is False: 88 packages += ['samplerate'] 89 else: 90 print("Info: not adding libsamplerate in double precision mode") 80 91 add_packages(packages, ext=ext) 81 92 if 'avcodec' in ext.libraries \ 82 93 and 'avformat' in ext.libraries \ 83 and 'avutil' in ext.libraries \ 84 and 'avresample' in ext.libraries: 85 ext.define_macros += [('HAVE_LIBAV', 1)] 86 if 'jack' in ext.libraries: 87 ext.define_macros += [('HAVE_JACK', 1)] 94 and 'avutil' in ext.libraries: 95 if 'swresample' in ext.libraries: 96 ext.define_macros += [('HAVE_SWRESAMPLE', 1)] 97 elif 'avresample' in ext.libraries: 98 ext.define_macros += [('HAVE_AVRESAMPLE', 1)] 99 if 'swresample' in ext.libraries or 'avresample' in ext.libraries: 100 ext.define_macros += [('HAVE_LIBAV', 1)] 88 101 if 'sndfile' in ext.libraries: 89 102 ext.define_macros += [('HAVE_SNDFILE', 1)] … … 108 121 ext.define_macros += [('HAVE_WAVWRITE', 1)] 109 122 ext.define_macros += [('HAVE_WAVREAD', 1)] 110 # TODO: 111 # add cblas123 124 # TODO: add cblas 112 125 if 0: 113 126 ext.libraries += ['cblas'] … … 116 129 def add_system_aubio(ext): 117 130 # use pkg-config to find aubio's location 118 add_packages(['aubio'], ext) 131 aubio_version = get_aubio_version() 132 add_packages(['aubio = ' + aubio_version], ext) 119 133 if 'aubio' not in ext.libraries: 120 print("Error: libaubio not found") 134 print("Info: aubio " + aubio_version + " was not found by pkg-config") 135 else: 136 print("Info: using system aubio " + aubio_version + " found in " + ' '.join(ext.library_dirs)) 137 138 def add_libav_on_win(ext): 139 """ no pkg-config on windows, simply assume these libs are available """ 140 ext.libraries += ['avformat', 'avutil', 'avcodec', 'swresample'] 141 for define_macro in ['HAVE_LIBAV', 'HAVE_SWRESAMPLE']: 142 ext.define_macros += [(define_macro, 1)] 121 143 122 144 class CleanGenerated(distutils.command.clean.clean): 123 145 def run(self): 124 distutils.dir_util.remove_tree(output_path)125 distutils.command.clean.clean.run(self)146 if os.path.isdir(output_path): 147 distutils.dir_util.remove_tree(output_path) 126 148 127 class GenerateCommand(distutils.cmd.Command): 128 description = 'generate gen/gen-*.c files from ../src/aubio.h' 129 user_options = [ 149 from distutils.command.build_ext import build_ext as _build_ext 150 class build_ext(_build_ext): 151 152 user_options = _build_ext.user_options + [ 130 153 # The format is (long option, short option, description). 131 154 ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'), … … 133 156 134 157 def initialize_options(self): 158 _build_ext.initialize_options(self) 135 159 self.enable_double = False 136 160 137 161 def finalize_options(self): 162 _build_ext.finalize_options(self) 138 163 if self.enable_double: 139 164 self.announce( … … 141 166 level=distutils.log.INFO) 142 167 143 def run(self): 144 self.announce( 'Generating code', level=distutils.log.INFO) 145 generated_object_files = generate_external(header, output_path, usedouble=self.enable_double) 168 def build_extension(self, extension): 169 if self.enable_double or 'HAVE_AUBIO_DOUBLE' in os.environ: 170 enable_double = True 171 else: 172 enable_double = False 173 # seack for aubio headers and lib in PKG_CONFIG_PATH 174 add_system_aubio(extension) 175 # the lib was not installed on this system 176 if 'aubio' not in extension.libraries: 177 # use local src/aubio.h 178 if os.path.isfile(os.path.join('src', 'aubio.h')): 179 add_local_aubio_header(extension) 180 add_local_macros(extension, usedouble=enable_double) 181 # look for a local waf build 182 if os.path.isfile(os.path.join('build','src', 'fvec.c.1.o')): 183 add_local_aubio_lib(extension) 184 else: 185 # check for external dependencies 186 add_external_deps(extension, usedouble=enable_double) 187 # force adding libav on windows 188 if os.name == 'nt' and ('WITH_LIBAV' in os.environ \ 189 or 'CONDA_PREFIX' in os.environ): 190 add_libav_on_win(extension) 191 # add libaubio sources and look for optional deps with pkg-config 192 add_local_aubio_sources(extension) 193 # generate files python/gen/*.c, python/gen/aubio-generated.h 194 extension.include_dirs += [ output_path ] 195 extension.sources += generate_external(header, output_path, overwrite = False, 196 usedouble=enable_double) 197 return _build_ext.build_extension(self, extension)
Note: See TracChangeset
for help on using the changeset viewer.