History

Merge branch 'master' into feature/pitchshift

Paul Brossier committed 7 years ago

Commit 633400d2 parents 5b46bc3 f19db54230 files · +12450 −4607Browse files

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

Unified · Split

61 files
  1. python/README.md +88 −79
  2. python/__init__.py +0 −0
  3. python/demos/demo_alsa.py +45 −0
  4. python/demos/demo_bench_yin.py +50 −0
  5. python/demos/demo_bpm_extract.py +44 −27
  6. python/demos/demo_filter.py +31 −14
  7. python/demos/demo_filterbank.py +31 −17
  8. python/demos/demo_notes.py +37 −0
  9. python/demos/demo_pitch_sinusoid.py +1 −1
  10. python/demos/demo_pyaudio.py +75 −0
  11. python/demos/demo_source_simple.py +13 −9
  12. python/demos/demo_tapthebeat.py +78 −0
  13. python/demos/demo_timestretch.py +1 −1
  14. python/demos/demo_timestretch_online.py +5 −3
  15. python/demos/demo_wav2midi.py +77 −0
  16. python/demos/demo_yin_compare.py +175 −0
  17. python/ext/aubio-types.h +9 −1
  18. python/ext/aubiomodule.c +167 −27
  19. python/ext/py-cvec.c +42 −12
  20. python/ext/py-filterbank.c +133 −9
  21. python/ext/py-musicutils.c +102 −0
  22. python/ext/py-musicutils.h +322 −25
  23. python/ext/py-phasevoc.c +116 −8
  24. python/ext/py-sink.c +89 −50
  25. python/ext/py-source.c +370 −41
  26. python/ext/ufuncs.c +44 −3
  27. python/lib/__init__.py +0 −0
  28. python/lib/aubio/__init__.py +72 −5
  29. python/lib/aubio/cmd.py +623 −0
  30. python/lib/aubio/cut.py +163 −0
  31. python/lib/aubio/midiconv.py +142 −17
  32. python/lib/aubio/slicing.py +81 −14
  33. python/lib/gen_code.py +65 −23
  34. python/lib/gen_external.py +180 −89
  35. python/lib/moresetuptools.py +78 −26
  36. python/scripts/aubiocut +0 −206
  37. python/tests/__init__.py +1 −0
  38. python/tests/eval_pitch +1 −1
  39. python/tests/test_aubio.py +5 −0
  40. python/tests/test_aubio_cmd.py +34 −0
  41. python/tests/test_aubio_cut.py +16 −0
  42. python/tests/test_dct.py +68 −0
  43. python/tests/test_fft.py +39 −12
  44. python/tests/test_filter.py +11 −1
  45. python/tests/test_filterbank.py +12 −5
  46. python/tests/test_filterbank_mel.py +136 −13
  47. python/tests/test_fvec.py +8 −0
  48. python/tests/test_fvec_shift.py +35 −0
  49. python/tests/test_hztomel.py +109 −0
  50. python/tests/test_mfcc.py +37 −0
  51. python/tests/test_midi2note.py +6 −4
  52. python/tests/test_note2midi.py +67 −7
  53. python/tests/test_notes.py +95 −0
  54. python/tests/test_onset.py +40 −8
  55. python/tests/test_phasevoc.py +17 −1
  56. python/tests/test_pitch.py +2 −2
  57. python/tests/test_sink.py +32 −1
  58. python/tests/test_slicing.py +27 −6
  59. python/tests/test_source.py +34 −7
  60. python/tests/test_source_channels.py +93 −0
  61. python/tests/test_tempo.py +91 −0
