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 @@
1Python aubio module1aubio
2===================2=====
3 3
4This module wraps the aubio library for Python using the numpy module.4aubio is a collection of tools for music and audio analysis.
5 5
6Using the Python aubio module6This package integrates the aubio library with [NumPy] to provide a set of
7-----------------------------7efficient tools to process and analyse audio signals, including:
8 8
9After 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
10 15
11 $ python16aubio works with both Python 2 and Python 3.
12 [...]
13 >>> import aubio
14 >>> help(aubio.miditofreq)
15 17
16Finding some inspiration18Links
17------------------------19-----
18 20
19Some examples are available in the `python/demos` directory. These scripts are21- [module documentation][doc_python]
20small programs written in python and using python-aubio.22- [installation instructions][doc_python_install]
23- [aubio manual][manual]
24- [aubio homepage][homepage]
25- [issue tracker][bugtracker]
21 26
22For instance, `demo_source.py` reads a media file.27Demos
28-----
23 29
24 $ ./python/demos/demo_source.py /path/to/sound/sample.wav30Some examples are available in the [`python/demos`][demos_dir] folder. Each
31script is a command line program which accepts one ore more argument.
25 32
26and `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.
27 34
28 $ ./python/demo/demo_timestretch_online.py loop.wav stretched_loop.wav 0.92`35### Analysis
29 36
30Note: you might need to install additional modules to run some of the demos.37- `demo_source.py` uses aubio to read audio samples from media files
31Some demos use [matplotlib](http://matplotlib.org/) to draw plots, others use38- `demo_onset_plot.py` detects attacks in a sound file and plots the results
32[PySoundCard](https://github.com/bastibe/PySoundCard) to play and record39 using [matplotlib]
33sounds.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.
34 44
35Testing the Python module45### Real-time
36-------------------------
37 46
38To 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]
39 50
40 $ ./python/tests/run_all_tests51### Others
41 52
42Each 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
43 57
44 $ ./python/tests/test_note2midi.py -v58### Example
45 59
46Install in a virtualenv60Use `demo_timestretch_online.py` to slow down `loop.wav`, write the results in
47-----------------------61`stretched_loop.wav`:
48 62
49You should be able to install python-aubio directly from the top source63 $ python demo_timestretch_online.py loop.wav stretched_loop.wav 0.92
50directory of aubio.
51 64
52First, create a virtualenv to hold the required python module:65Built with
53
54 $ virtualenv pyaubio
55 $ source pyaubio/bin/activate
56
57Now install and build the python extension using:
58
59 $ pip install .
60
61Install requirements
62--------------------
63
64Before compiling this module, you must have compiled libaubio.
65
66A simple way to do this is with pip:
67
68 $ pip install -r requirements.txt
69
70For more information about how this module works, please refer to the [Python/C
71API 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
74Compiling python aubio
75----------------------
76
77To build the aubio Python module, run the following command from the top source
78directory of aubio:
79
80 $ ./setup.py build
81
82Note: if libaubio was previously built using waf, the script will use it.
83Otherwise, the entire library will be built inside the python extension.
84
85To find out more about `setup.py` options:
86
87 $ ./setup.py --help
88
89Installing
90----------66----------
91 67
92To install the Python module:68The core of aubio is written in C for portability and speed. In addition to
93 69[NumPy], aubio can be optionally built to use one or more of the following
94 $ ./setup.py install70libraries:
95 71
96Alternatively, you may want to use the Python module without installing it by72- media file reading:
97setting your PYTHONPATH, for instance as follows:73
98 74 - [ffmpeg] / [avcodec] to decode and read audio from almost any format,
99 $ export PYTHONPATH=$PYTHONPATH:$PWD/`ls -rtd build/lib.* | head -1`:$PWD/tests75 - [libsndfile] to read audio from uncompressed sound files,
100 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
3import alsaaudio
4import numpy as np
5import aubio
6
7# constants
8samplerate = 44100
9win_s = 2048
10hop_s = win_s // 2
11framesize = hop_s
12
13# set up audio input
14recorder = alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
15recorder.setperiodsize(framesize)
16recorder.setrate(samplerate)
17recorder.setformat(alsaaudio.PCM_FORMAT_FLOAT_LE)
18recorder.setchannels(1)
19
20# create aubio pitch detection (first argument is method, "default" is
21# "yinfft", can also be "yin", "mcomb", fcomb", "schmitt").
22pitcher = aubio.pitch("default", win_s, hop_s, samplerate)
23# set output unit (can be 'midi', 'cent', 'Hz', ...)
24pitcher.set_unit("Hz")
25# ignore frames under this level (dB)
26pitcher.set_silence(-40)
27
28print("Starting to listen, press Ctrl+C to stop")
29
30# main loop
31while 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
3import numpy as np
4from aubio import pitch
5import pylab as plt
6
7buf_size = 2048 * 1
8hop_size = buf_size // 4
9
10samplerate = 44100
11minfreq = 40
12maxfreq = 6000
13
14def 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
21def 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
41if __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
3from aubio import source, tempo3from aubio import source, tempo
4from numpy import median, diff4from numpy import median, diff
5 5
6def get_file_bpm(path, params = None):6def get_file_bpm(path, params=None):
7 """ Calculate the beats per minute (bpm) of a given file.7 """ Calculate the beats per minute (bpm) of a given file.
8 path: path to the file8 path: path to the file
9 param: dictionary of parameters9 param: dictionary of parameters
10 """10 """
11 if params is None:11 if params is None:
12 params = {}12 params = {}
13 try:13 # default:
14 win_s = params['win_s']14 samplerate, win_s, hop_s = 44100, 1024, 512
15 samplerate = params['samplerate']15 if 'mode' in params:
16 hop_s = params['hop_s']16 if params.mode in ['super-fast']:
17 except KeyError:17 # super fast
18 """18 samplerate, win_s, hop_s = 4000, 128, 64
19 # super fast19 elif params.mode in ['fast']:
20 samplerate, win_s, hop_s = 4000, 128, 64 20 # fast
21 # fast21 samplerate, win_s, hop_s = 8000, 512, 128
22 samplerate, win_s, hop_s = 8000, 512, 12822 elif params.mode in ['default']:
23 """23 pass
24 # default:24 else:
25 samplerate, win_s, hop_s = 44100, 1024, 51225 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
26 33
27 s = source(path, samplerate, hop_s)34 s = source(path, samplerate, hop_s)
28 samplerate = s.samplerate35 samplerate = s.samplerate
@@ -44,19 +51,29 @@ def get_file_bpm(path, params = None): · 15 unmodified lines
44 if read < hop_s:51 if read < hop_s:
45 break52 break
46 53
47 # Convert to periods and to bpm 54 def beats_to_bpm(beats, path):
48 if len(beats) > 1:55 # if enough beats are found, convert to periods then to bpm
49 if len(beats) < 4:56 if len(beats) > 1:
50 print("few beats found in {:s}".format(path))57 if len(beats) < 4:
51 bpms = 60./diff(beats)58 print("few beats found in {:s}".format(path))
52 b = median(bpms)59 bpms = 60./diff(beats)
53 else:60 return median(bpms)
54 b = 061 else:
55 print("not enough beats found in {:s}".format(path))62 print("not enough beats found in {:s}".format(path))
56 return b63 return 0
64
65 return beats_to_bpm(beats, path)
57 66
58if __name__ == '__main__':67if __name__ == '__main__':
59 import sys68 import argparse
60 for f in sys.argv[1:]:69 parser = argparse.ArgumentParser()
61 bpm = get_file_bpm(f)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)
62 print("{:6s} {:s}".format("{:2f}".format(bpm), f))79 print("{:6s} {:s}".format("{:2f}".format(bpm), f))
python/demos/demo_filter.py+31−14
@@ -1,36 +1,53 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3import sys
4import os.path
5import aubio
3 6
4def apply_filter(path):
5 from aubio import source, sink, digital_filter
6 from os.path import basename, splitext
7 7
8def apply_filter(path, target):
8 # open input file, get its samplerate9 # open input file, get its samplerate
9 s = source(path)10 s = aubio.source(path)
10 samplerate = s.samplerate11 samplerate = s.samplerate
11 12
12 # create an A-weighting filter13 # create an A-weighting filter
13 f = digital_filter(7)14 f = aubio.digital_filter(7)
14 f.set_a_weighting(samplerate)15 f.set_a_weighting(samplerate)
15 # alternatively, apply another filter
16 16
17 # create output file17 # create output file
18 o = sink("filtered_" + splitext(basename(path))[0] + ".wav", samplerate)18 o = aubio.sink(target, samplerate)
19 19
20 total_frames = 020 total_frames = 0
21 while True:21 while True:
22 # read from source
22 samples, read = s()23 samples, read = s()
24 # filter samples
23 filtered_samples = f(samples)25 filtered_samples = f(samples)
26 # write to sink
24 o(filtered_samples, read)27 o(filtered_samples, read)
28 # count frames read
25 total_frames += read29 total_frames += read
26 if read < s.hop_size: break30 # end of file reached
31 if read < s.hop_size:
32 break
27 33
34 # print some info
28 duration = total_frames / float(samplerate)35 duration = total_frames / float(samplerate)
29 print ("read {:s}".format(s.uri))36 input_str = "input: {:s} ({:.2f} s, {:d} Hz)"
30 print ("applied A-weighting filtered ({:d} Hz)".format(samplerate))37 output_str = "output: {:s}, A-weighting filtered ({:d} frames total)"
31 print ("wrote {:s} ({:.2f} s)".format(o.uri, duration))38 print(input_str.format(s.uri, duration, samplerate))
39 print(output_str.format(o.uri, total_frames))
32 40
33if __name__ == '__main__':41if __name__ == '__main__':
34 import sys42 usage = "{:s} <input_file> [output_file]".format(sys.argv[0])
35 for f in sys.argv[1:]:43 if not 1 < len(sys.argv) < 4:
36 apply_filter(f)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 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3from aubio import filterbank, fvec3"""Create a filterbank from a list of frequencies.
4from pylab import loglog, show, xlim, ylim, xlabel, ylabel, title
5from numpy import vstack, arange
6 4
7win_s = 20485This demo uses `aubio.filterbank.set_triangle_bands` to build a set of
6triangular filters from a list of frequencies.
7
8The filterbank coefficients are then modified before being displayed."""
9
10import aubio
11import numpy as np
12import matplotlib.pyplot as plt
13
14# sampling rate and size of the fft
8samplerate = 4800015samplerate = 48000
16win_s = 2048
9 17
18# define a list of custom frequency
10freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]19freq_list = [60, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 24000]
20# number of filters to create
11n_filters = len(freq_list) - 221n_filters = len(freq_list) - 2
12 22
13f = filterbank(n_filters, win_s)23# create a new filterbank
14freqs = fvec(freq_list)24f = aubio.filterbank(n_filters, win_s)
25freqs = aubio.fvec(freq_list)
15f.set_triangle_bands(freqs, samplerate)26f.set_triangle_bands(freqs, samplerate)
16 27
28# get the coefficients from the filterbank
17coeffs = f.get_coeffs()29coeffs = f.get_coeffs()
18coeffs[4] *= 5.30# apply a gain to fifth band
19 31coeffs[4] *= 6.
32# load the modified coeffs into the filterbank
20f.set_coeffs(coeffs)33f.set_coeffs(coeffs)
21 34
22times = vstack([arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)35# display the band gains in a loglog plot
23title('Bank of filters built using a simple list of boundaries\nThe middle band has been amplified by 2.')36freqs = np.vstack([np.arange(win_s // 2 + 1) * samplerate / win_s] * n_filters)
24loglog(times.T, f.get_coeffs().T, '.-')37plt.title('filterbank built from a list of frequencies\n'
25xlim([50, samplerate/2])38 'The 5th band has been amplified by a factor 6.')
26ylim([1.0e-6, 2.0e-2])39plt.loglog(freqs.T, f.get_coeffs().T, '.-')
27xlabel('log frequency (Hz)')40plt.xlim([50, samplerate/2])
28ylabel('log amplitude')41plt.ylim([1.0e-6, 2.0e-2])
29 42plt.xlabel('log frequency (Hz)')
30show()43plt.ylabel('log amplitude')
44plt.show()
python/demos/demo_notes.py+37−0
@@ -0,0 +1,37 @@
1#! /usr/bin/env python
2
3import sys
4from aubio import source, notes
5
6if len(sys.argv) < 2:
7 print("Usage: %s <filename> [samplerate]" % sys.argv[0])
8 sys.exit(1)
9
10filename = sys.argv[1]
11
12downsample = 1
13samplerate = 44100 // downsample
14if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
15
16win_s = 512 // downsample # fft size
17hop_s = 256 // downsample # hop size
18
19s = source(filename, samplerate, hop_s)
20samplerate = s.samplerate
21
22tolerance = 0.8
23
24notes_o = notes("default", win_s, hop_s, samplerate)
25
26print("%8s" % "time","[ start","vel","last ]")
27
28# total number of frames read
29total_frames = 0
30while 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
37 37
38pointer += partition38pointer += partition
39pointer += partition39pointer += partition
40freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length/8)40freqs[ pointer : pointer + partition ] = 400 + 5 * np.random.random(sin_length//8)
41 41
42a = build_sinusoid(sin_length, freqs, samplerate)42a = build_sinusoid(sin_length, freqs, samplerate)
43 43
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
12import pyaudio
13import sys
14import numpy as np
15import aubio
16
17# initialise pyaudio
18p = pyaudio.PyAudio()
19
20# open stream
21buffer_size = 1024
22pyaudio_format = pyaudio.paFloat32
23n_channels = 1
24samplerate = 44100
25stream = p.open(format=pyaudio_format,
26 channels=n_channels,
27 rate=samplerate,
28 input=True,
29 frames_per_buffer=buffer_size)
30
31if 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
37else:
38 # run forever
39 outputsink = None
40 record_duration = None
41
42# setup pitch
43tolerance = 0.8
44win_s = 4096 # fft size
45hop_s = buffer_size # hop size
46pitch_o = aubio.pitch("default", win_s, hop_s, samplerate)
47pitch_o.set_unit("midi")
48pitch_o.set_tolerance(tolerance)
49
50print("*** starting recording")
51while 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
72print("*** done recording")
73stream.stop_stream()
74stream.close()
75p.terminate()
python/demos/demo_source_simple.py+13−9
@@ -1,16 +1,20 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2import sys, aubio2
3"""A simple example using aubio.source."""
4
5import sys
6import aubio
3 7
4samplerate = 0 # use original source samplerate8samplerate = 0 # use original source samplerate
5hop_size = 256 # number of frames to read in one block9hop_size = 256 # number of frames to read in one block
6s = aubio.source(sys.argv[1], samplerate, hop_size)10src = aubio.source(sys.argv[1], samplerate, hop_size)
7total_frames = 011total_frames = 0
8 12
9while True: # reading loop13while True:
10 samples, read = s()14 samples, read = src() # read hop_size new samples from source
11 total_frames += read15 total_frames += read # increment total number of frames
12 if read < hop_size: break # end of file reached16 if read < hop_size: # end of file reached
17 break
13 18
14fmt_string = "read {:d} frames at {:d}Hz from {:s}"19fmt_string = "read {:d} frames at {:d}Hz from {:s}"
15print (fmt_string.format(total_frames, s.samplerate, sys.argv[1]))20print(fmt_string.format(total_frames, src.samplerate, src.uri))
16
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
5Note you will need to have pyaudio installed: `pip install pyaudio`.
6
7Examples:
8 ./demo_tapthebeat.py ~/Music/track1.ogg
9
10When compiled with ffmpeg/libav, you should be able to open remote streams. For
11instance using youtube-dl (`pip install youtube-dl`):
12
13 ./demo_tapthebeat.py `youtube-dl -xg https://youtu.be/zZbM9n9j3_g`
14
15"""
16
17import sys
18import time
19import pyaudio
20import aubio
21import numpy as np
22
23win_s = 1024 # fft size
24hop_s = win_s // 2 # hop size
25
26# parse command line arguments
27if len(sys.argv) < 2:
28 print("Usage: %s <filename> [samplerate]" % sys.argv[0])
29 sys.exit(1)
30
31filename = sys.argv[1]
32
33samplerate = 0
34if len( sys.argv ) > 2: samplerate = int(sys.argv[2])
35
36# create aubio source
37a_source = aubio.source(filename, samplerate, hop_s)
38samplerate = a_source.samplerate
39
40# create aubio tempo detection
41a_tempo = aubio.tempo("default", win_s, hop_s, samplerate)
42
43# create a simple click sound
44click = 0.7 * np.sin(2. * np.pi * np.arange(hop_s) / hop_s * samplerate / 3000.)
45
46# pyaudio callback
47def 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
59p = pyaudio.PyAudio()
60pyaudio_format = pyaudio.paFloat32
61frames_per_buffer = hop_s
62n_channels = 1
63stream = 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
68stream.start_stream()
69
70# wait for stream to finish
71while stream.is_active():
72 time.sleep(0.1)
73
74# stop pyaudio stream
75stream.stop_stream()
76stream.close()
77# close pyaudio
78p.terminate()
python/demos/demo_timestretch.py+1−1
@@ -12,7 +12,7 @@ from aubio import unwrap2pi, float_type · 11 unmodified lines
12import numpy as np12import numpy as np
13 13
14win_s = 102414win_s = 1024
15hop_s = win_s / 8 # 87.5 % overlap15hop_s = win_s // 8 # 87.5 % overlap
16 16
17warmup = win_s // hop_s - 117warmup = win_s // hop_s - 1
18 18
python/demos/demo_timestretch_online.py+5−3
@@ -11,8 +11,8 @@ from aubio import source, sink, pvoc, cvec · 10 unmodified lines
11from aubio import unwrap2pi, float_type11from aubio import unwrap2pi, float_type
12import numpy as np12import numpy as np
13 13
14win_s = 102414win_s = 512
15hop_s = win_s / 8 # 87.5 % overlap15hop_s = win_s // 8 # 87.5 % overlap
16 16
17warmup = win_s // hop_s - 117warmup = win_s // hop_s - 1
18 18
@@ -92,8 +92,10 @@ while True: · 73 unmodified lines
92 old_grain.norm = np.copy(cur_grain.norm)92 old_grain.norm = np.copy(cur_grain.norm)
93 old_grain.phas = np.copy(cur_grain.phas)93 old_grain.phas = np.copy(cur_grain.phas)
94 94
95 block_read += 195 # until end of file
96 if read < hop_s: break96 if read < hop_s: break
97 # increment block counter
98 block_read += 1
97 99
98for t in range(warmup + 2): # purge the last frames from the phase vocoder100for t in range(warmup + 2): # purge the last frames from the phase vocoder
99 new_grain.norm[:] = 0101 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
10import sys
11from aubio import source, notes
12from mido import Message, MetaMessage, MidiFile, MidiTrack, second2tick, bpm2tempo
13
14if len(sys.argv) < 3:
15 print("Usage: %s <filename> <output> [samplerate]" % sys.argv[0])
16 sys.exit(1)
17
18filename = sys.argv[1]
19midioutput = sys.argv[2]
20
21downsample = 1
22samplerate = 44100 // downsample
23if len( sys.argv ) > 3: samplerate = int(sys.argv[3])
24
25win_s = 512 // downsample # fft size
26hop_s = 256 // downsample # hop size
27
28s = source(filename, samplerate, hop_s)
29samplerate = s.samplerate
30
31tolerance = 0.8
32
33notes_o = notes("default", win_s, hop_s, samplerate)
34
35print("%8s" % "time","[ start","vel","last ]")
36
37# create a midi file
38mid = MidiFile()
39track = MidiTrack()
40mid.tracks.append(track)
41
42ticks_per_beat = mid.ticks_per_beat # default: 480
43bpm = 120 # default midi tempo
44
45tempo = bpm2tempo(bpm)
46track.append(MetaMessage('set_tempo', tempo=tempo))
47track.append(MetaMessage('time_signature', numerator=4, denominator=4))
48
49def frames2tick(frames, samplerate=samplerate):
50 sec = frames / float(samplerate)
51 return int(second2tick(sec, ticks_per_beat, tempo))
52
53last_time = 0
54
55# total number of frames read
56total_frames = 0
57while 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
77mid.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
16import sys
17import numpy as np
18import matplotlib.pyplot as plt
19
20def 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
32def 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
57def 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
69def 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
95def 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
108def 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
130def 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
151testfreqs = [441., 800., 10000., 40.]
152
153if len(sys.argv) > 1:
154 testfreqs = map(float,sys.argv[1:])
155
156for 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)
175plt.show()
python/ext/aubio-types.h+9−1
@@ -27,7 +27,7 @@ · 26 unmodified lines
27#ifdef USE_LOCAL_AUBIO27#ifdef USE_LOCAL_AUBIO
28#include "aubio.h"28#include "aubio.h"
29#else29#else
30#include "aubio/aubio.h"30#include <aubio/aubio.h>
31#endif31#endif
32 32
33#define Py_default_vector_length 102433#define Py_default_vector_length 1024
@@ -46,6 +46,14 @@ · 12 unmodified lines
46#define AUBIO_NPY_SMPL_CHR "f"46#define AUBIO_NPY_SMPL_CHR "f"
47#endif47#endif
48 48
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
49// compat with Python < 2.657// compat with Python < 2.6
50#ifndef Py_TYPE58#ifndef Py_TYPE
51#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)59#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
python/ext/aubiomodule.c+167−27
@@ -2,77 +2,202 @@ · 1 unmodified line
2#include "aubio-types.h"2#include "aubio-types.h"
3#include "py-musicutils.h"3#include "py-musicutils.h"
4 4
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
5static char aubio_module_doc[] = "Python module for the aubio library";10static char aubio_module_doc[] = "Python module for the aubio library";
6 11
7static char Py_alpha_norm_doc[] = ""12static 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"
9"\n"16"\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"
11"\n"28"\n"
12"Example\n"29"Example\n"
13"-------\n"30"-------\n"
14"\n"31"\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"";
16 44
17static char Py_bintomidi_doc[] = ""45static 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"
19"\n"50"\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"
21"\n"64"\n"
22"Example\n"65"Example\n"
23"-------\n"66"-------\n"
24"\n"67"\n"
25">>> midi = bintomidi(float, samplerate = 44100, fftsize = 1024)";68">>> aubio.bintomidi(10, 44100, 1024)\n"
69"68.62871551513672\n"
70"";
26 71
27static char Py_miditobin_doc[] = ""72static char Py_miditobin_doc[] = ""
28"miditobin(float, samplerate = integer, fftsize = integer) -> float\n"73"miditobin(midi, samplerate, fftsize)\n"
29"\n"74"\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"
31"\n"77"\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"
33"-------\n"88"-------\n"
89"float\n"
90" Frequency converted to FFT bin.\n"
91"\n"
92"Examples\n"
93"--------\n"
34"\n"94"\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"";
36 100
37static char Py_bintofreq_doc[] = ""101static 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"
39"\n"106"\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"
41"\n"120"\n"
42"Example\n"121"Example\n"
43"-------\n"122"-------\n"
44"\n"123"\n"
45">>> freq = bintofreq(bin, samplerate = 44100, fftsize = 1024)";124">>> aubio.bintofreq(10, 44100, 1024)\n"
125"430.6640625\n"
126"";
46 127
47static char Py_freqtobin_doc[] = ""128static char Py_freqtobin_doc[] = ""
48"freqtobin(float, samplerate = integer, fftsize = integer) -> float\n"129"freqtobin(freq, samplerate, fftsize)\n"
49"\n"130"\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"
51"\n"133"\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"
53"-------\n"144"-------\n"
145"float\n"
146" Frequency converted to FFT bin.\n"
147"\n"
148"Examples\n"
149"--------\n"
54"\n"150"\n"
55">>> bin = freqtobin(freq, samplerate = 44100, fftsize = 1024)";151">>> aubio.freqtobin(440, 44100, 1024)\n"
152"10.216779708862305\n"
153"";
56 154
57static char Py_zero_crossing_rate_doc[] = ""155static 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"
59"\n"159"\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"
61"\n"169"\n"
62"Example\n"170"Example\n"
63"-------\n"171"-------\n"
64"\n"172"\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"";
66 177
67static char Py_min_removal_doc[] = ""178static 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"
69"\n"182"\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"
71"\n"194"\n"
72"Example\n"195"Example\n"
73"-------\n"196"-------\n"
74"\n"197"\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"";
76 201
77extern void add_ufuncs ( PyObject *m );202extern void add_ufuncs ( PyObject *m );
78extern int generated_types_ready(void);203extern int generated_types_ready(void);
@@ -112,7 +237,9 @@ Py_bintomidi (PyObject * self, PyObject * args) · 33 unmodified lines
112 smpl_t input, samplerate, fftsize;237 smpl_t input, samplerate, fftsize;
113 smpl_t output;238 smpl_t output;
114 239
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)) {
116 return NULL;243 return NULL;
117 }244 }
118 245
@@ -127,7 +254,9 @@ Py_miditobin (PyObject * self, PyObject * args) · 8 unmodified lines
127 smpl_t input, samplerate, fftsize;254 smpl_t input, samplerate, fftsize;
128 smpl_t output;255 smpl_t output;
129 256
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)) {
131 return NULL;260 return NULL;
132 }261 }
133 262
@@ -142,7 +271,9 @@ Py_bintofreq (PyObject * self, PyObject * args) · 8 unmodified lines
142 smpl_t input, samplerate, fftsize;271 smpl_t input, samplerate, fftsize;
143 smpl_t output;272 smpl_t output;
144 273
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)) {
146 return NULL;277 return NULL;
147 }278 }
148 279
@@ -157,7 +288,9 @@ Py_freqtobin (PyObject * self, PyObject * args) · 8 unmodified lines
157 smpl_t input, samplerate, fftsize;288 smpl_t input, samplerate, fftsize;
158 smpl_t output;289 smpl_t output;
159 290
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)) {
161 return NULL;294 return NULL;
162 }295 }
163 296
@@ -237,6 +370,12 @@ static PyMethodDef aubio_methods[] = { · 73 unmodified lines
237 {"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc},370 {"silence_detection", Py_aubio_silence_detection, METH_VARARGS, Py_aubio_silence_detection_doc},
238 {"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc},371 {"level_detection", Py_aubio_level_detection, METH_VARARGS, Py_aubio_level_detection_doc},
239 {"window", Py_aubio_window, METH_VARARGS, Py_aubio_window_doc},372 {"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},
240 {NULL, NULL, 0, NULL} /* Sentinel */379 {NULL, NULL, 0, NULL} /* Sentinel */
241};380};
242 381
@@ -323,6 +462,7 @@ initaubio (void) · 80 unmodified lines
323 PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType);462 PyModule_AddObject (m, "sink", (PyObject *) & Py_sinkType);
324 463
325 PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR);464 PyModule_AddStringConstant(m, "float_type", AUBIO_NPY_SMPL_STR);
465 PyModule_AddStringConstant(m, "__version__", DEFINEDSTRING(AUBIO_VERSION));
326 466
327 // add generated objects467 // add generated objects
328 add_generated_objects(m);468 add_generated_objects(m);
python/ext/py-cvec.c+42−12
@@ -19,7 +19,37 @@ typedef struct · 18 unmodified lines
19 uint_t length;19 uint_t length;
20} Py_cvec;20} Py_cvec;
21 21
22static char Py_cvec_doc[] = "cvec object";22static 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"";
23 53
24 54
25PyObject *55PyObject *
@@ -142,14 +172,14 @@ Py_cvec_set_norm (Py_cvec * vec, PyObject *input, void * closure) · 116 unmodified lines
142{172{
143 npy_intp length;173 npy_intp length;
144 if (!PyAubio_IsValidVector(input)) {174 if (!PyAubio_IsValidVector(input)) {
145 return 1;175 return -1;
146 }176 }
147 length = PyArray_SIZE ((PyArrayObject *)input);177 length = PyArray_SIZE ((PyArrayObject *)input);
148 if (length != vec->length) {178 if (length != vec->length) {
149 PyErr_Format (PyExc_ValueError,179 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,
151 vec->length);181 vec->length);
152 return 1;182 return -1;
153 }183 }
154 184
155 Py_XDECREF(vec->norm);185 Py_XDECREF(vec->norm);
@@ -163,14 +193,14 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 7 unmodified lines
163{193{
164 npy_intp length;194 npy_intp length;
165 if (!PyAubio_IsValidVector(input)) {195 if (!PyAubio_IsValidVector(input)) {
166 return 1;196 return -1;
167 }197 }
168 length = PyArray_SIZE ((PyArrayObject *)input);198 length = PyArray_SIZE ((PyArrayObject *)input);
169 if (length != vec->length) {199 if (length != vec->length) {
170 PyErr_Format (PyExc_ValueError,200 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,
172 vec->length);202 vec->length);
173 return 1;203 return -1;
174 }204 }
175 205
176 Py_XDECREF(vec->phas);206 Py_XDECREF(vec->phas);
@@ -182,7 +212,7 @@ Py_cvec_set_phas (Py_cvec * vec, PyObject *input, void * closure) · 5 unmodified lines
182static PyMemberDef Py_cvec_members[] = {212static PyMemberDef Py_cvec_members[] = {
183 // TODO remove READONLY flag and define getter/setter213 // TODO remove READONLY flag and define getter/setter
184 {"length", T_INT, offsetof (Py_cvec, length), READONLY,214 {"length", T_INT, offsetof (Py_cvec, length), READONLY,
185 "length attribute"},215 "int: Length of `norm` and `phas` vectors."},
186 {NULL} /* Sentinel */216 {NULL} /* Sentinel */
187};217};
188 218
@@ -191,11 +221,11 @@ static PyMethodDef Py_cvec_methods[] = { · 2 unmodified lines
191};221};
192 222
193static PyGetSetDef Py_cvec_getseters[] = {223static PyGetSetDef Py_cvec_getseters[] = {
194 {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm, 224 {"norm", (getter)Py_cvec_get_norm, (setter)Py_cvec_set_norm,
195 "Numpy vector of shape (length,) containing the magnitude",225 "numpy.ndarray: Vector of shape `(length,)` containing the magnitude.",
196 NULL},226 NULL},
197 {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas, 227 {"phas", (getter)Py_cvec_get_phas, (setter)Py_cvec_set_phas,
198 "Numpy vector of shape (length,) containing the phase",228 "numpy.ndarray: Vector of shape `(length,)` containing the phase.",
199 NULL},229 NULL},
200 {NULL} /* sentinel */230 {NULL} /* sentinel */
201};231};
python/ext/py-filterbank.c+133−9
@@ -94,7 +94,7 @@ Py_filterbank_do(Py_filterbank * self, PyObject * args) · 93 unmodified lines
94 94
95 if (self->vec.length != self->win_s / 2 + 1) {95 if (self->vec.length != self->win_s / 2 + 1) {
96 PyErr_Format(PyExc_ValueError,96 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",
98 self->vec.length, self->win_s / 2 + 1);98 self->vec.length, self->win_s / 2 + 1);
99 return NULL;99 return NULL;
100 }100 }
@@ -122,8 +122,8 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 21 unmodified lines
122 uint_t err = 0;122 uint_t err = 0;
123 123
124 PyObject *input;124 PyObject *input;
125 uint_t samplerate;125 smpl_t samplerate;
126 if (!PyArg_ParseTuple (args, "OI", &input, &samplerate)) {126 if (!PyArg_ParseTuple (args, "O" AUBIO_NPY_SMPL_CHR, &input, &samplerate)) {
127 return NULL;127 return NULL;
128 }128 }
129 129
@@ -138,8 +138,14 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 8 unmodified lines
138 err = aubio_filterbank_set_triangle_bands (self->o,138 err = aubio_filterbank_set_triangle_bands (self->o,
139 &(self->freqs), samplerate);139 &(self->freqs), samplerate);
140 if (err > 0) {140 if (err > 0) {
141 PyErr_SetString (PyExc_ValueError,141 if (PyErr_Occurred() == NULL) {
142 "error when setting filter to A-weighting");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 }
143 return NULL;149 return NULL;
144 }150 }
145 Py_RETURN_NONE;151 Py_RETURN_NONE;
@@ -150,15 +156,79 @@ Py_filterbank_set_mel_coeffs_slaney (Py_filterbank * self, PyObject *args) · 4 unmodified lines
150{156{
151 uint_t err = 0;157 uint_t err = 0;
152 158
153 uint_t samplerate;159 smpl_t samplerate;
154 if (!PyArg_ParseTuple (args, "I", &samplerate)) {160 if (!PyArg_ParseTuple (args, AUBIO_NPY_SMPL_CHR, &samplerate)) {
155 return NULL;161 return NULL;
156 }162 }
157 163
158 err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);164 err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);
159 if (err > 0) {165 if (err > 0) {
160 PyErr_SetString (PyExc_ValueError,166 if (PyErr_Occurred() == NULL) {
161 "error when setting filter to A-weighting");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
179static PyObject *
180Py_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
208static PyObject *
209Py_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 }
162 return NULL;232 return NULL;
163 }233 }
164 Py_RETURN_NONE;234 Py_RETURN_NONE;
@@ -195,15 +265,69 @@ Py_filterbank_get_coeffs (Py_filterbank * self, PyObject *unused) · 30 unmodified lines
195 aubio_filterbank_get_coeffs (self->o) );265 aubio_filterbank_get_coeffs (self->o) );
196}266}
197 267
268static PyObject *
269Py_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
291static PyObject *
292Py_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
198static PyMethodDef Py_filterbank_methods[] = {314static PyMethodDef Py_filterbank_methods[] = {
199 {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands,315 {"set_triangle_bands", (PyCFunction) Py_filterbank_set_triangle_bands,
200 METH_VARARGS, "set coefficients of filterbanks"},316 METH_VARARGS, "set coefficients of filterbanks"},
201 {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,317 {"set_mel_coeffs_slaney", (PyCFunction) Py_filterbank_set_mel_coeffs_slaney,
202 METH_VARARGS, "set coefficients of filterbank as in Auditory Toolbox"},318 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"},
203 {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,323 {"get_coeffs", (PyCFunction) Py_filterbank_get_coeffs,
204 METH_NOARGS, "get coefficients of filterbank"},324 METH_NOARGS, "get coefficients of filterbank"},
205 {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,325 {"set_coeffs", (PyCFunction) Py_filterbank_set_coeffs,
206 METH_VARARGS, "set coefficients of filterbank"},326 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"},
207 {NULL}331 {NULL}
208};332};
209 333
python/ext/py-musicutils.c+102−0
@@ -133,3 +133,105 @@ Py_aubio_level_detection(PyObject *self, PyObject *args) · 132 unmodified lines
133 133
134 return level_detection;134 return level_detection;
135}135}
136
137PyObject *
138Py_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
161PyObject *
162Py_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
185PyObject*
186Py_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
202PyObject*
203Py_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
219PyObject*
220Py_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
229PyObject*
230Py_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
2#define PY_AUBIO_MUSICUTILS_H2#define PY_AUBIO_MUSICUTILS_H
3 3
4static char Py_aubio_window_doc[] = ""4static char Py_aubio_window_doc[] = ""
5"window(string, integer) -> fvec\n"5"window(window_type, size)\n"
6"\n"6"\n"
7"Create a window\n"7"Create a window of length `size`. `window_type` should be one\n"
8"of the following:\n"
8"\n"9"\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"
10"-------\n"30"-------\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"
11"\n"41"\n"
12">>> window('hanningz', 1024)\n"42">>> aubio.window('hanningz', 1024)\n"
13"array([ 0.00000000e+00, 9.41753387e-06, 3.76403332e-05, ...,\n"43"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"";
15 94
16PyObject * Py_aubio_window(PyObject *self, PyObject *args);95PyObject * Py_aubio_window(PyObject *self, PyObject *args);
17 96
18static char Py_aubio_level_lin_doc[] = ""97static char Py_aubio_level_lin_doc[] = ""
19"level_lin(fvec) -> fvec\n"98"level_lin(x)\n"
20"\n"99"\n"
21"Compute sound level on a linear scale.\n"100"Compute sound pressure level of `x`, on a linear scale.\n"
22"\n"101"\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"
24"\n"111"\n"
25"Example\n"112"Example\n"
26"-------\n"113"-------\n"
27"\n"114"\n"
28">>> level_Lin(numpy.ones(1024))\n"115">>> aubio.level_lin(aubio.fvec(numpy.ones(1024)))\n"
29"1.0";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"";
30 128
31PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args);129PyObject * Py_aubio_level_lin(PyObject *self, PyObject *args);
32 130
33static char Py_aubio_db_spl_doc[] = ""131static char Py_aubio_db_spl_doc[] = ""
34"Compute sound pressure level (SPL) in dB\n"132"db_spl(x)\n"
35"\n"133"\n"
36"This quantity is often wrongly called 'loudness'.\n"134"Compute Sound Pressure Level (SPL) of `x`, in dB.\n"
37"\n"135"\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"
39"\n"145"\n"
40"Example\n"146"Example\n"
41"-------\n"147"-------\n"
42"\n"148"\n"
43">>> db_spl(numpy.ones(1024))\n"149">>> aubio.db_spl(aubio.fvec(np.ones(1024)))\n"
44"1.0";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"";
45 168
46PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args);169PyObject * Py_aubio_db_spl(PyObject *self, PyObject *args);
47 170
48static char Py_aubio_silence_detection_doc[] = ""171static 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"
50"\n"173"\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"
52"\n"175"\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"
54"-------\n"184"-------\n"
185"int\n"
186" `1` if level of `vec`, in dB SPL, is under `level`,\n"
187" `0` otherwise.\n"
55"\n"188"\n"
56">>> import numpy\n"""189"Examples\n"
57">>> silence_detection(numpy.ones(1024, dtype=\"float32\"), -80)\n"190"--------\n"
58"0";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"";
59 201
60PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args);202PyObject * Py_aubio_silence_detection(PyObject *self, PyObject *args);
61 203
62static char Py_aubio_level_detection_doc[] = ""204static 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"
64"\n"221"\n"
65"Example\n"222"Example\n"
66"-------\n"223"-------\n"
67"\n"224"\n"
68">>> import numpy\n"""225">>> aubio.level_detection(0.7*aubio.fvec(np.ones(1024)), -3.)\n"
69">>> level_detection(0.7*numpy.ones(1024, dtype=\"float32\"), -80)\n"226"1.0\n"
70"0";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"";
71 234
72PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args);235PyObject * Py_aubio_level_detection(PyObject *self, PyObject *args);
73 236
237static 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"";
268PyObject * Py_aubio_shift(PyObject *self, PyObject *args);
269
270static 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"";
301PyObject * Py_aubio_ishift(PyObject *self, PyObject *args);
302
303static 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"";
324PyObject * Py_aubio_hztomel(PyObject *self, PyObject *args);
325
326static 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"";
347PyObject * Py_aubio_meltohz(PyObject *self, PyObject *args);
348
349static 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"";
358PyObject * Py_aubio_hztomel_htk(PyObject *self, PyObject *args);
359
360static 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"";
369PyObject * Py_aubio_meltohz_htk(PyObject *self, PyObject *args);
370
74#endif /* PY_AUBIO_MUSICUTILS_H */371#endif /* PY_AUBIO_MUSICUTILS_H */
python/ext/py-phasevoc.c+116−8
@@ -1,6 +1,58 @@
1#include "aubio-types.h"1#include "aubio-types.h"
2 2
3static char Py_pvoc_doc[] = "pvoc object";3static 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
4 56
5typedef struct57typedef struct
6{58{
@@ -38,10 +90,6 @@ Py_pvoc_new (PyTypeObject * type, PyObject * args, PyObject * kwds) · 31 unmodified lines
38 self->win_s = Py_default_vector_length;90 self->win_s = Py_default_vector_length;
39 self->hop_s = Py_default_vector_length/2;91 self->hop_s = Py_default_vector_length/2;
40 92
41 if (self == NULL) {
42 return NULL;
43 }
44
45 if (win_s > 0) {93 if (win_s > 0) {
46 self->win_s = win_s;94 self->win_s = win_s;
47 } else if (win_s < 0) {95 } else if (win_s < 0) {
@@ -121,9 +169,11 @@ Py_pvoc_do(Py_pvoc * self, PyObject * args) · 73 unmodified lines
121 169
122static PyMemberDef Py_pvoc_members[] = {170static PyMemberDef Py_pvoc_members[] = {
123 {"win_s", T_INT, offsetof (Py_pvoc, win_s), READONLY,171 {"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 ""},
125 {"hop_s", T_INT, offsetof (Py_pvoc, hop_s), READONLY,174 {"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 ""},
127 { NULL } // sentinel177 { NULL } // sentinel
128};178};
129 179
@@ -155,9 +205,67 @@ Py_pvoc_rdo(Py_pvoc * self, PyObject * args) · 25 unmodified lines
155 return self->routput;205 return self->routput;
156}206}
157 207
208static PyObject *
209Pyaubio_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
158static PyMethodDef Py_pvoc_methods[] = {226static PyMethodDef Py_pvoc_methods[] = {
159 {"rdo", (PyCFunction) Py_pvoc_rdo, METH_VARARGS,227 {"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 ""},
161 {NULL}269 {NULL}
162};270};
163 271
python/ext/py-sink.c+89−50
@@ -12,53 +12,78 @@ typedef struct · 11 unmodified lines
12} Py_sink;12} Py_sink;
13 13
14static char Py_sink_doc[] = ""14static char Py_sink_doc[] = ""
15" __new__(path, samplerate = 44100, channels = 1)\n"15"sink(path, samplerate=44100, channels=1)\n"
16"\n"16"\n"
17" Create a new sink, opening the given path for writing.\n"17"Write audio samples to file.\n"
18"\n"18"\n"
19" Examples\n"19"Parameters\n"
20" --------\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"
21"\n"27"\n"
22" Create a new sink at 44100Hz, mono:\n"28"Examples\n"
29"--------\n"
23"\n"30"\n"
24" >>> sink('/tmp/t.wav')\n"31"Create a new sink at 44100Hz, mono:\n"
25"\n"32"\n"
26" Create a new sink at 8000Hz, mono:\n"33">>> snk = aubio.sink('out.wav')\n"
27"\n"34"\n"
28" >>> sink('/tmp/t.wav', samplerate = 8000)\n"35"Create a new sink at 32000Hz, stereo, write 100 samples into it:\n"
29"\n"36"\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"
31"\n"39"\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"
33"\n"41"\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"
35"\n"46"\n"
36" >>> sink('/tmp/t.wav', channels = 5, samplerate = 32000)\n"47"See also\n"
37"\n"48"--------\n"
38" __call__(vec, write)\n"49"source: read audio samples from a file.\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"
46"\n";50"\n";
47 51
48static char Py_sink_do_doc[] = ""52static char Py_sink_do_doc[] = ""
49"x.do(vec, write) <==> x(vec, write)\n"53"do(vec, write)\n"
50"\n"54"\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"";
52 64
53static char Py_sink_do_multi_doc[] = ""65static 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"
55"\n"69"\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"";
57 77
58static char Py_sink_close_doc[] = ""78static char Py_sink_close_doc[] = ""
59"x.close()\n"79"close()\n"
60"\n"80"\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"";
62 87
63static PyObject *88static PyObject *
64Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)89Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
@@ -80,27 +105,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 15 unmodified lines
80 return NULL;105 return NULL;
81 }106 }
82 107
83 self->uri = "none";108 self->uri = NULL;
84 if (uri != NULL) {109 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);
86 }112 }
87 113
88 self->samplerate = Py_aubio_default_samplerate;114 self->samplerate = Py_aubio_default_samplerate;
89 if ((sint_t)samplerate > 0) {115 if (samplerate != 0) {
90 self->samplerate = samplerate;116 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;
95 }117 }
96 118
97 self->channels = 1;119 self->channels = 1;
98 if ((sint_t)channels > 0) {120 if (channels != 0) {
99 self->channels = channels;121 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;
104 }122 }
105 123
106 return (PyObject *) self;124 return (PyObject *) self;
@@ -109,17 +127,20 @@ Py_sink_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 2 unmodified lines
109static int127static int
110Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds)128Py_sink_init (Py_sink * self, PyObject * args, PyObject * kwds)
111{129{
112 if (self->channels == 1) {130 self->o = new_aubio_sink ( self->uri, 0 );
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 }
119 if (self->o == NULL) {131 if (self->o == NULL) {
120 PyErr_SetString (PyExc_RuntimeError, "error creating sink with this uri");132 // error string was set in new_aubio_sink
121 return -1;133 return -1;
122 }134 }
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
123 self->samplerate = aubio_sink_get_samplerate ( self->o );144 self->samplerate = aubio_sink_get_samplerate ( self->o );
124 self->channels = aubio_sink_get_channels ( self->o );145 self->channels = aubio_sink_get_channels ( self->o );
125 146
@@ -131,6 +152,9 @@ Py_sink_del (Py_sink *self, PyObject *unused) · 5 unmodified lines
131{152{
132 del_aubio_sink(self->o);153 del_aubio_sink(self->o);
133 free(self->mwrite_data.data);154 free(self->mwrite_data.data);
155 if (self->uri) {
156 free(self->uri);
157 }
134 Py_TYPE(self)->tp_free((PyObject *) self);158 Py_TYPE(self)->tp_free((PyObject *) self);
135}159}
136 160
@@ -189,11 +213,11 @@ Py_sink_do_multi(Py_sink * self, PyObject * args) · 52 unmodified lines
189 213
190static PyMemberDef Py_sink_members[] = {214static PyMemberDef Py_sink_members[] = {
191 {"uri", T_STRING, offsetof (Py_sink, uri), READONLY,215 {"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."},
193 {"samplerate", T_INT, offsetof (Py_sink, samplerate), READONLY,217 {"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."},
195 {"channels", T_INT, offsetof (Py_sink, channels), READONLY,219 {"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."},
197 { NULL } // sentinel221 { NULL } // sentinel
198};222};
199 223
@@ -204,10 +228,25 @@ Pyaubio_sink_close (Py_sink *self, PyObject *unused) · 4 unmodified lines
204 Py_RETURN_NONE;228 Py_RETURN_NONE;
205}229}
206 230
231static char Pyaubio_sink_enter_doc[] = "";
232static PyObject* Pyaubio_sink_enter(Py_sink *self, PyObject *unused) {
233 Py_INCREF(self);
234 return (PyObject*)self;
235}
236
237static char Pyaubio_sink_exit_doc[] = "";
238static PyObject* Pyaubio_sink_exit(Py_sink *self, PyObject *unused) {
239 return Pyaubio_sink_close(self, unused);
240}
241
207static PyMethodDef Py_sink_methods[] = {242static PyMethodDef Py_sink_methods[] = {
208 {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc},243 {"do", (PyCFunction) Py_sink_do, METH_VARARGS, Py_sink_do_doc},
209 {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc},244 {"do_multi", (PyCFunction) Py_sink_do_multi, METH_VARARGS, Py_sink_do_multi_doc},
210 {"close", (PyCFunction) Pyaubio_sink_close, METH_NOARGS, Py_sink_close_doc},245 {"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},
211 {NULL} /* sentinel */250 {NULL} /* sentinel */
212};251};
213 252
python/ext/py-source.c+370−41
@@ -16,68 +16,316 @@ typedef struct · 15 unmodified lines
16} Py_source;16} Py_source;
17 17
18static char Py_source_doc[] = ""18static 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"
20"\n"20"\n"
21" Create a new source, opening the given path for reading.\n"21"Read audio samples from a media file.\n"
22"\n"22"\n"
23" Examples\n"23"`source` open the file specified in `path` and creates a callable\n"
24" --------\n"24"returning `hop_size` new audio samples at each invocation.\n"
25"\n"25"\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"
27"\n"29"\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"
29"\n"34"\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"
31"\n"37"\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"
33"\n"40"\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"
35"\n"51"\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"
37"\n"56"\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"
39"\n"60"\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"
41"\n"63"\n"
42" __call__()\n"64">>> src = aubio.source('stereo.wav')\n"
43" vec, read = x() <==> vec, read = x.do()\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"
44"\n"73"\n"
45" Read vector from source.\n"74"In a more Pythonic way, it can also look like this:\n"
46"\n"75"\n"
47" See also\n"76">>> total_read = 0\n"
48" --------\n"77">>> with aubio.source('stereo.wav') as src:\n"
49" aubio.source.do\n"78"... for frames in src:\n"
50"\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"";
51 217
52static char Py_source_get_samplerate_doc[] = ""218static 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"
54"\n"222"\n"
55"Get samplerate of source.";223"Returns\n"
224"-------\n"
225"int\n"
226" Sampling rate, in Hz.\n"
227"";
56 228
57static char Py_source_get_channels_doc[] = ""229static char Py_source_get_channels_doc[] = ""
58"x.get_channels() -> number of channels\n"230"get_channels()\n"
59"\n"231"\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"";
61 239
62static char Py_source_do_doc[] = ""240static 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"
64"\n"256"\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"";
66 268
67static char Py_source_do_multi_doc[] = ""269static 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"
69"\n"283"\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"";
71 307
72static char Py_source_close_doc[] = ""308static char Py_source_close_doc[] = ""
73"x.close()\n"309"close()\n"
74"\n"310"\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"";
76 315
77static char Py_source_seek_doc[] = ""316static char Py_source_seek_doc[] = ""
78"x.seek(position)\n"317"seek(position)\n"
318"\n"
319"Seek to position in file.\n"
79"\n"320"\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"";
81 329
82static PyObject *330static PyObject *
83Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)331Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds)
@@ -100,9 +348,10 @@ Py_source_new (PyTypeObject * pytype, PyObject * args, PyObject * kwds) · 16 unmodified lines
100 return NULL;348 return NULL;
101 }349 }
102 350
103 self->uri = "none";351 self->uri = NULL;
104 if (uri != NULL) {352 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);
106 }355 }
107 356
108 self->samplerate = 0;357 self->samplerate = 0;
@@ -163,6 +412,9 @@ Py_source_del (Py_source *self, PyObject *unused) · 54 unmodified lines
163 del_aubio_source(self->o);412 del_aubio_source(self->o);
164 free(self->c_mread_to.data);413 free(self->c_mread_to.data);
165 }414 }
415 if (self->uri) {
416 free(self->uri);
417 }
166 Py_XDECREF(self->read_to);418 Py_XDECREF(self->read_to);
167 Py_XDECREF(self->mread_to);419 Py_XDECREF(self->mread_to);
168 Py_TYPE(self)->tp_free((PyObject *) self);420 Py_TYPE(self)->tp_free((PyObject *) self);
@@ -213,15 +465,29 @@ Py_source_do_multi(Py_source * self, PyObject * args) · 44 unmodified lines
213 465
214static PyMemberDef Py_source_members[] = {466static PyMemberDef Py_source_members[] = {
215 {"uri", T_STRING, offsetof (Py_source, uri), READONLY,467 {"uri", T_STRING, offsetof (Py_source, uri), READONLY,
216 "path at which the source was created"},468 "str (read-only): pathname or URL"},
217 {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY,469 {"samplerate", T_INT, offsetof (Py_source, samplerate), READONLY,
218 "samplerate at which the source is viewed"},470 "int (read-only): sampling rate"},
219 {"channels", T_INT, offsetof (Py_source, channels), READONLY,471 {"channels", T_INT, offsetof (Py_source, channels), READONLY,
220 "number of channels found in the source"},472 "int (read-only): number of channels"},
221 {"hop_size", T_INT, offsetof (Py_source, hop_size), READONLY,473 {"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"},
223 {"duration", T_INT, offsetof (Py_source, duration), READONLY,475 {"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 ""},
225 { NULL } // sentinel491 { NULL } // sentinel
226};492};
227 493
@@ -242,7 +508,7 @@ Pyaubio_source_get_channels (Py_source *self, PyObject *unused) · 14 unmodified lines
242static PyObject *508static PyObject *
243Pyaubio_source_close (Py_source *self, PyObject *unused)509Pyaubio_source_close (Py_source *self, PyObject *unused)
244{510{
245 aubio_source_close (self->o);511 if (aubio_source_close(self->o) != 0) return NULL;
246 Py_RETURN_NONE;512 Py_RETURN_NONE;
247}513}
248 514
@@ -272,6 +538,65 @@ Pyaubio_source_seek (Py_source *self, PyObject *args) · 23 unmodified lines
272 Py_RETURN_NONE;538 Py_RETURN_NONE;
273}539}
274 540
541static char Pyaubio_source_enter_doc[] = "";
542static PyObject* Pyaubio_source_enter(Py_source *self, PyObject *unused) {
543 Py_INCREF(self);
544 return (PyObject*)self;
545}
546
547static char Pyaubio_source_exit_doc[] = "";
548static PyObject* Pyaubio_source_exit(Py_source *self, PyObject *unused) {
549 return Pyaubio_source_close(self, unused);
550}
551
552static PyObject* Pyaubio_source_iter(PyObject *self) {
553 Py_INCREF(self);
554 return (PyObject*)self;
555}
556
557static 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
275static PyMethodDef Py_source_methods[] = {600static PyMethodDef Py_source_methods[] = {
276 {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate,601 {"get_samplerate", (PyCFunction) Pyaubio_source_get_samplerate,
277 METH_NOARGS, Py_source_get_samplerate_doc},602 METH_NOARGS, Py_source_get_samplerate_doc},
@@ -285,6 +610,10 @@ static PyMethodDef Py_source_methods[] = { · 7 unmodified lines
285 METH_NOARGS, Py_source_close_doc},610 METH_NOARGS, Py_source_close_doc},
286 {"seek", (PyCFunction) Pyaubio_source_seek,611 {"seek", (PyCFunction) Pyaubio_source_seek,
287 METH_VARARGS, Py_source_seek_doc},612 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},
288 {NULL} /* sentinel */617 {NULL} /* sentinel */
289};618};
290 619
@@ -314,8 +643,8 @@ PyTypeObject Py_sourceType = { · 23 unmodified lines
314 0,643 0,
315 0,644 0,
316 0,645 0,
317 0,646 Pyaubio_source_iter,
318 0,647 (unaryfunc)Pyaubio_source_iter_next,
319 Py_source_methods,648 Py_source_methods,
320 Py_source_members,649 Py_source_members,
321 0,650 0,
python/ext/ufuncs.c+44−3
@@ -58,7 +58,22 @@ static char Py_aubio_unary_types[] = { · 57 unmodified lines
58 //NPY_OBJECT, NPY_OBJECT,58 //NPY_OBJECT, NPY_OBJECT,
59};59};
60 60
61static char Py_unwrap2pi_doc[] = "map angle to unit circle [-pi, pi[";61// Note: these docstrings should *not* include the function signatures
62
63static 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"";
62 77
63static void* Py_unwrap2pi_data[] = {78static void* Py_unwrap2pi_data[] = {
64 (void *)aubio_unwrap2pi,79 (void *)aubio_unwrap2pi,
@@ -67,14 +82,40 @@ static void* Py_unwrap2pi_data[] = { · 2 unmodified lines
67 //(void *)unwrap2pio,82 //(void *)unwrap2pio,
68};83};
69 84
70static char Py_freqtomidi_doc[] = "convert frequency to midi";85static 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"";
71 99
72static void* Py_freqtomidi_data[] = {100static void* Py_freqtomidi_data[] = {
73 (void *)aubio_freqtomidi,101 (void *)aubio_freqtomidi,
74 (void *)aubio_freqtomidi,102 (void *)aubio_freqtomidi,
75};103};
76 104
77static char Py_miditofreq_doc[] = "convert midi to frequency";105static 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"";
78 119
79static void* Py_miditofreq_data[] = {120static void* Py_miditofreq_data[] = {
80 (void *)aubio_miditofreq,121 (void *)aubio_miditofreq,
python/lib/__init__.py+0−0

Empty file deleted.

python/lib/aubio/__init__.py+72−5
@@ -1,18 +1,85 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2# -*- coding: utf8 -*-
3
4"""
5aubio
6=====
7
8Provides a number of classes and functions for music and audio signal
9analysis.
10
11How to use the documentation
12----------------------------
13
14Documentation of the python module is available as docstrings provided
15within the code, and a reference guide available online from `the
16aubio homepage <https://aubio.org/documentation>`_.
17
18The docstrings examples are written assuming `aubio` and `numpy` have been
19imported with:
20
21>>> import aubio
22>>> import numpy as np
23"""
2 24
3import numpy25import numpy
4from ._aubio import *26from ._aubio import __version__ as version
5from ._aubio import float_type27from ._aubio import float_type
28from ._aubio import *
6from .midiconv import *29from .midiconv import *
7from .slicing import *30from .slicing import *
8 31
32
9class fvec(numpy.ndarray):33class 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'))
11 58
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):
13 if isinstance(input_arg, int):75 if isinstance(input_arg, int):
14 if input_arg == 0:76 if input_arg == 0:
15 raise ValueError("vector length of 1 or more expected")77 raise ValueError("vector length of 1 or more expected")
16 return numpy.zeros(input_arg, dtype=float_type, **kwargs)78 return numpy.zeros(input_arg, dtype=float_type, order='C')
17 else:79 else:
18 return numpy.array(input_arg, dtype=float_type, **kwargs)80 np_input = numpy.array(input_arg, dtype=float_type, order='C')
81 if len(np_input.shape) != 1:
82 raise ValueError("input_arg should have shape (n,)")
83 if np_input.shape[0] == 0:
84 raise ValueError("vector length of 1 or more expected")
85 return np_input
python/lib/aubio/cmd.py+623−0
@@ -0,0 +1,623 @@
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""aubio command line tool
5
6This file was written by Paul Brossier <piem@aubio.org> and is released under
7the GNU/GPL v3.
8
9Note: this script is mostly about parsing command line arguments. For more
10readable code examples, check out the `python/demos` folder."""
11
12import sys
13import argparse
14import warnings
15import aubio
16
17def 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
41def 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
47def 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
64def 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
79def 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
89def 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
99def 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
111def 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
121def 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
131def 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
142def 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
159class 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
268def samples2seconds(n_frames, samplerate):
269 return "%f\t" % (n_frames / float(samplerate))
270
271def samples2milliseconds(n_frames, samplerate):
272 return "%f\t" % (1000. * n_frames / float(samplerate))
273
274def samples2samples(n_frames, _samplerate):
275 return "%d\t" % n_frames
276
277def 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
289class 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
318class 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
342class 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
360class 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
373class 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
393class 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
418class 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
442class 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
465class 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
498class 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
521def _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
547def 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
7import sys
8from aubio.cmd import AubioArgumentParser, _cut_slice
9
10def 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
93def _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
136def 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 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2""" utilities to convert midi note number to and from note names """2""" utilities to convert midi note number to and from note names """
3 3
4__all__ = ['note2midi', 'midi2note', 'freq2note']
5
6import sys4import sys
5from ._aubio import freqtomidi, miditofreq
6
7__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
8
7py3 = sys.version_info[0] == 39py3 = sys.version_info[0] == 3
8if py3:10if py3:
9 str_instances = str11 str_instances = str
@@ -12,18 +14,65 @@ else: · 2 unmodified lines
12 str_instances = (str, unicode)14 str_instances = (str, unicode)
13 int_instances = (int, long)15 int_instances = (int, long)
14 16
17
15def note2midi(note):18def note2midi(note):
16 " convert note name to midi note number, e.g. [C-1, G9] -> [0, 127] "19 """Convert note name to midi note number.
17 _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}20
18 _valid_modifiers = {None: 0, u'♮': 0, '#': +1, u'♯': +1, u'\udd2a': +2,21 Input string `note` should be composed of one note root
19 'b': -1, u'♭': -1, u'\ufffd': -2}22 and one octave, with optionally one modifier in between.
23
24 List of valid components:
25
26 - note roots: `C`, `D`, `E`, `F`, `G`, `A`, `B`,
27 - modifiers: `b`, `#`, as well as unicode characters
28 `𝄫`, `♭`, `♮`, `♯` and `𝄪`,
29 - octave numbers: `-1` -> `11`.
30
31 Parameters
32 ----------
33 note : str
34 note name
35
36 Returns
37 -------
38 int
39 corresponding midi note number
40
41 Examples
42 --------
43 >>> aubio.note2midi('C#4')
44 61
45 >>> aubio.note2midi('B♭5')
46 82
47
48 Raises
49 ------
50 TypeError
51 If `note` was not a string.
52 ValueError
53 If an error was found while converting `note`.
54
55 See Also
56 --------
57 midi2note, freqtomidi, miditofreq
58 """
59 _valid_notenames = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7,
60 'A': 9, 'B': 11}
61 _valid_modifiers = {
62 u'𝄫': -2, # double flat
63 u'♭': -1, 'b': -1, '\u266d': -1, # simple flat
64 u'♮': 0, '\u266e': 0, None: 0, # natural
65 '#': +1, u'♯': +1, '\u266f': +1, # sharp
66 u'𝄪': +2, # double sharp
67 }
20 _valid_octaves = range(-1, 10)68 _valid_octaves = range(-1, 10)
21 if not isinstance(note, str_instances):69 if not isinstance(note, str_instances):
22 raise TypeError("a string is required, got %s (%s)" % (note, str(type(note))))70 msg = "a string is required, got {:s} ({:s})"
71 raise TypeError(msg.format(str(type(note)), repr(note)))
23 if len(note) not in range(2, 5):72 if len(note) not in range(2, 5):
24 raise ValueError("string of 2 to 4 characters expected, got %d (%s)" \73 msg = "string of 2 to 4 characters expected, got {:d} ({:s})"
25 % (len(note), note))74 raise ValueError(msg.format(len(note), note))
26 notename, modifier, octave = [None]*375 notename, modifier, octave = [None] * 3
27 76
28 if len(note) == 4:77 if len(note) == 4:
29 notename, modifier, octave_sign, octave = note78 notename, modifier, octave_sign, octave = note
@@ -46,21 +95,97 @@ def note2midi(note): · 16 unmodified lines
46 if octave not in _valid_octaves:95 if octave not in _valid_octaves:
47 raise ValueError("%s is not a valid octave" % octave)96 raise ValueError("%s is not a valid octave" % octave)
48 97
49 midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier]98 midi = (octave + 1) * 12 + _valid_notenames[notename] \
99 + _valid_modifiers[modifier]
50 if midi > 127:100 if midi > 127:
51 raise ValueError("%s is outside of the range C-2 to G8" % note)101 raise ValueError("%s is outside of the range C-2 to G8" % note)
52 return midi102 return midi
53 103
104
54def midi2note(midi):105def midi2note(midi):
55 " convert midi note number to note name, e.g. [0, 127] -> [C-1, G9] "106 """Convert midi note number to note name.
107
108 Parameters
109 ----------
110 midi : int [0, 128]
111 input midi note number
112
113 Returns
114 -------
115 str
116 note name
117
118 Examples
119 --------
120 >>> aubio.midi2note(70)
121 'A#4'
122 >>> aubio.midi2note(59)
123 'B3'
124
125 Raises
126 ------
127 TypeError
128 If `midi` was not an integer.
129 ValueError
130 If `midi` is out of the range `[0, 128]`.
131
132 See Also
133 --------
134 note2midi, miditofreq, freqtomidi
135 """
56 if not isinstance(midi, int_instances):136 if not isinstance(midi, int_instances):
57 raise TypeError("an integer is required, got %s" % midi)137 raise TypeError("an integer is required, got %s" % midi)
58 if midi not in range(0, 128):138 if midi not in range(0, 128):
59 raise ValueError("an integer between 0 and 127 is excepted, got %d" % midi)139 msg = "an integer between 0 and 127 is excepted, got {:d}"
60 _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']140 raise ValueError(msg.format(midi))
141 _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#',
142 'A', 'A#', 'B']
61 return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)143 return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
62 144
145
63def freq2note(freq):146def freq2note(freq):
64 " convert frequency in Hz to nearest note name, e.g. [0, 22050.] -> [C-1, G9] "147 """Convert frequency in Hz to nearest note name.
65 from aubio import freqtomidi148
66 return midi2note(int(freqtomidi(freq)))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
170def 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
5 5
6_max_timestamp = 1e1206_max_timestamp = 1e120
7 7
8
8def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,9def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
9 output_dir=None, samplerate=0, hopsize=256):10 output_dir=None, samplerate=0, hopsize=256,
10 """ slice a sound file at given timestamps """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 """
11 75
12 if timestamps is None or len(timestamps) == 0:76 if not timestamps:
13 raise ValueError("no timestamps given")77 raise ValueError("no timestamps given")
14 78
15 if timestamps[0] != 0:79 if timestamps[0] != 0 and create_first:
16 timestamps = [0] + timestamps80 timestamps = [0] + timestamps
17 if timestamps_end is not None:81 if timestamps_end is not None:
18 timestamps_end = [timestamps[1] - 1] + timestamps_end82 timestamps_end = [timestamps[1] - 1] + timestamps_end
19 83
20 if timestamps_end is not None:84 if timestamps_end is not None:
21 if len(timestamps_end) != len(timestamps):85 if len(timestamps_end) == len(timestamps) - 1:
86 timestamps_end = timestamps_end + [_max_timestamp]
87 elif len(timestamps_end) != len(timestamps):
22 raise ValueError("len(timestamps_end) != len(timestamps)")88 raise ValueError("len(timestamps_end) != len(timestamps)")
23 else:89 else:
24 timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]90 timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
25 91
26 regions = list(zip(timestamps, timestamps_end))92 regions = list(zip(timestamps, timestamps_end))
27 #print regions
28 93
29 source_base_name, _ = os.path.splitext(os.path.basename(source_file))94 source_base_name, _ = os.path.splitext(os.path.basename(source_file))
30 if output_dir is not None:95 if output_dir is not None:
@@ -32,8 +97,8 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 1 unmodified line
32 os.makedirs(output_dir)97 os.makedirs(output_dir)
33 source_base_name = os.path.join(output_dir, source_base_name)98 source_base_name = os.path.join(output_dir, source_base_name)
34 99
35 def new_sink_name(source_base_name, timestamp, samplerate):100 def _new_sink_name(source_base_name, timestamp, samplerate):
36 """ create a sink based on a timestamp in samples, converted in seconds """101 # create name based on a timestamp in samples, converted in seconds
37 timestamp_seconds = timestamp / float(samplerate)102 timestamp_seconds = timestamp / float(samplerate)
38 return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'103 return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
39 104
@@ -48,16 +113,17 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 8 unmodified lines
48 # get hopsize new samples from source113 # get hopsize new samples from source
49 vec, read = _source.do_multi()114 vec, read = _source.do_multi()
50 # if the total number of frames read will exceed the next region start115 # if the total number of frames read will exceed the next region start
51 if len(regions) and total_frames + read >= regions[0][0]:116 while regions and total_frames + read >= regions[0][0]:
52 #print "getting", regions[0], "at", total_frames
53 # get next region117 # get next region
54 start_stamp, end_stamp = regions.pop(0)118 start_stamp, end_stamp = regions.pop(0)
55 # create a name for the sink119 # create a name for the sink
56 new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)120 new_sink_path = _new_sink_name(source_base_name, start_stamp,
121 samplerate)
57 # create its sink122 # create its sink
58 _sink = sink(new_sink_path, samplerate, _source.channels)123 _sink = sink(new_sink_path, samplerate, _source.channels)
59 # create a dictionary containing all this124 # create a dictionary containing all this
60 new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}125 new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp,
126 'sink': _sink}
61 # append the dictionary to the current list of slices127 # append the dictionary to the current list of slices
62 slices.append(new_slice)128 slices.append(new_slice)
63 129
@@ -69,18 +135,19 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 5 unmodified lines
69 start = max(start_stamp - total_frames, 0)135 start = max(start_stamp - total_frames, 0)
70 # number of samples yet to written be until end of region136 # number of samples yet to written be until end of region
71 remaining = end_stamp - total_frames + 1137 remaining = end_stamp - total_frames + 1
72 #print current_slice, remaining, start
73 # not enough frames remaining, time to split138 # not enough frames remaining, time to split
74 if remaining < read:139 if remaining < read:
75 if remaining > start:140 if remaining > start:
76 # write remaining samples from current region141 # write remaining samples from current region
77 _sink.do_multi(vec[:, start:remaining], remaining - start)142 _sink.do_multi(vec[:, start:remaining], remaining - start)
78 #print "closing region", "remaining", remaining
79 # close this file143 # close this file
80 _sink.close()144 _sink.close()
81 elif read > start:145 elif read > start:
82 # write all the samples146 # write all the samples
83 _sink.do_multi(vec[:, start:read], read - start)147 _sink.do_multi(vec[:, start:read], read - start)
84 total_frames += read148 total_frames += read
149 # remove old slices
150 slices = list(filter(lambda s: s['end_stamp'] > total_frames,
151 slices))
85 if read < hopsize:152 if read < hopsize:
86 break153 break
python/lib/gen_code.py+65−23
@@ -2,6 +2,7 @@ aubiodefvalue = { · 1 unmodified line
2 # we have some clean up to do2 # we have some clean up to do
3 'buf_size': 'Py_default_vector_length',3 'buf_size': 'Py_default_vector_length',
4 'win_s': 'Py_default_vector_length',4 'win_s': 'Py_default_vector_length',
5 'size': 'Py_default_vector_length',
5 # and here too6 # and here too
6 'hop_size': 'Py_default_vector_length / 2',7 'hop_size': 'Py_default_vector_length / 2',
7 'hop_s': 'Py_default_vector_length / 2',8 'hop_s': 'Py_default_vector_length / 2',
@@ -84,6 +85,7 @@ objoutsize = { · 76 unmodified lines
84 'filterbank': 'self->n_filters',85 'filterbank': 'self->n_filters',
85 'tss': 'self->buf_size',86 'tss': 'self->buf_size',
86 'pitchshift': 'self->hop_size',87 'pitchshift': 'self->hop_size',
88 'dct': 'self->size',
87 }89 }
88 90
89objinputsize = {91objinputsize = {
@@ -179,6 +181,10 @@ class MappedObject(object): · 89 unmodified lines
179 self.do_inputs = [get_params_types_names(self.do_proto)[1]]181 self.do_inputs = [get_params_types_names(self.do_proto)[1]]
180 self.do_outputs = get_params_types_names(self.do_proto)[2:]182 self.do_outputs = get_params_types_names(self.do_proto)[2:]
181 struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs]183 struct_output_str = ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in self.do_outputs]
184 if len(self.prototypes['rdo']):
185 rdo_outputs = get_params_types_names(prototypes['rdo'][0])[2:]
186 struct_output_str += ["PyObject *{0[name]}; {1} c_{0[name]}".format(i, i['type'][:-1]) for i in rdo_outputs]
187 self.outputs += rdo_outputs
182 self.struct_outputs = ";\n ".join(struct_output_str)188 self.struct_outputs = ";\n ".join(struct_output_str)
183 189
184 #print ("input_params: ", map(split_type, get_input_params(self.do_proto)))190 #print ("input_params: ", map(split_type, get_input_params(self.do_proto)))
@@ -186,17 +192,26 @@ class MappedObject(object): · 1 unmodified line
186 192
187 def gen_code(self):193 def gen_code(self):
188 out = ""194 out = ""
189 out += self.gen_struct()195 try:
190 out += self.gen_doc()196 out += self.gen_struct()
191 out += self.gen_new()197 out += self.gen_doc()
192 out += self.gen_init()198 out += self.gen_new()
193 out += self.gen_del()199 out += self.gen_init()
194 out += self.gen_do()200 out += self.gen_del()
195 out += self.gen_memberdef()201 out += self.gen_do()
196 out += self.gen_set()202 if len(self.prototypes['rdo']):
197 out += self.gen_get()203 self.do_proto = self.prototypes['rdo'][0]
198 out += self.gen_methodef()204 self.do_inputs = [get_params_types_names(self.do_proto)[1]]
199 out += self.gen_typeobject()205 self.do_outputs = get_params_types_names(self.do_proto)[2:]
206 out += self.gen_do(method='rdo')
207 out += self.gen_memberdef()
208 out += self.gen_set()
209 out += self.gen_get()
210 out += self.gen_methodef()
211 out += self.gen_typeobject()
212 except Exception as e:
213 print ("Failed generating code for", self.shortname)
214 raise
200 return out215 return out
201 216
202 def gen_struct(self):217 def gen_struct(self):
@@ -380,12 +395,12 @@ Py_{shortname}_del (Py_{shortname} * self, PyObject * unused) · 177 unmodified lines
380""".format(del_fn = del_fn)395""".format(del_fn = del_fn)
381 return out396 return out
382 397
383 def gen_do(self):398 def gen_do(self, method = 'do'):
384 out = """399 out = """
385// do {shortname}400// do {shortname}
386static PyObject*401static PyObject*
387Py_{shortname}_do (Py_{shortname} * self, PyObject * args)402Pyaubio_{shortname}_{method} (Py_{shortname} * self, PyObject * args)
388{{""".format(**self.__dict__)403{{""".format(method = method, **self.__dict__)
389 input_params = self.do_inputs404 input_params = self.do_inputs
390 output_params = self.do_outputs405 output_params = self.do_outputs
391 #print input_params406 #print input_params
@@ -461,30 +476,51 @@ Py_{shortname}_do (Py_{shortname} * self, PyObject * args) · 69 unmodified lines
461// {shortname} setters476// {shortname} setters
462""".format(**self.__dict__)477""".format(**self.__dict__)
463 for set_param in self.prototypes['set']:478 for set_param in self.prototypes['set']:
464 params = get_params_types_names(set_param)[1]479 params = get_params_types_names(set_param)[1:]
465 paramtype = params['type']480 param = self.shortname.split('_set_')[-1]
481 paramdecls = "".join(["""
482 {0} {1};""".format(p['type'], p['name']) for p in params])
466 method_name = get_name(set_param)483 method_name = get_name(set_param)
467 param = method_name.split('aubio_'+self.shortname+'_set_')[-1]484 param = method_name.split('aubio_'+self.shortname+'_set_')[-1]
468 pyparamtype = pyargparse_chars[paramtype]485 refs = ", ".join(["&%s" % p['name'] for p in params])
486 paramlist = ", ".join(["%s" % p['name'] for p in params])
487 if len(params):
488 paramlist = "," + paramlist
489 pyparamtypes = ''.join([pyargparse_chars[p['type']] for p in params])
469 out += """490 out += """
470static PyObject *491static PyObject *
471Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)492Pyaubio_{shortname}_set_{param} (Py_{shortname} *self, PyObject *args)
472{{493{{
473 uint_t err = 0;494 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 += """
475 500
476 if (!PyArg_ParseTuple (args, "{pyparamtype}", &{param})) {{501 if (!PyArg_ParseTuple (args, "{pyparamtypes}", {refs})) {{
477 return NULL;502 return NULL;
478 }}503 }}
479 err = aubio_{shortname}_set_{param} (self->o, {param});504""".format(pyparamtypes = pyparamtypes, refs = refs)
505
506 out += """
507 err = aubio_{shortname}_set_{param} (self->o {paramlist});
480 508
481 if (err > 0) {{509 if (err > 0) {{
482 PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");510 if (PyErr_Occurred() == NULL) {{
511 PyErr_SetString (PyExc_ValueError, "error running aubio_{shortname}_set_{param}");
512 }} else {{
513 // change the RuntimeError into ValueError
514 PyObject *type, *value, *traceback;
515 PyErr_Fetch(&type, &value, &traceback);
516 PyErr_Restore(PyExc_ValueError, value, traceback);
517 }}
483 return NULL;518 return NULL;
484 }}519 }}
485 Py_RETURN_NONE;520 Py_RETURN_NONE;
486}}521}}
487""".format(param = param, paramtype = paramtype, pyparamtype = pyparamtype, **self.__dict__)522""".format(param = param, refs = refs, paramdecls = paramdecls,
523 pyparamtypes = pyparamtypes, paramlist = paramlist, **self.__dict__)
488 return out524 return out
489 525
490 def gen_get(self):526 def gen_get(self):
@@ -525,6 +561,12 @@ static PyMethodDef Py_{shortname}_methods[] = {{""".format(**self.__dict__) · 34 unmodified lines
525 out += """561 out += """
526 {{"{shortname}", (PyCFunction) Py{name},562 {{"{shortname}", (PyCFunction) Py{name},
527 METH_NOARGS, ""}},""".format(name = name, shortname = shortname)563 METH_NOARGS, ""}},""".format(name = name, shortname = shortname)
564 for m in self.prototypes['rdo']:
565 name = get_name(m)
566 shortname = name.replace('aubio_%s_' % self.shortname, '')
567 out += """
568 {{"{shortname}", (PyCFunction) Py{name},
569 METH_VARARGS, ""}},""".format(name = name, shortname = shortname)
528 out += """570 out += """
529 {NULL} /* sentinel */571 {NULL} /* sentinel */
530};572};
@@ -550,7 +592,7 @@ PyTypeObject Py_{shortname}Type = {{ · 19 unmodified lines
550 0,592 0,
551 0,593 0,
552 0,594 0,
553 (ternaryfunc)Py_{shortname}_do,595 (ternaryfunc)Pyaubio_{shortname}_do,
554 0,596 0,
555 0,597 0,
556 0,598 0,
python/lib/gen_external.py+180−89
@@ -1,5 +1,10 @@
1import distutils.ccompiler1import distutils.ccompiler
2import sys, os, subprocess, glob2import sys
3import os
4import subprocess
5import glob
6from distutils.sysconfig import customize_compiler
7from gen_code import MappedObject
3 8
4header = os.path.join('src', 'aubio.h')9header = os.path.join('src', 'aubio.h')
5output_path = os.path.join('python', 'gen')10output_path = os.path.join('python', 'gen')
@@ -8,43 +13,44 @@ source_header = """// this file is generated! do not modify · 2 unmodified lines
8#include "aubio-types.h"13#include "aubio-types.h"
9"""14"""
10 15
11skip_objects = [16default_skip_objects = [
12 # already in ext/17 # already in ext/
13 'fft',18 'fft',
14 'pvoc',19 'pvoc',
15 'filter',20 'filter',
16 'filterbank',21 'filterbank',
17 #'resampler',22 # AUBIO_UNSTABLE
18 # AUBIO_UNSTABLE23 'hist',
19 'hist',24 'parameter',
20 'parameter',25 'scale',
21 'scale',26 'beattracking',
22 'beattracking',27 'resampler',
23 'resampler',28 'peakpicker',
24 'peakpicker',29 'pitchfcomb',
25 'pitchfcomb',30 'pitchmcomb',
26 'pitchmcomb',31 'pitchschmitt',
27 'pitchschmitt',32 'pitchspecacf',
28 'pitchspecacf',33 'pitchyin',
29 'pitchyin',34 'pitchyinfft',
30 'pitchyinfft',35 'pitchyinfast',
31 'sink',36 'sink',
32 'sink_apple_audio',37 'sink_apple_audio',
33 'sink_sndfile',38 'sink_sndfile',
34 'sink_wavwrite',39 'sink_wavwrite',
35 #'mfcc',40 #'mfcc',
36 'source',41 'source',
37 'source_apple_audio',42 'source_apple_audio',
38 'source_sndfile',43 'source_sndfile',
39 'source_avcodec',44 'source_avcodec',
40 'source_wavread',45 'source_wavread',
41 #'sampler',46 #'sampler',
42 'audio_unit',47 'audio_unit',
43 ]48 'spectral_whitening',
49]
50
44 51
45def get_preprocessor():52def get_preprocessor():
46 # findout which compiler to use53 # findout which compiler to use
47 from distutils.sysconfig import customize_compiler
48 compiler_name = distutils.ccompiler.get_default_compiler()54 compiler_name = distutils.ccompiler.get_default_compiler()
49 compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)55 compiler = distutils.ccompiler.new_compiler(compiler=compiler_name)
50 try:56 try:
@@ -59,26 +65,50 @@ def get_preprocessor(): · 8 unmodified lines
59 print("Warning: failed initializing compiler ({:s})".format(repr(e)))65 print("Warning: failed initializing compiler ({:s})".format(repr(e)))
60 66
61 cpp_cmd = None67 cpp_cmd = None
62 if hasattr(compiler, 'preprocessor'): # for unixccompiler68 if hasattr(compiler, 'preprocessor'): # for unixccompiler
63 cpp_cmd = compiler.preprocessor69 cpp_cmd = compiler.preprocessor
64 elif hasattr(compiler, 'compiler'): # for ccompiler70 elif hasattr(compiler, 'compiler'): # for ccompiler
65 cpp_cmd = compiler.compiler.split()71 cpp_cmd = compiler.compiler.split()
66 cpp_cmd += ['-E']72 cpp_cmd += ['-E']
67 elif hasattr(compiler, 'cc'): # for msvccompiler73 elif hasattr(compiler, 'cc'): # for msvccompiler
68 cpp_cmd = compiler.cc.split()74 cpp_cmd = compiler.cc.split()
69 cpp_cmd += ['-E']75 cpp_cmd += ['-E']
70 76
77 # On win-amd64 (py3.x), the default compiler is cross-compiling, from x86
78 # to amd64 with %WIN_SDK_ROOT%\x86_amd64\cl.exe, but using this binary as a
79 # pre-processor generates no output, so we use %WIN_SDK_ROOT%\cl.exe
80 # instead.
81 if len(cpp_cmd) > 1 and 'cl.exe' in cpp_cmd[-2]:
82 plat = os.path.basename(os.path.dirname(cpp_cmd[-2]))
83 if plat == 'x86_amd64':
84 print('workaround on win64 to avoid empty pre-processor output')
85 cpp_cmd[-2] = cpp_cmd[-2].replace('x86_amd64', '')
86 elif True in ['amd64' in f for f in cpp_cmd]:
87 print('warning: not using workaround for', cpp_cmd[0], plat)
88
71 if not cpp_cmd:89 if not cpp_cmd:
72 print("Warning: could not guess preprocessor, using env's CC")90 print("Warning: could not guess preprocessor, using env's CC")
73 cpp_cmd = os.environ.get('CC', 'cc').split()91 cpp_cmd = os.environ.get('CC', 'cc').split()
74 cpp_cmd += ['-E']92 cpp_cmd += ['-E']
75 93 if 'emcc' in cpp_cmd:
94 cpp_cmd += ['-x', 'c'] # emcc defaults to c++, force C language
76 return cpp_cmd95 return cpp_cmd
77 96
78def get_cpp_objects(header=header):97
98def 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
105def get_cpp_output(header=header, usedouble=False):
106 ''' find and run a C pre-processor on aubio.h '''
79 cpp_cmd = get_preprocessor()107 cpp_cmd = get_preprocessor()
80 108
81 macros = [('AUBIO_UNSTABLE', 1)]109 macros = [('AUBIO_UNSTABLE', 1)]
110 if usedouble:
111 macros += [('HAVE_AUBIO_DOUBLE', 1)]
82 112
83 if not os.path.isfile(header):113 if not os.path.isfile(header):
84 raise Exception("could not find include file " + header)114 raise Exception("could not find include file " + header)
@@ -89,56 +119,115 @@ def get_cpp_objects(header=header): · 4 unmodified lines
89 119
90 print("Running command: {:s}".format(" ".join(cpp_cmd)))120 print("Running command: {:s}".format(" ".join(cpp_cmd)))
91 proc = subprocess.Popen(cpp_cmd,121 proc = subprocess.Popen(cpp_cmd,
92 stderr=subprocess.PIPE,122 stderr=subprocess.PIPE,
93 stdout=subprocess.PIPE)123 stdout=subprocess.PIPE)
94 assert proc, 'Proc was none'124 assert proc, 'Proc was none'
95 cpp_output = proc.stdout.read()125 cpp_output = proc.stdout.read()
96 err_output = proc.stderr.read()126 err_output = proc.stderr.read()
127 if err_output:
128 print("Warning: preprocessor produced errors or warnings:\n%s" \
129 % err_output.decode('utf8'))
97 if not cpp_output:130 if not cpp_output:
98 raise Exception("preprocessor output is empty:\n%s" % err_output)131 raise_msg = "preprocessor output is empty! Running command " \
99 elif err_output:132 + "\"%s\" failed" % " ".join(cpp_cmd)
100 print ("Warning: preprocessor produced warnings:\n%s" % err_output)133 if err_output:
134 raise_msg += " with stderr: \"%s\"" % err_output.decode('utf8')
135 else:
136 raise_msg += " with no stdout or stderr"
137 raise Exception(raise_msg)
101 if not isinstance(cpp_output, list):138 if not isinstance(cpp_output, list):
102 cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]139 cpp_output = [l.strip() for l in cpp_output.decode('utf8').split('\n')]
103 140
104 cpp_output = filter(lambda y: len(y) > 1, cpp_output)141 return cpp_output
142
143def filter_cpp_output(cpp_raw_output):
144 ''' prepare cpp-output for parsing '''
145 cpp_output = filter(lambda y: len(y) > 1, cpp_raw_output)
105 cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))146 cpp_output = list(filter(lambda y: not y.startswith('#'), cpp_output))
106 147
107 i = 1148 i = 1
108 while 1:149 while 1:
109 if i >= len(cpp_output): break150 if i >= len(cpp_output):
110 if cpp_output[i-1].endswith(',') or cpp_output[i-1].endswith('{') or cpp_output[i].startswith('}'):151 break
111 cpp_output[i] = cpp_output[i-1] + ' ' + cpp_output[i]152 if ('{' in cpp_output[i - 1]) and ('}' not in cpp_output[i - 1]) or (';' not in cpp_output[i - 1]):
112 cpp_output.pop(i-1)153 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i]
154 cpp_output.pop(i - 1)
155 elif ('}' in cpp_output[i]):
156 cpp_output[i] = cpp_output[i - 1] + ' ' + cpp_output[i]
157 cpp_output.pop(i - 1)
113 else:158 else:
114 i += 1159 i += 1
115 160
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
117 166
167 return cpp_output
168
169
170def get_cpp_objects_from_c_declarations(c_declarations, skip_objects=None):
171 if skip_objects is None:
172 skip_objects = default_skip_objects
173 typedefs = filter(lambda y: y.startswith('typedef struct _aubio'), c_declarations)
118 cpp_objects = [a.split()[3][:-1] for a in typedefs]174 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
179def 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))
119 197
120 return cpp_output, cpp_objects198 return res
121 199
122 200
123def analyze_cpp_output(cpp_objects, cpp_output):201def generate_lib_from_c_declarations(cpp_objects, c_declarations):
202 ''' returns a lib from given cpp_object names
203
204 a lib is a dict grouping functions by family (onset,pitch...)
205 each eement is itself a dict of functions grouped by puposes as :
206 struct, new, del, do, get, set and other
207 '''
124 lib = {}208 lib = {}
125 209
126 for o in cpp_objects:210 for o in cpp_objects:
127 if o[:6] != 'aubio_':211 shortname = o
128 continue212 if o[:6] == 'aubio_':
129 shortname = o[6:-2]213 shortname = o[6:-2] # without aubio_ prefix and _t suffix
130 if shortname in skip_objects:214
131 continue215 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'rdo': [], 'get': [], 'set': [], 'other': []}
132 lib[shortname] = {'struct': [], 'new': [], 'del': [], 'do': [], 'get': [], 'set': [], 'other': []}
133 lib[shortname]['longname'] = o216 lib[shortname]['longname'] = o
134 lib[shortname]['shortname'] = shortname217 lib[shortname]['shortname'] = shortname
135 for fn in cpp_output:218
136 if o[:-1] in fn:219 fullshortname = o[:-2] # name without _t suffix
137 #print "found", o[:-1], "in", fn220
221 for fn in c_declarations:
222 func_name = fn.split('(')[0].strip().split(' ')[-1]
223 if func_name.startswith(fullshortname + '_') or func_name.endswith(fullshortname):
224 # print "found", shortname, "in", fn
138 if 'typedef struct ' in fn:225 if 'typedef struct ' in fn:
139 lib[shortname]['struct'].append(fn)226 lib[shortname]['struct'].append(fn)
140 elif '_do' in fn:227 elif '_do' in fn:
141 lib[shortname]['do'].append(fn)228 lib[shortname]['do'].append(fn)
229 elif '_rdo' in fn:
230 lib[shortname]['rdo'].append(fn)
142 elif 'new_' in fn:231 elif 'new_' in fn:
143 lib[shortname]['new'].append(fn)232 lib[shortname]['new'].append(fn)
144 elif 'del_' in fn:233 elif 'del_' in fn:
@@ -148,52 +237,52 @@ def analyze_cpp_output(cpp_objects, cpp_output): · 3 unmodified lines
148 elif '_set_' in fn:237 elif '_set_' in fn:
149 lib[shortname]['set'].append(fn)238 lib[shortname]['set'].append(fn)
150 else:239 else:
151 #print "no idea what to do about", fn240 # print "no idea what to do about", fn
152 lib[shortname]['other'].append(fn)241 lib[shortname]['other'].append(fn)
153 return lib242 return lib
154 243
155def print_cpp_output_results(lib, cpp_output):244
156 for fn in cpp_output:245def print_c_declarations_results(lib, c_declarations):
246 for fn in c_declarations:
157 found = 0247 found = 0
158 for o in lib:248 for o in lib:
159 for family in lib[o]:249 for family in lib[o]:
160 if fn in lib[o][family]:250 if fn in lib[o][family]:
161 found = 1251 found = 1
162 if found == 0:252 if found == 0:
163 print ("missing", fn)253 print("missing", fn)
164 254
165 for o in lib:255 for o in lib:
166 for family in lib[o]:256 for family in lib[o]:
167 if type(lib[o][family]) == str:257 if type(lib[o][family]) == str:
168 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )258 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
169 elif len(lib[o][family]) == 1:259 elif len(lib[o][family]) == 1:
170 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family][0] ) )260 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family][0]))
171 else:261 else:
172 print ( "{:15s} {:10s} {:s}".format(o, family, lib[o][family] ) )262 print("{:15s} {:10s} {:s}".format(o, family, lib[o][family]))
173 263
174 264
175def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):265def generate_external(header=header, output_path=output_path, usedouble=False, overwrite=True):
176 if not os.path.isdir(output_path): os.mkdir(output_path)266 if not os.path.isdir(output_path):
177 elif not overwrite: return glob.glob(os.path.join(output_path, '*.c'))267 os.mkdir(output_path)
268 elif not overwrite:
269 return sorted(glob.glob(os.path.join(output_path, '*.c')))
178 270
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)
180 273
181 lib = analyze_cpp_output(cpp_objects, cpp_output)274 lib = generate_lib_from_c_declarations(cpp_objects, c_declarations)
182 # print_cpp_output_results(lib, cpp_output)275 # print_c_declarations_results(lib, c_declarations)
183 276
184 sources_list = []277 sources_list = []
185 try:
186 from .gen_code import MappedObject
187 except (SystemError, ValueError):
188 from gen_code import MappedObject
189 for o in lib:278 for o in lib:
190 out = source_header279 out = source_header
191 mapped = MappedObject(lib[o], usedouble = usedouble)280 mapped = MappedObject(lib[o], usedouble=usedouble)
192 out += mapped.gen_code()281 out += mapped.gen_code()
193 output_file = os.path.join(output_path, 'gen-%s.c' % o)282 output_file = os.path.join(output_path, 'gen-%s.c' % o)
194 with open(output_file, 'w') as f:283 with open(output_file, 'w') as f:
195 f.write(out)284 f.write(out)
196 print ("wrote %s" % output_file )285 print("wrote %s" % output_file)
197 sources_list.append(output_file)286 sources_list.append(output_file)
198 287
199 out = source_header288 out = source_header
@@ -205,23 +294,23 @@ int generated_types_ready (void) · 5 unmodified lines
205{{294{{
206 return ({pycheck_types});295 return ({pycheck_types});
207}}296}}
208""".format(pycheck_types = check_types)297""".format(pycheck_types=check_types)
209 298
210 add_types = "".join(["""299 add_types = "".join(["""
211 Py_INCREF (&Py_{name}Type);300 Py_INCREF (&Py_{name}Type);
212 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name = o) for o in lib])301 PyModule_AddObject(m, "{name}", (PyObject *) & Py_{name}Type);""".format(name=o) for o in lib])
213 out += """302 out += """
214 303
215void add_generated_objects ( PyObject *m )304void add_generated_objects ( PyObject *m )
216{{305{{
217{add_types}306{add_types}
218}}307}}
219""".format(add_types = add_types)308""".format(add_types=add_types)
220 309
221 output_file = os.path.join(output_path, 'aubio-generated.c')310 output_file = os.path.join(output_path, 'aubio-generated.c')
222 with open(output_file, 'w') as f:311 with open(output_file, 'w') as f:
223 f.write(out)312 f.write(out)
224 print ("wrote %s" % output_file )313 print("wrote %s" % output_file)
225 sources_list.append(output_file)314 sources_list.append(output_file)
226 315
227 objlist = "".join(["extern PyTypeObject Py_%sType;\n" % p for p in lib])316 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
239{objlist}328{objlist}
240int generated_objects ( void );329int generated_objects ( void );
241void add_generated_objects( PyObject *m );330void add_generated_objects( PyObject *m );
242""".format(objlist = objlist)331""".format(objlist=objlist)
243 332
244 output_file = os.path.join(output_path, 'aubio-generated.h')333 output_file = os.path.join(output_path, 'aubio-generated.h')
245 with open(output_file, 'w') as f:334 with open(output_file, 'w') as f:
246 f.write(out)335 f.write(out)
247 print ("wrote %s" % output_file )336 print("wrote %s" % output_file)
248 # no need to add header to list of sources337 # no need to add header to list of sources
249 338
250 return sources_list339 return sorted(sources_list)
251 340
252if __name__ == '__main__':341if __name__ == '__main__':
253 if len(sys.argv) > 1: header = sys.argv[1]342 if len(sys.argv) > 1:
254 if len(sys.argv) > 2: output_path = sys.argv[2]343 header = sys.argv[1]
344 if len(sys.argv) > 2:
345 output_path = sys.argv[2]
255 generate_external(header, output_path)346 generate_external(header, output_path)
python/lib/moresetuptools.py+78−26
@@ -2,7 +2,9 @@ · 1 unmodified line
2#2#
3import sys, os, glob, subprocess3import sys, os, glob, subprocess
4import distutils, distutils.command.clean, distutils.dir_util4import distutils, distutils.command.clean, distutils.dir_util
5from .gen_external import generate_external, header, output_path5from gen_external import generate_external, header, output_path
6
7from this_version import get_aubio_version
6 8
7# inspired from https://gist.github.com/abergmeier/94889909# inspired from https://gist.github.com/abergmeier/9488990
8def add_packages(packages, ext=None, **kw):10def add_packages(packages, ext=None, **kw):
@@ -21,6 +23,7 @@ def add_packages(packages, ext=None, **kw): · 12 unmodified lines
21 }23 }
22 24
23 for package in packages:25 for package in packages:
26 print("checking for {:s}".format(package))
24 cmd = ['pkg-config', '--libs', '--cflags', package]27 cmd = ['pkg-config', '--libs', '--cflags', package]
25 try:28 try:
26 tokens = subprocess.check_output(cmd)29 tokens = subprocess.check_output(cmd)
@@ -54,13 +57,14 @@ def add_local_aubio_lib(ext): · 27 unmodified lines
54 57
55def add_local_aubio_sources(ext):58def add_local_aubio_sources(ext):
56 """ build aubio inside python module instead of linking against libaubio """59 """ build aubio inside python module instead of linking against libaubio """
57 print("Warning: libaubio was not built with waf, adding src/")60 print("Info: libaubio was not installed or built locally with waf, adding src/")
58 # create an empty header, macros will be passed on the command line61 aubio_sources = sorted(glob.glob(os.path.join('src', '**.c')))
59 fake_config_header = os.path.join('python', 'ext', 'config.h')62 aubio_sources += sorted(glob.glob(os.path.join('src', '*', '**.c')))
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'))
63 ext.sources += aubio_sources63 ext.sources += aubio_sources
64
65def add_local_macros(ext, usedouble = False):
66 if usedouble:
67 ext.define_macros += [('HAVE_AUBIO_DOUBLE', 1)]
64 # define macros (waf puts them in build/src/config.h)68 # define macros (waf puts them in build/src/config.h)
65 for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',69 for define_macro in ['HAVE_STDLIB_H', 'HAVE_STDIO_H',
66 'HAVE_MATH_H', 'HAVE_STRING_H',70 'HAVE_MATH_H', 'HAVE_STRING_H',
@@ -69,22 +73,31 @@ def add_local_aubio_sources(ext): · 2 unmodified lines
69 'HAVE_MEMCPY_HACKS']:73 'HAVE_MEMCPY_HACKS']:
70 ext.define_macros += [(define_macro, 1)]74 ext.define_macros += [(define_macro, 1)]
71 75
76def add_external_deps(ext, usedouble = False):
72 # loof for additional packages77 # loof for additional packages
73 print("Info: looking for *optional* additional packages")78 print("Info: looking for *optional* additional packages")
74 packages = ['libavcodec', 'libavformat', 'libavutil', 'libavresample',79 packages = ['libavcodec', 'libavformat', 'libavutil',
80 'libswresample', 'libavresample',
75 'jack',81 'jack',
76 'sndfile', 'samplerate',82 'sndfile',
77 'rubberband',83 'rubberband',
78 #'fftw3f',84 #'fftw3f',
79 ]85 ]
86 # samplerate only works with float
87 if usedouble is False:
88 packages += ['samplerate']
89 else:
90 print("Info: not adding libsamplerate in double precision mode")
80 add_packages(packages, ext=ext)91 add_packages(packages, ext=ext)
81 if 'avcodec' in ext.libraries \92 if 'avcodec' in ext.libraries \
82 and 'avformat' in ext.libraries \93 and 'avformat' in ext.libraries \
83 and 'avutil' in ext.libraries \94 and 'avutil' in ext.libraries:
84 and 'avresample' in ext.libraries:95 if 'swresample' in ext.libraries:
85 ext.define_macros += [('HAVE_LIBAV', 1)]96 ext.define_macros += [('HAVE_SWRESAMPLE', 1)]
86 if 'jack' in ext.libraries:97 elif 'avresample' in ext.libraries:
87 ext.define_macros += [('HAVE_JACK', 1)]98 ext.define_macros += [('HAVE_AVRESAMPLE', 1)]
99 if 'swresample' in ext.libraries or 'avresample' in ext.libraries:
100 ext.define_macros += [('HAVE_LIBAV', 1)]
88 if 'sndfile' in ext.libraries:101 if 'sndfile' in ext.libraries:
89 ext.define_macros += [('HAVE_SNDFILE', 1)]102 ext.define_macros += [('HAVE_SNDFILE', 1)]
90 if 'samplerate' in ext.libraries:103 if 'samplerate' in ext.libraries:
@@ -107,39 +120,78 @@ def add_local_aubio_sources(ext): · 16 unmodified lines
107 120
108 ext.define_macros += [('HAVE_WAVWRITE', 1)]121 ext.define_macros += [('HAVE_WAVWRITE', 1)]
109 ext.define_macros += [('HAVE_WAVREAD', 1)]122 ext.define_macros += [('HAVE_WAVREAD', 1)]
110 # TODO:123
111 # add cblas124 # TODO: add cblas
112 if 0:125 if 0:
113 ext.libraries += ['cblas']126 ext.libraries += ['cblas']
114 ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)]127 ext.define_macros += [('HAVE_ATLAS_CBLAS_H', 1)]
115 128
116def add_system_aubio(ext):129def add_system_aubio(ext):
117 # use pkg-config to find aubio's location130 # use pkg-config to find aubio's location
118 add_packages(['aubio'], ext)131 aubio_version = get_aubio_version()
132 add_packages(['aubio = ' + aubio_version], ext)
119 if 'aubio' not in ext.libraries:133 if 'aubio' not in ext.libraries:
120 print("Error: libaubio not found")134 print("Info: aubio " + aubio_version + " was not found by pkg-config")
135 else:
136 print("Info: using system aubio " + aubio_version + " found in " + ' '.join(ext.library_dirs))
137
138def add_libav_on_win(ext):
139 """ no pkg-config on windows, simply assume these libs are available """
140 ext.libraries += ['avformat', 'avutil', 'avcodec', 'swresample']
141 for define_macro in ['HAVE_LIBAV', 'HAVE_SWRESAMPLE']:
142 ext.define_macros += [(define_macro, 1)]
121 143
122class CleanGenerated(distutils.command.clean.clean):144class CleanGenerated(distutils.command.clean.clean):
123 def run(self):145 def run(self):
124 distutils.dir_util.remove_tree(output_path)146 if os.path.isdir(output_path):
125 distutils.command.clean.clean.run(self)147 distutils.dir_util.remove_tree(output_path)
148
149from distutils.command.build_ext import build_ext as _build_ext
150class build_ext(_build_ext):
126 151
127class GenerateCommand(distutils.cmd.Command):152 user_options = _build_ext.user_options + [
128 description = 'generate gen/gen-*.c files from ../src/aubio.h'
129 user_options = [
130 # The format is (long option, short option, description).153 # The format is (long option, short option, description).
131 ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'),154 ('enable-double', None, 'use HAVE_AUBIO_DOUBLE=1 (default: 0)'),
132 ]155 ]
133 156
134 def initialize_options(self):157 def initialize_options(self):
158 _build_ext.initialize_options(self)
135 self.enable_double = False159 self.enable_double = False
136 160
137 def finalize_options(self):161 def finalize_options(self):
162 _build_ext.finalize_options(self)
138 if self.enable_double:163 if self.enable_double:
139 self.announce(164 self.announce(
140 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1',165 'will generate code for aubio compiled with HAVE_AUBIO_DOUBLE=1',
141 level=distutils.log.INFO)166 level=distutils.log.INFO)
142 167
143 def run(self):168 def build_extension(self, extension):
144 self.announce( 'Generating code', level=distutils.log.INFO)169 if self.enable_double or 'HAVE_AUBIO_DOUBLE' in os.environ:
145 generated_object_files = generate_external(header, output_path, usedouble=self.enable_double)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
7import sys
8#from aubio.task import *
9
10usage = "usage: %s [options] -i soundfile" % sys.argv[0]
11usage += "\n help: %s -h" % sys.argv[0]
12
13def 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
141if __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
24import time24import time
25import os.path25import os.path
26import numpy26import numpy
27from utils import array_from_text_file, array_from_yaml_file27from .utils import array_from_text_file, array_from_yaml_file
28from aubio import source, pitch, freqtomidi28from aubio import source, pitch, freqtomidi
29 29
30start = time.time()30start = time.time()
python/tests/test_aubio.py+5−0
@@ -9,6 +9,11 @@ class aubiomodule_test_case(TestCase): · 8 unmodified lines
9 """ try importing aubio """9 """ try importing aubio """
10 import aubio10 import aubio
11 11
12 def test_version(self):
13 """ test aubio.version """
14 import aubio
15 self.assertEqual('0', aubio.version[0])
16
12if __name__ == '__main__':17if __name__ == '__main__':
13 main()18 main()
14 19
python/tests/test_aubio_cmd.py+34−0
@@ -0,0 +1,34 @@
1#! /usr/bin/env python
2
3import aubio.cmd
4from nose2 import main
5from numpy.testing import TestCase
6
7class 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
19class 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
33if __name__ == '__main__':
34 main()
python/tests/test_aubio_cut.py+16−0
@@ -0,0 +1,16 @@
1#! /usr/bin/env python
2
3import aubio.cut
4from nose2 import main
5from numpy.testing import TestCase
6
7class 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
15if __name__ == '__main__':
16 main()
python/tests/test_dct.py+68−0
@@ -0,0 +1,68 @@
1#! /usr/bin/env python
2
3
4import numpy as np
5from numpy.testing import TestCase, assert_almost_equal
6import aubio
7
8precomputed_arange = [ 9.89949512, -6.44232273, 0., -0.67345482, 0.,
9 -0.20090288, 0., -0.05070186]
10
11precomputed_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
16class 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
33 f = fft (win_s)33 f = fft (win_s)
34 fftgrain = f (timegrain)34 fftgrain = f (timegrain)
35 assert_equal ( fftgrain.norm, 0 )35 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.')
37 44
38 def test_impulse(self):45 def test_impulse(self):
39 """ check the transform of one impulse at a random place """46 """ check the transform of one impulse at a random place """
@@ -135,6 +142,37 @@ class aubio_fft_test_case(TestCase): · 95 unmodified lines
135 assert_almost_equal ( r[0], impulse, decimal = 6)142 assert_almost_equal ( r[0], impulse, decimal = 6)
136 assert_almost_equal ( r[1:], 0)143 assert_almost_equal ( r[1:], 0)
137 144
145class 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
174class aubio_fft_wrong_params(TestCase):
175
138 def test_large_input_timegrain(self):176 def test_large_input_timegrain(self):
139 win_s = 1024177 win_s = 1024
140 f = fft(win_s)178 f = fft(win_s)
@@ -163,22 +201,11 @@ class aubio_fft_test_case(TestCase): · 22 unmodified lines
163 with self.assertRaises(ValueError):201 with self.assertRaises(ValueError):
164 f.rdo(s)202 f.rdo(s)
165 203
166class aubio_fft_wrong_params(TestCase):
167
168 def test_wrong_buf_size(self):204 def test_wrong_buf_size(self):
169 win_s = -1205 win_s = -1
170 with self.assertRaises(ValueError):206 with self.assertRaises(ValueError):
171 fft(win_s)207 fft(win_s)
172 208
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
182 def test_buf_size_too_small(self):209 def test_buf_size_too_small(self):
183 win_s = 1210 win_s = 1
184 with self.assertRaises(RuntimeError):211 with self.assertRaises(RuntimeError):
python/tests/test_filter.py+11−1
@@ -3,7 +3,7 @@ · 2 unmodified lines
3from unittest import main3from unittest import main
4from numpy.testing import TestCase, assert_equal, assert_almost_equal4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5from aubio import fvec, digital_filter5from aubio import fvec, digital_filter
6from utils import array_from_text_file6from .utils import array_from_text_file
7 7
8class aubio_filter_test_case(TestCase):8class aubio_filter_test_case(TestCase):
9 9
@@ -77,6 +77,16 @@ class aubio_filter_test_case(TestCase): · 67 unmodified lines
77 with self.assertRaises(ValueError):77 with self.assertRaises(ValueError):
78 f.set_biquad(0., 0., 0, 0., 0.)78 f.set_biquad(0., 0., 0, 0., 0.)
79 79
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
80class aubio_filter_wrong_params(TestCase):90class aubio_filter_wrong_params(TestCase):
81 91
82 def test_negative_order(self):92 def test_negative_order(self):
python/tests/test_filterbank.py+12−5
@@ -1,11 +1,10 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3from unittest import main
4from numpy.testing import TestCase
5from numpy.testing import assert_equal, assert_almost_equal
6import numpy as np3import numpy as np
4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5
7from aubio import cvec, filterbank, float_type6from aubio import cvec, filterbank, float_type
8from utils import array_from_text_file7from .utils import array_from_text_file
9 8
10class aubio_filterbank_test_case(TestCase):9class aubio_filterbank_test_case(TestCase):
11 10
@@ -62,6 +61,13 @@ class aubio_filterbank_test_case(TestCase): · 50 unmodified lines
62 f.set_mel_coeffs_slaney(16000)61 f.set_mel_coeffs_slaney(16000)
63 assert_almost_equal ( expected, f.get_coeffs() )62 assert_almost_equal ( expected, f.get_coeffs() )
64 63
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
65class aubio_filterbank_wrong_values(TestCase):71class aubio_filterbank_wrong_values(TestCase):
66 72
67 def test_negative_window(self):73 def test_negative_window(self):
@@ -81,4 +87,5 @@ class aubio_filterbank_wrong_values(TestCase): · 13 unmodified lines
81 f(cvec(256))87 f(cvec(256))
82 88
83if __name__ == '__main__':89if __name__ == '__main__':
84 main()90 import nose2
91 nose2.main()
python/tests/test_filterbank_mel.py+136−13
@@ -1,10 +1,12 @@
1#! /usr/bin/env python1#! /usr/bin/env python
2 2
3from unittest import main3import numpy as np
4from numpy.testing import TestCase4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5from numpy.testing import assert_equal, assert_almost_equal5
6from numpy import array, shape6from aubio import fvec, cvec, filterbank, float_type
7from aubio import cvec, filterbank, float_type7
8import warnings
9warnings.filterwarnings('ignore', category=UserWarning, append=True)
8 10
9class aubio_filterbank_mel_test_case(TestCase):11class aubio_filterbank_mel_test_case(TestCase):
10 12
@@ -12,38 +14,159 @@ class aubio_filterbank_mel_test_case(TestCase): · 1 unmodified line
12 f = filterbank(40, 512)14 f = filterbank(40, 512)
13 f.set_mel_coeffs_slaney(16000)15 f.set_mel_coeffs_slaney(16000)
14 a = f.get_coeffs()16 a = f.get_coeffs()
15 assert_equal(shape (a), (40, 512/2 + 1) )17 assert_equal(np.shape (a), (40, 512/2 + 1) )
16 18
17 def test_other_slaney(self):19 def test_other_slaney(self):
18 f = filterbank(40, 512*2)20 f = filterbank(40, 512*2)
19 f.set_mel_coeffs_slaney(44100)21 f.set_mel_coeffs_slaney(44100)
20 _ = f.get_coeffs()22 self.assertIsInstance(f.get_coeffs(), np.ndarray)
21 #print "sum is", sum(sum(a))23 #print "sum is", sum(sum(a))
22 for win_s in [256, 512, 1024, 2048, 4096]:24 for win_s in [256, 512, 1024, 2048, 4096]:
23 f = filterbank(40, win_s)25 f = filterbank(40, win_s)
24 f.set_mel_coeffs_slaney(32000)26 f.set_mel_coeffs_slaney(32000)
25 _ = f.get_coeffs()
26 #print "sum is", sum(sum(a))27 #print "sum is", sum(sum(a))
28 self.assertIsInstance(f.get_coeffs(), np.ndarray)
27 29
28 def test_triangle_freqs_zeros(self):30 def test_triangle_freqs_zeros(self):
29 f = filterbank(9, 1024)31 f = filterbank(9, 1024)
30 freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000]32 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)
32 f.set_triangle_bands(freqs, 48000)34 f.set_triangle_bands(freqs, 48000)
33 _ = f.get_coeffs().T
34 assert_equal ( f(cvec(1024)), 0)35 assert_equal ( f(cvec(1024)), 0)
36 self.assertIsInstance(f.get_coeffs(), np.ndarray)
35 37
36 def test_triangle_freqs_ones(self):38 def test_triangle_freqs_ones(self):
37 f = filterbank(9, 1024)39 f = filterbank(9, 1024)
38 freq_list = [40, 80, 200, 400, 800, 1600, 3200, 6400, 12800, 15000, 24000]40 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)
40 f.set_triangle_bands(freqs, 48000)42 f.set_triangle_bands(freqs, 48000)
41 _ = f.get_coeffs().T43 self.assertIsInstance(f.get_coeffs(), np.ndarray)
42 spec = cvec(1024)44 spec = cvec(1024)
43 spec.norm[:] = 145 spec.norm[:] = 1
44 assert_almost_equal ( f(spec),46 assert_almost_equal ( f(spec),
45 [ 0.02070313, 0.02138672, 0.02127604, 0.02135417,47 [ 0.02070313, 0.02138672, 0.02127604, 0.02135417,
46 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])48 0.02133301, 0.02133301, 0.02133311, 0.02133334, 0.02133345])
47 49
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
48if __name__ == '__main__':170if __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
60 self.assertRaises(IndexError, a.__getitem__, 3)60 self.assertRaises(IndexError, a.__getitem__, 3)
61 self.assertRaises(IndexError, a.__getitem__, 2)61 self.assertRaises(IndexError, a.__getitem__, 2)
62 62
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
63class aubio_wrong_fvec_input(TestCase):71class aubio_wrong_fvec_input(TestCase):
64 """ uses min_removal to test PyAubio_IsValidVector """72 """ uses min_removal to test PyAubio_IsValidVector """
65 73
python/tests/test_fvec_shift.py+35−0
@@ -0,0 +1,35 @@
1#! /usr/bin/env python
2
3import numpy as np
4from numpy.testing import TestCase, assert_equal
5import aubio
6
7class 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
33from unittest import main
34if __name__ == '__main__':
35 main()
python/tests/test_hztomel.py+109−0
@@ -0,0 +1,109 @@
1#! /usr/bin/env python
2
3from unittest import main
4from numpy.testing import TestCase
5from numpy.testing import assert_equal, assert_almost_equal
6import numpy as np
7import aubio
8
9from aubio import hztomel, meltohz
10from aubio import hztomel_htk, meltohz_htk
11
12
13class 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
48class 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
77class 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
107if __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
110 o(spec)110 o(spec)
111 #print coeffs111 #print coeffs
112 112
113
114class 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
113if __name__ == '__main__':150if __name__ == '__main__':
114 main()151 main()
python/tests/test_midi2note.py+6−4
@@ -2,6 +2,7 @@ · 1 unmodified line
2# -*- coding: utf-8 -*-2# -*- coding: utf-8 -*-
3 3
4from aubio import midi2note4from aubio import midi2note
5from nose2.tools import params
5import unittest6import unittest
6 7
7list_of_known_midis = (8list_of_known_midis = (
@@ -16,10 +17,10 @@ list_of_known_midis = ( · 8 unmodified lines
16 17
17class midi2note_good_values(unittest.TestCase):18class midi2note_good_values(unittest.TestCase):
18 19
19 def test_midi2note_known_values(self):20 @params(*list_of_known_midis)
21 def test_midi2note_known_values(self, midi, note):
20 " known values are correctly converted "22 " known values are correctly converted "
21 for midi, note in list_of_known_midis:23 self.assertEqual ( midi2note(midi), note )
22 self.assertEqual ( midi2note(midi), note )
23 24
24class midi2note_wrong_values(unittest.TestCase):25class midi2note_wrong_values(unittest.TestCase):
25 26
@@ -40,4 +41,5 @@ class midi2note_wrong_values(unittest.TestCase): · 14 unmodified lines
40 self.assertRaises(TypeError, midi2note, "a")41 self.assertRaises(TypeError, midi2note, "a")
41 42
42if __name__ == '__main__':43if __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
3 3
4from __future__ import unicode_literals4from __future__ import unicode_literals
5 5
6from aubio import note2midi, freq2note6from aubio import note2midi, freq2note, note2freq, float_type
7from nose2.tools import params
7import unittest8import unittest
8 9
9list_of_known_notes = (10list_of_known_notes = (
@@ -13,24 +14,57 @@ list_of_known_notes = ( · 3 unmodified lines
13 ( 'C3', 48 ),14 ( 'C3', 48 ),
14 ( 'B3', 59 ),15 ( 'B3', 59 ),
15 ( 'B#3', 60 ),16 ( 'B#3', 60 ),
17 ( 'C♯4', 61 ),
16 ( 'A4', 69 ),18 ( 'A4', 69 ),
17 ( 'A#4', 70 ),19 ( 'A#4', 70 ),
20 ( 'A♯4', 70 ),
21 ( 'A\u266f4', 70 ),
18 ( 'Bb4', 70 ),22 ( 'Bb4', 70 ),
19 ( 'B♭4', 70 ),23 ( 'B♭4', 70 ),
24 ( 'B\u266d4', 70 ),
20 ( 'G8', 115 ),25 ( 'G8', 115 ),
21 ( 'G♯8', 116 ),26 ( 'G♯8', 116 ),
22 ( 'G9', 127 ),27 ( 'G9', 127 ),
23 ( 'G\udd2a2', 45 ),
24 ( 'B\ufffd2', 45 ),
25 ( 'A♮2', 45 ),28 ( 'A♮2', 45 ),
26 )29 )
27 30
31list_of_known_notes_with_unicode_issues = (
32 ('C𝄪4', 62 ),
33 ('E𝄫4', 62 ),
34 )
35
36list_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
28class note2midi_good_values(unittest.TestCase):47class note2midi_good_values(unittest.TestCase):
29 48
30 def test_note2midi_known_values(self):49 @params(*list_of_known_notes)
50 def test_note2midi_known_values(self, note, midi):
31 " known values are correctly converted "51 " 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:
33 self.assertEqual ( note2midi(note), midi )58 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
34 68
35class note2midi_wrong_values(unittest.TestCase):69class note2midi_wrong_values(unittest.TestCase):
36 70
@@ -66,12 +100,38 @@ class note2midi_wrong_values(unittest.TestCase): · 29 unmodified lines
66 " fails when passed a non-string value "100 " fails when passed a non-string value "
67 self.assertRaises(TypeError, note2midi, 123)101 self.assertRaises(TypeError, note2midi, 123)
68 102
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)
69 111
70class freq2note_simple_test(unittest.TestCase):112class freq2note_simple_test(unittest.TestCase):
71 113
72 def test_freq2note(self):114 def test_freq2note_above(self):
73 " make sure freq2note(441) == A4 "115 " make sure freq2note(441) == A4 "
74 self.assertEqual("A4", freq2note(441))116 self.assertEqual("A4", freq2note(441))
75 117
118 def test_freq2note_under(self):
119 " make sure freq2note(439) == A4 "
120 self.assertEqual("A4", freq2note(439))
121
122class 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
76if __name__ == '__main__':135if __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
3from unittest import main
4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5from aubio import notes
6
7AUBIO_DEFAULT_NOTES_SILENCE = -70.
8AUBIO_DEFAULT_NOTES_RELEASE_DROP = 10.
9AUBIO_DEFAULT_NOTES_MINIOI_MS = 30.
10
11class 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
19class 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
55from .utils import list_all_sounds
56list_of_sounds = list_all_sounds('sounds')
57
58class 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
94if __name__ == '__main__':
95 main()
python/tests/test_onset.py+40−8
@@ -2,7 +2,7 @@ · 1 unmodified line
2 2
3from unittest import main3from unittest import main
4from numpy.testing import TestCase, assert_equal, assert_almost_equal4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5from aubio import onset5from aubio import onset, fvec
6 6
7class aubio_onset_default(TestCase):7class aubio_onset_default(TestCase):
8 8
@@ -19,25 +19,25 @@ class aubio_onset_params(TestCase): · 10 unmodified lines
19 self.o = onset(samplerate = self.samplerate)19 self.o = onset(samplerate = self.samplerate)
20 20
21 def test_get_delay(self):21 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)
23 23
24 def test_get_delay_s(self):24 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.)
26 26
27 def test_get_delay_ms(self):27 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.)
29 29
30 def test_get_minioi(self):30 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)
32 32
33 def test_get_minioi_s(self):33 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.)
35 35
36 def test_get_minioi_ms(self):36 def test_get_minioi_ms(self):
37 assert_equal (self.o.get_minioi_ms(), 20.)37 self.assertGreater(self.o.get_minioi_ms(), 0.)
38 38
39 def test_get_threshold(self):39 def test_get_threshold(self):
40 assert_almost_equal (self.o.get_threshold(), 0.3)40 self.assertGreater(self.o.get_threshold(), 0.)
41 41
42 def test_set_delay(self):42 def test_set_delay(self):
43 val = 25643 val = 256
@@ -83,5 +83,37 @@ class aubio_onset_32000(aubio_onset_params): · 39 unmodified lines
83class aubio_onset_8000(aubio_onset_params):83class aubio_onset_8000(aubio_onset_params):
84 samplerate = 800084 samplerate = 8000
85 85
86class 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
86if __name__ == '__main__':118if __name__ == '__main__':
87 main()119 main()
python/tests/test_phasevoc.py+17−1
@@ -46,9 +46,25 @@ class aubio_pvoc_test_case(TestCase): · 45 unmodified lines
46 r = f.rdo(s)46 r = f.rdo(s)
47 assert_equal ( t, 0.)47 assert_equal ( t, 0.)
48 assert_equal ( s.norm, 0.)48 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.')
50 assert_equal ( r, 0.)57 assert_equal ( r, 0.)
51 58
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
52 @params(68 @params(
53 ( 256, 8),69 ( 256, 8),
54 ( 256, 4),70 ( 256, 4),
python/tests/test_pitch.py+2−2
@@ -70,8 +70,8 @@ class aubio_pitch_Sinusoid(TestCase): · 69 unmodified lines
70 #print 'len(pitches), cut:', len(pitches), cut70 #print 'len(pitches), cut:', len(pitches), cut
71 #print 'median errors: ', median(errors), 'median pitches: ', median(pitches)71 #print 'median errors: ', median(errors), 'median pitches: ', median(pitches)
72 72
73pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" , "specacf" ]73pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" , "specacf" ]
74pitch_algorithms = [ "default", "yinfft", "yin", "schmitt", "mcomb", "fcomb" ]74pitch_algorithms = [ "default", "yinfft", "yin", "yinfast", "schmitt", "mcomb", "fcomb" ]
75 75
76#freqs = [ 27.5, 55., 110., 220., 440., 880., 1760., 3520. ]76#freqs = [ 27.5, 55., 110., 220., 440., 880., 1760., 3520. ]
77freqs = [ 110., 220., 440., 880., 1760., 3520. ]77freqs = [ 110., 220., 440., 880., 1760., 3520. ]
python/tests/test_sink.py+32−1
@@ -4,7 +4,10 @@ from nose2 import main · 3 unmodified lines
4from nose2.tools import params4from nose2.tools import params
5from numpy.testing import TestCase5from numpy.testing import TestCase
6from aubio import fvec, source, sink6from aubio import fvec, source, sink
7from utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path7from .utils import list_all_sounds, get_tmp_sink_path, del_tmp_sink_path
8
9import warnings
10warnings.filterwarnings('ignore', category=UserWarning, append=True)
8 11
9list_of_sounds = list_all_sounds('sounds')12list_of_sounds = list_all_sounds('sounds')
10samplerates = [0, 44100, 8000, 32000]13samplerates = [0, 44100, 8000, 32000]
@@ -26,6 +29,26 @@ class aubio_sink_test_case(TestCase): · 15 unmodified lines
26 if not len(list_of_sounds):29 if not len(list_of_sounds):
27 self.skipTest('add some sound files in \'python/tests/sounds\'')30 self.skipTest('add some sound files in \'python/tests/sounds\'')
28 31
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
29 def test_many_sinks(self):52 def test_many_sinks(self):
30 from tempfile import mkdtemp53 from tempfile import mkdtemp
31 import os.path54 import os.path
@@ -93,5 +116,13 @@ class aubio_sink_test_case(TestCase): · 61 unmodified lines
93 g.close()116 g.close()
94 del_tmp_sink_path(sink_path)117 del_tmp_sink_path(sink_path)
95 118
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
96if __name__ == '__main__':127if __name__ == '__main__':
97 main()128 main()
python/tests/test_slicing.py+27−6
@@ -3,8 +3,8 @@ · 2 unmodified lines
3from unittest import main3from unittest import main
4from numpy.testing import TestCase, assert_equal4from numpy.testing import TestCase, assert_equal
5from aubio import slice_source_at_stamps5from aubio import slice_source_at_stamps
6from utils import count_files_in_directory, get_default_test_sound6from .utils import count_files_in_directory, get_default_test_sound
7from utils import count_samples_in_directory, count_samples_in_file7from .utils import count_samples_in_directory, count_samples_in_file
8 8
9import tempfile9import tempfile
10import shutil10import shutil
@@ -23,19 +23,27 @@ class aubio_slicing_test_case(TestCase): · 12 unmodified lines
23 23
24 def test_slice_start_only_no_zero(self):24 def test_slice_start_only_no_zero(self):
25 regions_start = [i*1000 for i in range(1, n_slices)]25 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)
27 28
28 def test_slice_start_beyond_end(self):29 def test_slice_start_beyond_end(self):
29 regions_start = [i*1000 for i in range(1, n_slices)]30 regions_start = [i*1000 for i in range(1, n_slices)]
30 regions_start += [count_samples_in_file(self.source_file) + 1000]31 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)
32 34
33 def test_slice_start_every_blocksize(self):35 def test_slice_start_every_blocksize(self):
34 hopsize = 20036 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)]
36 slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir,38 slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir,
37 hopsize = 200)39 hopsize = 200)
38 40
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
39 def tearDown(self):47 def tearDown(self):
40 original_samples = count_samples_in_file(self.source_file)48 original_samples = count_samples_in_file(self.source_file)
41 written_samples = count_samples_in_directory(self.output_dir)49 written_samples = count_samples_in_directory(self.output_dir)
@@ -91,6 +99,19 @@ class aubio_slicing_with_ends_test_case(TestCase): · 49 unmodified lines
91 assert_equal(written_samples, expected_samples,99 assert_equal(written_samples, expected_samples,
92 "number of samples written different from number of original samples")100 "number of samples written different from number of original samples")
93 101
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
94 def tearDown(self):115 def tearDown(self):
95 shutil.rmtree(self.output_dir)116 shutil.rmtree(self.output_dir)
96 117
@@ -133,7 +154,7 @@ class aubio_slicing_wrong_ends_test_case(TestCase): · 36 unmodified lines
133 regions_start = [i*1000 for i in range(1, n_slices)]154 regions_start = [i*1000 for i in range(1, n_slices)]
134 regions_end = None155 regions_end = None
135 slice_source_at_stamps (self.source_file, regions_start, regions_end,156 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)
137 total_files = count_files_in_directory(self.output_dir)158 total_files = count_files_in_directory(self.output_dir)
138 assert_equal(n_slices, total_files,159 assert_equal(n_slices, total_files,
139 "number of slices created different from expected")160 "number of slices created different from expected")
python/tests/test_source.py+34−7
@@ -2,9 +2,9 @@ · 1 unmodified line
2 2
3from nose2 import main3from nose2 import main
4from nose2.tools import params4from nose2.tools import params
5from numpy.testing import TestCase5from numpy.testing import TestCase, assert_equal
6from aubio import source6from aubio import source
7from utils import list_all_sounds7from .utils import list_all_sounds
8 8
9import warnings9import warnings
10warnings.filterwarnings('ignore', category=UserWarning, append=True)10warnings.filterwarnings('ignore', category=UserWarning, append=True)
@@ -51,9 +51,11 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 40 unmodified lines
51 def read_from_source(self, f):51 def read_from_source(self, f):
52 total_frames = 052 total_frames = 0
53 while True:53 while True:
54 _ , read = f()54 samples , read = f()
55 total_frames += read55 total_frames += read
56 if read < f.hop_size: break56 if read < f.hop_size:
57 assert_equal(samples[read:], 0)
58 break
57 #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}"59 #result_str = "read {:.2f}s ({:d} frames in {:d} blocks at {:d}Hz) from {:s}"
58 #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri60 #result_params = total_frames / float(f.samplerate), total_frames, total_frames//f.hop_size, f.samplerate, f.uri
59 #print (result_str.format(*result_params))61 #print (result_str.format(*result_params))
@@ -66,7 +68,14 @@ class aubio_source_read_test_case(aubio_source_test_case_base): · 6 unmodified lines
66 except RuntimeError as e:68 except RuntimeError as e:
67 self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e)))69 self.skipTest('failed opening with hop_s = {:d}, samplerate = {:d} ({:s})'.format(hop_size, samplerate, str(e)))
68 assert f.samplerate != 070 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)
70 79
71 @params(*list_of_sounds)80 @params(*list_of_sounds)
72 def test_samplerate_none(self, p):81 def test_samplerate_none(self, p):
@@ -149,13 +158,31 @@ class aubio_source_readmulti_test_case(aubio_source_read_test_case): · 76 unmodified lines
149 def read_from_source(self, f):158 def read_from_source(self, f):
150 total_frames = 0159 total_frames = 0
151 while True:160 while True:
152 _, read = f.do_multi()161 samples, read = f.do_multi()
153 total_frames += read162 total_frames += read
154 if read < f.hop_size: break163 if read < f.hop_size:
164 assert_equal(samples[:,read:], 0)
165 break
155 #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}"166 #result_str = "read {:.2f}s ({:d} frames in {:d} channels and {:d} blocks at {:d}Hz) from {:s}"
156 #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri167 #result_params = total_frames / float(f.samplerate), total_frames, f.channels, int(total_frames/f.hop_size), f.samplerate, f.uri
157 #print (result_str.format(*result_params))168 #print (result_str.format(*result_params))
158 return total_frames169 return total_frames
159 170
171class 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
160if __name__ == '__main__':187if __name__ == '__main__':
161 main()188 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
4file, then `source` to check the correct content is read from the files."""
5
6import os.path
7import unittest
8import aubio
9import numpy as np
10from numpy.testing import assert_equal
11from .utils import get_tmp_sink_path
12
13class 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
92if __name__ == '__main__':
93 unittest.main()
python/tests/test_tempo.py+91−0
@@ -0,0 +1,91 @@
1#! /usr/bin/env python
2
3from unittest import main
4from numpy.testing import TestCase, assert_equal, assert_almost_equal
5import aubio
6
7class 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
14class 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
90if __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