python/README.md+88−79
@@ -1,100 +1,109 @@
1−Python aubio module
2−===================
1+aubio
2+=====
33
4−This module wraps the aubio library for Python using the numpy module.
4+aubio is a collection of tools for music and audio analysis.
55
6−Using the Python aubio module
7−-----------------------------
6+This package integrates the aubio library with [NumPy] to provide a set of
7+efficient tools to process and analyse audio signals, including:
88
9−After installing python-aubio, you will be able to import the aubio module:
9+- read audio from any media file, including videos and remote streams
10+- high quality phase vocoder, spectral filterbanks, and linear filters
11+- Mel-Frequency Cepstrum Coefficients and standard spectral descriptors
12+- detection of note attacks (onset)
13+- pitch tracking (fundamental frequency estimation)
14+- beat detection and tempo tracking
1015
11− $ python
12− [...]
13− >>> import aubio
14− >>> help(aubio.miditofreq)
16+aubio works with both Python 2 and Python 3.
1517
16−Finding some inspiration
17−------------------------
18+Links
19+-----
1820
19−Some examples are available in the `python/demos` directory. These scripts are
20−small programs written in python and using python-aubio.
21+- [module documentation][doc_python]
22+- [installation instructions][doc_python_install]
23+- [aubio manual][manual]
24+- [aubio homepage][homepage]
25+- [issue tracker][bugtracker]
2126
22−For instance, `demo_source.py` reads a media file.
27+Demos
28+-----
2329
24− $ ./python/demos/demo_source.py /path/to/sound/sample.wav
30+Some examples are available in the [`python/demos`][demos_dir] folder. Each
31+script is a command line program which accepts one ore more argument.
2532
26−and `demo_timestretch_online.py` stretches the original file into a new one:
33+**Notes**: installing additional modules is required to run some of the demos.
2734
28− $ ./python/demo/demo_timestretch_online.py loop.wav stretched_loop.wav 0.92`
35+### Analysis
2936
30−Note: you might need to install additional modules to run some of the demos.
31−Some demos use [matplotlib](http://matplotlib.org/) to draw plots, others use
32−[PySoundCard](https://github.com/bastibe/PySoundCard) to play and record
33−sounds.
37+- `demo_source.py` uses aubio to read audio samples from media files
38+- `demo_onset_plot.py` detects attacks in a sound file and plots the results
39+ using [matplotlib]
40+- `demo_pitch.py` looks for fundamental frequency in a sound file and plots the
41+ results using [matplotlib]
42+- `demo_spectrogram.py`, `demo_specdesc.py`, `demo_mfcc.py` for spectral
43+ analysis.
3444
35−Testing the Python module
36−-------------------------
45+### Real-time
3746
38−To run the all the python tests, use the script:
47+- `demo_pyaudio.py` and `demo_tapthebeat.py` use [pyaudio]
48+- `demo_pysoundcard_play.py`, `demo_pysoundcard.py` use [PySoundCard]
49+- `demo_alsa.py` uses [pyalsaaudio]
3950
40− $ ./python/tests/run_all_tests
51+### Others
4152
42−Each test script can also be called one at a time. For instance:
53+- `demo_timestretch.py` can change the duration of an input file and write the
54+ new sound to disk,
55+- `demo_wav2midi.py` detects the notes in a file and uses [mido] to write the
56+ results into a MIDI file
4357
44− $ ./python/tests/test_note2midi.py -v
58+### Example
4559
46−Install in a virtualenv
47−-----------------------
60+Use `demo_timestretch_online.py` to slow down `loop.wav`, write the results in
61+`stretched_loop.wav`:
4862
49−You should be able to install python-aubio directly from the top source
50−directory of aubio.
63+ $ python demo_timestretch_online.py loop.wav stretched_loop.wav 0.92
5164
52−First, create a virtualenv to hold the required python module:
53−
54− $ virtualenv pyaubio
55− $ source pyaubio/bin/activate
56−
57−Now install and build the python extension using:
58−
59− $ pip install .
60−
61−Install requirements
62−--------------------
63−
64−Before compiling this module, you must have compiled libaubio.
65−
66−A simple way to do this is with pip:
67−
68− $ pip install -r requirements.txt
69−
70−For more information about how this module works, please refer to the [Python/C
71−API Reference Manual] (http://docs.python.org/c-api/index.html) and the
72−[Numpy/C API Reference](http://docs.scipy.org/doc/numpy/reference/c-api.html).
73−
74−Compiling python aubio
75−----------------------
76−
77−To build the aubio Python module, run the following command from the top source
78−directory of aubio:
79−
80− $ ./setup.py build
81−
82−Note: if libaubio was previously built using waf, the script will use it.
83−Otherwise, the entire library will be built inside the python extension.
84−
85−To find out more about `setup.py` options:
86−
87− $ ./setup.py --help
88−
89−Installing
65+Built with
9066 ----------
9167
92−To install the Python module:
93−
94− $ ./setup.py install
95−
96−Alternatively, you may want to use the Python module without installing it by
97−setting your PYTHONPATH, for instance as follows:
98−
99− $ export PYTHONPATH=$PYTHONPATH:$PWD/`ls -rtd build/lib.* | head -1`:$PWD/tests
100−
68+The core of aubio is written in C for portability and speed. In addition to
69+[NumPy], aubio can be optionally built to use one or more of the following
70+libraries:
71+
72+- media file reading:
73+
74+ - [ffmpeg] / [avcodec] to decode and read audio from almost any format,
75+ - [libsndfile] to read audio from uncompressed sound files,
76+ - [libsamplerate] to re-sample audio signals,
77+ - [CoreAudio] to read all media formats supported by macOS, iOS, and tvOS.
78+
79+- hardware acceleration:
80+
81+ - [Atlas] and [Blas], for accelerated vector and matrix computations,
82+ - [fftw3], to compute fast Fourier Transforms of any size,
83+ - [Accelerate] for accelerated FFT and matrix computations (macOS/iOS),
84+ - [Intel IPP], accelerated vector computation and FFT implementation.
85+
86+[ffmpeg]: https://ffmpeg.org
87+[avcodec]: https://libav.org
88+[libsndfile]: http://www.mega-nerd.com/libsndfile/
89+[libsamplerate]: http://www.mega-nerd.com/SRC/
90+[CoreAudio]: https://developer.apple.com/reference/coreaudio
91+[Atlas]: http://math-atlas.sourceforge.net/
92+[Blas]: https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms
93+[fftw3]: http://fftw.org
94+[Accelerate]: https://developer.apple.com/reference/accelerate
95+[Intel IPP]: https://software.intel.com/en-us/intel-ipp
96+
97+[demos_dir]:https://github.com/aubio/aubio/tree/master/python/demos
98+[pyaudio]:https://people.csail.mit.edu/hubert/pyaudio/
99+[PySoundCard]:https://github.com/bastibe/PySoundCard
100+[pyalsaaudio]:https://larsimmisch.github.io/pyalsaaudio/
101+[mido]:https://mido.readthedocs.io
102+
103+[manual]: https://aubio.org/manual/latest/
104+[doc_python]: https://aubio.org/manual/latest/python.html
105+[doc_python_install]: https://aubio.org/manual/latest/python_module.html
106+[homepage]: https://aubio.org
107+[NumPy]: https://www.numpy.org
108+[bugtracker]: https://github.com/aubio/aubio/issues
109+[matplotlib]:https://matplotlib.org/
python/__init__.py+0−0

Empty file deleted.

python/demos/demo_alsa.py+45−0
@@ -0,0 +1,45 @@
1+#! /usr/bin/env python
2+
3+import alsaaudio
4+import numpy as np
5+import aubio
6+
7+# constants
8+samplerate = 44100
9+win_s = 2048
10+hop_s = win_s // 2
11+framesize = hop_s
12+
13+# set up audio input
14+recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
15+recorder.setperiodsize(framesize)
16+recorder.setrate(samplerate)
17+recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE)
18+recorder.setchannels(1)
19+
20+# create aubio pitch detection (first argument is method, "default" is
21+# "yinfft", can also be "yin", "mcomb", fcomb", "schmitt").
22+pitcher = aubio.pitch("default", win_s, hop_s, samplerate)
23+# set output unit (can be 'midi', 'cent', 'Hz', ...)
24+pitcher.set_unit("Hz")
25+# ignore frames under this level (dB)
26+pitcher.set_silence(-40)
27+
28+print("Starting to listen, press Ctrl+C to stop")
29+
30+# main loop
31+while True:
32+ try:
33+ # read data from audio input
34+ _, data = recorder.read()
35+ # convert data to aubio float samples
36+ samples = np.fromstring(data, dtype=aubio.float_type)
37+ # pitch of current frame
38+ freq = pitcher(samples)[0]
39+ # compute energy of current block
40+ energy = np.sum(samples**2)/len(samples)
41+ # do something with the results
42+ print("{:10.4f} {:10.4f}".format(freq,energy))
43+ except KeyboardInterrupt:
44+ print("Ctrl+C pressed, exiting")
45+ break
python/demos/demo_bench_yin.py+50−0
@@ -0,0 +1,50 @@
1+#! /usr/bin/env python
2+
3+import numpy as np
4+from aubio import pitch
5+import pylab as plt
6+
7+buf_size = 2048 * 1
8+hop_size = buf_size // 4
9+
10+samplerate = 44100
11+minfreq = 40
12+maxfreq = 6000
13+
14+def sinewave(freq, duration, samplerate = samplerate):
15+ """ generate a sinewave """
16+ length = hop_size
17+ while length < duration * samplerate:
18+ length += hop_size
19+ return np.sin( 2. * np.pi * np.arange(length) * freq / samplerate ).astype("float32")
20+
21+def get_stats_for_pitch_method(method, freqs, samplerate = samplerate):
22+ """ for a given pitch method and a list of frequency, generate a sinewave
23+ and get mean deviation """
24+ means = np.zeros(len(freqs))
25+ medians = np.zeros(len(freqs))
26+ for freq, fn in zip(freqs, range(len(freqs))):
27+ s = sinewave(freq, .50).reshape(-1, hop_size)
28+ #s = (sinewave(freq, .50) + .0*sinewave(freq/2., .50)).reshape(-1, hop_size)
29+ p = pitch(method, buf_size, hop_size, samplerate = samplerate)
30+ candidates = np.zeros(len(s))
31+ #samples = np.zeros(buf_size)
32+ for frame, i in zip(s, range(len(s))):
33+ candidates[i] = p(frame)[0]
34+ # skip first few candidates
35+ candidates = candidates[4:]
36+ means[fn] = np.mean(candidates[candidates != 0] - freq)
37+ medians[fn] = np.median(candidates[candidates != 0] - freq)
38+ print (freq, means[fn], medians[fn])
39+ return means, medians
40+
41+if __name__ == '__main__':
42+ freqs = np.arange(minfreq, maxfreq, 1.)
43+ modes = ["yin", "yinfft"]
44+ for mode in modes:
45+ means, medians = get_stats_for_pitch_method(mode, freqs)
46+ plt.figure()
47+ plt.plot(freqs, means, 'g-')
48+ plt.plot(freqs, medians, 'r--')
49+ #plt.savefig(mode + '_deviations_test.png', dpi=300)
50+ plt.show()
python/demos/demo_bpm_extract.py+44−27
@@ -3,26 +3,33 @@ · 2 unmodified lines
33 from aubio import source, tempo
44 from numpy import median, diff
55
6−def get_file_bpm(path, params = None):
6+def get_file_bpm(path, params=None):
77 """ Calculate the beats per minute (bpm) of a given file.
88 path: path to the file
99 param: dictionary of parameters
1010 """
1111 if params is None:
1212 params = {}
13− try:
14− win_s = params['win_s']
15− samplerate = params['samplerate']
16− hop_s = params['hop_s']
17− except KeyError:
18− """
19− # super fast
20− samplerate, win_s, hop_s = 4000, 128, 64
21− # fast
22− samplerate, win_s, hop_s = 8000, 512, 128
23− """
24− # default:
25− samplerate, win_s, hop_s = 44100, 1024, 512
13+ # default:
14+ samplerate, win_s, hop_s = 44100, 1024, 512
15+ if 'mode' in params:
16+ if params.mode in ['super-fast']:
17+ # super fast
18+ samplerate, win_s, hop_s = 4000, 128, 64
19+ elif params.mode in ['fast']:
20+ # fast
21+ samplerate, win_s, hop_s = 8000, 512, 128
22+ elif params.mode in ['default']:
23+ pass
24+ else:
25+ raise ValueError("unknown mode {:s}".format(params.mode))
26+ # manual settings
27+ if 'samplerate' in params:
28+ samplerate = params.samplerate
29+ if 'win_s' in params:
30+ win_s = params.win_s
31+ if 'hop_s' in params:
32+ hop_s = params.hop_s
2633
2734 s = source(path, samplerate, hop_s)
2835 samplerate = s.samplerate
@@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines
4451 if read < hop_s:
4552 break
4653
47− # Convert to periods and to bpm
48− if len(beats) > 1:
49− if len(beats) < 4:
50− print("few beats found in {:s}".format(path))
51− bpms = 60./diff(beats)
52− b = median(bpms)
53− else:
54− b = 0
55− print("not enough beats found in {:s}".format(path))
56− return b
54+ def beats_to_bpm(beats, path):
55+ # if enough beats are found, convert to periods then to bpm
56+ if len(beats) > 1:
57+ if len(beats) < 4:
58+ print("few beats found in {:s}".format(path))
59+ bpms = 60./diff(beats)
60+ return median(bpms)
61+ else:
62+ print("not enough beats found in {:s}".format(path))
63+ return 0
64+
65+ return beats_to_bpm(beats, path)
5766
5867 if __name__ == '__main__':
59− import sys
60− for f in sys.argv[1:]:
61− bpm = get_file_bpm(f)
68+ import argparse
69+ parser = argparse.ArgumentParser()
70+ parser.add_argument('-m', '--mode',
71+ help="mode [default|fast|super-fast]",
72+ dest="mode", default='default')
73+ parser.add_argument('sources',
74+ nargs='+',
75+ help="input_files")
76+ args = parser.parse_args()
77+ for f in args.sources:
78+ bpm = get_file_bpm(f, params = args)
6279 print("{:6s} {:s}".format("{:2f}".format(bpm), f))
python/demos/demo_filter.py+31−14
@@ -1,36 +1,53 @@
11 #! /usr/bin/env python
22
3+import sys
4+import os.path
5+import aubio
36
4−def apply_filter(path):
5− from aubio import source, sink, digital_filter
6− from os.path import basename, splitext
77
8+def apply_filter(path, target):
89 # open input file, get its samplerate
9− s = source(path)
10+ s = aubio.source(path)
1011 samplerate = s.samplerate
1112
1213 # create an A-weighting filter
13− f = digital_filter(7)
14+ f = aubio.digital_filter(7)
1415 f.set_a_weighting(samplerate)
15− # alternatively, apply another filter
1616
1717 # create output file
18− o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate)
18+ o = aubio.sink(target, samplerate)
1919
2020 total_frames = 0
2121 while True:
22+ # read from source
2223 samples, read = s()
24+ # filter samples
2325 filtered_samples = f(samples)
26+ # write to sink
2427 o(filtered_samples, read)
28+ # count frames read
2529 total_frames += read
26− if read < s.hop_size: break
30+ # end of file reached
31+ if read < s.hop_size:
32+ break
2733
34+ # print some info
2835 duration = total_frames / float(samplerate)
29− print ("read {:s}".format(s.uri))
30− print ("applied A-weighting filtered ({:d} Hz)".format(samplerate))
31− print ("wrote {:s} ({:.2f} s)".format(o.uri, duration))
36+ input_str = "input: {:s} ({:.2f} s, {:d} Hz)"
37+ output_str = "output: {:s}, A-weighting filtered ({:d} frames total)"
38+ print(input_str.format(s.uri, duration, samplerate))
39+ print(output_str.format(o.uri, total_frames))
3240
3341 if __name__ == '__main__':
34− import sys
35− for f in sys.argv[1:]:
36− apply_filter(f)
42+ usage = "{:s} <input_file> [output_file]".format(sys.argv[0])
43+ if not 1 < len(sys.argv) < 4:
44+ print(usage)
45+ sys.exit(1)
46+ if len(sys.argv) < 3:
47+ input_path = sys.argv[1]
48+ basename = os.path.splitext(os.path.basename(input_path))[0] + ".wav"
49+ output_path = "filtered_" + basename
50+ else:
51+ input_path, output_path = sys.argv[1:]
52+ # run function
53+ apply_filter(input_path, output_path)
python/demos/demo_filterbank.py+31−17
@@ -1,30 +1,44 @@
11 #! /usr/bin/env python
22
3−from aubio import filterbank, fvec
4−from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
5−from numpy import vstack, arange
3+"""Create a filterbank from a list of frequencies.
64
7−win_s = 2048
5+This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
6+triangular filters from a list of frequencies.
7+
8+The filterbank coefficients are then modified before being displayed."""
9+
10+import aubio
11+import numpy as np
12+import matplotlib.pyplot as plt
13+
14+# sampling rate and size of the fft
815 samplerate = 48000
16+win_s = 2048
917
18+# define a list of custom frequency
1019 freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
20+# number of filters to create
1121 n_filters = len(freq_list) - 2
1222
13−f = filterbank(n_filters, win_s)
14−freqs = fvec(freq_list)
23+# create a new filterbank
24+f = aubio.filterbank(n_filters, win_s)
25+freqs = aubio.fvec(freq_list)
1526 f.set_triangle_bands(freqs, samplerate)
1627
28+# get the coefficients from the filterbank
1729 coeffs = f.get_coeffs()
18−coeffs[4] *= 5.
19−
30+# apply a gain to fifth band
31+coeffs[4] *= 6.
32+# load the modified coeffs into the filterbank
2033 f.set_coeffs(coeffs)
2134
22−times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
23−title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')
24−loglog(times.T, f.get_coeffs().T, '.-')
25−xlim([50, samplerate/2])
26−ylim([1.0e-6, 2.0e-2])
27−xlabel('log frequency (Hz)')
28−ylabel('log amplitude')
29−
30−show()
35+# display the band gains in a loglog plot
36+freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
37+plt.title('filterbank built from a list of frequencies\n'
38+ 'The 5th band has been amplified by a factor 6.')
39+plt.loglog(freqs.T, f.get_coeffs().T, '.-')
40+plt.xlim([50, samplerate/2])
41+plt.ylim([1.0e-6, 2.0e-2])
42+plt.xlabel('log frequency (Hz)')
43+plt.ylabel('log amplitude')
44+plt.show()
python/demos/demo_notes.py+37−0
@@ -0,0 +1,37 @@
1+#! /usr/bin/env python
2+
3+import sys
4+from aubio import source, notes
5+
6+if len(sys.argv) < 2:
7+ print("Usage: %s <filename> [samplerate]" % sys.argv[0])
8+ sys.exit(1)
9+
10+filename = sys.argv[1]
11+
12+downsample = 1
13+samplerate = 44100 // downsample
14+if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
15+
16+win_s = 512 // downsample # fft size
17+hop_s = 256 // downsample # hop size
18+
19+s = source(filename, samplerate, hop_s)
20+samplerate = s.samplerate
21+
22+tolerance = 0.8
23+
24+notes_o = notes("default", win_s, hop_s, samplerate)
25+
26+print("%8s" % "time","[ start","vel","last ]")
27+
28+# total number of frames read
29+total_frames = 0
30+while True:
31+ samples, read = s()
32+ new_note = notes_o(samples)
33+ if (new_note[0] != 0):
34+ note_str = ' '.join(["%.2f" % i for i in new_note])
35+ print("%.6f" % (total_frames/float(samplerate)), new_note)
36+ total_frames += read
37+ if read < hop_s: break
python/demos/demo_pitch_sinusoid.py+1−1
@@ -37,7 +37,7 @@ freqs[ pointer : pointer + partition ] = 1480 · 36 unmodified lines
3737
3838 pointer += partition
3939 pointer += partition
40−freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8)
40+freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8)
4141
4242 a = build_sinusoid(sin_length, freqs, samplerate)
4343
python/demos/demo_pyaudio.py+75−0
@@ -0,0 +1,75 @@
1+#! /usr/bin/env python
2+
3+# Use pyaudio to open the microphone and run aubio.pitch on the stream of
4+# incoming samples. If a filename is given as the first argument, it will
5+# record 5 seconds of audio to this location. Otherwise, the script will
6+# run until Ctrl+C is pressed.
7+
8+# Examples:
9+# $ ./python/demos/demo_pyaudio.py
10+# $ ./python/demos/demo_pyaudio.py /tmp/recording.wav
11+
12+import pyaudio
13+import sys
14+import numpy as np
15+import aubio
16+
17+# initialise pyaudio
18+p = pyaudio.PyAudio()
19+
20+# open stream
21+buffer_size = 1024
22+pyaudio_format = pyaudio.paFloat32
23+n_channels = 1
24+samplerate = 44100
25+stream = p.open(format=pyaudio_format,
26+ channels=n_channels,
27+ rate=samplerate,
28+ input=True,
29+ frames_per_buffer=buffer_size)
30+
31+if len(sys.argv) > 1:
32+ # record 5 seconds
33+ output_filename = sys.argv[1]
34+ record_duration = 5 # exit 1
35+ outputsink = aubio.sink(sys.argv[1], samplerate)
36+ total_frames = 0
37+else:
38+ # run forever
39+ outputsink = None
40+ record_duration = None
41+
42+# setup pitch
43+tolerance = 0.8
44+win_s = 4096 # fft size
45+hop_s = buffer_size # hop size
46+pitch_o = aubio.pitch("default", win_s, hop_s, samplerate)
47+pitch_o.set_unit("midi")
48+pitch_o.set_tolerance(tolerance)
49+
50+print("*** starting recording")
51+while True:
52+ try:
53+ audiobuffer = stream.read(buffer_size)
54+ signal = np.fromstring(audiobuffer, dtype=np.float32)
55+
56+ pitch = pitch_o(signal)[0]
57+ confidence = pitch_o.get_confidence()
58+
59+ print("{} / {}".format(pitch,confidence))
60+
61+ if outputsink:
62+ outputsink(signal, len(signal))
63+
64+ if record_duration:
65+ total_frames += len(signal)
66+ if record_duration * samplerate < total_frames:
67+ break
68+ except KeyboardInterrupt:
69+ print("*** Ctrl+C pressed, exiting")
70+ break
71+
72+print("*** done recording")
73+stream.stop_stream()
74+stream.close()
75+p.terminate()
python/demos/demo_source_simple.py+13−9
@@ -1,16 +1,20 @@
11 #! /usr/bin/env python
2−import sys, aubio
2+
3+"""A simple example using aubio.source."""
4+
5+import sys
6+import aubio
37
48 samplerate = 0 # use original source samplerate
5−hop_size = 256 # number of frames to read in one block
6−s = aubio.source(sys.argv[1], samplerate, hop_size)
9+hop_size = 256 # number of frames to read in one block
10+src = aubio.source(sys.argv[1], samplerate, hop_size)
711 total_frames = 0
812
9−while True: # reading loop
10− samples, read = s()
11− total_frames += read
12− if read < hop_size: break # end of file reached
13+while True:
14+ samples, read = src() # read hop_size new samples from source
15+ total_frames += read # increment total number of frames
16+ if read < hop_size: # end of file reached
17+ break
1318
1419 fmt_string = "read {:d} frames at {:d}Hz from {:s}"
15−print (fmt_string.format(total_frames, s.samplerate, sys.argv[1]))
16−
20+print(fmt_string.format(total_frames, src.samplerate, src.uri))
python/demos/demo_tapthebeat.py+78−0
@@ -0,0 +1,78 @@
1+#! /usr/bin/env python
2+
3+""" A simple demo using aubio and pyaudio to play beats in real time
4+
5+Note you will need to have pyaudio installed: `pip install pyaudio`.
6+
7+Examples:
8+ ./demo_tapthebeat.py ~/Music/track1.ogg
9+
10+When compiled with ffmpeg/libav, you should be able to open remote streams. For
11+instance using youtube-dl (`pip install youtube-dl`):
12+
13+ ./demo_tapthebeat.py `youtube-dl -xg https://youtu.be/zZbM9n9j3_g`
14+
15+"""
16+
17+import sys
18+import time
19+import pyaudio
20+import aubio
21+import numpy as np
22+
23+win_s = 1024 # fft size
24+hop_s = win_s // 2 # hop size
25+
26+# parse command line arguments
27+if len(sys.argv) < 2:
28+ print("Usage: %s <filename> [samplerate]" % sys.argv[0])
29+ sys.exit(1)
30+
31+filename = sys.argv[1]
32+
33+samplerate = 0
34+if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
35+
36+# create aubio source
37+a_source = aubio.source(filename, samplerate, hop_s)
38+samplerate = a_source.samplerate
39+
40+# create aubio tempo detection
41+a_tempo = aubio.tempo("default", win_s, hop_s, samplerate)
42+
43+# create a simple click sound
44+click = 0.7 * np.sin(2. * np.pi * np.arange(hop_s) / hop_s * samplerate / 3000.)
45+
46+# pyaudio callback
47+def pyaudio_callback(_in_data, _frame_count, _time_info, _status):
48+ samples, read = a_source()
49+ is_beat = a_tempo(samples)
50+ if is_beat:
51+ samples += click
52+ #print ('tick') # avoid print in audio callback
53+ audiobuf = samples.tobytes()
54+ if read < hop_s:
55+ return (audiobuf, pyaudio.paComplete)
56+ return (audiobuf, pyaudio.paContinue)
57+
58+# create pyaudio stream with frames_per_buffer=hop_s and format=paFloat32
59+p = pyaudio.PyAudio()
60+pyaudio_format = pyaudio.paFloat32
61+frames_per_buffer = hop_s
62+n_channels = 1
63+stream = p.open(format=pyaudio_format, channels=n_channels, rate=samplerate,
64+ output=True, frames_per_buffer=frames_per_buffer,
65+ stream_callback=pyaudio_callback)
66+
67+# start pyaudio stream
68+stream.start_stream()
69+
70+# wait for stream to finish
71+while stream.is_active():
72+ time.sleep(0.1)
73+
74+# stop pyaudio stream
75+stream.stop_stream()
76+stream.close()
77+# close pyaudio
78+p.terminate()
python/demos/demo_timestretch.py+1−1
@@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines
1212 import numpy as np
1313
1414 win_s = 1024
15−hop_s = win_s / 8 # 87.5 % overlap
15+hop_s = win_s // 8 # 87.5 % overlap
1616
1717 warmup = win_s // hop_s - 1
1818
python/demos/demo_timestretch_online.py+5−3
@@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines
1111 from aubio import unwrap2pi, float_type
1212 import numpy as np
1313
14−win_s = 1024
15−hop_s = win_s / 8 # 87.5 % overlap
14+win_s = 512
15+hop_s = win_s // 8 # 87.5 % overlap
1616
1717 warmup = win_s // hop_s - 1
1818
@@ -92,8 +92,10 @@ while True: · 73 unmodified lines
9292 old_grain.norm = np.copy(cur_grain.norm)
9393 old_grain.phas = np.copy(cur_grain.phas)
9494
95− block_read += 1
95+ # until end of file
9696 if read < hop_s: break
97+ # increment block counter
98+ block_read += 1
9799
98100 for t in range(warmup + 2): # purge the last frames from the phase vocoder
99101 new_grain.norm[:] = 0
python/demos/demo_wav2midi.py+77−0
@@ -0,0 +1,77 @@
1+#! /usr/bin/env python
2+
3+# Simple demo to extract notes from a sound file, and store them in a midi file
4+# using mido.
5+#
6+# Install mido: `pip instal mido`
7+#
8+# Documentation: https://mido.readthedocs.io/
9+
10+import sys
11+from aubio import source, notes
12+from mido import Message, MetaMessage, MidiFile, MidiTrack, second2tick, bpm2tempo
13+
14+if len(sys.argv) < 3:
15+ print("Usage: %s <filename> <output> [samplerate]" % sys.argv[0])
16+ sys.exit(1)
17+
18+filename = sys.argv[1]
19+midioutput = sys.argv[2]
20+
21+downsample = 1
22+samplerate = 44100 // downsample
23+if len( sys.argv ) > 3: samplerate = int(sys.argv[3])
24+
25+win_s = 512 // downsample # fft size
26+hop_s = 256 // downsample # hop size
27+
28+s = source(filename, samplerate, hop_s)
29+samplerate = s.samplerate
30+
31+tolerance = 0.8
32+
33+notes_o = notes("default", win_s, hop_s, samplerate)
34+
35+print("%8s" % "time","[ start","vel","last ]")
36+
37+# create a midi file
38+mid = MidiFile()
39+track = MidiTrack()
40+mid.tracks.append(track)
41+
42+ticks_per_beat = mid.ticks_per_beat # default: 480
43+bpm = 120 # default midi tempo
44+
45+tempo = bpm2tempo(bpm)
46+track.append(MetaMessage('set_tempo', tempo=tempo))
47+track.append(MetaMessage('time_signature', numerator=4, denominator=4))
48+
49+def frames2tick(frames, samplerate=samplerate):
50+ sec = frames / float(samplerate)
51+ return int(second2tick(sec, ticks_per_beat, tempo))
52+
53+last_time = 0
54+
55+# total number of frames read
56+total_frames = 0
57+while True:
58+ samples, read = s()
59+ new_note = notes_o(samples)
60+ if (new_note[0] != 0):
61+ note_str = ' '.join(["%.2f" % i for i in new_note])
62+ print("%.6f" % (total_frames/float(samplerate)), new_note)
63+ delta = frames2tick(total_frames) - last_time
64+ if new_note[2] > 0:
65+ track.append(Message('note_off', note=int(new_note[2]),
66+ velocity=127, time=0)
67+ )
68+ track.append(Message('note_on',
69+ note=int(new_note[0]),
70+ velocity=int(new_note[1]),
71+ time=delta)
72+ )
73+ last_time = frames2tick(total_frames)
74+ total_frames += read
75+ if read < hop_s: break
76+
77+mid.save(midioutput)
python/demos/demo_yin_compare.py+175−0
@@ -0,0 +1,175 @@
1+#! /usr/bin/env python
2+# -*- coding: utf8 -*-
3+
4+""" Pure python implementation of the sum of squared difference
5+
6+ sqd_yin: original sum of squared difference [0]
7+ d_t(tau) = x ⊗ kernel
8+ sqd_yinfast: sum of squared diff using complex domain [0]
9+ sqd_yinfftslow: tappered squared diff [1]
10+ sqd_yinfft: modified squared diff using complex domain [1]
11+
12+[0]:http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf
13+[1]:https://aubio.org/phd/
14+"""
15+
16+import sys
17+import numpy as np
18+import matplotlib.pyplot as plt
19+
20+def sqd_yin(samples):
21+ """ compute original sum of squared difference
22+
23+ Brute-force computation (cost o(N**2), slow)."""
24+ B = len(samples)
25+ W = B//2
26+ yin = np.zeros(W)
27+ for j in range(W):
28+ for tau in range(1, W):
29+ yin[tau] += (samples[j] - samples[j+tau])**2
30+ return yin
31+
32+def sqd_yinfast(samples):
33+ """ compute approximate sum of squared difference
34+
35+ Using complex convolution (fast, cost o(n*log(n)) )"""
36+ # yin_t(tau) = (r_t(0) + r_(t+tau)(0)) - 2r_t(tau)
37+ B = len(samples)
38+ W = B//2
39+ yin = np.zeros(W)
40+ sqdiff = np.zeros(W)
41+ kernel = np.zeros(B)
42+ # compute r_(t+tau)(0)
43+ squares = samples**2
44+ for tau in range(W):
45+ sqdiff[tau] = squares[tau:tau+W].sum()
46+ # add r_t(0)
47+ sqdiff += sqdiff[0]
48+ # compute r_t(tau) using kernel convolution in complex domain
49+ samples_fft = np.fft.fft(samples)
50+ kernel[1:W+1] = samples[W-1::-1] # first half, reversed
51+ kernel_fft = np.fft.fft(kernel)
52+ r_t_tau = np.fft.ifft(samples_fft * kernel_fft).real[W:]
53+ # compute yin_t(tau)
54+ yin = sqdiff - 2 * r_t_tau
55+ return yin
56+
57+def sqd_yintapered(samples):
58+ """ compute tappered sum of squared difference
59+
60+ Brute-force computation (cost o(N**2), slow)."""
61+ B = len(samples)
62+ W = B//2
63+ yin = np.zeros(W)
64+ for tau in range(1, W):
65+ for j in range(W - tau):
66+ yin[tau] += (samples[j] - samples[j+tau])**2
67+ return yin
68+
69+def sqd_yinfft(samples):
70+ """ compute yinfft modified sum of squared differences
71+
72+ Very fast, improved performance in transients.
73+
74+ FIXME: biased."""
75+ B = len(samples)
76+ W = B//2
77+ yin = np.zeros(W)
78+ def hanningz(W):
79+ return .5 * (1. - np.cos(2. * np.pi * np.arange(W) / W))
80+ #win = np.ones(B)
81+ win = hanningz(B)
82+ sqrmag = np.zeros(B)
83+ fftout = np.fft.fft(win*samples)
84+ sqrmag[0] = fftout[0].real**2
85+ for l in range(1, W):
86+ sqrmag[l] = fftout[l].real**2 + fftout[l].imag**2
87+ sqrmag[B-l] = sqrmag[l]
88+ sqrmag[W] = fftout[W].real**2
89+ fftout = np.fft.fft(sqrmag)
90+ sqrsum = 2.*sqrmag[:W + 1].sum()
91+ yin[0] = 0
92+ yin[1:] = sqrsum - fftout.real[1:W]
93+ return yin / B
94+
95+def cumdiff(yin):
96+ """ compute the cumulative mean normalized difference """
97+ W = len(yin)
98+ yin[0] = 1.
99+ cumsum = 0.
100+ for tau in range(1, W):
101+ cumsum += yin[tau]
102+ if cumsum != 0:
103+ yin[tau] *= tau/cumsum
104+ else:
105+ yin[tau] = 1
106+ return yin
107+
108+def compute_all(x):
109+ import time
110+ now = time.time()
111+
112+ yin = sqd_yin(x)
113+ t1 = time.time()
114+ print ("yin took %.2fms" % ((t1-now) * 1000.))
115+
116+ yinfast = sqd_yinfast(x)
117+ t2 = time.time()
118+ print ("yinfast took: %.2fms" % ((t2-t1) * 1000.))
119+
120+ yintapered = sqd_yintapered(x)
121+ t3 = time.time()
122+ print ("yintapered took: %.2fms" % ((t3-t2) * 1000.))
123+
124+ yinfft = sqd_yinfft(x)
125+ t4 = time.time()
126+ print ("yinfft took: %.2fms" % ((t4-t3) * 1000.))
127+
128+ return yin, yinfast, yintapered, yinfft
129+
130+def plot_all(yin, yinfast, yintapered, yinfft):
131+ fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey='col')
132+
133+ axes[0, 0].plot(yin, label='yin')
134+ axes[0, 0].plot(yintapered, label='yintapered')
135+ axes[0, 0].set_ylim(bottom=0)
136+ axes[0, 0].legend()
137+ axes[1, 0].plot(yinfast, '-', label='yinfast')
138+ axes[1, 0].plot(yinfft, label='yinfft')
139+ axes[1, 0].legend()
140+
141+ axes[0, 1].plot(cumdiff(yin), label='yin')
142+ axes[0, 1].plot(cumdiff(yintapered), label='yin tapered')
143+ axes[0, 1].set_ylim(bottom=0)
144+ axes[0, 1].legend()
145+ axes[1, 1].plot(cumdiff(yinfast), '-', label='yinfast')
146+ axes[1, 1].plot(cumdiff(yinfft), label='yinfft')
147+ axes[1, 1].legend()
148+
149+ fig.tight_layout()
150+
151+testfreqs = [441., 800., 10000., 40.]
152+
153+if len(sys.argv) > 1:
154+ testfreqs = map(float,sys.argv[1:])
155+
156+for f in testfreqs:
157+ print ("Comparing yin implementations for sine wave at %.fHz" % f)
158+ samplerate = 44100.
159+ win_s = 4096
160+
161+ x = np.cos(2.*np.pi * np.arange(win_s) * f / samplerate)
162+
163+ n_times = 1#00
164+ for n in range(n_times):
165+ yin, yinfast, yinfftslow, yinfft = compute_all(x)
166+ if 0: # plot difference
167+ plt.plot(yin-yinfast)
168+ plt.tight_layout()
169+ plt.show()
170+ if 1:
171+ plt.plot(yinfftslow-yinfft)
172+ plt.tight_layout()
173+ plt.show()
174+ plot_all(yin, yinfast, yinfftslow, yinfft)
175+plt.show()
python/ext/aubio-types.h+9−1
@@ -27,7 +27,7 @@ · 26 unmodified lines
2727 #ifdef USE_LOCAL_AUBIO
2828 #include "aubio.h"
2929 #else
30−#include "aubio/aubio.h"
30+#include <aubio/aubio.h>
3131 #endif
3232
3333 #define Py_default_vector_length 1024
@@ -46,6 +46,14 @@ · 12 unmodified lines
4646 #define AUBIO_NPY_SMPL_CHR "f"
4747 #endif
4848
49+#ifndef PATH_MAX
50+#ifdef MAX_PATH
51+#define PATH_MAX MAX_PATH
52+#else
53+#define PATH_MAX 1024
54+#endif
55+#endif
56+
4957 // compat with Python < 2.6
5058 #ifndef Py_TYPE
5159 #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
python/ext/aubiomodule.c+167−27
@@ -2,77 +2,202 @@ · 1 unmodified line
22 #include "aubio-types.h"
33 #include "py-musicutils.h"
44
5+// this dummy macro is used to convince windows that a string passed as -D flag
6+// is just that, a string, and not a double.
7+#define REDEFINESTRING(x) #x
8+#define DEFINEDSTRING(x) REDEFINESTRING(x)
9+
510 static char aubio_module_doc[] = "Python module for the aubio library";
611
712 static char Py_alpha_norm_doc[] = ""
8−"alpha_norm(fvec, integer) -> float\n"
13+"alpha_norm(vec, alpha)\n"
14+"\n"
15+"Compute `alpha` normalisation factor of vector `vec`.\n"
916 "\n"
10−"Compute alpha normalisation factor on vector, given alpha\n"
17+"Parameters\n"
18+"----------\n"
19+"vec : fvec\n"
20+" input vector\n"
21+"alpha : float\n"
22+" norm factor\n"
23+"\n"
24+"Returns\n"
25+"-------\n"
26+"float\n"
27+" p-norm of the input vector, where `p=alpha`\n"
1128 "\n"
1229 "Example\n"
1330 "-------\n"
1431 "\n"
15−">>> b = alpha_norm(a, 9)";
32+">>> a = aubio.fvec(np.arange(10)); alpha = 2\n"
33+">>> aubio.alpha_norm(a, alpha), (sum(a**alpha)/len(a))**(1./alpha)\n"
34+"(5.338539123535156, 5.338539126015656)\n"
35+"\n"
36+"Note\n"
37+"----\n"
38+"Computed as:\n"
39+"\n"
40+".. math::\n"
41+" l_{\\alpha} = \n"
42+" \\|\\frac{\\sum_{n=0}^{N-1}{{x_n}^{\\alpha}}}{N}\\|^{1/\\alpha}\n"
43+"";
1644
1745 static char Py_bintomidi_doc[] = ""
18−"bintomidi(float, samplerate = integer, fftsize = integer) -> float\n"
46+"bintomidi(fftbin, samplerate, fftsize)\n"
47+"\n"
48+"Convert FFT bin to frequency in midi note, given the sampling rate\n"
49+"and the size of the FFT.\n"
1950 "\n"
20−"Convert bin (float) to midi (float), given the sampling rate and the FFT size\n"
51+"Parameters\n"
52+"----------\n"
53+"fftbin : float\n"
54+" input frequency bin\n"
55+"samplerate : float\n"
56+" sampling rate of the signal\n"
57+"fftsize : float\n"
58+" size of the FFT\n"
59+"\n"
60+"Returns\n"
61+"-------\n"
62+"float\n"
63+" Frequency converted to midi note.\n"
2164 "\n"
2265 "Example\n"
2366 "-------\n"
2467 "\n"
25−">>> midi = bintomidi(float, samplerate = 44100, fftsize = 1024)";
68+">>> aubio.bintomidi(10, 44100, 1024)\n"
69+"68.62871551513672\n"
70+"";
2671
2772 static char Py_miditobin_doc[] = ""
28−"miditobin(float, samplerate = integer, fftsize = integer) -> float\n"
73+"miditobin(midi, samplerate, fftsize)\n"
2974 "\n"
30−"Convert midi (float) to bin (float), given the sampling rate and the FFT size\n"
75+"Convert frequency in midi note to FFT bin, given the sampling rate\n"
76+"and the size of the FFT.\n"
3177 "\n"
32−"Example\n"
78+"Parameters\n"
79+"----------\n"
80+"midi : float\n"
81+" input frequency, in midi note\n"
82+"samplerate : float\n"
83+" sampling rate of the signal\n"
84+"fftsize : float\n"
85+" size of the FFT\n"
86+"\n"
87+"Returns\n"
3388 "-------\n"
89+"float\n"
90+" Frequency converted to FFT bin.\n"
91+"\n"
92+"Examples\n"
93+"--------\n"
3494 "\n"
35−">>> bin = miditobin(midi, samplerate = 44100, fftsize = 1024)";
95+">>> aubio.miditobin(69, 44100, 1024)\n"
96+"10.216779708862305\n"
97+">>> aubio.miditobin(75.08, 32000, 512)\n"
98+"10.002175331115723\n"
99+"";
36100
37101 static char Py_bintofreq_doc[] = ""
38−"bintofreq(float, samplerate = integer, fftsize = integer) -> float\n"
102+"bintofreq(fftbin, samplerate, fftsize)\n"
103+"\n"
104+"Convert FFT bin to frequency in Hz, given the sampling rate\n"
105+"and the size of the FFT.\n"
39106 "\n"
40−"Convert bin number (float) in frequency (Hz), given the sampling rate and the FFT size\n"
107+"Parameters\n"
108+"----------\n"
109+"fftbin : float\n"
110+" input frequency bin\n"
111+"samplerate : float\n"
112+" sampling rate of the signal\n"
113+"fftsize : float\n"
114+" size of the FFT\n"
115+"\n"
116+"Returns\n"
117+"-------\n"
118+"float\n"
119+" Frequency converted to Hz.\n"
41120 "\n"
42121 "Example\n"
43122 "-------\n"
44123 "\n"
45−">>> freq = bintofreq(bin, samplerate = 44100, fftsize = 1024)";
124+">>> aubio.bintofreq(10, 44100, 1024)\n"
125+"430.6640625\n"
126+"";
46127
47128 static char Py_freqtobin_doc[] = ""
48−"freqtobin(float, samplerate = integer, fftsize = integer) -> float\n"
129+"freqtobin(freq, samplerate, fftsize)\n"
49130 "\n"
50−"Convert frequency (Hz) in bin number (float), given the sampling rate and the FFT size\n"
131+"Convert frequency in Hz to FFT bin, given the sampling rate\n"
132+"and the size of the FFT.\n"
51133 "\n"
52−"Example\n"
134+"Parameters\n"
135+"----------\n"
136+"midi : float\n"
137+" input frequency, in midi note\n"
138+"samplerate : float\n"
139+" sampling rate of the signal\n"
140+"fftsize : float\n"
141+" size of the FFT\n"
142+"\n"
143+"Returns\n"
53144 "-------\n"
145+"float\n"
146+" Frequency converted to FFT bin.\n"
147+"\n"
148+"Examples\n"
149+"--------\n"
54150 "\n"
55−">>> bin = freqtobin(freq, samplerate = 44100, fftsize = 1024)";
151+">>> aubio.freqtobin(440, 44100, 1024)\n"
152+"10.216779708862305\n"
153+"";
56154
57155 static char Py_zero_crossing_rate_doc[] = ""
58−"zero_crossing_rate(fvec) -> float\n"
156+"zero_crossing_rate(vec)\n"
157+"\n"
158+"Compute zero-crossing rate of `vec`.\n"
59159 "\n"
60−"Compute Zero crossing rate of a vector\n"
160+"Parameters\n"
161+"----------\n"
162+"vec : fvec\n"
163+" input vector\n"
164+"\n"
165+"Returns\n"
166+"-------\n"
167+"float\n"
168+" Zero-crossing rate.\n"
61169 "\n"
62170 "Example\n"
63171 "-------\n"
64172 "\n"
65−">>> z = zero_crossing_rate(a)";
173+">>> a = np.linspace(-1., 1., 1000, dtype=aubio.float_type)\n"
174+">>> aubio.zero_crossing_rate(a), 1/1000\n"
175+"(0.0010000000474974513, 0.001)\n"
176+"";
66177
67178 static char Py_min_removal_doc[] = ""
68−"min_removal(fvec) -> float\n"
179+"min_removal(vec)\n"
180+"\n"
181+"Remove the minimum value of a vector to each of its element.\n"
69182 "\n"
70−"Remove the minimum value of a vector, in-place modification\n"
183+"Modifies the input vector in-place and returns a reference to it.\n"
184+"\n"
185+"Parameters\n"
186+"----------\n"
187+"vec : fvec\n"
188+" input vector\n"
189+"\n"
190+"Returns\n"
191+"-------\n"
192+"fvec\n"
193+" modified input vector\n"
71194 "\n"
72195 "Example\n"
73196 "-------\n"
74197 "\n"
75−">>> min_removal(a)";
198+">>> aubio.min_removal(aubio.fvec(np.arange(1,4)))\n"
199+"array([0., 1., 2.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
200+"";
76201
77202 extern void add_ufuncs ( PyObject *m );
78203 extern int generated_types_ready(void);
@@ -112,7 +237,9 @@ Py_bintomidi (PyObject * self, PyObject * args) · 33 unmodified lines
112237 smpl_t input, samplerate, fftsize;
113238 smpl_t output;
114239
115− if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) {
240+ if (!PyArg_ParseTuple (args,
241+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
242+ &input, &samplerate, &fftsize)) {
116243 return NULL;
117244 }
118245
@@ -127,7 +254,9 @@ Py_miditobin (PyObject * self, PyObject * args) · 8 unmodified lines
127254 smpl_t input, samplerate, fftsize;
128255 smpl_t output;
129256
130− if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR , &input, &samplerate, &fftsize)) {
257+ if (!PyArg_ParseTuple (args,
258+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
259+ &input, &samplerate, &fftsize)) {
131260 return NULL;
132261 }
133262
@@ -142,7 +271,9 @@ Py_bintofreq (PyObject * self, PyObject * args) · 8 unmodified lines
142271 smpl_t input, samplerate, fftsize;
143272 smpl_t output;
144273
145− if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) {
274+ if (!PyArg_ParseTuple (args,
275+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
276+ &input, &samplerate, &fftsize)) {
146277 return NULL;
147278 }
148279
@@ -157,7 +288,9 @@ Py_freqtobin (PyObject * self, PyObject * args) · 8 unmodified lines
157288 smpl_t input, samplerate, fftsize;
158289 smpl_t output;
159290
160− if (!PyArg_ParseTuple (args, "|" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR, &input, &samplerate, &fftsize)) {
291+ if (!PyArg_ParseTuple (args,
292+ "" AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR,
293+ &input, &samplerate, &fftsize)) {
161294 return NULL;
162295 }
163296
@@ -237,6 +370,12 @@ static PyMethodDef aubio_methods[] = { · 73 unmodified lines
237370 {"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc},
238371 {"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc},
239372 {"window", Py_aubio_window, METH_VARARGS, Py_aubio_window_doc},
373+ {"shift", Py_aubio_shift, METH_VARARGS, Py_aubio_shift_doc},
374+ {"ishift", Py_aubio_ishift, METH_VARARGS, Py_aubio_ishift_doc},
375+ {"hztomel", Py_aubio_hztomel, METH_VARARGS|METH_KEYWORDS, Py_aubio_hztomel_doc},
376+ {"meltohz", Py_aubio_meltohz, METH_VARARGS|METH_KEYWORDS, Py_aubio_meltohz_doc},
377+ {"hztomel_htk", Py_aubio_hztomel_htk, METH_VARARGS, Py_aubio_hztomel_htk_doc},
378+ {"meltohz_htk", Py_aubio_meltohz_htk, METH_VARARGS, Py_aubio_meltohz_htk_doc},
240379 {NULL, NULL, 0, NULL} /* Sentinel */
241380 };
242381
@@ -323,6 +462,7 @@ initaubio (void) · 80 unmodified lines
323462 PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType);
324463
325464 PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR);
465+ PyModule_AddStringConstant(m, "__version__", DEFINEDSTRING(AUBIO_VERSION));
326466
327467 // add generated objects
328468 add_generated_objects(m);
python/ext/py-cvec.c+42−12
@@ -19,7 +19,37 @@ typedef struct · 18 unmodified lines
1919 uint_t length;
2020 } Py_cvec;
2121
22−static char Py_cvec_doc[] = "cvec object";
22+static char Py_cvec_doc[] = ""
23+"cvec(size)\n"
24+"\n"
25+"A container holding spectral data.\n"
26+"\n"
27+"Create one `cvec` to store the spectral information of a window\n"
28+"of `size` points. The data will be stored in two vectors,\n"
29+":attr:`phas` and :attr:`norm`, each of shape (:attr:`length`,),\n"
30+"with `length = size // 2 + 1`.\n"
31+"\n"
32+"Parameters\n"
33+"----------\n"
34+"size: int\n"
35+" Size of spectrum to create.\n"
36+"\n"
37+"Examples\n"
38+"--------\n"
39+">>> c = aubio.cvec(1024)\n"
40+">>> c\n"
41+"aubio cvec of 513 elements\n"
42+">>> c.length\n"
43+"513\n"
44+">>> c.norm.dtype, c.phas.dtype\n"
45+"(dtype('float32'), dtype('float32'))\n"
46+">>> c.norm.shape, c.phas.shape\n"
47+"((513,), (513,))\n"
48+"\n"
49+"See Also\n"
50+"--------\n"
51+"fvec, fft, pvoc\n"
52+"";
2353
2454
2555 PyObject *
@@ -142,14 +172,14 @@ Py_cvec_set_norm (Py_cvec * vec, PyObject *input, void * closure) · 116 unmodified lines
142172 {
143173 npy_intp length;
144174 if (!PyAubio_IsValidVector(input)) {
145− return 1;
175+ return -1;
146176 }
147177 length = PyArray_SIZE ((PyArrayObject *)input);
148178 if (length != vec->length) {
149179 PyErr_Format (PyExc_ValueError,
150− "input array has length %ld, but cvec has length %d", length,
180+ "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length,
151181 vec->length);
152− return 1;
182+ return -1;
153183 }
154184
155185 Py_XDECREF(vec->norm);
@@ -163,14 +193,14 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 7 unmodified lines
163193 {
164194 npy_intp length;
165195 if (!PyAubio_IsValidVector(input)) {
166− return 1;
196+ return -1;
167197 }
168198 length = PyArray_SIZE ((PyArrayObject *)input);
169199 if (length != vec->length) {
170200 PyErr_Format (PyExc_ValueError,
171− "input array has length %ld, but cvec has length %d", length,
201+ "input array has length %" NPY_INTP_FMT ", but cvec has length %d", length,
172202 vec->length);
173− return 1;
203+ return -1;
174204 }
175205
176206 Py_XDECREF(vec->phas);
@@ -182,7 +212,7 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 5 unmodified lines
182212 static PyMemberDef Py_cvec_members[] = {
183213 // TODO remove READONLY flag and define getter/setter
184214 {"length", T_INT, offsetof (Py_cvec, length), READONLY,
185− "length attribute"},
215+ "int: Length of `norm` and `phas` vectors."},
186216 {NULL} /* Sentinel */
187217 };
188218
@@ -191,11 +221,11 @@ static PyMethodDef Py_cvec_methods[] = { · 2 unmodified lines
191221 };
192222
193223 static PyGetSetDef Py_cvec_getseters[] = {
194− {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm,
195− "Numpy vector of shape (length,) containing the magnitude",
224+ {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm,
225+ "numpy.ndarray: Vector of shape `(length,)` containing the magnitude.",
196226 NULL},
197− {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas,
198− "Numpy vector of shape (length,) containing the phase",
227+ {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas,
228+ "numpy.ndarray: Vector of shape `(length,)` containing the phase.",
199229 NULL},
200230 {NULL} /* sentinel */
201231 };
python/ext/py-filterbank.c+133−9
@@ -94,7 +94,7 @@ Py_filterbank_do(Py_filterbank * self, PyObject * args) · 93 unmodified lines
9494
9595 if (self->vec.length != self->win_s / 2 + 1) {
9696 PyErr_Format(PyExc_ValueError,
97− "input cvec has length %d, but fft expects length %d",
97+ "input cvec has length %d, but filterbank expects length %d",
9898 self->vec.length, self->win_s / 2 + 1);
9999 return NULL;
100100 }
@@ -122,8 +122,8 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 21 unmodified lines
122122 uint_t err = 0;
123123
124124 PyObject *input;
125− uint_t samplerate;
126− if (!PyArg_ParseTuple (args, "OI", &input, &samplerate)) {
125+ smpl_t samplerate;
126+ if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR, &input, &samplerate)) {
127127 return NULL;
128128 }
129129
@@ -138,8 +138,14 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 8 unmodified lines
138138 err = aubio_filterbank_set_triangle_bands (self->o,
139139 &(self->freqs), samplerate);
140140 if (err > 0) {
141− PyErr_SetString (PyExc_ValueError,
142− "error when setting filter to A-weighting");
141+ if (PyErr_Occurred() == NULL) {
142+ PyErr_SetString (PyExc_ValueError, "error running set_triangle_bands");
143+ } else {
144+ // change the RuntimeError into ValueError
145+ PyObject *type, *value, *traceback;
146+ PyErr_Fetch(&type, &value, &traceback);
147+ PyErr_Restore(PyExc_ValueError, value, traceback);
148+ }
143149 return NULL;
144150 }
145151 Py_RETURN_NONE;
@@ -150,15 +156,79 @@ Py_filterbank_set_mel_coeffs_slaney (Py_filterbank * self, PyObject *args) · 4 unmodified lines
150156 {
151157 uint_t err = 0;
152158
153− uint_t samplerate;
154− if (!PyArg_ParseTuple (args, "I", &samplerate)) {
159+ smpl_t samplerate;
160+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &samplerate)) {
155161 return NULL;
156162 }
157163
158164 err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);
159165 if (err > 0) {
160− PyErr_SetString (PyExc_ValueError,
161− "error when setting filter to A-weighting");
166+ if (PyErr_Occurred() == NULL) {
167+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_slaney");
168+ } else {
169+ // change the RuntimeError into ValueError
170+ PyObject *type, *value, *traceback;
171+ PyErr_Fetch(&type, &value, &traceback);
172+ PyErr_Restore(PyExc_ValueError, value, traceback);
173+ }
174+ return NULL;
175+ }
176+ Py_RETURN_NONE;
177+}
178+
179+static PyObject *
180+Py_filterbank_set_mel_coeffs (Py_filterbank * self, PyObject *args)
181+{
182+ uint_t err = 0;
183+
184+ smpl_t samplerate;
185+ smpl_t freq_min;
186+ smpl_t freq_max;
187+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
188+ AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
189+ return NULL;
190+ }
191+
192+ err = aubio_filterbank_set_mel_coeffs (self->o, samplerate,
193+ freq_min, freq_max);
194+ if (err > 0) {
195+ if (PyErr_Occurred() == NULL) {
196+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs");
197+ } else {
198+ // change the RuntimeError into ValueError
199+ PyObject *type, *value, *traceback;
200+ PyErr_Fetch(&type, &value, &traceback);
201+ PyErr_Restore(PyExc_ValueError, value, traceback);
202+ }
203+ return NULL;
204+ }
205+ Py_RETURN_NONE;
206+}
207+
208+static PyObject *
209+Py_filterbank_set_mel_coeffs_htk (Py_filterbank * self, PyObject *args)
210+{
211+ uint_t err = 0;
212+
213+ smpl_t samplerate;
214+ smpl_t freq_min;
215+ smpl_t freq_max;
216+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR AUBIO_NPY_SMPL_CHR
217+ AUBIO_NPY_SMPL_CHR, &samplerate, &freq_min, &freq_max)) {
218+ return NULL;
219+ }
220+
221+ err = aubio_filterbank_set_mel_coeffs_htk (self->o, samplerate,
222+ freq_min, freq_max);
223+ if (err > 0) {
224+ if (PyErr_Occurred() == NULL) {
225+ PyErr_SetString (PyExc_ValueError, "error running set_mel_coeffs_htk");
226+ } else {
227+ // change the RuntimeError into ValueError
228+ PyObject *type, *value, *traceback;
229+ PyErr_Fetch(&type, &value, &traceback);
230+ PyErr_Restore(PyExc_ValueError, value, traceback);
231+ }
162232 return NULL;
163233 }
164234 Py_RETURN_NONE;
@@ -195,15 +265,69 @@ Py_filterbank_get_coeffs (Py_filterbank * self, PyObject *unused) · 30 unmodified lines
195265 aubio_filterbank_get_coeffs (self->o) );
196266 }
197267
268+static PyObject *
269+Py_filterbank_set_power(Py_filterbank *self, PyObject *args)
270+{
271+ smpl_t power;
272+
273+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &power)) {
274+ return NULL;
275+ }
276+ if(aubio_filterbank_set_power (self->o, power)) {
277+ if (PyErr_Occurred() == NULL) {
278+ PyErr_SetString (PyExc_ValueError,
279+ "error running filterbank.set_power");
280+ } else {
281+ // change the RuntimeError into ValueError
282+ PyObject *type, *value, *traceback;
283+ PyErr_Fetch(&type, &value, &traceback);
284+ PyErr_Restore(PyExc_ValueError, value, traceback);
285+ }
286+ return NULL;
287+ }
288+ Py_RETURN_NONE;
289+}
290+
291+static PyObject *
292+Py_filterbank_set_norm(Py_filterbank *self, PyObject *args)
293+{
294+ smpl_t norm;
295+
296+ if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &norm)) {
297+ return NULL;
298+ }
299+ if(aubio_filterbank_set_norm (self->o, norm)) {
300+ if (PyErr_Occurred() == NULL) {
301+ PyErr_SetString (PyExc_ValueError,
302+ "error running filterbank.set_power");
303+ } else {
304+ // change the RuntimeError into ValueError
305+ PyObject *type, *value, *traceback;
306+ PyErr_Fetch(&type, &value, &traceback);
307+ PyErr_Restore(PyExc_ValueError, value, traceback);
308+ }
309+ return NULL;
310+ }
311+ Py_RETURN_NONE;
312+}
313+
198314 static PyMethodDef Py_filterbank_methods[] = {
199315 {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands,
200316 METH_VARARGS, "set coefficients of filterbanks"},
201317 {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,
202318 METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"},
319+ {"set_mel_coeffs", (PyCFunction) Py_filterbank_set_mel_coeffs,
320+ METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
321+ {"set_mel_coeffs_htk", (PyCFunction) Py_filterbank_set_mel_coeffs_htk,
322+ METH_VARARGS, "set coefficients of filterbank to linearly spaced mel scale"},
203323 {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,
204324 METH_NOARGS, "get coefficients of filterbank"},
205325 {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,
206326 METH_VARARGS, "set coefficients of filterbank"},
327+ {"set_power", (PyCFunction) Py_filterbank_set_power,
328+ METH_VARARGS, "set power applied to filterbank input spectrum"},
329+ {"set_norm", (PyCFunction) Py_filterbank_set_norm,
330+ METH_VARARGS, "set norm applied to filterbank input spectrum"},
207331 {NULL}
208332 };
209333
python/ext/py-musicutils.c+102−0
@@ -133,3 +133,105 @@ Py_aubio_level_detection(PyObject *self, PyObject *args) · 132 unmodified lines
133133
134134 return level_detection;
135135 }
136+
137+PyObject *
138+Py_aubio_shift(PyObject *self, PyObject *args)
139+{
140+ PyObject *input;
141+ fvec_t vec;
142+
143+ if (!PyArg_ParseTuple (args, "O:shift", &input)) {
144+ return NULL;
145+ }
146+
147+ if (input == NULL) {
148+ return NULL;
149+ }
150+
151+ if (!PyAubio_ArrayToCFvec(input, &vec)) {
152+ return NULL;
153+ }
154+
155+ fvec_shift(&vec);
156+
157+ //Py_RETURN_NONE;
158+ return (PyObject *) PyAubio_CFvecToArray(&vec);
159+}
160+
161+PyObject *
162+Py_aubio_ishift(PyObject *self, PyObject *args)
163+{
164+ PyObject *input;
165+ fvec_t vec;
166+
167+ if (!PyArg_ParseTuple (args, "O:shift", &input)) {
168+ return NULL;
169+ }
170+
171+ if (input == NULL) {
172+ return NULL;
173+ }
174+
175+ if (!PyAubio_ArrayToCFvec(input, &vec)) {
176+ return NULL;
177+ }
178+
179+ fvec_ishift(&vec);
180+
181+ //Py_RETURN_NONE;
182+ return (PyObject *) PyAubio_CFvecToArray(&vec);
183+}
184+
185+PyObject*
186+Py_aubio_hztomel(PyObject *self, PyObject *args, PyObject *kwds)
187+{
188+ smpl_t v;
189+ PyObject *htk = NULL;
190+ static char *kwlist[] = {"f", "htk", NULL};
191+ if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
192+ kwlist, &v, &htk))
193+ {
194+ return NULL;
195+ }
196+ if (htk != NULL && PyObject_IsTrue(htk) == 1)
197+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v));
198+ else
199+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel(v));
200+}
201+
202+PyObject*
203+Py_aubio_meltohz(PyObject *self, PyObject *args, PyObject *kwds)
204+{
205+ smpl_t v;
206+ PyObject *htk = NULL;
207+ static char *kwlist[] = {"m", "htk", NULL};
208+ if (!PyArg_ParseTupleAndKeywords(args, kwds, AUBIO_NPY_SMPL_CHR "|O",
209+ kwlist, &v, &htk))
210+ {
211+ return NULL;
212+ }
213+ if (htk != NULL && PyObject_IsTrue(htk) == 1)
214+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v));
215+ else
216+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz(v));
217+}
218+
219+PyObject*
220+Py_aubio_hztomel_htk(PyObject *self, PyObject *args)
221+{
222+ smpl_t v;
223+ if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
224+ return NULL;
225+ }
226+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_hztomel_htk(v));
227+}
228+
229+PyObject*
230+Py_aubio_meltohz_htk(PyObject *self, PyObject *args)
231+{
232+ smpl_t v;
233+ if (!PyArg_ParseTuple(args, AUBIO_NPY_SMPL_CHR, &v)) {
234+ return NULL;
235+ }
236+ return Py_BuildValue(AUBIO_NPY_SMPL_CHR, aubio_meltohz_htk(v));
237+}
python/ext/py-musicutils.h+322−25
@@ -2,73 +2,370 @@ · 1 unmodified line
22 #define PY_AUBIO_MUSICUTILS_H
33
44 static char Py_aubio_window_doc[] = ""
5−"window(string, integer) -> fvec\n"
5+"window(window_type, size)\n"
66 "\n"
7−"Create a window\n"
7+"Create a window of length `size`. `window_type` should be one\n"
8+"of the following:\n"
89 "\n"
9−"Example\n"
10+"- `default` (same as `hanningz`).\n"
11+"- `ones`\n"
12+"- `rectangle`\n"
13+"- `hamming`\n"
14+"- `hanning`\n"
15+"- `hanningz` [1]_\n"
16+"- `blackman`\n"
17+"- `blackman_harris`\n"
18+"- `gaussian`\n"
19+"- `welch`\n"
20+"- `parzen`\n"
21+"\n"
22+"Parameters\n"
23+"----------\n"
24+"window_type : str\n"
25+" Type of window.\n"
26+"size : int\n"
27+" Length of window.\n"
28+"\n"
29+"Returns\n"
1030 "-------\n"
31+"fvec\n"
32+" Array of shape `(length,)` containing the new window.\n"
33+"\n"
34+"See Also\n"
35+"--------\n"
36+"pvoc, fft\n"
37+"\n"
38+"Examples\n"
39+"--------\n"
40+"Compute a zero-phase Hann window on `1024` points:\n"
1141 "\n"
12−">>> window('hanningz', 1024)\n"
42+">>> aubio.window('hanningz', 1024)\n"
1343 "array([ 0.00000000e+00, 9.41753387e-06, 3.76403332e-05, ...,\n"
14−" 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)";
44+" 8.46982002e-05, 3.76403332e-05, 9.41753387e-06], dtype=float32)\n"
45+"\n"
46+"Plot different window types with `matplotlib <https://matplotlib.org/>`_:\n"
47+"\n"
48+">>> import matplotlib.pyplot as plt\n"
49+">>> modes = ['default', 'ones', 'rectangle', 'hamming', 'hanning',\n"
50+"... 'hanningz', 'blackman', 'blackman_harris', 'gaussian',\n"
51+"... 'welch', 'parzen']; n = 2048\n"
52+">>> for m in modes: plt.plot(aubio.window(m, n), label=m)\n"
53+"...\n"
54+">>> plt.legend(); plt.show()\n"
55+"\n"
56+"Note\n"
57+"----\n"
58+"The following examples contain the equivalent source code to compute\n"
59+"each type of window with `NumPy <https://numpy.org>`_:\n"
60+"\n"
61+">>> n = 1024; x = np.arange(n, dtype=aubio.float_type)\n"
62+">>> ones = np.ones(n).astype(aubio.float_type)\n"
63+">>> rectangle = 0.5 * ones\n"
64+">>> hanning = 0.5 - 0.5 * np.cos(2 * np.pi * x / n)\n"
65+">>> hanningz = 0.5 * (1 - np.cos(2 * np.pi * x / n))\n"
66+">>> hamming = 0.54 - 0.46 * np.cos(2.*np.pi * x / (n - 1))\n"
67+">>> blackman = 0.42 \\\n"
68+"... - 0.50 * np.cos(2 * np.pi * x / (n - 1)) \\\n"
69+"... + 0.08 * np.cos(4 * np.pi * x / (n - 1))\n"
70+">>> blackman_harris = 0.35875 \\\n"
71+"... - 0.48829 * np.cos(2 * np.pi * x / (n - 1)) \\\n"
72+"... + 0.14128 * np.cos(4 * np.pi * x / (n - 1)) \\\n"
73+"... + 0.01168 * np.cos(6 * np.pi * x / (n - 1))\n"
74+">>> gaussian = np.exp( - 0.5 * ((x - 0.5 * (n - 1)) \\\n"
75+"... / (0.25 * (n - 1)) )**2 )\n"
76+">>> welch = 1 - ((2 * x - n) / (n + 1))**2\n"
77+">>> parzen = 1 - np.abs((2 * x - n) / (n + 1))\n"
78+">>> default = hanningz\n"
79+"References\n"
80+"----------\n"
81+#if 0
82+"`Window function <https://en.wikipedia.org/wiki/Window_function>`_ on\n"
83+"Wikipedia.\n"
84+"\n"
85+#endif
86+".. [1] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n"
87+" (?) implementations of a phase vocoder: the tricks of the trade.\n"
88+" In *Proceedings of the International Conference on Digital Audio\n"
89+" Effects* (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n"
90+" (`online version <"
91+"https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf"
92+">`_).\n"
93+"";
1594
1695 PyObject * Py_aubio_window(PyObject *self, PyObject *args);
1796
1897 static char Py_aubio_level_lin_doc[] = ""
19−"level_lin(fvec) -> fvec\n"
98+"level_lin(x)\n"
2099 "\n"
21−"Compute sound level on a linear scale.\n"
100+"Compute sound pressure level of `x`, on a linear scale.\n"
22101 "\n"
23−"This gives the average of the square amplitudes.\n"
102+"Parameters\n"
103+"----------\n"
104+"x : fvec\n"
105+" input vector\n"
106+"\n"
107+"Returns\n"
108+"-------\n"
109+"float\n"
110+" Linear level of `x`.\n"
24111 "\n"
25112 "Example\n"
26113 "-------\n"
27114 "\n"
28−">>> level_Lin(numpy.ones(1024))\n"
29−"1.0";
115+">>> aubio.level_lin(aubio.fvec(numpy.ones(1024)))\n"
116+"1.0\n"
117+"\n"
118+"Note\n"
119+"----\n"
120+"Computed as the average of the squared amplitudes:\n"
121+"\n"
122+".. math:: L = \\frac {\\sum_{n=0}^{N-1} {x_n}^2} {N}\n"
123+"\n"
124+"See Also\n"
125+"--------\n"
126+"db_spl, silence_detection, level_detection\n"
127+"";
30128
31129 PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args);
32130
33131 static char Py_aubio_db_spl_doc[] = ""
34−"Compute sound pressure level (SPL) in dB\n"
132+"db_spl(x)\n"
35133 "\n"
36−"This quantity is often wrongly called 'loudness'.\n"
134+"Compute Sound Pressure Level (SPL) of `x`, in dB.\n"
37135 "\n"
38−"This gives ten times the log10 of the average of the square amplitudes.\n"
136+"Parameters\n"
137+"----------\n"
138+"x : fvec\n"
139+" input vector\n"
140+"\n"
141+"Returns\n"
142+"-------\n"
143+"float\n"
144+" Level of `x`, in dB SPL.\n"
39145 "\n"
40146 "Example\n"
41147 "-------\n"
42148 "\n"
43−">>> db_spl(numpy.ones(1024))\n"
44−"1.0";
149+">>> aubio.db_spl(aubio.fvec(np.ones(1024)))\n"
150+"1.0\n"
151+">>> aubio.db_spl(0.7*aubio.fvec(np.ones(32)))\n"
152+"-3.098040819168091\n"
153+"\n"
154+"Note\n"
155+"----\n"
156+"Computed as `log10` of :py:func:`level_lin`:\n"
157+"\n"
158+".. math::\n"
159+"\n"
160+" {SPL}_{dB} = log10{\\frac {\\sum_{n=0}^{N-1}{x_n}^2} {N}}\n"
161+"\n"
162+"This quantity is often incorrectly called 'loudness'.\n"
163+"\n"
164+"See Also\n"
165+"--------\n"
166+"level_lin, silence_detection, level_detection\n"
167+"";
45168
46169 PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args);
47170
48171 static char Py_aubio_silence_detection_doc[] = ""
49−"Check if buffer level in dB SPL is under a given threshold\n"
172+"silence_detection(vec, level)\n"
50173 "\n"
51−"Return 0 if level is under the given threshold, 1 otherwise.\n"
174+"Check if level of `vec`, in dB SPL, is under a given threshold.\n"
52175 "\n"
53−"Example\n"
176+"Parameters\n"
177+"----------\n"
178+"vec : fvec\n"
179+" input vector\n"
180+"level : float\n"
181+" level threshold, in dB SPL\n"
182+"\n"
183+"Returns\n"
54184 "-------\n"
185+"int\n"
186+" `1` if level of `vec`, in dB SPL, is under `level`,\n"
187+" `0` otherwise.\n"
55188 "\n"
56−">>> import numpy\n"""
57−">>> silence_detection(numpy.ones(1024, dtype=\"float32\"), -80)\n"
58−"0";
189+"Examples\n"
190+"--------\n"
191+"\n"
192+">>> aubio.silence_detection(aubio.fvec(32), -100.)\n"
193+"1\n"
194+">>> aubio.silence_detection(aubio.fvec(np.ones(32)), 0.)\n"
195+"0\n"
196+"\n"
197+"See Also\n"
198+"--------\n"
199+"level_detection, db_spl, level_lin\n"
200+"";
59201
60202 PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args);
61203
62204 static char Py_aubio_level_detection_doc[] = ""
63−"Get buffer level in dB SPL if over a given threshold, 1. otherwise.\n"
205+"level_detection(vec, level)\n"
206+"\n"
207+"Check if `vec` is above threshold `level`, in dB SPL.\n"
208+"\n"
209+"Parameters\n"
210+"----------\n"
211+"vec : fvec\n"
212+" input vector\n"
213+"level : float\n"
214+" level threshold, in dB SPL\n"
215+"\n"
216+"Returns\n"
217+"-------\n"
218+"float\n"
219+" `1.0` if level of `vec` in dB SPL is under `level`,\n"
220+" `db_spl(vec)` otherwise.\n"
64221 "\n"
65222 "Example\n"
66223 "-------\n"
67224 "\n"
68−">>> import numpy\n"""
69−">>> level_detection(0.7*numpy.ones(1024, dtype=\"float32\"), -80)\n"
70−"0";
225+">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -3.)\n"
226+"1.0\n"
227+">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -4.)\n"
228+"-3.0980708599090576\n"
229+"\n"
230+"See Also\n"
231+"--------\n"
232+"silence_detection, db_spl, level_lin\n"
233+"";
71234
72235 PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args);
73236
237+static char Py_aubio_shift_doc[] = ""
238+"shift(vec)\n"
239+"\n"
240+"Swap left and right partitions of a vector, in-place.\n"
241+"\n"
242+"Parameters\n"
243+"----------\n"
244+"vec : fvec\n"
245+" input vector to shift\n"
246+"\n"
247+"Returns\n"
248+"-------\n"
249+"fvec\n"
250+" The swapped vector.\n"
251+"\n"
252+"Notes\n"
253+"-----\n"
254+"The input vector is also modified.\n"
255+"\n"
256+"For a vector of length N, the partition is split at index N - N//2.\n"
257+"\n"
258+"Example\n"
259+"-------\n"
260+"\n"
261+">>> aubio.shift(aubio.fvec(np.arange(3)))\n"
262+"array([2., 0., 1.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
263+"\n"
264+"See Also\n"
265+"--------\n"
266+"ishift\n"
267+"";
268+PyObject * Py_aubio_shift(PyObject *self, PyObject *args);
269+
270+static char Py_aubio_ishift_doc[] = ""
271+"ishift(vec)\n"
272+"\n"
273+"Swap right and left partitions of a vector, in-place.\n"
274+"\n"
275+"Parameters\n"
276+"----------\n"
277+"vec : fvec\n"
278+" input vector to shift\n"
279+"\n"
280+"Returns\n"
281+"-------\n"
282+"fvec\n"
283+" The swapped vector.\n"
284+"\n"
285+"Notes\n"
286+"-----\n"
287+"The input vector is also modified.\n"
288+"\n"
289+"Unlike with :py:func:`shift`, the partition is split at index N//2.\n"
290+"\n"
291+"Example\n"
292+"-------\n"
293+"\n"
294+">>> aubio.ishift(aubio.fvec(np.arange(3)))\n"
295+"array([1., 2., 0.], dtype=" AUBIO_NPY_SMPL_STR ")\n"
296+"\n"
297+"See Also\n"
298+"--------\n"
299+"shift\n"
300+"";
301+PyObject * Py_aubio_ishift(PyObject *self, PyObject *args);
302+
303+static char Py_aubio_hztomel_doc[] = ""
304+"hztomel(f, htk=False)\n"
305+"\n"
306+"Convert a scalar from frequency to mel scale.\n"
307+"\n"
308+"Parameters\n"
309+"----------\n"
310+"m : float\n"
311+" input frequency, in Hz\n"
312+"htk : bool\n"
313+" if `True`, use Htk mel scale instead of Slaney.\n"
314+"\n"
315+"Returns\n"
316+"-------\n"
317+"float\n"
318+" output mel\n"
319+"\n"
320+"See Also\n"
321+"--------\n"
322+"meltohz\n"
323+"";
324+PyObject * Py_aubio_hztomel(PyObject *self, PyObject *args);
325+
326+static char Py_aubio_meltohz_doc[] = ""
327+"meltohz(m, htk=False)\n"
328+"\n"
329+"Convert a scalar from mel scale to frequency.\n"
330+"\n"
331+"Parameters\n"
332+"----------\n"
333+"m : float\n"
334+" input mel\n"
335+"htk : bool\n"
336+" if `True`, use Htk mel scale instead of Slaney.\n"
337+"\n"
338+"Returns\n"
339+"-------\n"
340+"float\n"
341+" output frequency, in Hz\n"
342+"\n"
343+"See Also\n"
344+"--------\n"
345+"hztomel\n"
346+"";
347+PyObject * Py_aubio_meltohz(PyObject *self, PyObject *args);
348+
349+static char Py_aubio_hztomel_htk_doc[] = ""
350+"hztomel_htk(m)\n"
351+"\n"
352+"Same as `hztomel(m, htk=True)`\n"
353+"\n"
354+"See Also\n"
355+"--------\n"
356+"hztomel\n"
357+"";
358+PyObject * Py_aubio_hztomel_htk(PyObject *self, PyObject *args);
359+
360+static char Py_aubio_meltohz_htk_doc[] = ""
361+"meltohz_htk(m)\n"
362+"\n"
363+"Same as `meltohz(m, htk=True)`\n"
364+"\n"
365+"See Also\n"
366+"--------\n"
367+"meltohz\n"
368+"";
369+PyObject * Py_aubio_meltohz_htk(PyObject *self, PyObject *args);
370+
74371 #endif /* PY_AUBIO_MUSICUTILS_H */
python/ext/py-phasevoc.c+116−8
@@ -1,6 +1,58 @@
11 #include "aubio-types.h"
22
3−static char Py_pvoc_doc[] = "pvoc object";
3+static char Py_pvoc_doc[] = ""
4+"pvoc(win_s=512, hop_s=256)\n"
5+"\n"
6+"Phase vocoder.\n"
7+"\n"
8+"`pvoc` creates callable object implements a phase vocoder [1]_,\n"
9+"using the tricks detailed in [2]_.\n"
10+"\n"
11+"The call function takes one input of type `fvec` and of size\n"
12+"`hop_s`, and returns a `cvec` of length `win_s//2+1`.\n"
13+"\n"
14+"Parameters\n"
15+"----------\n"
16+"win_s : int\n"
17+" number of channels in the phase-vocoder.\n"
18+"hop_s : int\n"
19+" number of samples expected between each call\n"
20+"\n"
21+"Examples\n"
22+"--------\n"
23+">>> x = aubio.fvec(256)\n"
24+">>> pv = aubio.pvoc(512, 256)\n"
25+">>> pv(x)\n"
26+"aubio cvec of 257 elements\n"
27+"\n"
28+"Default values for hop_s and win_s are provided:\n"
29+"\n"
30+">>> pv = aubio.pvoc()\n"
31+">>> pv.win_s, pv.hop_s\n"
32+"512, 256\n"
33+"\n"
34+"A `cvec` can be resynthesised using `rdo()`:\n"
35+"\n"
36+">>> pv = aubio.pvoc(512, 256)\n"
37+">>> y = aubio.cvec(512)\n"
38+">>> x_reconstructed = pv.rdo(y)\n"
39+">>> x_reconstructed.shape\n"
40+"(256,)\n"
41+"\n"
42+"References\n"
43+"----------\n"
44+".. [1] James A. Moorer. The use of the phase vocoder in computer music\n"
45+" applications. `Journal of the Audio Engineering Society`,\n"
46+" 26(1/2):42–45, 1978.\n"
47+".. [2] Amalia de Götzen, Nicolas Bernardini, and Daniel Arfib. Traditional\n"
48+" (?) implementations of a phase vocoder: the tricks of the trade.\n"
49+" In `Proceedings of the International Conference on Digital Audio\n"
50+" Effects` (DAFx-00), pages 37–44, University of Verona, Italy, 2000.\n"
51+" (`online version <"
52+"https://www.cs.princeton.edu/courses/archive/spr09/cos325/Bernardini.pdf"
53+">`_).\n"
54+"";
55+
456
557 typedef struct
658 {
@@ -38,10 +90,6 @@ Py_pvoc_new (PyTypeObject * type, PyObject * args, PyObject * kwds) · 31 unmodified lines
3890 self->win_s = Py_default_vector_length;
3991 self->hop_s = Py_default_vector_length/2;
4092
41− if (self == NULL) {
42− return NULL;
43− }
44−
4593 if (win_s > 0) {
4694 self->win_s = win_s;
4795 } else if (win_s < 0) {
@@ -121,9 +169,11 @@ Py_pvoc_do(Py_pvoc * self, PyObject * args) · 73 unmodified lines
121169
122170 static PyMemberDef Py_pvoc_members[] = {
123171 {"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY,
124− "size of the window"},
172+ "int: Size of phase vocoder analysis windows, in samples.\n"
173+ ""},
125174 {"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY,
126− "size of the hop"},
175+ "int: Interval between two analysis, in samples.\n"
176+ ""},
127177 { NULL } // sentinel
128178 };
129179
@@ -155,9 +205,67 @@ Py_pvoc_rdo(Py_pvoc * self, PyObject * args) · 25 unmodified lines
155205 return self->routput;
156206 }
157207
208+static PyObject *
209+Pyaubio_pvoc_set_window (Py_pvoc *self, PyObject *args)
210+{
211+ uint_t err = 0;
212+ char_t *window = NULL;
213+
214+ if (!PyArg_ParseTuple (args, "s", &window)) {
215+ return NULL;
216+ }
217+ err = aubio_pvoc_set_window (self->o, window);
218+
219+ if (err > 0) {
220+ PyErr_SetString (PyExc_ValueError, "error running aubio_pvoc_set_window");
221+ return NULL;
222+ }
223+ Py_RETURN_NONE;
224+}
225+
158226 static PyMethodDef Py_pvoc_methods[] = {
159227 {"rdo", (PyCFunction) Py_pvoc_rdo, METH_VARARGS,
160− "synthesis of spectral grain"},
228+ "rdo(fftgrain)\n"
229+ "\n"
230+ "Read a new spectral grain and resynthesise the next `hop_s`\n"
231+ "output samples.\n"
232+ "\n"
233+ "Parameters\n"
234+ "----------\n"
235+ "fftgrain : cvec\n"
236+ " new input `cvec` to synthesize from, should be of size `win_s/2+1`\n"
237+ "\n"
238+ "Returns\n"
239+ "-------\n"
240+ "fvec\n"
241+ " re-synthesised output of shape `(hop_s,)`\n"
242+ "\n"
243+ "Example\n"
244+ "-------\n"
245+ ">>> pv = aubio.pvoc(2048, 512)\n"
246+ ">>> out = pv.rdo(aubio.cvec(2048))\n"
247+ ">>> out.shape\n"
248+ "(512,)\n"
249+ ""},
250+ {"set_window", (PyCFunction) Pyaubio_pvoc_set_window, METH_VARARGS,
251+ "set_window(window_type)\n"
252+ "\n"
253+ "Set window function\n"
254+ "\n"
255+ "Parameters\n"
256+ "----------\n"
257+ "window_type : str\n"
258+ " the window type to use for this phase vocoder\n"
259+ "\n"
260+ "Raises\n"
261+ "------\n"
262+ "ValueError\n"
263+ " If an unknown window type was given.\n"
264+ "\n"
265+ "See Also\n"
266+ "--------\n"
267+ "window : create a window.\n"
268+ ""},
161269 {NULL}
162270 };
163271
python/ext/py-sink.c+89−50
@@ -12,53 +12,78 @@ typedef struct · 11 unmodified lines
1212 } Py_sink;
1313
1414 static char Py_sink_doc[] = ""
15−" __new__(path, samplerate = 44100, channels = 1)\n"
15+"sink(path, samplerate=44100, channels=1)\n"
1616 "\n"
17−" Create a new sink, opening the given path for writing.\n"
17+"Write audio samples to file.\n"
1818 "\n"
19−" Examples\n"
20−" --------\n"
19+"Parameters\n"
20+"----------\n"
21+"path : str\n"
22+" Pathname of the file to be opened for writing.\n"
23+"samplerate : int\n"
24+" Sampling rate of the file, in Hz.\n"
25+"channels : int\n"
26+" Number of channels to create the file with.\n"
2127 "\n"
22−" Create a new sink at 44100Hz, mono:\n"
28+"Examples\n"
29+"--------\n"
2330 "\n"
24−" >>> sink('/tmp/t.wav')\n"
31+"Create a new sink at 44100Hz, mono:\n"
2532 "\n"
26−" Create a new sink at 8000Hz, mono:\n"
33+">>> snk = aubio.sink('out.wav')\n"
2734 "\n"
28−" >>> sink('/tmp/t.wav', samplerate = 8000)\n"
35+"Create a new sink at 32000Hz, stereo, write 100 samples into it:\n"
2936 "\n"
30−" Create a new sink at 32000Hz, stereo:\n"
37+">>> snk = aubio.sink('out.wav', samplerate=16000, channels=3)\n"
38+">>> snk(aubio.fvec(100), 100)\n"
3139 "\n"
32−" >>> sink('/tmp/t.wav', samplerate = 32000, channels = 2)\n"
40+"Open a new sink at 48000Hz, stereo, write `1234` samples into it:\n"
3341 "\n"
34−" Create a new sink at 32000Hz, 5 channels:\n"
42+">>> with aubio.sink('out.wav', samplerate=48000, channels=2) as src:\n"
43+"... snk(aubio.fvec(1024), 1024)\n"
44+"... snk(aubio.fvec(210), 210)\n"
45+"...\n"
3546 "\n"
36−" >>> sink('/tmp/t.wav', channels = 5, samplerate = 32000)\n"
37−"\n"
38−" __call__(vec, write)\n"
39−" x(vec,write) <==> x.do(vec, write)\n"
40−"\n"
41−" Write vector to sink.\n"
42−"\n"
43−" See also\n"
44−" --------\n"
45−" aubio.sink.do\n"
47+"See also\n"
48+"--------\n"
49+"source: read audio samples from a file.\n"
4650 "\n";
4751
4852 static char Py_sink_do_doc[] = ""
49−"x.do(vec, write) <==> x(vec, write)\n"
53+"do(vec, write)\n"
5054 "\n"
51−"write monophonic vector to sink";
55+"Write a single channel vector to sink.\n"
56+"\n"
57+"Parameters\n"
58+"----------\n"
59+"vec : fvec\n"
60+" input vector `(n,)` where `n >= 0`.\n"
61+"write : int\n"
62+" Number of samples to write.\n"
63+"";
5264
5365 static char Py_sink_do_multi_doc[] = ""
54−"x.do_multi(mat, write)\n"
66+"do_multi(mat, write)\n"
67+"\n"
68+"Write a matrix containing vectors from multiple channels to sink.\n"
5569 "\n"
56−"write polyphonic vector to sink";
70+"Parameters\n"
71+"----------\n"
72+"mat : numpy.ndarray\n"
73+" input matrix of shape `(channels, n)`, where `n >= 0`.\n"
74+"write : int\n"
75+" Number of frames to write.\n"
76+"";
5777
5878 static char Py_sink_close_doc[] = ""
59−"x.close()\n"
79+"close()\n"
6080 "\n"
61−"close this sink now";
81+"Close this sink now.\n"
82+"\n"
83+"By default, the sink will be closed before being deleted.\n"
84+"Explicitely closing a sink can be useful to control the number\n"
85+"of files simultaneously opened.\n"
86+"";
6287
6388 static PyObject *
6489 Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
@@ -80,27 +105,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 15 unmodified lines
80105 return NULL;
81106 }
82107
83− self->uri = "none";
108+ self->uri = NULL;
84109 if (uri != NULL) {
85− self->uri = uri;
110+ self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1));
111+ strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1);
86112 }
87113
88114 self->samplerate = Py_aubio_default_samplerate;
89− if ((sint_t)samplerate > 0) {
115+ if (samplerate != 0) {
90116 self->samplerate = samplerate;
91− } else if ((sint_t)samplerate < 0) {
92− PyErr_SetString (PyExc_ValueError,
93− "can not use negative value for samplerate");
94− return NULL;
95117 }
96118
97119 self->channels = 1;
98− if ((sint_t)channels > 0) {
120+ if (channels != 0) {
99121 self->channels = channels;
100− } else if ((sint_t)channels < 0) {
101− PyErr_SetString (PyExc_ValueError,
102− "can not use negative or null value for channels");
103− return NULL;
104122 }
105123
106124 return (PyObject *) self;
@@ -109,17 +127,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 2 unmodified lines
109127 static int
110128 Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds)
111129 {
112− if (self->channels == 1) {
113− self->o = new_aubio_sink ( self->uri, self->samplerate );
114− } else {
115− self->o = new_aubio_sink ( self->uri, 0 );
116− aubio_sink_preset_channels ( self->o, self->channels );
117− aubio_sink_preset_samplerate ( self->o, self->samplerate );
118− }
130+ self->o = new_aubio_sink ( self->uri, 0 );
119131 if (self->o == NULL) {
120− PyErr_SetString (PyExc_RuntimeError, "error creating sink with this uri");
132+ // error string was set in new_aubio_sink
121133 return -1;
122134 }
135+ if (aubio_sink_preset_channels(self->o, self->channels) != 0) {
136+ // error string was set in aubio_sink_preset_channels
137+ return -1;
138+ }
139+ if (aubio_sink_preset_samplerate(self->o, self->samplerate) != 0) {
140+ // error string was set in aubio_sink_preset_samplerate
141+ return -1;
142+ }
143+
123144 self->samplerate = aubio_sink_get_samplerate ( self->o );
124145 self->channels = aubio_sink_get_channels ( self->o );
125146
@@ -131,6 +152,9 @@ Py_sink_del (Py_sink *self, PyObject *unused) · 5 unmodified lines
131152 {
132153 del_aubio_sink(self->o);
133154 free(self->mwrite_data.data);
155+ if (self->uri) {
156+ free(self->uri);
157+ }
134158 Py_TYPE(self)->tp_free((PyObject *) self);
135159 }
136160
@@ -189,11 +213,11 @@ Py_sink_do_multi(Py_sink * self, PyObject * args) · 52 unmodified lines
189213
190214 static PyMemberDef Py_sink_members[] = {
191215 {"uri", T_STRING, offsetof (Py_sink, uri), READONLY,
192− "path at which the sink was created"},
216+ "str (read-only): Path at which the sink was created."},
193217 {"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY,
194− "samplerate at which the sink was created"},
218+ "int (read-only): Samplerate at which the sink was created."},
195219 {"channels", T_INT, offsetof (Py_sink, channels), READONLY,
196− "number of channels with which the sink was created"},
220+ "int (read-only): Number of channels with which the sink was created."},
197221 { NULL } // sentinel
198222 };
199223
@@ -204,10 +228,25 @@ Pyaubio_sink_close (Py_sink *self, PyObject *unused) · 4 unmodified lines
204228 Py_RETURN_NONE;
205229 }
206230
231+static char Pyaubio_sink_enter_doc[] = "";
232+static PyObject* Pyaubio_sink_enter(Py_sink *self, PyObject *unused) {
233+ Py_INCREF(self);
234+ return (PyObject*)self;
235+}
236+
237+static char Pyaubio_sink_exit_doc[] = "";
238+static PyObject* Pyaubio_sink_exit(Py_sink *self, PyObject *unused) {
239+ return Pyaubio_sink_close(self, unused);
240+}
241+
207242 static PyMethodDef Py_sink_methods[] = {
208243 {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc},
209244 {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc},
210245 {"close", (PyCFunction) Pyaubio_sink_close, METH_NOARGS, Py_sink_close_doc},
246+ {"__enter__", (PyCFunction)Pyaubio_sink_enter, METH_NOARGS,
247+ Pyaubio_sink_enter_doc},
248+ {"__exit__", (PyCFunction)Pyaubio_sink_exit, METH_VARARGS,
249+ Pyaubio_sink_exit_doc},
211250 {NULL} /* sentinel */
212251 };
213252
python/ext/py-source.c+370−41
@@ -16,68 +16,316 @@ typedef struct · 15 unmodified lines
1616 } Py_source;
1717
1818 static char Py_source_doc[] = ""
19−" __new__(path, samplerate = 0, hop_size = 512, channels = 1)\n"
19+"source(path, samplerate=0, hop_size=512, channels=0)\n"
2020 "\n"
21−" Create a new source, opening the given path for reading.\n"
21+"Read audio samples from a media file.\n"
2222 "\n"
23−" Examples\n"
24−" --------\n"
23+"`source` open the file specified in `path` and creates a callable\n"
24+"returning `hop_size` new audio samples at each invocation.\n"
2525 "\n"
26−" Create a new source, using the original samplerate, with hop_size = 512:\n"
26+"If `samplerate=0` (default), the original sampling rate of `path`\n"
27+"will be used. Otherwise, the output audio samples will be\n"
28+"resampled at the desired sampling-rate.\n"
2729 "\n"
28−" >>> source('/tmp/t.wav')\n"
30+"If `channels=0` (default), the original number of channels\n"
31+"in `path` will be used. Otherwise, the output audio samples\n"
32+"will be down-mixed or up-mixed to the desired number of\n"
33+"channels.\n"
2934 "\n"
30−" Create a new source, resampling the original to 8000Hz:\n"
35+"If `path` is a URL, a remote connection will be attempted to\n"
36+"open the resource and stream data from it.\n"
3137 "\n"
32−" >>> source('/tmp/t.wav', samplerate = 8000)\n"
38+"The parameter `hop_size` determines how many samples should be\n"
39+"read at each consecutive calls.\n"
3340 "\n"
34−" Create a new source, resampling it at 32000Hz, hop_size = 32:\n"
41+"Parameters\n"
42+"----------\n"
43+"path : str\n"
44+" pathname (or URL) of the file to be opened for reading\n"
45+"samplerate : int, optional\n"
46+" sampling rate of the file\n"
47+"hop_size : int, optional\n"
48+" number of samples to be read per iteration\n"
49+"channels : int, optional\n"
50+" number of channels of the file\n"
3551 "\n"
36−" >>> source('/tmp/t.wav', samplerate = 32000, hop_size = 32)\n"
52+"Examples\n"
53+"--------\n"
54+"By default, when only `path` is given, the file will be opened\n"
55+"with its original sampling rate and channel:\n"
3756 "\n"
38−" Create a new source, using its original samplerate:\n"
57+">>> src = aubio.source('stereo.wav')\n"
58+">>> src.uri, src.samplerate, src.channels, src.duration\n"
59+"('stereo.wav', 48000, 2, 86833)\n"
3960 "\n"
40−" >>> source('/tmp/t.wav', samplerate = 0)\n"
61+"A typical loop to read all samples from a local file could\n"
62+"look like this:\n"
4163 "\n"
42−" __call__()\n"
43−" vec, read = x() <==> vec, read = x.do()\n"
64+">>> src = aubio.source('stereo.wav')\n"
65+">>> total_read = 0\n"
66+">>> while True:\n"
67+"... samples, read = src()\n"
68+"... # do something with samples\n"
69+"... total_read += read\n"
70+"... if read < src.hop_size:\n"
71+"... break\n"
72+"...\n"
4473 "\n"
45−" Read vector from source.\n"
74+"In a more Pythonic way, it can also look like this:\n"
4675 "\n"
47−" See also\n"
48−" --------\n"
49−" aubio.source.do\n"
50−"\n";
76+">>> total_read = 0\n"
77+">>> with aubio.source('stereo.wav') as src:\n"
78+"... for frames in src:\n"
79+"... total_read += samples.shape[-1]\n"
80+"...\n"
81+"\n"
82+".. rubric:: Basic interface\n"
83+"\n"
84+"`source` is a **callable**; its :meth:`__call__` method\n"
85+"returns a tuple containing:\n"
86+"\n"
87+"- a vector of shape `(hop_size,)`, filled with the `read` next\n"
88+" samples available, zero-padded if `read < hop_size`\n"
89+"- `read`, an integer indicating the number of samples read\n"
90+"\n"
91+"To read the first `hop_size` samples from the source, simply call\n"
92+"the instance itself, with no argument:\n"
93+"\n"
94+">>> src = aubio.source('song.ogg')\n"
95+">>> samples, read = src()\n"
96+">>> samples.shape, read, src.hop_size\n"
97+"((512,), 512, 512)\n"
98+"\n"
99+"The first call returned the slice of samples `[0 : hop_size]`.\n"
100+"The next call will return samples `[hop_size: 2*hop_size]`.\n"
101+"\n"
102+"After several invocations of :meth:`__call__`, when reaching the end\n"
103+"of the opened stream, `read` might become less than `hop_size`:\n"
104+"\n"
105+">>> samples, read = src()\n"
106+">>> samples.shape, read\n"
107+"((512,), 354)\n"
108+"\n"
109+"The end of the vector `samples` is filled with zeros.\n"
110+"\n"
111+"After the end of the stream, `read` will be `0` since no more\n"
112+"samples are available:\n"
113+"\n"
114+">>> samples, read = src()\n"
115+">>> samples.shape, read\n"
116+"((512,), 0)\n"
117+"\n"
118+"**Note**: when the source has more than one channels, they\n"
119+"are be down-mixed to mono when invoking :meth:`__call__`.\n"
120+"To read from each individual channel, see :meth:`__next__`.\n"
121+"\n"
122+".. rubric:: ``for`` statements\n"
123+"\n"
124+"The `source` objects are **iterables**. This allows using them\n"
125+"directly in a ``for`` loop, which calls :meth:`__next__` until\n"
126+"the end of the stream is reached:\n"
127+"\n"
128+">>> src = aubio.source('stereo.wav')\n"
129+">>> for frames in src:\n"
130+">>> print (frames.shape)\n"
131+"...\n"
132+"(2, 512)\n"
133+"(2, 512)\n"
134+"(2, 230)\n"
135+"\n"
136+"**Note**: When `next(self)` is called on a source with multiple\n"
137+"channels, an array of shape `(channels, read)` is returned,\n"
138+"unlike with :meth:`__call__` which always returns the down-mixed\n"
139+"channels.\n"
140+"\n"
141+"If the file is opened with a single channel, `next(self)` returns\n"
142+"an array of shape `(read,)`:\n"
143+"\n"
144+">>> src = aubio.source('stereo.wav', channels=1)\n"
145+">>> next(src).shape\n"
146+"(512,)\n"
147+"\n"
148+".. rubric:: ``with`` statements\n"
149+"\n"
150+"The `source` objects are **context managers**, which allows using\n"
151+"them in ``with`` statements:\n"
152+"\n"
153+">>> with aubio.source('audiotrack.wav') as source:\n"
154+"... n_frames=0\n"
155+"... for samples in source:\n"
156+"... n_frames += len(samples)\n"
157+"... print('read', n_frames, 'samples in', samples.shape[0], 'channels',\n"
158+"... 'from file \"%%s\"' %% source.uri)\n"
159+"...\n"
160+"read 239334 samples in 2 channels from file \"audiotrack.wav\"\n"
161+"\n"
162+"The file will be closed before exiting the statement.\n"
163+"\n"
164+"See also the methods implementing the context manager,\n"
165+":meth:`__enter__` and :meth:`__exit__`.\n"
166+"\n"
167+".. rubric:: Seeking and closing\n"
168+"\n"
169+"At any time, :meth:`seek` can be used to move to any position in\n"
170+"the file. For instance, to rewind to the start of the stream:\n"
171+"\n"
172+">>> src.seek(0)\n"
173+"\n"
174+"The opened file will be automatically closed when the object falls\n"
175+"out of scope and is scheduled for garbage collection.\n"
176+"\n"
177+"In some cases, it is useful to manually :meth:`close` a given source,\n"
178+"for instance to limit the number of simultaneously opened files:\n"
179+"\n"
180+">>> src.close()\n"
181+"\n"
182+".. rubric:: Input formats\n"
183+"\n"
184+"Depending on how aubio was compiled, :class:`source` may or may not\n"
185+"open certain **files format**. Below are some examples that assume\n"
186+"support for compressed files and remote urls was compiled in:\n"
187+"\n"
188+"- open a local file using its original sampling rate and channels,\n"
189+" and with the default hop size:\n"
190+"\n"
191+">>> s = aubio.source('sample.wav')\n"
192+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
193+"('sample.wav', 44100, 2, 512)\n"
194+"\n"
195+"- open a local compressed audio file, resampling to 32000Hz if needed:\n"
196+"\n"
197+">>> s = aubio.source('song.mp3', samplerate=32000)\n"
198+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
199+"('song.mp3', 32000, 2, 512)\n"
200+"\n"
201+"- open a local video file, down-mixing and resampling it to 16kHz:\n"
202+"\n"
203+">>> s = aubio.source('movie.mp4', samplerate=16000, channels=1)\n"
204+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
205+"('movie.mp4', 16000, 1, 512)\n"
206+"\n"
207+"- open a remote resource, with hop_size = 1024:\n"
208+"\n"
209+">>> s = aubio.source('https://aubio.org/drum.ogg', hop_size=1024)\n"
210+">>> s.uri, s.samplerate, s.channels, s.hop_size\n"
211+"('https://aubio.org/drum.ogg', 48000, 2, 1024)\n"
212+"\n"
213+"See Also\n"
214+"--------\n"
215+"sink: write audio samples to a file.\n"
216+"";
51217
52218 static char Py_source_get_samplerate_doc[] = ""
53−"x.get_samplerate() -> source samplerate\n"
219+"get_samplerate()\n"
220+"\n"
221+"Get sampling rate of source.\n"
54222 "\n"
55−"Get samplerate of source.";
223+"Returns\n"
224+"-------\n"
225+"int\n"
226+" Sampling rate, in Hz.\n"
227+"";
56228
57229 static char Py_source_get_channels_doc[] = ""
58−"x.get_channels() -> number of channels\n"
230+"get_channels()\n"
59231 "\n"
60−"Get number of channels in source.";
232+"Get number of channels in source.\n"
233+"\n"
234+"Returns\n"
235+"-------\n"
236+"int\n"
237+" Number of channels.\n"
238+"";
61239
62240 static char Py_source_do_doc[] = ""
63−"vec, read = x.do() <==> vec, read = x()\n"
241+"source.do()\n"
242+"\n"
243+"Read vector of audio samples.\n"
244+"\n"
245+"If the audio stream in the source has more than one channel,\n"
246+"the channels will be down-mixed.\n"
247+"\n"
248+"Returns\n"
249+"-------\n"
250+"samples : numpy.ndarray\n"
251+" `fvec` of size `hop_size` containing the new samples.\n"
252+"read : int\n"
253+" Number of samples read from the source, equals to `hop_size`\n"
254+" before the end-of-file is reached, less when it is reached,\n"
255+" and `0` after.\n"
64256 "\n"
65−"Read monophonic vector from source.";
257+"See Also\n"
258+"--------\n"
259+"do_multi\n"
260+"\n"
261+"Examples\n"
262+"--------\n"
263+">>> src = aubio.source('sample.wav', hop_size=1024)\n"
264+">>> src.do()\n"
265+"(array([-0.00123001, -0.00036685, 0.00097106, ..., -0.2031033 ,\n"
266+" -0.2025854 , -0.20221856], dtype=" AUBIO_NPY_SMPL_STR "), 1024)\n"
267+"";
66268
67269 static char Py_source_do_multi_doc[] = ""
68−"mat, read = x.do_multi()\n"
270+"do_multi()\n"
271+"\n"
272+"Read multiple channels of audio samples.\n"
273+"\n"
274+"If the source was opened with the same number of channels\n"
275+"found in the stream, each channel will be read individually.\n"
276+"\n"
277+"If the source was opened with less channels than the number\n"
278+"of channels in the stream, only the first channels will be read.\n"
279+"\n"
280+"If the source was opened with more channels than the number\n"
281+"of channel in the original stream, the first channels will\n"
282+"be duplicated on the additional output channel.\n"
69283 "\n"
70−"Read polyphonic vector from source.";
284+"Returns\n"
285+"-------\n"
286+"samples : numpy.ndarray\n"
287+" NumPy array of shape `(hop_size, channels)` containing the new\n"
288+" audio samples.\n"
289+"read : int\n"
290+" Number of samples read from the source, equals to `hop_size`\n"
291+" before the end-of-file is reached, less when it is reached,\n"
292+" and `0` after.\n"
293+"\n"
294+"See Also\n"
295+"--------\n"
296+"do\n"
297+"\n"
298+"Examples\n"
299+"--------\n"
300+">>> src = aubio.source('sample.wav')\n"
301+">>> src.do_multi()\n"
302+"(array([[ 0.00668335, 0.0067749 , 0.00714111, ..., -0.05737305,\n"
303+" -0.05856323, -0.06018066],\n"
304+" [-0.00842285, -0.0072937 , -0.00576782, ..., -0.09405518,\n"
305+" -0.09558105, -0.09725952]], dtype=" AUBIO_NPY_SMPL_STR "), 512)\n"
306+"";
71307
72308 static char Py_source_close_doc[] = ""
73−"x.close()\n"
309+"close()\n"
74310 "\n"
75−"Close this source now.";
311+"Close this source now.\n"
312+"\n"
313+".. note:: Closing twice a source will **not** raise any exception.\n"
314+"";
76315
77316 static char Py_source_seek_doc[] = ""
78−"x.seek(position)\n"
317+"seek(position)\n"
318+"\n"
319+"Seek to position in file.\n"
79320 "\n"
80−"Seek to resampled frame position.";
321+"If the source was not opened with its original sampling-rate,\n"
322+"`position` corresponds to the position in the re-sampled file.\n"
323+"\n"
324+"Parameters\n"
325+"----------\n"
326+"position : str\n"
327+" position to seek to, in samples\n"
328+"";
81329
82330 static PyObject *
83331 Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
@@ -100,9 +348,10 @@ Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 16 unmodified lines
100348 return NULL;
101349 }
102350
103− self->uri = "none";
351+ self->uri = NULL;
104352 if (uri != NULL) {
105− self->uri = uri;
353+ self->uri = (char_t *)malloc(sizeof(char_t) * (strnlen(uri, PATH_MAX) + 1));
354+ strncpy(self->uri, uri, strnlen(uri, PATH_MAX) + 1);
106355 }
107356
108357 self->samplerate = 0;
@@ -163,6 +412,9 @@ Py_source_del (Py_source *self, PyObject *unused) · 54 unmodified lines
163412 del_aubio_source(self->o);
164413 free(self->c_mread_to.data);
165414 }
415+ if (self->uri) {
416+ free(self->uri);
417+ }
166418 Py_XDECREF(self->read_to);
167419 Py_XDECREF(self->mread_to);
168420 Py_TYPE(self)->tp_free((PyObject *) self);
@@ -213,15 +465,29 @@ Py_source_do_multi(Py_source * self, PyObject * args) · 44 unmodified lines
213465
214466 static PyMemberDef Py_source_members[] = {
215467 {"uri", T_STRING, offsetof (Py_source, uri), READONLY,
216− "path at which the source was created"},
468+ "str (read-only): pathname or URL"},
217469 {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY,
218− "samplerate at which the source is viewed"},
470+ "int (read-only): sampling rate"},
219471 {"channels", T_INT, offsetof (Py_source, channels), READONLY,
220− "number of channels found in the source"},
472+ "int (read-only): number of channels"},
221473 {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY,
222− "number of consecutive frames that will be read at each do or do_multi call"},
474+ "int (read-only): number of samples read per iteration"},
223475 {"duration", T_INT, offsetof (Py_source, duration), READONLY,
224− "total number of frames in the source (estimated)"},
476+ "int (read-only): total number of frames in the source\n"
477+ "\n"
478+ "Can be estimated, for instance if the opened stream is\n"
479+ "a compressed media or a remote resource.\n"
480+ "\n"
481+ "Example\n"
482+ "-------\n"
483+ ">>> n = 0\n"
484+ ">>> src = aubio.source('track1.mp3')\n"
485+ ">>> for samples in src:\n"
486+ "... n += samples.shape[-1]\n"
487+ "...\n"
488+ ">>> n, src.duration\n"
489+ "(9638784, 9616561)\n"
490+ ""},
225491 { NULL } // sentinel
226492 };
227493
@@ -242,7 +508,7 @@ Pyaubio_source_get_channels (Py_source *self, PyObject *unused) · 14 unmodified lines
242508 static PyObject *
243509 Pyaubio_source_close (Py_source *self, PyObject *unused)
244510 {
245− aubio_source_close (self->o);
511+ if (aubio_source_close(self->o) != 0) return NULL;
246512 Py_RETURN_NONE;
247513 }
248514
@@ -272,6 +538,65 @@ Pyaubio_source_seek (Py_source *self, PyObject *args) · 23 unmodified lines
272538 Py_RETURN_NONE;
273539 }
274540
541+static char Pyaubio_source_enter_doc[] = "";
542+static PyObject* Pyaubio_source_enter(Py_source *self, PyObject *unused) {
543+ Py_INCREF(self);
544+ return (PyObject*)self;
545+}
546+
547+static char Pyaubio_source_exit_doc[] = "";
548+static PyObject* Pyaubio_source_exit(Py_source *self, PyObject *unused) {
549+ return Pyaubio_source_close(self, unused);
550+}
551+
552+static PyObject* Pyaubio_source_iter(PyObject *self) {
553+ Py_INCREF(self);
554+ return (PyObject*)self;
555+}
556+
557+static PyObject* Pyaubio_source_iter_next(Py_source *self) {
558+ PyObject *done, *size;
559+ if (self->channels == 1) {
560+ done = Py_source_do(self, NULL);
561+ } else {
562+ done = Py_source_do_multi(self, NULL);
563+ }
564+ if (!PyTuple_Check(done)) {
565+ PyErr_Format(PyExc_ValueError,
566+ "error when reading source: not opened?");
567+ return NULL;
568+ }
569+ size = PyTuple_GetItem(done, 1);
570+ if (size != NULL && PyLong_Check(size)) {
571+ if (PyLong_AsLong(size) == (long)self->hop_size) {
572+ PyObject *vec = PyTuple_GetItem(done, 0);
573+ return vec;
574+ } else if (PyLong_AsLong(size) > 0) {
575+ // short read, return a shorter array
576+ PyArrayObject *shortread = (PyArrayObject*)PyTuple_GetItem(done, 0);
577+ PyArray_Dims newdims;
578+ PyObject *reshaped;
579+ newdims.len = PyArray_NDIM(shortread);
580+ newdims.ptr = PyArray_DIMS(shortread);
581+ // mono or multiple channels?
582+ if (newdims.len == 1) {
583+ newdims.ptr[0] = PyLong_AsLong(size);
584+ } else {
585+ newdims.ptr[1] = PyLong_AsLong(size);
586+ }
587+ reshaped = PyArray_Newshape(shortread, &newdims, NPY_CORDER);
588+ Py_DECREF(shortread);
589+ return reshaped;
590+ } else {
591+ PyErr_SetNone(PyExc_StopIteration);
592+ return NULL;
593+ }
594+ } else {
595+ PyErr_SetNone(PyExc_StopIteration);
596+ return NULL;
597+ }
598+}
599+
275600 static PyMethodDef Py_source_methods[] = {
276601 {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate,
277602 METH_NOARGS, Py_source_get_samplerate_doc},
@@ -285,6 +610,10 @@ static PyMethodDef Py_source_methods[] = { · 7 unmodified lines
285610 METH_NOARGS, Py_source_close_doc},
286611 {"seek", (PyCFunction) Pyaubio_source_seek,
287612 METH_VARARGS, Py_source_seek_doc},
613+ {"__enter__", (PyCFunction)Pyaubio_source_enter, METH_NOARGS,
614+ Pyaubio_source_enter_doc},
615+ {"__exit__", (PyCFunction)Pyaubio_source_exit, METH_VARARGS,
616+ Pyaubio_source_exit_doc},
288617 {NULL} /* sentinel */
289618 };
290619
@@ -314,8 +643,8 @@ PyTypeObject Py_sourceType = { · 23 unmodified lines
314643 0,
315644 0,
316645 0,
317− 0,
318− 0,
646+ Pyaubio_source_iter,
647+ (unaryfunc)Pyaubio_source_iter_next,
319648 Py_source_methods,
320649 Py_source_members,
321650 0,
python/ext/ufuncs.c+44−3
@@ -58,7 +58,22 @@ static char Py_aubio_unary_types[] = { · 57 unmodified lines
5858 //NPY_OBJECT, NPY_OBJECT,
5959 };
6060
61−static char Py_unwrap2pi_doc[] = "map angle to unit circle [-pi, pi[";
61+// Note: these docstrings should *not* include the function signatures
62+
63+static char Py_unwrap2pi_doc[] = ""
64+"\n"
65+"Map angle to unit circle :math:`[-\\pi, \\pi[`.\n"
66+"\n"
67+"Parameters\n"
68+"----------\n"
69+"x : numpy.ndarray\n"
70+" input array\n"
71+"\n"
72+"Returns\n"
73+"-------\n"
74+"numpy.ndarray\n"
75+" values clamped to the unit circle :math:`[-\\pi, \\pi[`\n"
76+"";
6277
6378 static void* Py_unwrap2pi_data[] = {
6479 (void *)aubio_unwrap2pi,
@@ -67,14 +82,40 @@ static void* Py_unwrap2pi_data[] = { · 2 unmodified lines
6782 //(void *)unwrap2pio,
6883 };
6984
70−static char Py_freqtomidi_doc[] = "convert frequency to midi";
85+static char Py_freqtomidi_doc[] = ""
86+"\n"
87+"Convert frequency `[0; 23000[` to midi `[0; 128[`.\n"
88+"\n"
89+"Parameters\n"
90+"----------\n"
91+"x : numpy.ndarray\n"
92+" Array of frequencies, in Hz.\n"
93+"\n"
94+"Returns\n"
95+"-------\n"
96+"numpy.ndarray\n"
97+" Converted frequencies, in midi note.\n"
98+"";
7199
72100 static void* Py_freqtomidi_data[] = {
73101 (void *)aubio_freqtomidi,
74102 (void *)aubio_freqtomidi,
75103 };
76104
77−static char Py_miditofreq_doc[] = "convert midi to frequency";
105+static char Py_miditofreq_doc[] = ""
106+"\n"
107+"Convert midi `[0; 128[` to frequency `[0, 23000]`.\n"
108+"\n"
109+"Parameters\n"
110+"----------\n"
111+"x : numpy.ndarray\n"
112+" Array of frequencies, in midi note.\n"
113+"\n"
114+"Returns\n"
115+"-------\n"
116+"numpy.ndarray\n"
117+" Converted frequencies, in Hz\n"
118+"";
78119
79120 static void* Py_miditofreq_data[] = {
80121 (void *)aubio_miditofreq,
python/lib/__init__.py+0−0

Empty file deleted.

python/lib/aubio/__init__.py+72−5
@@ -1,18 +1,85 @@
11 #! /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+"""
224
325 import numpy
4−from ._aubio import *
26+from ._aubio import __version__ as version
527 from ._aubio import float_type
28+from ._aubio import *
629 from .midiconv import *
730 from .slicing import *
831
32+
933 class fvec(numpy.ndarray):
10− """a numpy vector holding audio samples"""
34+ """fvec(input_arg=1024)
35+ A vector holding float samples.
36+
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'))
1158
12− def __new__(cls, input_arg=1024, **kwargs):
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):
1375 if isinstance(input_arg, int):
1476 if input_arg == 0:
1577 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')
1779 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/cmd.py+623−0
@@ -0,0 +1,623 @@
1+#! /usr/bin/env python
2+# -*- coding: utf-8 -*-
3+
4+"""aubio command line tool
5+
6+This file was written by Paul Brossier <piem@aubio.org> and is released under
7+the GNU/GPL v3.
8+
9+Note: this script is mostly about parsing command line arguments. For more
10+readable code examples, check out the `python/demos` folder."""
11+
12+import sys
13+import argparse
14+import warnings
15+import aubio
16+
17+def aubio_parser():
18+ epilog = 'use "%(prog)s <command> --help" for more info about each command'
19+ parser = argparse.ArgumentParser(epilog=epilog)
20+ parser.add_argument('-V', '--version', help="show version",
21+ action="store_true", dest="show_version")
22+
23+ subparsers = parser.add_subparsers(title='commands', dest='command',
24+ parser_class= AubioArgumentParser,
25+ metavar="")
26+
27+ parser_add_subcommand_help(subparsers)
28+
29+ parser_add_subcommand_onset(subparsers)
30+ parser_add_subcommand_pitch(subparsers)
31+ parser_add_subcommand_beat(subparsers)
32+ parser_add_subcommand_tempo(subparsers)
33+ parser_add_subcommand_notes(subparsers)
34+ parser_add_subcommand_mfcc(subparsers)
35+ parser_add_subcommand_melbands(subparsers)
36+ parser_add_subcommand_quiet(subparsers)
37+ parser_add_subcommand_cut(subparsers)
38+
39+ return parser
40+
41+def parser_add_subcommand_help(subparsers):
42+ # global help subcommand
43+ subparsers.add_parser('help',
44+ help='show help message',
45+ formatter_class = argparse.ArgumentDefaultsHelpFormatter)
46+
47+def parser_add_subcommand_onset(subparsers):
48+ # onset subcommand
49+ subparser = subparsers.add_parser('onset',
50+ help='estimate time of onsets (beginning of sound event)',
51+ formatter_class = argparse.ArgumentDefaultsHelpFormatter)
52+ subparser.add_input()
53+ subparser.add_buf_hop_size()
54+ helpstr = "onset novelty function"
55+ helpstr += " <default|energy|hfc|complex|phase|specdiff|kl|mkl|specflux>"
56+ subparser.add_method(helpstr=helpstr)
57+ subparser.add_threshold()
58+ subparser.add_silence()
59+ subparser.add_minioi()
60+ subparser.add_time_format()
61+ subparser.add_verbose_help()
62+ subparser.set_defaults(process=process_onset)
63+
64+def parser_add_subcommand_pitch(subparsers):
65+ # pitch subcommand
66+ subparser = subparsers.add_parser('pitch',
67+ help='estimate fundamental frequency (monophonic)')
68+ subparser.add_input()
69+ subparser.add_buf_hop_size(buf_size=2048)
70+ helpstr = "pitch detection method <default|yinfft|yin|mcomb|fcomb|schmitt>"
71+ subparser.add_method(helpstr=helpstr)
72+ subparser.add_threshold()
73+ subparser.add_pitch_unit()
74+ subparser.add_silence()
75+ subparser.add_time_format()
76+ subparser.add_verbose_help()
77+ subparser.set_defaults(process=process_pitch)
78+
79+def parser_add_subcommand_beat(subparsers):
80+ # beat subcommand
81+ subparser = subparsers.add_parser('beat',
82+ help='estimate location of beats')
83+ subparser.add_input()
84+ subparser.add_buf_hop_size(buf_size=1024, hop_size=512)
85+ subparser.add_time_format()
86+ subparser.add_verbose_help()
87+ subparser.set_defaults(process=process_beat)
88+
89+def parser_add_subcommand_tempo(subparsers):
90+ # tempo subcommand
91+ subparser = subparsers.add_parser('tempo',
92+ help='estimate overall tempo in bpm')
93+ subparser.add_input()
94+ subparser.add_buf_hop_size(buf_size=1024, hop_size=512)
95+ subparser.add_time_format()
96+ subparser.add_verbose_help()
97+ subparser.set_defaults(process=process_tempo)
98+
99+def parser_add_subcommand_notes(subparsers):
100+ # notes subcommand
101+ subparser = subparsers.add_parser('notes',
102+ help='estimate midi-like notes (monophonic)')
103+ subparser.add_input()
104+ subparser.add_buf_hop_size()
105+ subparser.add_silence()
106+ subparser.add_release_drop()
107+ subparser.add_time_format()
108+ subparser.add_verbose_help()
109+ subparser.set_defaults(process=process_notes)
110+
111+def parser_add_subcommand_mfcc(subparsers):
112+ # mfcc subcommand
113+ subparser = subparsers.add_parser('mfcc',
114+ help='extract Mel-Frequency Cepstrum Coefficients')
115+ subparser.add_input()
116+ subparser.add_buf_hop_size()
117+ subparser.add_time_format()
118+ subparser.add_verbose_help()
119+ subparser.set_defaults(process=process_mfcc)
120+
121+def parser_add_subcommand_melbands(subparsers):
122+ # melbands subcommand
123+ subparser = subparsers.add_parser('melbands',
124+ help='extract energies in Mel-frequency bands')
125+ subparser.add_input()
126+ subparser.add_buf_hop_size()
127+ subparser.add_time_format()
128+ subparser.add_verbose_help()
129+ subparser.set_defaults(process=process_melbands)
130+
131+def parser_add_subcommand_quiet(subparsers):
132+ # quiet subcommand
133+ subparser = subparsers.add_parser('quiet',
134+ help='extract timestamps of quiet and loud regions')
135+ subparser.add_input()
136+ subparser.add_hop_size()
137+ subparser.add_silence()
138+ subparser.add_time_format()
139+ subparser.add_verbose_help()
140+ subparser.set_defaults(process=process_quiet)
141+
142+def parser_add_subcommand_cut(subparsers):
143+ # cut subcommand
144+ subparser = subparsers.add_parser('cut',
145+ help='slice at timestamps')
146+ subparser.add_input()
147+ helpstr = "onset novelty function"
148+ helpstr += " <default|energy|hfc|complex|phase|specdiff|kl|mkl|specflux>"
149+ subparser.add_method(helpstr=helpstr)
150+ subparser.add_buf_hop_size()
151+ subparser.add_silence()
152+ subparser.add_threshold(default=0.3)
153+ subparser.add_minioi()
154+ subparser.add_slicer_options()
155+ subparser.add_time_format()
156+ subparser.add_verbose_help()
157+ subparser.set_defaults(process=process_cut)
158+
159+class AubioArgumentParser(argparse.ArgumentParser):
160+
161+ def add_input(self):
162+ self.add_argument("source_uri", default=None, nargs='?',
163+ help="input sound file to analyse", metavar = "<source_uri>")
164+ self.add_argument("-i", "--input", dest = "source_uri2",
165+ help="input sound file to analyse", metavar = "<source_uri>")
166+ self.add_argument("-r", "--samplerate",
167+ metavar = "<freq>", type=int,
168+ action="store", dest="samplerate", default=0,
169+ help="samplerate at which the file should be represented")
170+
171+ def add_verbose_help(self):
172+ self.add_argument("-v", "--verbose",
173+ action="count", dest="verbose", default=1,
174+ help="make lots of noise [default]")
175+ self.add_argument("-q", "--quiet",
176+ action="store_const", dest="verbose", const=0,
177+ help="be quiet")
178+
179+ def add_buf_hop_size(self, buf_size=512, hop_size=256):
180+ self.add_buf_size(buf_size=buf_size)
181+ self.add_hop_size(hop_size=hop_size)
182+
183+ def add_buf_size(self, buf_size=512):
184+ self.add_argument("-B", "--bufsize",
185+ action="store", dest="buf_size", default=buf_size,
186+ metavar = "<size>", type=int,
187+ help="buffer size [default=%d]" % buf_size)
188+
189+ def add_hop_size(self, hop_size=256):
190+ self.add_argument("-H", "--hopsize",
191+ metavar = "<size>", type=int,
192+ action="store", dest="hop_size", default=hop_size,
193+ help="overlap size [default=%d]" % hop_size)
194+
195+ def add_method(self, method='default', helpstr='method'):
196+ self.add_argument("-m", "--method",
197+ metavar = "<method>", type=str,
198+ action="store", dest="method", default=method,
199+ help="%s [default=%s]" % (helpstr, method))
200+
201+ def add_threshold(self, default=None):
202+ self.add_argument("-t", "--threshold",
203+ metavar = "<threshold>", type=float,
204+ action="store", dest="threshold", default=default,
205+ help="threshold [default=%s]" % default)
206+
207+ def add_silence(self):
208+ self.add_argument("-s", "--silence",
209+ metavar = "<value>", type=float,
210+ action="store", dest="silence", default=-70,
211+ help="silence threshold")
212+
213+ def add_release_drop(self):
214+ self.add_argument("-d", "--release-drop",
215+ metavar = "<value>", type=float,
216+ action="store", dest="release_drop", default=10,
217+ help="release drop threshold")
218+
219+ def add_minioi(self, default="12ms"):
220+ self.add_argument("-M", "--minioi",
221+ metavar = "<value>", type=str,
222+ action="store", dest="minioi", default=default,
223+ help="minimum Inter-Onset Interval [default=%s]" % default)
224+
225+ def add_pitch_unit(self, default="Hz"):
226+ help_str = "frequency unit, should be one of Hz, midi, bin, cent"
227+ help_str += " [default=%s]" % default
228+ self.add_argument("-u", "--pitch-unit",
229+ metavar = "<value>", type=str,
230+ action="store", dest="pitch_unit", default=default,
231+ help=help_str)
232+
233+ def add_time_format(self):
234+ helpstr = "select time values output format (samples, ms, seconds)"
235+ helpstr += " [default=seconds]"
236+ self.add_argument("-T", "--time-format",
237+ metavar='format',
238+ dest="time_format",
239+ default=None,
240+ help=helpstr)
241+
242+ def add_slicer_options(self):
243+ self.add_argument("-o", "--output", type = str,
244+ metavar = "<outputdir>",
245+ action="store", dest="output_directory", default=None,
246+ help="specify path where slices of the original file should"
247+ " be created")
248+ self.add_argument("--cut-until-nsamples", type = int,
249+ metavar = "<samples>",
250+ action = "store", dest = "cut_until_nsamples", default = None,
251+ help="how many extra samples should be added at the end of"
252+ " each slice")
253+ self.add_argument("--cut-every-nslices", type = int,
254+ metavar = "<samples>",
255+ action = "store", dest = "cut_every_nslices", default = None,
256+ help="how many slices should be groupped together at each cut")
257+ self.add_argument("--cut-until-nslices", type = int,
258+ metavar = "<slices>",
259+ action = "store", dest = "cut_until_nslices", default = None,
260+ help="how many extra slices should be added at the end of"
261+ " each slice")
262+ self.add_argument("--create-first",
263+ action = "store_true", dest = "create_first", default = False,
264+ help="always include first slice")
265+
266+# some utilities
267+
268+def samples2seconds(n_frames, samplerate):
269+ return "%f\t" % (n_frames / float(samplerate))
270+
271+def samples2milliseconds(n_frames, samplerate):
272+ return "%f\t" % (1000. * n_frames / float(samplerate))
273+
274+def samples2samples(n_frames, _samplerate):
275+ return "%d\t" % n_frames
276+
277+def timefunc(mode):
278+ if mode is None or mode == 'seconds' or mode == 's':
279+ return samples2seconds
280+ elif mode == 'ms' or mode == 'milliseconds':
281+ return samples2milliseconds
282+ elif mode == 'samples':
283+ return samples2samples
284+ else:
285+ raise ValueError("invalid time format '%s'" % mode)
286+
287+# definition of processing classes
288+
289+class default_process(object):
290+ def __init__(self, args):
291+ if 'time_format' in args:
292+ self.time2string = timefunc(args.time_format)
293+ if args.verbose > 2 and hasattr(self, 'options'):
294+ name = type(self).__name__.split('_')[1]
295+ optstr = ' '.join(['running', name, 'with options',
296+ repr(self.options), '\n'])
297+ sys.stderr.write(optstr)
298+ def flush(self, frames_read, samplerate):
299+ # optionally called at the end of process
300+ pass
301+
302+ def parse_options(self, args, valid_opts):
303+ # get any valid options found in a dictionnary of arguments
304+ options = {k: v for k, v in vars(args).items() if k in valid_opts}
305+ self.options = options
306+
307+ def remap_pvoc_options(self, options):
308+ # FIXME: we need to remap buf_size to win_s, hop_size to hop_s
309+ # adjust python/ext/py-phasevoc.c to understand buf_size/hop_size
310+ if 'buf_size' in options:
311+ options['win_s'] = options['buf_size']
312+ del options['buf_size']
313+ if 'hop_size' in options:
314+ options['hop_s'] = options['hop_size']
315+ del options['hop_size']
316+ self.options = options
317+
318+class process_onset(default_process):
319+ valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
320+ def __init__(self, args):
321+ self.parse_options(args, self.valid_opts)
322+ self.onset = aubio.onset(**self.options)
323+ if args.threshold is not None:
324+ self.onset.set_threshold(args.threshold)
325+ if args.minioi:
326+ if args.minioi.endswith('ms'):
327+ self.onset.set_minioi_ms(float(args.minioi[:-2]))
328+ elif args.minioi.endswith('s'):
329+ self.onset.set_minioi_s(float(args.minioi[:-1]))
330+ else:
331+ self.onset.set_minioi(int(args.minioi))
332+ if args.silence:
333+ self.onset.set_silence(args.silence)
334+ super(process_onset, self).__init__(args)
335+ def __call__(self, block):
336+ return self.onset(block)
337+ def repr_res(self, res, _frames_read, samplerate):
338+ if res[0] != 0:
339+ outstr = self.time2string(self.onset.get_last(), samplerate)
340+ sys.stdout.write(outstr + '\n')
341+
342+class process_pitch(default_process):
343+ valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
344+ def __init__(self, args):
345+ self.parse_options(args, self.valid_opts)
346+ self.pitch = aubio.pitch(**self.options)
347+ if args.pitch_unit is not None:
348+ self.pitch.set_unit(args.pitch_unit)
349+ if args.threshold is not None:
350+ self.pitch.set_tolerance(args.threshold)
351+ if args.silence is not None:
352+ self.pitch.set_silence(args.silence)
353+ super(process_pitch, self).__init__(args)
354+ def __call__(self, block):
355+ return self.pitch(block)
356+ def repr_res(self, res, frames_read, samplerate):
357+ fmt_out = self.time2string(frames_read, samplerate)
358+ sys.stdout.write(fmt_out + "%.6f\n" % res[0])
359+
360+class process_beat(default_process):
361+ valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
362+ def __init__(self, args):
363+ self.parse_options(args, self.valid_opts)
364+ self.tempo = aubio.tempo(**self.options)
365+ super(process_beat, self).__init__(args)
366+ def __call__(self, block):
367+ return self.tempo(block)
368+ def repr_res(self, res, _frames_read, samplerate):
369+ if res[0] != 0:
370+ outstr = self.time2string(self.tempo.get_last(), samplerate)
371+ sys.stdout.write(outstr + '\n')
372+
373+class process_tempo(process_beat):
374+ def __init__(self, args):
375+ super(process_tempo, self).__init__(args)
376+ self.beat_locations = []
377+ def repr_res(self, res, _frames_read, samplerate):
378+ if res[0] != 0:
379+ self.beat_locations.append(self.tempo.get_last_s())
380+ def flush(self, frames_read, samplerate):
381+ import numpy as np
382+ if len(self.beat_locations) < 2:
383+ outstr = "unknown bpm"
384+ else:
385+ bpms = 60. / np.diff(self.beat_locations)
386+ median_bpm = np.mean(bpms)
387+ if len(self.beat_locations) < 10:
388+ outstr = "%.2f bpm (uncertain)" % median_bpm
389+ else:
390+ outstr = "%.2f bpm" % median_bpm
391+ sys.stdout.write(outstr + '\n')
392+
393+class process_notes(default_process):
394+ valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
395+ def __init__(self, args):
396+ self.parse_options(args, self.valid_opts)
397+ self.notes = aubio.notes(**self.options)
398+ if args.silence is not None:
399+ self.notes.set_silence(args.silence)
400+ if args.release_drop is not None:
401+ self.notes.set_release_drop(args.release_drop)
402+ super(process_notes, self).__init__(args)
403+ def __call__(self, block):
404+ return self.notes(block)
405+ def repr_res(self, res, frames_read, samplerate):
406+ if res[2] != 0: # note off
407+ fmt_out = self.time2string(frames_read, samplerate)
408+ sys.stdout.write(fmt_out + '\n')
409+ if res[0] != 0: # note on
410+ lastmidi = res[0]
411+ fmt_out = "%f\t" % lastmidi
412+ fmt_out += self.time2string(frames_read, samplerate)
413+ sys.stdout.write(fmt_out) # + '\t')
414+ def flush(self, frames_read, samplerate):
415+ eof = self.time2string(frames_read, samplerate)
416+ sys.stdout.write(eof + '\n')
417+
418+class process_mfcc(default_process):
419+ def __init__(self, args):
420+ valid_opts1 = ['hop_size', 'buf_size']
421+ self.parse_options(args, valid_opts1)
422+ self.remap_pvoc_options(self.options)
423+ self.pv = aubio.pvoc(**self.options)
424+
425+ valid_opts2 = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate']
426+ self.parse_options(args, valid_opts2)
427+ self.mfcc = aubio.mfcc(**self.options)
428+
429+ # remember all options
430+ self.parse_options(args, list(set(valid_opts1 + valid_opts2)))
431+
432+ super(process_mfcc, self).__init__(args)
433+
434+ def __call__(self, block):
435+ fftgrain = self.pv(block)
436+ return self.mfcc(fftgrain)
437+ def repr_res(self, res, frames_read, samplerate):
438+ fmt_out = self.time2string(frames_read, samplerate)
439+ fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()])
440+ sys.stdout.write(fmt_out + '\n')
441+
442+class process_melbands(default_process):
443+ def __init__(self, args):
444+ self.args = args
445+ valid_opts = ['hop_size', 'buf_size']
446+ self.parse_options(args, valid_opts)
447+ self.remap_pvoc_options(self.options)
448+ self.pv = aubio.pvoc(**self.options)
449+
450+ valid_opts = ['buf_size', 'n_filters']
451+ self.parse_options(args, valid_opts)
452+ self.remap_pvoc_options(self.options)
453+ self.filterbank = aubio.filterbank(**self.options)
454+ self.filterbank.set_mel_coeffs_slaney(args.samplerate)
455+
456+ super(process_melbands, self).__init__(args)
457+ def __call__(self, block):
458+ fftgrain = self.pv(block)
459+ return self.filterbank(fftgrain)
460+ def repr_res(self, res, frames_read, samplerate):
461+ fmt_out = self.time2string(frames_read, samplerate)
462+ fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()])
463+ sys.stdout.write(fmt_out + '\n')
464+
465+class process_quiet(default_process):
466+ def __init__(self, args):
467+ self.args = args
468+ valid_opts = ['hop_size', 'silence']
469+ self.parse_options(args, valid_opts)
470+ self.wassilence = 1
471+
472+ if args.silence is not None:
473+ self.silence = args.silence
474+ super(process_quiet, self).__init__(args)
475+
476+ def __call__(self, block):
477+ if aubio.silence_detection(block, self.silence) == 1:
478+ if self.wassilence != 1:
479+ self.wassilence = 1
480+ return 2 # newly found silence
481+ return 1 # silence again
482+ else:
483+ if self.wassilence != 0:
484+ self.wassilence = 0
485+ return -1 # newly found noise
486+ return 0 # noise again
487+
488+ def repr_res(self, res, frames_read, samplerate):
489+ fmt_out = None
490+ if res == -1:
491+ fmt_out = "NOISY: "
492+ if res == 2:
493+ fmt_out = "QUIET: "
494+ if fmt_out is not None:
495+ fmt_out += self.time2string(frames_read, samplerate)
496+ sys.stdout.write(fmt_out + '\n')
497+
498+class process_cut(process_onset):
499+ def __init__(self, args):
500+ super(process_cut, self).__init__(args)
501+ self.slices = []
502+ self.options = args
503+
504+ def __call__(self, block):
505+ ret = super(process_cut, self).__call__(block)
506+ if ret:
507+ self.slices.append(self.onset.get_last())
508+ return ret
509+
510+ def flush(self, frames_read, samplerate):
511+ _cut_slice(self.options, self.slices)
512+ duration = float(frames_read) / float(samplerate)
513+ base_info = '%(source_file)s' % \
514+ {'source_file': self.options.source_uri}
515+ base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
516+ {'duration': duration, 'samplerate': samplerate}
517+ info = "created %d slices from " % len(self.slices)
518+ info += base_info
519+ sys.stderr.write(info)
520+
521+def _cut_slice(options, timestamps):
522+ # cutting pass
523+ nstamps = len(timestamps)
524+ if nstamps > 0:
525+ # generate output files
526+ timestamps_end = None
527+ if options.cut_every_nslices:
528+ timestamps = timestamps[::options.cut_every_nslices]
529+ nstamps = len(timestamps)
530+ if options.cut_until_nslices and options.cut_until_nsamples:
531+ msg = "using cut_until_nslices, but cut_until_nsamples is set"
532+ warnings.warn(msg)
533+ if options.cut_until_nsamples:
534+ lag = options.cut_until_nsamples
535+ timestamps_end = [t + lag for t in timestamps[1:]]
536+ timestamps_end += [1e120]
537+ if options.cut_until_nslices:
538+ slice_lag = options.cut_until_nslices
539+ timestamps_end = [t for t in timestamps[1 + slice_lag:]]
540+ timestamps_end += [1e120] * (options.cut_until_nslices + 1)
541+ aubio.slice_source_at_stamps(options.source_uri,
542+ timestamps, timestamps_end = timestamps_end,
543+ output_dir = options.output_directory,
544+ samplerate = options.samplerate,
545+ create_first = options.create_first)
546+
547+def main():
548+ parser = aubio_parser()
549+ if sys.version_info[0] != 3:
550+ # on py2, create a dummy ArgumentParser to workaround the
551+ # optional subcommand issue. See https://bugs.python.org/issue9253
552+ # This ensures that:
553+ # - version string is shown when only '-V' is passed
554+ # - help is printed if '-V' is passed with any other argument
555+ # - any other argument get forwarded to the real parser
556+ parser_root = argparse.ArgumentParser(add_help=False)
557+ parser_root.add_argument('-V', '--version', help="show version",
558+ action="store_true", dest="show_version")
559+ args, extras = parser_root.parse_known_args()
560+ if not args.show_version: # no -V, forward to parser
561+ args = parser.parse_args(extras, namespace=args)
562+ elif len(extras) != 0: # -V with other arguments, print help
563+ parser.print_help()
564+ sys.exit(1)
565+ else: # in py3, we can simply use parser directly
566+ args = parser.parse_args()
567+ if 'show_version' in args and args.show_version:
568+ sys.stdout.write('aubio version ' + aubio.version + '\n')
569+ sys.exit(0)
570+ elif 'verbose' in args and args.verbose > 3:
571+ sys.stderr.write('aubio version ' + aubio.version + '\n')
572+ if 'command' not in args or args.command is None \
573+ or args.command in ['help']:
574+ # no command given, print help and return 1
575+ parser.print_help()
576+ if args.command and args.command in ['help']:
577+ sys.exit(0)
578+ else:
579+ sys.exit(1)
580+ elif not args.source_uri and not args.source_uri2:
581+ sys.stderr.write("Error: a source is required\n")
582+ parser.print_help()
583+ sys.exit(1)
584+ elif args.source_uri2 is not None:
585+ args.source_uri = args.source_uri2
586+ try:
587+ # open source_uri
588+ with aubio.source(args.source_uri, hop_size=args.hop_size,
589+ samplerate=args.samplerate) as a_source:
590+ # always update args.samplerate to native samplerate, in case
591+ # source was opened with args.samplerate=0
592+ args.samplerate = a_source.samplerate
593+ # create the processor for this subcommand
594+ processor = args.process(args)
595+ frames_read = 0
596+ while True:
597+ # read new block from source
598+ block, read = a_source()
599+ # execute processor on this block
600+ res = processor(block)
601+ # print results for this block
602+ if args.verbose > 0:
603+ processor.repr_res(res, frames_read, a_source.samplerate)
604+ # increment total number of frames read
605+ frames_read += read
606+ # exit loop at end of file
607+ if read < a_source.hop_size:
608+ break
609+ # flush the processor if needed
610+ processor.flush(frames_read, a_source.samplerate)
611+ if args.verbose > 1:
612+ fmt_string = "read {:.2f}s"
613+ fmt_string += " ({:d} samples in {:d} blocks of {:d})"
614+ fmt_string += " from {:s} at {:d}Hz\n"
615+ sys.stderr.write(fmt_string.format(
616+ frames_read / float(a_source.samplerate),
617+ frames_read,
618+ frames_read // a_source.hop_size + 1,
619+ a_source.hop_size,
620+ a_source.uri,
621+ a_source.samplerate))
622+ except KeyboardInterrupt:
623+ sys.exit(1)
python/lib/aubio/cut.py+163−0
@@ -0,0 +1,163 @@
1+#! /usr/bin/env python
2+
3+""" this file was written by Paul Brossier
4+ it is released under the GNU/GPL license.
5+"""
6+
7+import sys
8+from aubio.cmd import AubioArgumentParser, _cut_slice
9+
10+def aubio_cut_parser():
11+ parser = AubioArgumentParser()
12+ parser.add_input()
13+ parser.add_argument("-O", "--onset-method",
14+ action="store", dest="onset_method", default='default',
15+ metavar = "<onset_method>",
16+ help="onset detection method [default=default] \
17+ complexdomain|hfc|phase|specdiff|energy|kl|mkl")
18+ # cutting methods
19+ parser.add_argument("-b", "--beat",
20+ action="store_true", dest="beat", default=False,
21+ help="slice at beat locations")
22+ """
23+ parser.add_argument("-S", "--silencecut",
24+ action="store_true", dest="silencecut", default=False,
25+ help="use silence locations")
26+ parser.add_argument("-s", "--silence",
27+ metavar = "<value>",
28+ action="store", dest="silence", default=-70,
29+ help="silence threshold [default=-70]")
30+ """
31+ # algorithm parameters
32+ parser.add_buf_hop_size()
33+ parser.add_argument("-t", "--threshold", "--onset-threshold",
34+ metavar = "<threshold>", type=float,
35+ action="store", dest="threshold", default=0.3,
36+ help="onset peak picking threshold [default=0.3]")
37+ parser.add_argument("-c", "--cut",
38+ action="store_true", dest="cut", default=False,
39+ help="cut input sound file at detected labels")
40+ parser.add_minioi()
41+
42+ """
43+ parser.add_argument("-D", "--delay",
44+ action = "store", dest = "delay", type = float,
45+ metavar = "<seconds>", default=0,
46+ help="number of seconds to take back [default=system]\
47+ default system delay is 3*hopsize/samplerate")
48+ parser.add_argument("-C", "--dcthreshold",
49+ metavar = "<value>",
50+ action="store", dest="dcthreshold", default=1.,
51+ help="onset peak picking DC component [default=1.]")
52+ parser.add_argument("-L", "--localmin",
53+ action="store_true", dest="localmin", default=False,
54+ help="use local minima after peak detection")
55+ parser.add_argument("-d", "--derivate",
56+ action="store_true", dest="derivate", default=False,
57+ help="derivate onset detection function")
58+ parser.add_argument("-z", "--zerocross",
59+ metavar = "<value>",
60+ action="store", dest="zerothres", default=0.008,
61+ help="zero-crossing threshold for slicing [default=0.00008]")
62+ # plotting functions
63+ parser.add_argument("-p", "--plot",
64+ action="store_true", dest="plot", default=False,
65+ help="draw plot")
66+ parser.add_argument("-x", "--xsize",
67+ metavar = "<size>",
68+ action="store", dest="xsize", default=1.,
69+ type=float, help="define xsize for plot")
70+ parser.add_argument("-y", "--ysize",
71+ metavar = "<size>",
72+ action="store", dest="ysize", default=1.,
73+ type=float, help="define ysize for plot")
74+ parser.add_argument("-f", "--function",
75+ action="store_true", dest="func", default=False,
76+ help="print detection function")
77+ parser.add_argument("-n", "--no-onsets",
78+ action="store_true", dest="nplot", default=False,
79+ help="do not plot detected onsets")
80+ parser.add_argument("-O", "--outplot",
81+ metavar = "<output_image>",
82+ action="store", dest="outplot", default=None,
83+ help="save plot to output.{ps,png}")
84+ parser.add_argument("-F", "--spectrogram",
85+ action="store_true", dest="spectro", default=False,
86+ help="add spectrogram to the plot")
87+ """
88+ parser.add_slicer_options()
89+ parser.add_verbose_help()
90+ return parser
91+
92+
93+def _cut_analyze(options):
94+ hopsize = options.hop_size
95+ bufsize = options.buf_size
96+ samplerate = options.samplerate
97+ source_uri = options.source_uri
98+
99+ # analyze pass
100+ from aubio import onset, tempo, source
101+
102+ s = source(source_uri, samplerate, hopsize)
103+ if samplerate == 0:
104+ samplerate = s.samplerate
105+ options.samplerate = samplerate
106+
107+ if options.beat:
108+ o = tempo(options.onset_method, bufsize, hopsize,
109+ samplerate=samplerate)
110+ else:
111+ o = onset(options.onset_method, bufsize, hopsize,
112+ samplerate=samplerate)
113+ if options.minioi:
114+ if options.minioi.endswith('ms'):
115+ o.set_minioi_ms(int(options.minioi[:-2]))
116+ elif options.minioi.endswith('s'):
117+ o.set_minioi_s(int(options.minioi[:-1]))
118+ else:
119+ o.set_minioi(int(options.minioi))
120+ o.set_threshold(options.threshold)
121+
122+ timestamps = []
123+ total_frames = 0
124+ while True:
125+ samples, read = s()
126+ if o(samples):
127+ timestamps.append(o.get_last())
128+ if options.verbose:
129+ print("%.4f" % o.get_last_s())
130+ total_frames += read
131+ if read < hopsize:
132+ break
133+ del s
134+ return timestamps, total_frames
135+
136+def main():
137+ parser = aubio_cut_parser()
138+ options = parser.parse_args()
139+ if not options.source_uri and not options.source_uri2:
140+ sys.stderr.write("Error: no file name given\n")
141+ parser.print_help()
142+ sys.exit(1)
143+ elif options.source_uri2 is not None:
144+ options.source_uri = options.source_uri2
145+
146+ # analysis
147+ timestamps, total_frames = _cut_analyze(options)
148+
149+ # print some info
150+ duration = float(total_frames) / float(options.samplerate)
151+ base_info = '%(source_uri)s' % {'source_uri': options.source_uri}
152+ base_info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % \
153+ {'duration': duration, 'samplerate': options.samplerate}
154+
155+ info = "found %d timestamps in " % len(timestamps)
156+ info += base_info
157+ sys.stderr.write(info)
158+
159+ if options.cut:
160+ _cut_slice(options, timestamps)
161+ info = "created %d slices from " % len(timestamps)
162+ info += base_info
163+ sys.stderr.write(info)
python/lib/aubio/midiconv.py+142−17
@@ -1,9 +1,11 @@
11 # -*- coding: utf-8 -*-
22 """ utilities to convert midi note number to and from note names """
33
4−__all__ = ['note2midi', 'midi2note', 'freq2note']
5−
64 import sys
5+from ._aubio import freqtomidi, miditofreq
6+
7+__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
8+
79 py3 = sys.version_info[0] == 3
810 if py3:
911 str_instances = str
@@ -12,18 +14,65 @@ else: · 2 unmodified lines
1214 str_instances = (str, unicode)
1315 int_instances = (int, long)
1416
17+
1518 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+ }
2068 _valid_octaves = range(-1, 10)
2169 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)))
2372 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]*3
73+ 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
2776
2877 if len(note) == 4:
2978 notename, modifier, octave_sign, octave = note
@@ -46,21 +95,97 @@ def note2midi(note): · 16 unmodified lines
4695 if octave not in _valid_octaves:
4796 raise ValueError("%s is not a valid octave" % octave)
4897
49− midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier]
98+ midi = (octave + 1) * 12 + _valid_notenames[notename] \
99+ + _valid_modifiers[modifier]
50100 if midi > 127:
51101 raise ValueError("%s is outside of the range C-2 to G8" % note)
52102 return midi
53103
104+
54105 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+ """
56136 if not isinstance(midi, int_instances):
57137 raise TypeError("an integer is required, got %s" % midi)
58138 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']
61143 return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
62144
145+
63146 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+81−14
@@ -5,26 +5,91 @@ from aubio import source, sink · 4 unmodified lines
55
66 _max_timestamp = 1e120
77
8+
89 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.
13+
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+ """
1175
12− if timestamps is None or len(timestamps) == 0:
76+ if not timestamps:
1377 raise ValueError("no timestamps given")
1478
15− if timestamps[0] != 0:
79+ if timestamps[0] != 0 and create_first:
1680 timestamps = [0] + timestamps
1781 if timestamps_end is not None:
1882 timestamps_end = [timestamps[1] - 1] + timestamps_end
1983
2084 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):
2288 raise ValueError("len(timestamps_end) != len(timestamps)")
2389 else:
2490 timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
2591
2692 regions = list(zip(timestamps, timestamps_end))
27− #print regions
2893
2994 source_base_name, _ = os.path.splitext(os.path.basename(source_file))
3095 if output_dir is not None:
@@ -32,8 +97,8 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 1 unmodified line
3297 os.makedirs(output_dir)
3398 source_base_name = os.path.join(output_dir, source_base_name)
3499
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
37102 timestamp_seconds = timestamp / float(samplerate)
38103 return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
39104
@@ -48,16 +113,17 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 8 unmodified lines
48113 # get hopsize new samples from source
49114 vec, read = _source.do_multi()
50115 # 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]:
53117 # get next region
54118 start_stamp, end_stamp = regions.pop(0)
55119 # 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)
57122 # create its sink
58123 _sink = sink(new_sink_path, samplerate, _source.channels)
59124 # 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}
61127 # append the dictionary to the current list of slices
62128 slices.append(new_slice)
63129
@@ -69,18 +135,19 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 5 unmodified lines
69135 start = max(start_stamp - total_frames, 0)
70136 # number of samples yet to written be until end of region
71137 remaining = end_stamp - total_frames + 1
72− #print current_slice, remaining, start
73138 # not enough frames remaining, time to split
74139 if remaining < read:
75140 if remaining > start:
76141 # write remaining samples from current region
77142 _sink.do_multi(vec[:, start:remaining], remaining - start)
78− #print "closing region", "remaining", remaining
79143 # close this file
80144 _sink.close()
81145 elif read > start:
82146 # write all the samples
83147 _sink.do_multi(vec[:, start:read], read - start)
84148 total_frames += read
149+ # remove old slices
150+ slices = list(filter(lambda s: s['end_stamp'] > total_frames,
151+ slices))
85152 if read < hopsize:
86153 break
python/lib/gen_code.py+65−23
@@ -2,6 +2,7 @@ aubiodefvalue = { · 1 unmodified line
22 # we have some clean up to do
33 'buf_size': 'Py_default_vector_length',
44 'win_s': 'Py_default_vector_length',
5+ 'size': 'Py_default_vector_length',
56 # and here too
67 'hop_size': 'Py_default_vector_length / 2',
78 'hop_s': 'Py_default_vector_length / 2',
@@ -84,6 +85,7 @@ objoutsize = { · 76 unmodified lines
8485 'filterbank': 'self->n_filters',
8586 'tss': 'self->buf_size',
8687 'pitchshift': 'self->hop_size',
88+ 'dct': 'self->size',
8789 }
8890
8991 objinputsize = {
@@ -179,6 +181,10 @@ class MappedObject(object): · 89 unmodified lines
179181 self.do_inputs = [get_params_types_names(self.do_proto)[1]]
180182 self.do_outputs = get_params_types_names(self.do_proto)[2:]
181183 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
182188 self.struct_outputs = ";\n ".join(struct_output_str)
183189
184190 #print ("input_params: ", map(split_type, get_input_params(self.do_proto)))
@@ -186,17 +192,26 @@ class MappedObject(object): · 1 unmodified line
186192
187193 def gen_code(self):
188194 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
200215 return out
201216
202217 def gen_struct(self):
@@ -380,12 +395,12 @@ Py_{shortname}_del (Py_{shortname} * self, PyObject * unused) · 177 unmodified lines
380395 """.format(del_fn = del_fn)
381396 return out
382397
383− def gen_do(self):
398+ def gen_do(self, method = 'do'):
384399 out = """
385400 // do {shortname}
386401 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__)
389404 input_params = self.do_inputs
390405 output_params = self.do_outputs
391406 #print input_params
@@ -461,30 +476,51 @@ Py_{shortname}_do (Py_{shortname} * self, PyObject * args) · 69 unmodified lines
461476 // {shortname} setters
462477 """.format(**self.__dict__)
463478 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])
466483 method_name = get_name(set_param)
467484 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])
469490 out += """
470491 static PyObject *
471492 Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)
472493 {{
473494 uint_t err = 0;
474− {paramtype} {param};
495+ {paramdecls}
496+""".format(param = param, paramdecls = paramdecls, **self.__dict__)
497+
498+ if len(refs) and len(pyparamtypes):
499+ out += """
475500
476− if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{
501+ if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{
477502 return NULL;
478503 }}
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});
480508
481509 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+ }}
483518 return NULL;
484519 }}
485520 Py_RETURN_NONE;
486521 }}
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__)
488524 return out
489525
490526 def gen_get(self):
@@ -525,6 +561,12 @@ static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__) · 34 unmodified lines
525561 out += """
526562 {{"{shortname}", (PyCFunction) Py{name},
527563 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)
528570 out += """
529571 {NULL} /* sentinel */
530572 };
@@ -550,7 +592,7 @@ PyTypeObject Py_{shortname}Type = {{ · 19 unmodified lines
550592 0,
551593 0,
552594 0,
553− (ternaryfunc)Py_{shortname}_do,
595+ (ternaryfunc)Pyaubio_{shortname}_do,
554596 0,
555597 0,
556598 0,
python/lib/gen_external.py+180−89
@@ -1,5 +1,10 @@
11 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
38
49 header = os.path.join('src', 'aubio.h')
510 output_path = os.path.join('python', 'gen')
@@ -8,43 +13,44 @@ source_header = """// this file is generated! do not modify · 2 unmodified lines
813 #include "aubio-types.h"
914 """
1015
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+
4451
4552 def get_preprocessor():
4653 # findout which compiler to use
47− from distutils.sysconfig import customize_compiler
4854 compiler_name = distutils.ccompiler.get_default_compiler()
4955 compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
5056 try:
@@ -59,26 +65,50 @@ def get_preprocessor(): · 8 unmodified lines
5965 print("Warning: failed initializing compiler ({:s})".format(repr(e)))
6066
6167 cpp_cmd = None
62− if hasattr(compiler, 'preprocessor'): # for unixccompiler
68+ if hasattr(compiler, 'preprocessor'): # for unixccompiler
6369 cpp_cmd = compiler.preprocessor
64− elif hasattr(compiler, 'compiler'): # for ccompiler
70+ elif hasattr(compiler, 'compiler'): # for ccompiler
6571 cpp_cmd = compiler.compiler.split()
6672 cpp_cmd += ['-E']
67− elif hasattr(compiler, 'cc'): # for msvccompiler
73+ elif hasattr(compiler, 'cc'): # for msvccompiler
6874 cpp_cmd = compiler.cc.split()
6975 cpp_cmd += ['-E']
7076
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)
88+
7189 if not cpp_cmd:
7290 print("Warning: could not guess preprocessor, using env's CC")
7391 cpp_cmd = os.environ.get('CC', 'cc').split()
7492 cpp_cmd += ['-E']
75−
93+ if 'emcc' in cpp_cmd:
94+ cpp_cmd += ['-x', 'c'] # emcc defaults to c++, force C language
7695 return cpp_cmd
7796
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 '''
79107 cpp_cmd = get_preprocessor()
80108
81109 macros = [('AUBIO_UNSTABLE', 1)]
110+ if usedouble:
111+ macros += [('HAVE_AUBIO_DOUBLE', 1)]
82112
83113 if not os.path.isfile(header):
84114 raise Exception("could not find include file " + header)
@@ -89,56 +119,115 @@ def get_cpp_objects(header=header): · 4 unmodified lines
89119
90120 print("Running command: {:s}".format(" ".join(cpp_cmd)))
91121 proc = subprocess.Popen(cpp_cmd,
92− stderr=subprocess.PIPE,
93− stdout=subprocess.PIPE)
122+ stderr=subprocess.PIPE,
123+ stdout=subprocess.PIPE)
94124 assert proc, 'Proc was none'
95125 cpp_output = proc.stdout.read()
96126 err_output = proc.stderr.read()
127+ if err_output:
128+ print("Warning: preprocessor produced errors or warnings:\n%s" \
129+ % err_output.decode('utf8'))
97130 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)
101138 if not isinstance(cpp_output, list):
102139 cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
103140
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)
105146 cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
106147
107148 i = 1
108149 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)
113158 else:
114159 i += 1
115160
116− typedefs = filter(lambda y: y.startswith ('typedef struct _aubio'), cpp_output)
161+ # clean pointer notations
162+ tmp = []
163+ for l in cpp_output:
164+ tmp += [l.replace(' *', ' * ')]
165+ cpp_output = tmp
117166
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)
118174 cpp_objects = [a.split()[3][:-1] for a in typedefs]
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))
119197
120− return cpp_output, cpp_objects
198+ return res
121199
122200
123−def analyze_cpp_output(cpp_objects, cpp_output):
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+ '''
124208 lib = {}
125209
126210 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': []}
133216 lib[shortname]['longname'] = o
134217 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
138225 if 'typedef struct ' in fn:
139226 lib[shortname]['struct'].append(fn)
140227 elif '_do' in fn:
141228 lib[shortname]['do'].append(fn)
229+ elif '_rdo' in fn:
230+ lib[shortname]['rdo'].append(fn)
142231 elif 'new_' in fn:
143232 lib[shortname]['new'].append(fn)
144233 elif 'del_' in fn:
@@ -148,52 +237,52 @@ def analyze_cpp_output(cpp_objects, cpp_output): · 3 unmodified lines
148237 elif '_set_' in fn:
149238 lib[shortname]['set'].append(fn)
150239 else:
151− #print "no idea what to do about", fn
240+ # print "no idea what to do about", fn
152241 lib[shortname]['other'].append(fn)
153242 return lib
154243
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:
157247 found = 0
158248 for o in lib:
159249 for family in lib[o]:
160250 if fn in lib[o][family]:
161251 found = 1
162252 if found == 0:
163− print ("missing", fn)
253+ print("missing", fn)
164254
165255 for o in lib:
166256 for family in lib[o]:
167257 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]))
169259 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]))
171261 else:
172− print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )
262+ print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
173263
174264
175265 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'))
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')))
178270
179− cpp_output, cpp_objects = get_cpp_objects(header)
271+ c_declarations = get_c_declarations(header, usedouble=usedouble)
272+ cpp_objects = get_cpp_objects_from_c_declarations(c_declarations)
180273
181− lib = analyze_cpp_output(cpp_objects, cpp_output)
182− # print_cpp_output_results(lib, cpp_output)
274+ lib = generate_lib_from_c_declarations(cpp_objects, c_declarations)
275+ # print_c_declarations_results(lib, c_declarations)
183276
184277 sources_list = []
185− try:
186− from .gen_code import MappedObject
187− except (SystemError, ValueError):
188− from gen_code import MappedObject
189278 for o in lib:
190279 out = source_header
191− mapped = MappedObject(lib[o], usedouble = usedouble)
280+ mapped = MappedObject(lib[o], usedouble=usedouble)
192281 out += mapped.gen_code()
193282 output_file = os.path.join(output_path, 'gen-%s.c' % o)
194283 with open(output_file, 'w') as f:
195284 f.write(out)
196− print ("wrote %s" % output_file )
285+ print("wrote %s" % output_file)
197286 sources_list.append(output_file)
198287
199288 out = source_header
@@ -205,23 +294,23 @@ int generated_types_ready (void) · 5 unmodified lines
205294 {{
206295 return ({pycheck_types});
207296 }}
208−""".format(pycheck_types = check_types)
297+""".format(pycheck_types=check_types)
209298
210299 add_types = "".join(["""
211300 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])
213302 out += """
214303
215304 void add_generated_objects ( PyObject *m )
216305 {{
217306 {add_types}
218307 }}
219−""".format(add_types = add_types)
308+""".format(add_types=add_types)
220309
221310 output_file = os.path.join(output_path, 'aubio-generated.c')
222311 with open(output_file, 'w') as f:
223312 f.write(out)
224− print ("wrote %s" % output_file )
313+ print("wrote %s" % output_file)
225314 sources_list.append(output_file)
226315
227316 objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])
@@ -239,17 +328,19 @@ void add_generated_objects ( PyObject *m ) · 11 unmodified lines
239328 {objlist}
240329 int generated_objects ( void );
241330 void add_generated_objects( PyObject *m );
242−""".format(objlist = objlist)
331+""".format(objlist=objlist)
243332
244333 output_file = os.path.join(output_path, 'aubio-generated.h')
245334 with open(output_file, 'w') as f:
246335 f.write(out)
247− print ("wrote %s" % output_file )
336+ print("wrote %s" % output_file)
248337 # no need to add header to list of sources
249338
250− return sources_list
339+ return sorted(sources_list)
251340
252341 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]
255346 generate_external(header, output_path)
python/lib/moresetuptools.py+78−26
@@ -2,7 +2,9 @@ · 1 unmodified line
22 #
33 import sys, os, glob, subprocess
44 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
68
79 # inspired from https://gist.github.com/abergmeier/9488990
810 def add_packages(packages, ext=None, **kw):
@@ -21,6 +23,7 @@ def add_packages(packages, ext=None, **kw): · 12 unmodified lines
2123 }
2224
2325 for package in packages:
26+ print("checking for {:s}".format(package))
2427 cmd = ['pkg-config', '--libs', '--cflags', package]
2528 try:
2629 tokens = subprocess.check_output(cmd)
@@ -54,13 +57,14 @@ def add_local_aubio_lib(ext): · 27 unmodified lines
5457
5558 def add_local_aubio_sources(ext):
5659 """ 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')))
6363 ext.sources += aubio_sources
64+
65+def add_local_macros(ext, usedouble = False):
66+ if usedouble:
67+ ext.define_macros += [('HAVE_AUBIO_DOUBLE', 1)]
6468 # define macros (waf puts them in build/src/config.h)
6569 for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',
6670 'HAVE_MATH_H', 'HAVE_STRING_H',
@@ -69,22 +73,31 @@ def add_local_aubio_sources(ext): · 2 unmodified lines
6973 'HAVE_MEMCPY_HACKS']:
7074 ext.define_macros += [(define_macro, 1)]
7175
76+def add_external_deps(ext, usedouble = False):
7277 # loof for additional packages
7378 print("Info: looking for *optional* additional packages")
74− packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample',
79+ packages = ['libavcodec', 'libavformat', 'libavutil',
80+ 'libswresample', 'libavresample',
7581 'jack',
76− 'sndfile', 'samplerate',
82+ 'sndfile',
7783 'rubberband',
7884 #'fftw3f',
7985 ]
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")
8091 add_packages(packages, ext=ext)
8192 if 'avcodec' in ext.libraries \
8293 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)]
88101 if 'sndfile' in ext.libraries:
89102 ext.define_macros += [('HAVE_SNDFILE', 1)]
90103 if 'samplerate' in ext.libraries:
@@ -107,39 +120,78 @@ def add_local_aubio_sources(ext): · 16 unmodified lines
107120
108121 ext.define_macros += [('HAVE_WAVWRITE', 1)]
109122 ext.define_macros += [('HAVE_WAVREAD', 1)]
110− # TODO:
111− # add cblas
123+
124+ # TODO: add cblas
112125 if 0:
113126 ext.libraries += ['cblas']
114127 ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)]
115128
116129 def add_system_aubio(ext):
117130 # 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)
119133 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)]
121143
122144 class CleanGenerated(distutils.command.clean.clean):
123145 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)
148+
149+from distutils.command.build_ext import build_ext as _build_ext
150+class build_ext(_build_ext):
126151
127−class GenerateCommand(distutils.cmd.Command):
128− description = 'generate gen/gen-*.c files from ../src/aubio.h'
129− user_options = [
152+ user_options = _build_ext.user_options + [
130153 # The format is (long option, short option, description).
131154 ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'),
132155 ]
133156
134157 def initialize_options(self):
158+ _build_ext.initialize_options(self)
135159 self.enable_double = False
136160
137161 def finalize_options(self):
162+ _build_ext.finalize_options(self)
138163 if self.enable_double:
139164 self.announce(
140165 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1',
141166 level=distutils.log.INFO)
142167
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)
python/scripts/aubiocut+0−206
@@ -1,206 +0,0 @@
1−#! /usr/bin/env python
2−
3−""" this file was written by Paul Brossier
4− it is released under the GNU/GPL license.
5−"""
6−
7−import sys
8−#from aubio.task import *
9−
10−usage = "usage: %s [options] -i soundfile" % sys.argv[0]
11−usage += "\n help: %s -h" % sys.argv[0]
12−
13−def parse_args():
14− from optparse import OptionParser
15− parser = OptionParser(usage=usage)
16− parser.add_option("-i", "--input", action = "store", dest = "source_file",
17− help="input sound file to analyse", metavar = "<source_file>")
18− parser.add_option("-O","--onset-method",
19− action="store", dest="onset_method", default='default',
20− metavar = "<onset_method>",
21− help="onset detection method [default=default] \
22− complexdomain|hfc|phase|specdiff|energy|kl|mkl")
23− # cutting methods
24− parser.add_option("-b","--beat",
25− action="store_true", dest="beat", default=False,
26− help="use beat locations")
27− """
28− parser.add_option("-S","--silencecut",
29− action="store_true", dest="silencecut", default=False,
30− help="use silence locations")
31− parser.add_option("-s","--silence",
32− metavar = "<value>",
33− action="store", dest="silence", default=-70,
34− help="silence threshold [default=-70]")
35− """
36− # algorithm parameters
37− parser.add_option("-r", "--samplerate",
38− metavar = "<freq>", type='int',
39− action="store", dest="samplerate", default=0,
40− help="samplerate at which the file should be represented")
41− parser.add_option("-B","--bufsize",
42− action="store", dest="bufsize", default=512,
43− metavar = "<size>", type='int',
44− help="buffer size [default=512]")
45− parser.add_option("-H","--hopsize",
46− metavar = "<size>", type='int',
47− action="store", dest="hopsize", default=256,
48− help="overlap size [default=256]")
49− parser.add_option("-t","--onset-threshold",
50− metavar = "<value>", type="float",
51− action="store", dest="threshold", default=0.3,
52− help="onset peak picking threshold [default=0.3]")
53− parser.add_option("-c","--cut",
54− action="store_true", dest="cut", default=False,
55− help="cut input sound file at detected labels \
56− best used with option -L")
57−
58− # minioi
59− parser.add_option("-M","--minioi",
60− metavar = "<value>", type='string',
61− action="store", dest="minioi", default="12ms",
62− help="minimum inter onset interval [default=12ms]")
63−
64− """
65− parser.add_option("-D","--delay",
66− action = "store", dest = "delay", type = "float",
67− metavar = "<seconds>", default=0,
68− help="number of seconds to take back [default=system]\
69− default system delay is 3*hopsize/samplerate")
70− parser.add_option("-C","--dcthreshold",
71− metavar = "<value>",
72− action="store", dest="dcthreshold", default=1.,
73− help="onset peak picking DC component [default=1.]")
74− parser.add_option("-L","--localmin",
75− action="store_true", dest="localmin", default=False,
76− help="use local minima after peak detection")
77− parser.add_option("-d","--derivate",
78− action="store_true", dest="derivate", default=False,
79− help="derivate onset detection function")
80− parser.add_option("-z","--zerocross",
81− metavar = "<value>",
82− action="store", dest="zerothres", default=0.008,
83− help="zero-crossing threshold for slicing [default=0.00008]")
84− """
85− # plotting functions
86− """
87− parser.add_option("-p","--plot",
88− action="store_true", dest="plot", default=False,
89− help="draw plot")
90− parser.add_option("-x","--xsize",
91− metavar = "<size>",
92− action="store", dest="xsize", default=1.,
93− type='float', help="define xsize for plot")
94− parser.add_option("-y","--ysize",
95− metavar = "<size>",
96− action="store", dest="ysize", default=1.,
97− type='float', help="define ysize for plot")
98− parser.add_option("-f","--function",
99− action="store_true", dest="func", default=False,
100− help="print detection function")
101− parser.add_option("-n","--no-onsets",
102− action="store_true", dest="nplot", default=False,
103− help="do not plot detected onsets")
104− parser.add_option("-O","--outplot",
105− metavar = "<output_image>",
106− action="store", dest="outplot", default=None,
107− help="save plot to output.{ps,png}")
108− parser.add_option("-F","--spectrogram",
109− action="store_true", dest="spectro", default=False,
110− help="add spectrogram to the plot")
111− """
112− parser.add_option("-o","--output", type = str,
113− metavar = "<outputdir>",
114− action="store", dest="output_directory", default=None,
115− help="specify path where slices of the original file should be created")
116− parser.add_option("--cut-until-nsamples", type = int,
117− metavar = "<samples>",
118− action = "store", dest = "cut_until_nsamples", default = None,
119− help="how many extra samples should be added at the end of each slice")
120− parser.add_option("--cut-until-nslices", type = int,
121− metavar = "<slices>",
122− action = "store", dest = "cut_until_nslices", default = None,
123− help="how many extra slices should be added at the end of each slice")
124−
125− parser.add_option("-v","--verbose",
126− action="store_true", dest="verbose", default=True,
127− help="make lots of noise [default]")
128− parser.add_option("-q","--quiet",
129− action="store_false", dest="verbose", default=True,
130− help="be quiet")
131− (options, args) = parser.parse_args()
132− if not options.source_file:
133− import os.path
134− if len(args) == 1:
135− options.source_file = args[0]
136− else:
137− print ("no file name given\n" + usage)
138− sys.exit(1)
139− return options, args
140−
141−if __name__ == '__main__':
142− options, args = parse_args()
143−
144− hopsize = options.hopsize
145− bufsize = options.bufsize
146− samplerate = options.samplerate
147− source_file = options.source_file
148−
149− from aubio import onset, tempo, source, sink
150−
151− s = source(source_file, samplerate, hopsize)
152− if samplerate == 0: samplerate = s.get_samplerate()
153−
154− if options.beat:
155− o = tempo(options.onset_method, bufsize, hopsize)
156− else:
157− o = onset(options.onset_method, bufsize, hopsize)
158− if options.minioi:
159− if options.minioi.endswith('ms'):
160− o.set_minioi_ms(int(options.minioi[:-2]))
161− elif options.minioi.endswith('s'):
162− o.set_minioi_s(int(options.minioi[:-1]))
163− else:
164− o.set_minioi(int(options.minioi))
165− o.set_threshold(options.threshold)
166−
167− timestamps = []
168− total_frames = 0
169− # analyze pass
170− while True:
171− samples, read = s()
172− if o(samples):
173− timestamps.append (o.get_last())
174− if options.verbose: print ("%.4f" % o.get_last_s())
175− total_frames += read
176− if read < hopsize: break
177− del s
178− # print some info
179− nstamps = len(timestamps)
180− duration = float (total_frames) / float(samplerate)
181− info = 'found %(nstamps)d timestamps in %(source_file)s' % locals()
182− info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals()
183− sys.stderr.write(info)
184−
185− # cutting pass
186− if options.cut and nstamps > 0:
187− # generate output files
188− from aubio.slicing import slice_source_at_stamps
189− timestamps_end = None
190− if options.cut_until_nslices and options.cut_until_nsamples:
191− print ("warning: using cut_until_nslices, but cut_until_nsamples is set")
192− if options.cut_until_nsamples:
193− timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]]
194− timestamps_end += [ 1e120 ]
195− if options.cut_until_nslices:
196− timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]]
197− timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1)
198− slice_source_at_stamps(source_file, timestamps, timestamps_end = timestamps_end,
199− output_dir = options.output_directory,
200− samplerate = samplerate)
201−
202− # print some info
203− duration = float (total_frames) / float(samplerate)
204− info = 'created %(nstamps)d slices from %(source_file)s' % locals()
205− info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals()
206− sys.stderr.write(info)
python/tests/__init__.py+1−0
@@ -0,0 +1 @@
1+
python/tests/eval_pitch+1−1
@@ -24,7 +24,7 @@ import sys · 23 unmodified lines
2424 import time
2525 import os.path
2626 import numpy
27−from utils import array_from_text_file, array_from_yaml_file
27+from .utils import array_from_text_file, array_from_yaml_file
2828 from aubio import source, pitch, freqtomidi
2929
3030 start = time.time()
python/tests/test_aubio.py+5−0
@@ -9,6 +9,11 @@ class aubiomodule_test_case(TestCase): · 8 unmodified lines
99 """ try importing aubio """
1010 import aubio
1111
12+ def test_version(self):
13+ """ test aubio.version """
14+ import aubio
15+ self.assertEqual('0', aubio.version[0])
16+
1217 if __name__ == '__main__':
1318 main()
1419
python/tests/test_aubio_cmd.py+34−0
@@ -0,0 +1,34 @@
1+#! /usr/bin/env python
2+
3+import aubio.cmd
4+from nose2 import main
5+from numpy.testing import TestCase
6+
7+class aubio_cmd(TestCase):
8+
9+ def setUp(self):
10+ self.a_parser = aubio.cmd.aubio_parser()
11+
12+ def test_default_creation(self):
13+ try:
14+ assert self.a_parser.parse_args(['-V']).show_version
15+ except SystemExit:
16+ url = 'https://bugs.python.org/issue9253'
17+ self.skipTest('subcommand became optional in py3, see %s' % url)
18+
19+class aubio_cmd_utils(TestCase):
20+
21+ def test_samples2seconds(self):
22+ self.assertEqual(aubio.cmd.samples2seconds(3200, 32000),
23+ "0.100000\t")
24+
25+ def test_samples2milliseconds(self):
26+ self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000),
27+ "100.000000\t")
28+
29+ def test_samples2samples(self):
30+ self.assertEqual(aubio.cmd.samples2samples(3200, 32000),
31+ "3200\t")
32+
33+if __name__ == '__main__':
34+ main()
python/tests/test_aubio_cut.py+16−0
@@ -0,0 +1,16 @@
1+#! /usr/bin/env python
2+
3+import aubio.cut
4+from nose2 import main
5+from numpy.testing import TestCase
6+
7+class aubio_cut(TestCase):
8+
9+ def setUp(self):
10+ self.a_parser = aubio.cut.aubio_cut_parser()
11+
12+ def test_default_creation(self):
13+ assert self.a_parser.parse_args(['-v']).verbose
14+
15+if __name__ == '__main__':
16+ main()
python/tests/test_dct.py+68−0
@@ -0,0 +1,68 @@
1+#! /usr/bin/env python
2+
3+
4+import numpy as np
5+from numpy.testing import TestCase, assert_almost_equal
6+import aubio
7+
8+precomputed_arange = [ 9.89949512, -6.44232273, 0., -0.67345482, 0.,
9+ -0.20090288, 0., -0.05070186]
10+
11+precomputed_some_ones = [ 4.28539848, 0.2469689, -0.14625292, -0.58121818,
12+ -0.83483052, -0.75921834, -0.35168475, 0.24087936,
13+ 0.78539824, 1.06532764, 0.97632152, 0.57164496, 0.03688532,
14+ -0.39446154, -0.54619485, -0.37771079]
15+
16+class aubio_dct(TestCase):
17+
18+ def test_init(self):
19+ """ test that aubio.dct() is created with expected size """
20+ a_dct = aubio.dct()
21+ self.assertEqual(a_dct.size, 1024)
22+
23+ def test_arange(self):
24+ """ test that dct(arange(8)) is computed correctly
25+
26+ >>> from scipy.fftpack import dct
27+ >>> a_in = np.arange(8).astype(aubio.float_type)
28+ >>> precomputed = dct(a_in, norm='ortho')
29+ """
30+ N = len(precomputed_arange)
31+ a_dct = aubio.dct(8)
32+ a_in = np.arange(8).astype(aubio.float_type)
33+ a_expected = aubio.fvec(precomputed_arange)
34+ assert_almost_equal(a_dct(a_in), a_expected, decimal=5)
35+
36+ def test_some_ones(self):
37+ """ test that dct(somevector) is computed correctly """
38+ a_dct = aubio.dct(16)
39+ a_in = np.ones(16).astype(aubio.float_type)
40+ a_in[1] = 0
41+ a_in[3] = np.pi
42+ a_expected = aubio.fvec(precomputed_some_ones)
43+ assert_almost_equal(a_dct(a_in), a_expected, decimal=6)
44+
45+ def test_reconstruction(self):
46+ """ test that some_ones vector can be recontructed """
47+ a_dct = aubio.dct(16)
48+ a_in = np.ones(16).astype(aubio.float_type)
49+ a_in[1] = 0
50+ a_in[3] = np.pi
51+ a_dct_in = a_dct(a_in)
52+ a_dct_reconstructed = a_dct.rdo(a_dct_in)
53+ assert_almost_equal(a_dct_reconstructed, a_in, decimal=6)
54+
55+ def test_negative_size(self):
56+ """ test that creation fails with a negative size """
57+ with self.assertRaises(ValueError):
58+ aubio.dct(-1)
59+
60+ def test_wrong_size(self):
61+ """ test that creation fails with a non power-of-two size """
62+ # supports for non 2** fft sizes only when compiled with fftw3
63+ size = 13
64+ try:
65+ with self.assertRaises(RuntimeError):
66+ aubio.dct(size)
67+ except AssertionError:
68+ self.skipTest('creating aubio.dct with size %d did not fail' % size)
python/tests/test_fft.py+39−12
@@ -33,7 +33,14 @@ class aubio_fft_test_case(TestCase): · 32 unmodified lines
3333 f = fft (win_s)
3434 fftgrain = f (timegrain)
3535 assert_equal ( fftgrain.norm, 0 )
36− assert_equal ( fftgrain.phas, 0 )
36+ try:
37+ assert_equal ( fftgrain.phas, 0 )
38+ except AssertionError:
39+ assert_equal (fftgrain.phas[fftgrain.phas > 0], +pi)
40+ assert_equal (fftgrain.phas[fftgrain.phas < 0], -pi)
41+ assert_equal (np.abs(fftgrain.phas[np.abs(fftgrain.phas) != pi]), 0)
42+ self.skipTest('fft(fvec(%d)).phas != +0, ' % win_s \
43+ + 'This is expected when using fftw3 on powerpc.')
3744
3845 def test_impulse(self):
3946 """ check the transform of one impulse at a random place """
@@ -135,6 +142,37 @@ class aubio_fft_test_case(TestCase): · 95 unmodified lines
135142 assert_almost_equal ( r[0], impulse, decimal = 6)
136143 assert_almost_equal ( r[1:], 0)
137144
145+class aubio_fft_odd_sizes(TestCase):
146+
147+ def test_reconstruct_with_odd_size(self):
148+ win_s = 29
149+ self.recontruct(win_s, 'odd sizes not supported')
150+
151+ def test_reconstruct_with_radix15(self):
152+ win_s = 2 ** 4 * 15
153+ self.recontruct(win_s, 'radix 15 supported')
154+
155+ def test_reconstruct_with_radix5(self):
156+ win_s = 2 ** 4 * 5
157+ self.recontruct(win_s, 'radix 5 supported')
158+
159+ def test_reconstruct_with_radix3(self):
160+ win_s = 2 ** 4 * 3
161+ self.recontruct(win_s, 'radix 3 supported')
162+
163+ def recontruct(self, win_s, skipMessage):
164+ try:
165+ f = fft(win_s)
166+ except RuntimeError:
167+ self.skipTest(skipMessage)
168+ input_signal = fvec(win_s)
169+ input_signal[win_s//2] = 1
170+ c = f(input_signal)
171+ output_signal = f.rdo(c)
172+ assert_almost_equal(input_signal, output_signal)
173+
174+class aubio_fft_wrong_params(TestCase):
175+
138176 def test_large_input_timegrain(self):
139177 win_s = 1024
140178 f = fft(win_s)
@@ -163,22 +201,11 @@ class aubio_fft_test_case(TestCase): · 22 unmodified lines
163201 with self.assertRaises(ValueError):
164202 f.rdo(s)
165203
166−class aubio_fft_wrong_params(TestCase):
167−
168204 def test_wrong_buf_size(self):
169205 win_s = -1
170206 with self.assertRaises(ValueError):
171207 fft(win_s)
172208
173− def test_buf_size_not_power_of_two(self):
174− # when compiled with fftw3, aubio supports non power of two fft sizes
175− win_s = 320
176− try:
177− with self.assertRaises(RuntimeError):
178− fft(win_s)
179− except AssertionError:
180− self.skipTest('creating aubio.fft with size %d did not fail' % win_s)
181−
182209 def test_buf_size_too_small(self):
183210 win_s = 1
184211 with self.assertRaises(RuntimeError):
python/tests/test_filter.py+11−1
@@ -3,7 +3,7 @@ · 2 unmodified lines
33 from unittest import main
44 from numpy.testing import TestCase, assert_equal, assert_almost_equal
55 from aubio import fvec, digital_filter
6−from utils import array_from_text_file
6+from .utils import array_from_text_file
77
88 class aubio_filter_test_case(TestCase):
99
@@ -77,6 +77,16 @@ class aubio_filter_test_case(TestCase): · 67 unmodified lines
7777 with self.assertRaises(ValueError):
7878 f.set_biquad(0., 0., 0, 0., 0.)
7979
80+ def test_all_available_presets(self):
81+ f = digital_filter(7)
82+ for sr in [8000, 11025, 16000, 22050, 24000, 32000,
83+ 44100, 48000, 88200, 96000, 192000]:
84+ f.set_a_weighting(sr)
85+ f = digital_filter(5)
86+ for sr in [8000, 11025, 16000, 22050, 24000, 32000,
87+ 44100, 48000, 88200, 96000, 192000]:
88+ f.set_c_weighting(sr)
89+
8090 class aubio_filter_wrong_params(TestCase):
8191
8292 def test_negative_order(self):
python/tests/test_filterbank.py+12−5
@@ -1,11 +1,10 @@
11 #! /usr/bin/env python
22
3−from unittest import main
4−from numpy.testing import TestCase
5−from numpy.testing import assert_equal, assert_almost_equal
63 import numpy as np
4+from numpy.testing import TestCase, assert_equal, assert_almost_equal
5+
76 from aubio import cvec, filterbank, float_type
8−from utils import array_from_text_file
7+from .utils import array_from_text_file
98
109 class aubio_filterbank_test_case(TestCase):
1110
@@ -62,6 +61,13 @@ class aubio_filterbank_test_case(TestCase): · 50 unmodified lines
6261 f.set_mel_coeffs_slaney(16000)
6362 assert_almost_equal ( expected, f.get_coeffs() )
6463
64+ def test_mfcc_coeffs_get_coeffs(self):
65+ f = filterbank(40, 512)
66+ coeffs = f.get_coeffs()
67+ self.assertIsInstance(coeffs, np.ndarray)
68+ assert_equal (coeffs, 0)
69+ assert_equal (np.shape(coeffs), (40, 512 / 2 + 1))
70+
6571 class aubio_filterbank_wrong_values(TestCase):
6672
6773 def test_negative_window(self):
@@ -81,4 +87,5 @@ class aubio_filterbank_wrong_values(TestCase): · 13 unmodified lines
8187 f(cvec(256))
8288
8389 if __name__ == '__main__':
84− main()
90+ import nose2
91+ nose2.main()
python/tests/test_filterbank_mel.py+136−13
@@ -1,10 +1,12 @@
11 #! /usr/bin/env python
22
3−from unittest import main
4−from numpy.testing import TestCase
5−from numpy.testing import assert_equal, assert_almost_equal
6−from numpy import array, shape
7−from aubio import cvec, filterbank, float_type
3+import numpy as np
4+from numpy.testing import TestCase, assert_equal, assert_almost_equal
5+
6+from aubio import fvec, cvec, filterbank, float_type
7+
8+import warnings
9+warnings.filterwarnings('ignore', category=UserWarning, append=True)
810
911 class aubio_filterbank_mel_test_case(TestCase):
1012
@@ -12,38 +14,159 @@ class aubio_filterbank_mel_test_case(TestCase): · 1 unmodified line
1214 f = filterbank(40, 512)
1315 f.set_mel_coeffs_slaney(16000)
1416 a = f.get_coeffs()
15− assert_equal(shape (a), (40, 512/2 + 1) )
17+ assert_equal(np.shape (a), (40, 512/2 + 1) )
1618
1719 def test_other_slaney(self):
1820 f = filterbank(40, 512*2)
1921 f.set_mel_coeffs_slaney(44100)
20− _ = f.get_coeffs()
22+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
2123 #print "sum is", sum(sum(a))
2224 for win_s in [256, 512, 1024, 2048, 4096]:
2325 f = filterbank(40, win_s)
2426 f.set_mel_coeffs_slaney(32000)
25− _ = f.get_coeffs()
2627 #print "sum is", sum(sum(a))
28+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
2729
2830 def test_triangle_freqs_zeros(self):
2931 f = filterbank(9, 1024)
3032 freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000]
31− freqs = array(freq_list, dtype = float_type)
33+ freqs = np.array(freq_list, dtype = float_type)
3234 f.set_triangle_bands(freqs, 48000)
33− _ = f.get_coeffs().T
3435 assert_equal ( f(cvec(1024)), 0)
36+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
3537
3638 def test_triangle_freqs_ones(self):
3739 f = filterbank(9, 1024)
3840 freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000]
39− freqs = array(freq_list, dtype = float_type)
41+ freqs = np.array(freq_list, dtype = float_type)
4042 f.set_triangle_bands(freqs, 48000)
41− _ = f.get_coeffs().T
43+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
4244 spec = cvec(1024)
4345 spec.norm[:] = 1
4446 assert_almost_equal ( f(spec),
4547 [ 0.02070313, 0.02138672, 0.02127604, 0.02135417,
4648 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
4749
50+ def test_triangle_freqs_with_zeros(self):
51+ """make sure set_triangle_bands works when list starts with 0"""
52+ freq_list = [0, 40, 80]
53+ freqs = np.array(freq_list, dtype = float_type)
54+ f = filterbank(len(freqs)-2, 1024)
55+ f.set_triangle_bands(freqs, 48000)
56+ assert_equal ( f(cvec(1024)), 0)
57+ self.assertIsInstance(f.get_coeffs(), np.ndarray)
58+
59+ def test_triangle_freqs_with_wrong_negative(self):
60+ """make sure set_triangle_bands fails when list contains a negative"""
61+ freq_list = [-10, 0, 80]
62+ f = filterbank(len(freq_list)-2, 1024)
63+ with self.assertRaises(ValueError):
64+ f.set_triangle_bands(fvec(freq_list), 48000)
65+
66+ def test_triangle_freqs_with_wrong_ordering(self):
67+ """make sure set_triangle_bands fails when list not ordered"""
68+ freq_list = [0, 80, 40]
69+ f = filterbank(len(freq_list)-2, 1024)
70+ with self.assertRaises(ValueError):
71+ f.set_triangle_bands(fvec(freq_list), 48000)
72+
73+ def test_triangle_freqs_with_large_freq(self):
74+ """make sure set_triangle_bands warns when freq > nyquist"""
75+ samplerate = 22050
76+ freq_list = [0, samplerate//4, samplerate // 2 + 1]
77+ f = filterbank(len(freq_list)-2, 1024)
78+ # TODO add assert_warns
79+ f.set_triangle_bands(fvec(freq_list), samplerate)
80+
81+ def test_triangle_freqs_with_not_enough_filters(self):
82+ """make sure set_triangle_bands warns when not enough filters"""
83+ samplerate = 22050
84+ freq_list = [0, 100, 1000, 4000, 8000, 10000]
85+ f = filterbank(len(freq_list)-3, 1024)
86+ # TODO add assert_warns
87+ f.set_triangle_bands(fvec(freq_list), samplerate)
88+
89+ def test_triangle_freqs_with_too_many_filters(self):
90+ """make sure set_triangle_bands warns when too many filters"""
91+ samplerate = 22050
92+ freq_list = [0, 100, 1000, 4000, 8000, 10000]
93+ f = filterbank(len(freq_list)-1, 1024)
94+ # TODO add assert_warns
95+ f.set_triangle_bands(fvec(freq_list), samplerate)
96+
97+ def test_triangle_freqs_with_double_value(self):
98+ """make sure set_triangle_bands works with 2 duplicate freqs"""
99+ samplerate = 22050
100+ freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
101+ f = filterbank(len(freq_list)-2, 1024)
102+ # TODO add assert_warns
103+ f.set_triangle_bands(fvec(freq_list), samplerate)
104+
105+ def test_triangle_freqs_with_triple(self):
106+ """make sure set_triangle_bands works with 3 duplicate freqs"""
107+ samplerate = 22050
108+ freq_list = [0, 100, 1000, 4000, 4000, 4000, 10000]
109+ f = filterbank(len(freq_list)-2, 1024)
110+ # TODO add assert_warns
111+ f.set_triangle_bands(fvec(freq_list), samplerate)
112+
113+ def test_triangle_freqs_without_norm(self):
114+ """make sure set_triangle_bands works without """
115+ samplerate = 22050
116+ freq_list = fvec([0, 100, 1000, 10000])
117+ f = filterbank(len(freq_list) - 2, 1024)
118+ f.set_norm(0)
119+ f.set_triangle_bands(freq_list, samplerate)
120+ expected = f.get_coeffs()
121+ f.set_norm(1)
122+ f.set_triangle_bands(fvec(freq_list), samplerate)
123+ assert_almost_equal(f.get_coeffs().T,
124+ expected.T * 2. / (freq_list[2:] - freq_list[:-2]))
125+
126+ def test_triangle_freqs_wrong_norm(self):
127+ f = filterbank(10, 1024)
128+ with self.assertRaises(ValueError):
129+ f.set_norm(-1)
130+
131+ def test_triangle_freqs_with_power(self):
132+ f = filterbank(9, 1024)
133+ freqs = fvec([40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000,
134+ 24000])
135+ f.set_power(2)
136+ f.set_triangle_bands(freqs, 48000)
137+ spec = cvec(1024)
138+ spec.norm[:] = .1
139+ expected = fvec([0.02070313, 0.02138672, 0.02127604, 0.02135417,
140+ 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
141+ expected /= 100.
142+ assert_almost_equal(f(spec), expected)
143+
144+ def test_mel_coeffs(self):
145+ f = filterbank(40, 1024)
146+ f.set_mel_coeffs(44100, 0, 44100 / 2)
147+
148+ def test_zero_fmax(self):
149+ f = filterbank(40, 1024)
150+ f.set_mel_coeffs(44100, 0, 0)
151+
152+ def test_wrong_mel_coeffs(self):
153+ f = filterbank(40, 1024)
154+ with self.assertRaises(ValueError):
155+ f.set_mel_coeffs_slaney(0)
156+ with self.assertRaises(ValueError):
157+ f.set_mel_coeffs(44100, 0, -44100 / 2)
158+ with self.assertRaises(ValueError):
159+ f.set_mel_coeffs(44100, -0.1, 44100 / 2)
160+ with self.assertRaises(ValueError):
161+ f.set_mel_coeffs(-44100, 0.1, 44100 / 2)
162+ with self.assertRaises(ValueError):
163+ f.set_mel_coeffs_htk(-1, 0, 0)
164+
165+ def test_mel_coeffs_htk(self):
166+ f = filterbank(40, 1024)
167+ f.set_mel_coeffs_htk(44100, 0, 44100 / 2)
168+
169+
48170 if __name__ == '__main__':
49− main()
171+ import nose2
172+ nose2.main()
python/tests/test_fvec.py+8−0
@@ -60,6 +60,14 @@ class aubio_fvec_wrong_values(TestCase): · 59 unmodified lines
6060 self.assertRaises(IndexError, a.__getitem__, 3)
6161 self.assertRaises(IndexError, a.__getitem__, 2)
6262
63+ def test_wrong_dimensions(self):
64+ a = np.array([[[1, 2], [3, 4]]], dtype=float_type)
65+ self.assertRaises(ValueError, fvec, a)
66+
67+ def test_wrong_size(self):
68+ a = np.ndarray([0,], dtype=float_type)
69+ self.assertRaises(ValueError, fvec, a)
70+
6371 class aubio_wrong_fvec_input(TestCase):
6472 """ uses min_removal to test PyAubio_IsValidVector """
6573
python/tests/test_fvec_shift.py+35−0
@@ -0,0 +1,35 @@
1+#! /usr/bin/env python
2+
3+import numpy as np
4+from numpy.testing import TestCase, assert_equal
5+import aubio
6+
7+class aubio_shift_test_case(TestCase):
8+
9+ def run_shift_ishift(self, n):
10+ ramp = np.arange(n, dtype=aubio.float_type)
11+ # construct expected output
12+ # even length: [5. 6. 7. 8. 9. 0. 1. 2. 3. 4.]
13+ # odd length: [4. 5. 6. 0. 1. 2. 3.]
14+ half = n - n//2
15+ expected = np.concatenate([np.arange(half, n), np.arange(half)])
16+ # shift in place, returns modified copy
17+ assert_equal(aubio.shift(ramp), expected)
18+ # check input was changed as expected
19+ assert_equal(ramp, expected)
20+ # construct expected output
21+ expected = np.arange(n)
22+ # revert shift in place, returns modifed copy
23+ assert_equal(aubio.ishift(ramp), expected)
24+ # check input was shifted back
25+ assert_equal(ramp, expected)
26+
27+ def test_can_shift_fvec(self):
28+ self.run_shift_ishift(10)
29+
30+ def test_can_shift_fvec_odd(self):
31+ self.run_shift_ishift(7)
32+
33+from unittest import main
34+if __name__ == '__main__':
35+ main()
python/tests/test_hztomel.py+109−0
@@ -0,0 +1,109 @@
1+#! /usr/bin/env python
2+
3+from unittest import main
4+from numpy.testing import TestCase
5+from numpy.testing import assert_equal, assert_almost_equal
6+import numpy as np
7+import aubio
8+
9+from aubio import hztomel, meltohz
10+from aubio import hztomel_htk, meltohz_htk
11+
12+
13+class aubio_hztomel_test_case(TestCase):
14+
15+ def test_hztomel(self):
16+ assert_equal(hztomel(0.), 0.)
17+ assert_almost_equal(hztomel(400. / 3.), 2., decimal=5)
18+ assert_almost_equal(hztomel(1000. / 3), 5.)
19+ assert_equal(hztomel(200.), 3.)
20+ assert_almost_equal(hztomel(1000.), 15)
21+ assert_almost_equal(hztomel(6400), 42)
22+ assert_almost_equal(hztomel(40960), 69)
23+
24+ for m in np.linspace(0, 1000, 100):
25+ assert_almost_equal(hztomel(meltohz(m)) - m, 0, decimal=3)
26+
27+ def test_meltohz(self):
28+ assert_equal(meltohz(0.), 0.)
29+ assert_almost_equal(meltohz(2), 400. / 3., decimal=4)
30+ assert_equal(meltohz(3.), 200.)
31+ assert_almost_equal(meltohz(5), 1000. / 3., decimal=4)
32+ assert_almost_equal(meltohz(15), 1000., decimal=4)
33+ assert_almost_equal(meltohz(42), 6400., decimal=2)
34+ assert_almost_equal(meltohz(69), 40960., decimal=1)
35+
36+ for f in np.linspace(0, 20000, 1000):
37+ assert_almost_equal(meltohz(hztomel(f)) - f, 0, decimal=1)
38+
39+ def test_meltohz_negative(self):
40+ # TODO add assert_warns
41+ assert_equal(meltohz(-1), 0)
42+
43+ def test_hztomel_negative(self):
44+ # TODO add assert_warns
45+ assert_equal(hztomel(-1), 0)
46+
47+
48+class aubio_hztomel_htk_test_case(TestCase):
49+
50+ def test_meltohz(self):
51+ assert_equal(meltohz(0, htk=True), 0)
52+ assert_almost_equal(meltohz(2595, htk=True), 6300., decimal=1)
53+
54+ def test_hztomel(self):
55+ assert_equal(hztomel(0, htk=True), 0)
56+ assert_almost_equal(hztomel(3428.7, htk=True), 2000., decimal=1)
57+ assert_almost_equal(hztomel(6300, htk=True), 2595., decimal=1)
58+
59+ def test_meltohz_negative(self):
60+ # TODO add assert_warns
61+ assert_equal(meltohz(-1, htk=True), 0)
62+ assert_almost_equal(meltohz(2000, htk=True), 3428.7, decimal=1)
63+ assert_almost_equal(meltohz(1000, htk=True), 1000., decimal=1)
64+
65+ def test_hztomel_negative(self):
66+ # TODO add assert_warns
67+ assert_equal(hztomel(-1, htk=True), 0)
68+ assert_almost_equal(hztomel(1000, htk=True), 1000., decimal=1)
69+
70+ def test_hztomel_htk(self):
71+ for f in np.linspace(0, 20000, 1000):
72+ assert_almost_equal(meltohz_htk(hztomel_htk(f)) - f, 0, decimal=1)
73+ for f in np.linspace(0, 20000, 1000):
74+ assert_almost_equal(hztomel_htk(meltohz_htk(f)) - f, 0, decimal=1)
75+
76+
77+class aubio_hztomel_wrong_values(TestCase):
78+ """ more tests to cover all branches """
79+
80+ def test_hztomel_wrong_values(self):
81+ with self.assertRaises(TypeError):
82+ hztomel('s')
83+
84+ def test_meltohz_wrong_values(self):
85+ with self.assertRaises(TypeError):
86+ meltohz(bytes('ad'))
87+
88+ def test_meltohz_no_arg(self):
89+ with self.assertRaises(TypeError):
90+ meltohz()
91+
92+ def test_meltohz_htk_no_arg(self):
93+ with self.assertRaises(TypeError):
94+ meltohz_htk()
95+
96+ def test_hztomel_htk_wrong_values(self):
97+ with self.assertRaises(TypeError):
98+ hztomel_htk('0')
99+
100+ def test_hztomel_htk_false(self):
101+ assert hztomel(120, htk=False) == hztomel(120)
102+
103+ def test_meltohz_htk_false(self):
104+ assert meltohz(12, htk=False) == meltohz(12)
105+
106+
107+if __name__ == '__main__':
108+ from unittest import main
109+ main()
python/tests/test_mfcc.py+37−0
@@ -110,5 +110,42 @@ class aubio_mfcc_all_parameters(TestCase): · 109 unmodified lines
110110 o(spec)
111111 #print coeffs
112112
113+
114+class aubio_mfcc_fb_params(TestCase):
115+
116+ def test_set_scale(self):
117+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
118+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
119+ m.set_scale(10.5)
120+ assert m.get_scale() == 10.5
121+ m(cvec(buf_size))
122+
123+ def test_set_power(self):
124+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
125+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
126+ m.set_power(2.5)
127+ assert m.get_power() == 2.5
128+ m(cvec(buf_size))
129+
130+ def test_set_mel_coeffs(self):
131+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
132+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
133+ m.set_mel_coeffs(0., samplerate/2.)
134+ m(cvec(buf_size))
135+
136+ def test_set_mel_coeffs_htk(self):
137+ buf_size, n_filters, n_coeffs, samplerate = 512, 20, 10, 16000
138+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
139+ m.set_mel_coeffs_htk(0., samplerate/2.)
140+ m(cvec(buf_size))
141+
142+ def test_set_mel_coeffs_slaney(self):
143+ buf_size, n_filters, n_coeffs, samplerate = 512, 40, 10, 16000
144+ m = mfcc(buf_size, n_filters, n_coeffs, samplerate)
145+ m.set_mel_coeffs_slaney()
146+ m(cvec(buf_size))
147+ assert m.get_power() == 1
148+ assert m.get_scale() == 1
149+
113150 if __name__ == '__main__':
114151 main()
python/tests/test_midi2note.py+6−4
@@ -2,6 +2,7 @@ · 1 unmodified line
22 # -*- coding: utf-8 -*-
33
44 from aubio import midi2note
5+from nose2.tools import params
56 import unittest
67
78 list_of_known_midis = (
@@ -16,10 +17,10 @@ list_of_known_midis = ( · 8 unmodified lines
1617
1718 class midi2note_good_values(unittest.TestCase):
1819
19− def test_midi2note_known_values(self):
20+ @params(*list_of_known_midis)
21+ def test_midi2note_known_values(self, midi, note):
2022 " known values are correctly converted "
21− for midi, note in list_of_known_midis:
22− self.assertEqual ( midi2note(midi), note )
23+ self.assertEqual ( midi2note(midi), note )
2324
2425 class midi2note_wrong_values(unittest.TestCase):
2526
@@ -40,4 +41,5 @@ class midi2note_wrong_values(unittest.TestCase): · 14 unmodified lines
4041 self.assertRaises(TypeError, midi2note, "a")
4142
4243 if __name__ == '__main__':
43− unittest.main()
44+ import nose2
45+ nose2.main()
python/tests/test_note2midi.py+67−7
@@ -3,7 +3,8 @@ · 2 unmodified lines
33
44 from __future__ import unicode_literals
55
6−from aubio import note2midi, freq2note
6+from aubio import note2midi, freq2note, note2freq, float_type
7+from nose2.tools import params
78 import unittest
89
910 list_of_known_notes = (
@@ -13,24 +14,57 @@ list_of_known_notes = ( · 3 unmodified lines
1314 ( 'C3', 48 ),
1415 ( 'B3', 59 ),
1516 ( 'B#3', 60 ),
17+ ( 'C♯4', 61 ),
1618 ( 'A4', 69 ),
1719 ( 'A#4', 70 ),
20+ ( 'A♯4', 70 ),
21+ ( 'A\u266f4', 70 ),
1822 ( 'Bb4', 70 ),
1923 ( 'B♭4', 70 ),
24+ ( 'B\u266d4', 70 ),
2025 ( 'G8', 115 ),
2126 ( 'G♯8', 116 ),
2227 ( 'G9', 127 ),
23− ( 'G\udd2a2', 45 ),
24− ( 'B\ufffd2', 45 ),
2528 ( 'A♮2', 45 ),
2629 )
2730
31+list_of_known_notes_with_unicode_issues = (
32+ ('C𝄪4', 62 ),
33+ ('E𝄫4', 62 ),
34+ )
35+
36+list_of_unknown_notes = (
37+ ( 'G\udd2a2' ),
38+ ( 'B\ufffd2' ),
39+ ( 'B\u266e\u266e2' ),
40+ ( 'B\u266f\u266d3' ),
41+ ( 'B33' ),
42+ ( 'C.3' ),
43+ ( 'A' ),
44+ ( '2' ),
45+ )
46+
2847 class note2midi_good_values(unittest.TestCase):
2948
30− def test_note2midi_known_values(self):
49+ @params(*list_of_known_notes)
50+ def test_note2midi_known_values(self, note, midi):
3151 " known values are correctly converted "
32− for note, midi in list_of_known_notes:
52+ self.assertEqual ( note2midi(note), midi )
53+
54+ @params(*list_of_known_notes_with_unicode_issues)
55+ def test_note2midi_known_values_with_unicode_issues(self, note, midi):
56+ " known values are correctly converted, unless decoding is expected to fail"
57+ try:
3358 self.assertEqual ( note2midi(note), midi )
59+ except UnicodeEncodeError as e:
60+ import sys
61+ strfmt = "len(u'\\U0001D12A') != 1, excpected decoding failure | {:s} | {:s} {:s}"
62+ strres = strfmt.format(e, sys.platform, sys.version)
63+ # happens with: darwin 2.7.10, windows 2.7.12
64+ if len('\U0001D12A') != 1 and sys.version[0] == '2':
65+ self.skipTest(strres + " | upgrade to Python 3 to fix")
66+ else:
67+ raise
3468
3569 class note2midi_wrong_values(unittest.TestCase):
3670
@@ -66,12 +100,38 @@ class note2midi_wrong_values(unittest.TestCase): · 29 unmodified lines
66100 " fails when passed a non-string value "
67101 self.assertRaises(TypeError, note2midi, 123)
68102
103+ def test_note2midi_wrong_data_too_long(self):
104+ " fails when passed a note with a note name longer than expected"
105+ self.assertRaises(ValueError, note2midi, 'CB+-3')
106+
107+ @params(*list_of_unknown_notes)
108+ def test_note2midi_unknown_values(self, note):
109+ " unknown values throw out an error "
110+ self.assertRaises(ValueError, note2midi, note)
69111
70112 class freq2note_simple_test(unittest.TestCase):
71113
72− def test_freq2note(self):
114+ def test_freq2note_above(self):
73115 " make sure freq2note(441) == A4 "
74116 self.assertEqual("A4", freq2note(441))
75117
118+ def test_freq2note_under(self):
119+ " make sure freq2note(439) == A4 "
120+ self.assertEqual("A4", freq2note(439))
121+
122+class note2freq_simple_test(unittest.TestCase):
123+
124+ def test_note2freq(self):
125+ " make sure note2freq('A3') == 220"
126+ self.assertEqual(220, note2freq("A3"))
127+
128+ def test_note2freq_under(self):
129+ " make sure note2freq(A4) == 440"
130+ if float_type == 'float32':
131+ self.assertEqual(440, note2freq("A4"))
132+ else:
133+ self.assertLess(abs(note2freq("A4")-440), 1.e-12)
134+
76135 if __name__ == '__main__':
77− unittest.main()
136+ import nose2
137+ nose2.main()
python/tests/test_notes.py+95−0
@@ -0,0 +1,95 @@
1+#! /usr/bin/env python
2+
3+from unittest import main
4+from numpy.testing import TestCase, assert_equal, assert_almost_equal
5+from aubio import notes
6+
7+AUBIO_DEFAULT_NOTES_SILENCE = -70.
8+AUBIO_DEFAULT_NOTES_RELEASE_DROP = 10.
9+AUBIO_DEFAULT_NOTES_MINIOI_MS = 30.
10+
11+class aubio_notes_default(TestCase):
12+
13+ def test_members(self):
14+ o = notes()
15+ assert_equal ([o.buf_size, o.hop_size, o.method, o.samplerate],
16+ [1024,512,'default',44100])
17+
18+
19+class aubio_notes_params(TestCase):
20+
21+ samplerate = 44100
22+
23+ def setUp(self):
24+ self.o = notes(samplerate = self.samplerate)
25+
26+ def test_get_minioi_ms(self):
27+ assert_equal (self.o.get_minioi_ms(), AUBIO_DEFAULT_NOTES_MINIOI_MS)
28+
29+ def test_set_minioi_ms(self):
30+ val = 40.
31+ self.o.set_minioi_ms(val)
32+ assert_almost_equal (self.o.get_minioi_ms(), val)
33+
34+ def test_get_silence(self):
35+ assert_equal (self.o.get_silence(), AUBIO_DEFAULT_NOTES_SILENCE)
36+
37+ def test_set_silence(self):
38+ val = -50
39+ self.o.set_silence(val)
40+ assert_equal (self.o.get_silence(), val)
41+
42+ def test_get_release_drop(self):
43+ assert_equal (self.o.get_release_drop(), AUBIO_DEFAULT_NOTES_RELEASE_DROP)
44+
45+ def test_set_release_drop(self):
46+ val = 50
47+ self.o.set_release_drop(val)
48+ assert_equal (self.o.get_release_drop(), val)
49+
50+ def test_set_release_drop_wrong(self):
51+ val = -10
52+ with self.assertRaises(ValueError):
53+ self.o.set_release_drop(val)
54+
55+from .utils import list_all_sounds
56+list_of_sounds = list_all_sounds('sounds')
57+
58+class aubio_notes_sinewave(TestCase):
59+
60+ def analyze_file(self, filepath, samplerate=0):
61+ from aubio import source
62+ import numpy as np
63+ win_s = 512 # fft size
64+ hop_s = 256 # hop size
65+
66+ s = source(filepath, samplerate, hop_s)
67+ samplerate = s.samplerate
68+
69+ tolerance = 0.8
70+
71+ notes_o = notes("default", win_s, hop_s, samplerate)
72+ total_frames = 0
73+
74+ results = []
75+ while True:
76+ samples, read = s()
77+ new_note = notes_o(samples)
78+ if (new_note[0] != 0):
79+ note_str = ' '.join(["%.2f" % i for i in new_note])
80+ results.append( [total_frames, np.copy(new_note)] )
81+ total_frames += read
82+ if read < hop_s: break
83+ return results
84+
85+ def test_sinewave(self):
86+ for filepath in list_of_sounds:
87+ if '44100Hz_44100f_sine441.wav' in filepath:
88+ results = self.analyze_file(filepath)
89+ assert_equal (len(results), 1)
90+ assert_equal (len(results[0]), 2)
91+ assert_equal (results[0][0], 1280)
92+ assert_equal (results[0][1], [69, 123, -1])
93+
94+if __name__ == '__main__':
95+ main()
python/tests/test_onset.py+40−8
@@ -2,7 +2,7 @@ · 1 unmodified line
22
33 from unittest import main
44 from numpy.testing import TestCase, assert_equal, assert_almost_equal
5−from aubio import onset
5+from aubio import onset, fvec
66
77 class aubio_onset_default(TestCase):
88
@@ -19,25 +19,25 @@ class aubio_onset_params(TestCase): · 10 unmodified lines
1919 self.o = onset(samplerate = self.samplerate)
2020
2121 def test_get_delay(self):
22− assert_equal (self.o.get_delay(), int(4.3 * self.o.hop_size))
22+ self.assertGreater(self.o.get_delay(), 0)
2323
2424 def test_get_delay_s(self):
25− assert_almost_equal (self.o.get_delay_s(), self.o.get_delay() / float(self.samplerate))
25+ self.assertGreater(self.o.get_delay_s(), 0.)
2626
2727 def test_get_delay_ms(self):
28− assert_almost_equal (self.o.get_delay_ms(), self.o.get_delay() * 1000. / self.samplerate, 5)
28+ self.assertGreater(self.o.get_delay_ms(), 0.)
2929
3030 def test_get_minioi(self):
31− assert_almost_equal (self.o.get_minioi(), 0.02 * self.samplerate)
31+ self.assertGreater(self.o.get_minioi(), 0)
3232
3333 def test_get_minioi_s(self):
34− assert_almost_equal (self.o.get_minioi_s(), 0.02)
34+ self.assertGreater(self.o.get_minioi_s(), 0.)
3535
3636 def test_get_minioi_ms(self):
37− assert_equal (self.o.get_minioi_ms(), 20.)
37+ self.assertGreater(self.o.get_minioi_ms(), 0.)
3838
3939 def test_get_threshold(self):
40− assert_almost_equal (self.o.get_threshold(), 0.3)
40+ self.assertGreater(self.o.get_threshold(), 0.)
4141
4242 def test_set_delay(self):
4343 val = 256
@@ -83,5 +83,37 @@ class aubio_onset_32000(aubio_onset_params): · 39 unmodified lines
8383 class aubio_onset_8000(aubio_onset_params):
8484 samplerate = 8000
8585
86+class aubio_onset_coverate(TestCase):
87+ # extra tests to execute the C routines and improve coverage
88+
89+ def test_all_methods(self):
90+ for method in ['default', 'energy', 'hfc', 'complexdomain', 'complex',
91+ 'phase', 'wphase', 'mkl', 'kl', 'specflux', 'specdiff',
92+ 'old_default']:
93+ o = onset(method=method, buf_size=512, hop_size=256)
94+ o(fvec(256))
95+
96+ def test_get_methods(self):
97+ o = onset(method='default', buf_size=512, hop_size=256)
98+
99+ assert o.get_silence() == -70
100+ o.set_silence(-20)
101+ assert_almost_equal(o.get_silence(), -20)
102+
103+ assert o.get_compression() == 1
104+ o.set_compression(.99)
105+ assert_almost_equal(o.get_compression(), .99)
106+
107+ assert o.get_awhitening() == 0
108+ o.set_awhitening(1)
109+ assert o.get_awhitening() == 1
110+
111+ o.get_last()
112+ o.get_last_ms()
113+ o.get_last_s()
114+ o.get_descriptor()
115+ o.get_thresholded_descriptor()
116+
117+
86118 if __name__ == '__main__':
87119 main()
python/tests/test_phasevoc.py+17−1
@@ -46,9 +46,25 @@ class aubio_pvoc_test_case(TestCase): · 45 unmodified lines
4646 r = f.rdo(s)
4747 assert_equal ( t, 0.)
4848 assert_equal ( s.norm, 0.)
49− assert_equal ( s.phas, 0.)
49+ try:
50+ assert_equal ( s.phas, 0 )
51+ except AssertionError:
52+ assert_equal (s.phas[s.phas > 0], +np.pi)
53+ assert_equal (s.phas[s.phas < 0], -np.pi)
54+ assert_equal (np.abs(s.phas[np.abs(s.phas) != np.pi]), 0)
55+ self.skipTest('pvoc(fvec(%d)).phas != +0, ' % win_s \
56+ + 'This is expected when using fftw3 on powerpc.')
5057 assert_equal ( r, 0.)
5158
59+ def test_no_overlap(self):
60+ win_s, hop_s = 1024, 1024
61+ f = pvoc (win_s, hop_s)
62+ t = fvec (hop_s)
63+ for _ in range(4):
64+ s = f(t)
65+ r = f.rdo(s)
66+ assert_equal ( t, 0.)
67+
5268 @params(
5369 ( 256, 8),
5470 ( 256, 4),
python/tests/test_pitch.py+2−2
@@ -70,8 +70,8 @@ class aubio_pitch_Sinusoid(TestCase): · 69 unmodified lines
7070 #print 'len(pitches), cut:', len(pitches), cut
7171 #print 'median errors: ', median(errors), 'median pitches: ', median(pitches)
7272
73−pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" , "specacf" ]
74−pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" ]
73+pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" , "specacf" ]
74+pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" ]
7575
7676 #freqs = [ 27.5, 55., 110., 220., 440., 880., 1760., 3520. ]
7777 freqs = [ 110., 220., 440., 880., 1760., 3520. ]
python/tests/test_sink.py+32−1
@@ -4,7 +4,10 @@ from nose2 import main · 3 unmodified lines
44 from nose2.tools import params
55 from numpy.testing import TestCase
66 from aubio import fvec, source, sink
7−from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
7+from .utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
8+
9+import warnings
10+warnings.filterwarnings('ignore', category=UserWarning, append=True)
811
912 list_of_sounds = list_all_sounds('sounds')
1013 samplerates = [0, 44100, 8000, 32000]
@@ -26,6 +29,26 @@ class aubio_sink_test_case(TestCase): · 15 unmodified lines
2629 if not len(list_of_sounds):
2730 self.skipTest('add some sound files in \'python/tests/sounds\'')
2831
32+ def test_wrong_filename(self):
33+ with self.assertRaises(RuntimeError):
34+ sink('')
35+
36+ def test_wrong_samplerate(self):
37+ with self.assertRaises(RuntimeError):
38+ sink(get_tmp_sink_path(), -1)
39+
40+ def test_wrong_samplerate_too_large(self):
41+ with self.assertRaises(RuntimeError):
42+ sink(get_tmp_sink_path(), 1536001, 2)
43+
44+ def test_wrong_channels(self):
45+ with self.assertRaises(RuntimeError):
46+ sink(get_tmp_sink_path(), 44100, -1)
47+
48+ def test_wrong_channels_too_large(self):
49+ with self.assertRaises(RuntimeError):
50+ sink(get_tmp_sink_path(), 44100, 202020)
51+
2952 def test_many_sinks(self):
3053 from tempfile import mkdtemp
3154 import os.path
@@ -93,5 +116,13 @@ class aubio_sink_test_case(TestCase): · 61 unmodified lines
93116 g.close()
94117 del_tmp_sink_path(sink_path)
95118
119+ def test_read_with(self):
120+ samplerate = 44100
121+ sink_path = get_tmp_sink_path()
122+ vec = fvec(128)
123+ with sink(sink_path, samplerate) as g:
124+ for _ in range(10):
125+ g(vec, 128)
126+
96127 if __name__ == '__main__':
97128 main()
python/tests/test_slicing.py+27−6
@@ -3,8 +3,8 @@ · 2 unmodified lines
33 from unittest import main
44 from numpy.testing import TestCase, assert_equal
55 from aubio import slice_source_at_stamps
6−from utils import count_files_in_directory, get_default_test_sound
7−from utils import count_samples_in_directory, count_samples_in_file
6+from .utils import count_files_in_directory, get_default_test_sound
7+from .utils import count_samples_in_directory, count_samples_in_file
88
99 import tempfile
1010 import shutil
@@ -23,19 +23,27 @@ class aubio_slicing_test_case(TestCase): · 12 unmodified lines
2323
2424 def test_slice_start_only_no_zero(self):
2525 regions_start = [i*1000 for i in range(1, n_slices)]
26− slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
26+ slice_source_at_stamps(self.source_file, regions_start,
27+ output_dir = self.output_dir, create_first=True)
2728
2829 def test_slice_start_beyond_end(self):
2930 regions_start = [i*1000 for i in range(1, n_slices)]
3031 regions_start += [count_samples_in_file(self.source_file) + 1000]
31− slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
32+ slice_source_at_stamps(self.source_file, regions_start,
33+ output_dir = self.output_dir, create_first=True)
3234
3335 def test_slice_start_every_blocksize(self):
3436 hopsize = 200
35− regions_start = [i*hopsize for i in range(1, n_slices)]
37+ regions_start = [i*hopsize for i in range(0, n_slices)]
3638 slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir,
3739 hopsize = 200)
3840
41+ def test_slice_start_every_half_blocksize(self):
42+ hopsize = 200
43+ regions_start = [i*hopsize//2 for i in range(0, n_slices)]
44+ slice_source_at_stamps(self.source_file, regions_start,
45+ output_dir = self.output_dir, hopsize = 200)
46+
3947 def tearDown(self):
4048 original_samples = count_samples_in_file(self.source_file)
4149 written_samples = count_samples_in_directory(self.output_dir)
@@ -91,6 +99,19 @@ class aubio_slicing_with_ends_test_case(TestCase): · 49 unmodified lines
9199 assert_equal(written_samples, expected_samples,
92100 "number of samples written different from number of original samples")
93101
102+ def test_slice_start_and_ends_with_missing_end(self):
103+ regions_start = [i*1000 for i in range(n_slices)]
104+ regions_ends = [r-1 for r in regions_start[1:]]
105+ slice_source_at_stamps(self.source_file, regions_start, regions_ends,
106+ output_dir = self.output_dir)
107+ written_samples = count_samples_in_directory(self.output_dir)
108+ original_samples = count_samples_in_file(self.source_file)
109+ total_files = count_files_in_directory(self.output_dir)
110+ assert_equal(n_slices, total_files,
111+ "number of slices created different from expected")
112+ assert_equal(written_samples, original_samples,
113+ "number of samples written different from number of original samples")
114+
94115 def tearDown(self):
95116 shutil.rmtree(self.output_dir)
96117
@@ -133,7 +154,7 @@ class aubio_slicing_wrong_ends_test_case(TestCase): · 36 unmodified lines
133154 regions_start = [i*1000 for i in range(1, n_slices)]
134155 regions_end = None
135156 slice_source_at_stamps (self.source_file, regions_start, regions_end,
136− output_dir = self.output_dir)
157+ output_dir = self.output_dir, create_first=True)
137158 total_files = count_files_in_directory(self.output_dir)
138159 assert_equal(n_slices, total_files,
139160 "number of slices created different from expected")
python/tests/test_source.py+34−7
@@ -2,9 +2,9 @@ · 1 unmodified line
22
33 from nose2 import main
44 from nose2.tools import params
5−from numpy.testing import TestCase
5+from numpy.testing import TestCase, assert_equal
66 from aubio import source
7−from utils import list_all_sounds
7+from .utils import list_all_sounds
88
99 import warnings
1010 warnings.filterwarnings('ignore', category=UserWarning, append=True)
@@ -51,9 +51,11 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 40 unmodified lines
5151 def read_from_source(self, f):
5252 total_frames = 0
5353 while True:
54− _ , read = f()
54+ samples , read = f()
5555 total_frames += read
56− if read < f.hop_size: break
56+ if read < f.hop_size:
57+ assert_equal(samples[read:], 0)
58+ break
5759 #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}"
5860 #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri
5961 #print (result_str.format(*result_params))
@@ -66,7 +68,14 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 6 unmodified lines
6668 except RuntimeError as e:
6769 self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e)))
6870 assert f.samplerate != 0
69− self.read_from_source(f)
71+ read_frames = self.read_from_source(f)
72+ if 'f_' in soundfile and samplerate == 0:
73+ import re
74+ f = re.compile('.*_\([0:9]*f\)_.*')
75+ match_f = re.findall('([0-9]*)f_', soundfile)
76+ if len(match_f) == 1:
77+ expected_frames = int(match_f[0])
78+ self.assertEqual(expected_frames, read_frames)
7079
7180 @params(*list_of_sounds)
7281 def test_samplerate_none(self, p):
@@ -149,13 +158,31 @@ class aubio_source_readmulti_test_case(aubio_source_read_test_case): · 76 unmodified lines
149158 def read_from_source(self, f):
150159 total_frames = 0
151160 while True:
152− _, read = f.do_multi()
161+ samples, read = f.do_multi()
153162 total_frames += read
154− if read < f.hop_size: break
163+ if read < f.hop_size:
164+ assert_equal(samples[:,read:], 0)
165+ break
155166 #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}"
156167 #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri
157168 #print (result_str.format(*result_params))
158169 return total_frames
159170
171+class aubio_source_with(aubio_source_test_case_base):
172+
173+ #@params(*list_of_sounds)
174+ @params(*list_of_sounds)
175+ def test_read_from_mono(self, filename):
176+ total_frames = 0
177+ hop_size = 2048
178+ with source(filename, 0, hop_size) as input_source:
179+ assert_equal(input_source.hop_size, hop_size)
180+ #assert_equal(input_source.samplerate, samplerate)
181+ total_frames = 0
182+ for frames in input_source:
183+ total_frames += frames.shape[-1]
184+ # check we read as many samples as we expected
185+ assert_equal(total_frames, input_source.duration)
186+
160187 if __name__ == '__main__':
161188 main()
python/tests/test_source_channels.py+93−0
@@ -0,0 +1,93 @@
1+#! /usr/bin/env python
2+
3+"""A brute force test using `sink` to create and write samples to a stereo
4+file, then `source` to check the correct content is read from the files."""
5+
6+import os.path
7+import unittest
8+import aubio
9+import numpy as np
10+from numpy.testing import assert_equal
11+from .utils import get_tmp_sink_path
12+
13+class aubio_source_test_case(unittest.TestCase):
14+
15+ def test_read_from_mono(self):
16+ out = get_tmp_sink_path()
17+ samplerate = 44100
18+ hop_size = 256
19+ blocks = 10
20+ channels = 1
21+ write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
22+ write_samples *= .5
23+ self.check_write_and_read(samplerate, channels, hop_size, blocks,
24+ write_samples)
25+
26+ def test_read_from_stereo(self):
27+ out = get_tmp_sink_path()
28+ samplerate = 44100
29+ hop_size = 256
30+ blocks = 10
31+ channels = 1
32+ write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
33+ write_samples *= .5
34+ self.check_write_and_read(samplerate, channels, hop_size, blocks,
35+ write_samples)
36+
37+ def test_read_from_half_stereo(self):
38+ samplerate = 16000
39+ channels = 2
40+ hop_size = 512
41+ blocks = 10
42+ write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
43+ write_samples *= .5
44+ write_samples[1, :] = 0
45+ self.check_write_and_read(samplerate, channels, hop_size, blocks,
46+ write_samples)
47+
48+ def test_read_from_cancelling_channels(self):
49+ samplerate = 16000
50+ channels = 2
51+ hop_size = 512
52+ blocks = 10
53+ write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
54+ write_samples *= .5
55+ write_samples[1] *= -1
56+ self.check_write_and_read(samplerate, channels, hop_size, blocks,
57+ write_samples)
58+
59+ def test_read_from_strange_three_channels(self):
60+ samplerate = 8000
61+ channels = 3
62+ hop_size = 123
63+ blocks = 10
64+ write_samples = np.ones([channels, hop_size], dtype=aubio.float_type)
65+ write_samples *= .5
66+ write_samples[1, :] = 0
67+ self.check_write_and_read(samplerate, channels, hop_size, blocks,
68+ write_samples)
69+
70+ def check_write_and_read(self, samplerate, channels,
71+ hop_size, blocks, write_samples):
72+ expected_mono = np.sum(write_samples, axis=0)/write_samples.shape[0]
73+ out = get_tmp_sink_path()
74+ snk = aubio.sink(out, samplerate, channels=channels)
75+ for i in range(blocks):
76+ snk.do_multi(write_samples, hop_size)
77+ # close the sink before reading from it
78+ snk.close()
79+
80+ src = aubio.source(out, samplerate, hop_size)
81+ for i in range(blocks):
82+ read_samples, read = src.do_multi()
83+ assert_equal (read_samples, write_samples)
84+ assert_equal (read, hop_size)
85+
86+ src.seek(0)
87+ for i in range(blocks):
88+ read_samples, read = src()
89+ assert_equal (read, hop_size)
90+ assert_equal (read_samples, expected_mono)
91+
92+if __name__ == '__main__':
93+ unittest.main()
python/tests/test_tempo.py+91−0
@@ -0,0 +1,91 @@
1+#! /usr/bin/env python
2+
3+from unittest import main
4+from numpy.testing import TestCase, assert_equal, assert_almost_equal
5+import aubio
6+
7+class aubio_tempo_default(TestCase):
8+
9+ def test_members(self):
10+ o = aubio.tempo()
11+ assert_equal ([o.buf_size, o.hop_size, o.method, o.samplerate],
12+ [1024,512,'default',44100])
13+
14+class aubio_tempo_params(TestCase):
15+
16+ samplerate = 44100
17+
18+ def setUp(self):
19+ self.o = aubio.tempo(samplerate = self.samplerate)
20+
21+ def test_get_delay(self):
22+ self.assertEqual(self.o.get_delay(), 0)
23+
24+ def test_set_delay(self):
25+ val = 256
26+ self.o.set_delay(val)
27+ assert_equal (self.o.get_delay(), val)
28+
29+ def test_get_delay_s(self):
30+ self.assertEqual(self.o.get_delay_s(), 0.)
31+
32+ def test_set_delay_s(self):
33+ val = .05
34+ self.o.set_delay_s(val)
35+ assert_almost_equal (self.o.get_delay_s(), val)
36+
37+ def test_get_delay_ms(self):
38+ self.assertEqual(self.o.get_delay_ms(), 0.)
39+
40+ def test_set_delay_ms(self):
41+ val = 50.
42+ self.o.set_delay_ms(val)
43+ assert_almost_equal (self.o.get_delay_ms(), val)
44+
45+ def test_get_threshold(self):
46+ assert_almost_equal(self.o.get_threshold(), 0.3)
47+
48+ def test_set_threshold(self):
49+ val = .1
50+ self.o.set_threshold(val)
51+ assert_almost_equal (self.o.get_threshold(), val)
52+
53+ def test_get_silence(self):
54+ self.assertEqual(self.o.get_silence(), -90.)
55+
56+ def test_set_silence(self):
57+ val = -50.
58+ self.o.set_silence(val)
59+ assert_almost_equal (self.o.get_silence(), val)
60+
61+ def test_get_last(self):
62+ self.assertEqual(self.o.get_last(), 0.)
63+
64+ def test_get_last_s(self):
65+ self.assertEqual(self.o.get_last_s(), 0.)
66+
67+ def test_get_last_ms(self):
68+ self.assertEqual(self.o.get_last_ms(), 0.)
69+
70+ def test_get_period(self):
71+ self.assertEqual(self.o.get_period(), 0.)
72+
73+ def test_get_period_s(self):
74+ self.assertEqual(self.o.get_period_s(), 0.)
75+
76+ def test_get_last_tatum(self):
77+ self.assertEqual(self.o.get_last_tatum(), 0.)
78+
79+ def test_set_tatum_signature(self):
80+ self.o.set_tatum_signature(8)
81+ self.o.set_tatum_signature(64)
82+ self.o.set_tatum_signature(1)
83+
84+ def test_set_wrong_tatum_signature(self):
85+ with self.assertRaises(ValueError):
86+ self.o.set_tatum_signature(101)
87+ with self.assertRaises(ValueError):
88+ self.o.set_tatum_signature(0)
89+
90+if __name__ == '__main__':
91+ main()

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