History

Merge branch 'master' into feature/cdocstrings

Paul Brossier committed 8 years ago

Commit 81abf912 parents 92948ab 4bc10e230 files · +362 −101Browse files

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

Unified · Split

11 files
  1. python/demos/demo_bpm_extract.py +3 −3
  2. python/ext/aubiomodule.c +12 −4
  3. python/ext/py-filterbank.c +3 −3
  4. python/lib/aubio/cmd.py +33 −1
  5. python/lib/aubio/cut.py +3 −2
  6. python/lib/aubio/midiconv.py +38 −9
  7. python/lib/aubio/slicing.py +11 −5
  8. python/tests/test_aubio_cmd.py +6 −3
  9. python/tests/test_note2midi.py +19 −2
  10. python/tests/test_notes.py +14 −0
  11. python/tests/test_slicing.py +25 −4
python/demos/demo_bpm_extract.py+3−3
@@ -22,7 +22,7 @@ def get_file_bpm(path, params=None): · 21 unmodified lines
2222 elif params.mode in ['default']:
2323 pass
2424 else:
25− print("unknown mode {:s}".format(params.mode))
25+ raise ValueError("unknown mode {:s}".format(params.mode))
2626 # manual settings
2727 if 'samplerate' in params:
2828 samplerate = params.samplerate
@@ -69,9 +69,9 @@ if __name__ == '__main__': · 40 unmodified lines
6969 parser = argparse.ArgumentParser()
7070 parser.add_argument('-m', '--mode',
7171 help="mode [default|fast|super-fast]",
72− dest="mode")
72+ dest="mode", default='default')
7373 parser.add_argument('sources',
74− nargs='*',
74+ nargs='+',
7575 help="input_files")
7676 args = parser.parse_args()
7777 for f in args.sources:
python/ext/aubiomodule.c+12−4
@@ -237,7 +237,9 @@ Py_bintomidi (PyObject * self, PyObject * args) · 236 unmodified lines
237237 smpl_t input, samplerate, fftsize;
238238 smpl_t output;
239239
240− 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)) {
241243 return NULL;
242244 }
243245
@@ -252,7 +254,9 @@ Py_miditobin (PyObject * self, PyObject * args) · 8 unmodified lines
252254 smpl_t input, samplerate, fftsize;
253255 smpl_t output;
254256
255− 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)) {
256260 return NULL;
257261 }
258262
@@ -267,7 +271,9 @@ Py_bintofreq (PyObject * self, PyObject * args) · 8 unmodified lines
267271 smpl_t input, samplerate, fftsize;
268272 smpl_t output;
269273
270− 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)) {
271277 return NULL;
272278 }
273279
@@ -282,7 +288,9 @@ Py_freqtobin (PyObject * self, PyObject * args) · 8 unmodified lines
282288 smpl_t input, samplerate, fftsize;
283289 smpl_t output;
284290
285− 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)) {
286294 return NULL;
287295 }
288296
python/ext/py-filterbank.c+3−3
@@ -94,7 +94,7 @@ Py_filterbank_do(Py_filterbank * self, PyObject * args) · 93 unmodified lines
9494
9595 if (self->vec.length != self->win_s / 2 + 1) {
9696 PyErr_Format(PyExc_ValueError,
97− "input cvec has length %d, but fft expects length %d",
97+ "input cvec has length %d, but filterbank expects length %d",
9898 self->vec.length, self->win_s / 2 + 1);
9999 return NULL;
100100 }
@@ -139,7 +139,7 @@ Py_filterbank_set_triangle_bands (Py_filterbank * self, PyObject *args) · 38 unmodified lines
139139 &(self->freqs), samplerate);
140140 if (err > 0) {
141141 PyErr_SetString (PyExc_ValueError,
142− "error when setting filter to A-weighting");
142+ "error when running set_triangle_bands");
143143 return NULL;
144144 }
145145 Py_RETURN_NONE;
@@ -158,7 +158,7 @@ Py_filterbank_set_mel_coeffs_slaney (Py_filterbank * self, PyObject *args) · 12 unmodified lines
158158 err = aubio_filterbank_set_mel_coeffs_slaney (self->o, samplerate);
159159 if (err > 0) {
160160 PyErr_SetString (PyExc_ValueError,
161− "error when setting filter to A-weighting");
161+ "error when running set_mel_coeffs_slaney");
162162 return NULL;
163163 }
164164 Py_RETURN_NONE;
python/lib/aubio/cmd.py+33−1
@@ -101,6 +101,8 @@ def parser_add_subcommand_notes(subparsers): · 100 unmodified lines
101101 help='estimate midi-like notes (monophonic)')
102102 subparser.add_input()
103103 subparser.add_buf_hop_size()
104+ subparser.add_silence()
105+ subparser.add_release_drop()
104106 subparser.add_time_format()
105107 subparser.add_verbose_help()
106108 subparser.set_defaults(process=process_notes)
@@ -207,6 +209,12 @@ class AubioArgumentParser(argparse.ArgumentParser): · 100 unmodified lines
207209 action="store", dest="silence", default=-70,
208210 help="silence threshold")
209211
212+ def add_release_drop(self):
213+ self.add_argument("-d", "--release-drop",
214+ metavar = "<value>", type=float,
215+ action="store", dest="release_drop", default=10,
216+ help="release drop threshold")
217+
210218 def add_minioi(self, default="12ms"):
211219 self.add_argument("-M", "--minioi",
212220 metavar = "<value>", type=str,
@@ -247,6 +255,9 @@ class AubioArgumentParser(argparse.ArgumentParser): · 34 unmodified lines
247255 metavar = "<slices>",
248256 action = "store", dest = "cut_until_nslices", default = None,
249257 help="how many extra slices should be added at the end of each slice")
258+ self.add_argument("--create-first",
259+ action = "store_true", dest = "create_first", default = False,
260+ help="always include first slice")
250261
251262 # some utilities
252263
@@ -379,6 +390,10 @@ class process_notes(default_process): · 126 unmodified lines
379390 def __init__(self, args):
380391 self.parse_options(args, self.valid_opts)
381392 self.notes = aubio.notes(**self.options)
393+ if args.silence is not None:
394+ self.notes.set_silence(args.silence)
395+ if args.release_drop is not None:
396+ self.notes.set_release_drop(args.release_drop)
382397 super(process_notes, self).__init__(args)
383398 def __call__(self, block):
384399 return self.notes(block)
@@ -499,7 +514,24 @@ class process_cut(process_onset): · 114 unmodified lines
499514
500515 def main():
501516 parser = aubio_parser()
502− args = parser.parse_args()
517+ if sys.version_info[0] != 3:
518+ # on py2, create a dummy ArgumentParser to workaround the
519+ # optional subcommand issue. See https://bugs.python.org/issue9253
520+ # This ensures that:
521+ # - version string is shown when only '-V' is passed
522+ # - help is printed if '-V' is passed with any other argument
523+ # - any other argument get forwarded to the real parser
524+ parser_root = argparse.ArgumentParser(add_help=False)
525+ parser_root.add_argument('-V', '--version', help="show version",
526+ action="store_true", dest="show_version")
527+ args, extras = parser_root.parse_known_args()
528+ if args.show_version == False: # no -V, forward to parser
529+ args = parser.parse_args(extras, namespace=args)
530+ elif len(extras) != 0: # -V with other arguments, print help
531+ parser.print_help()
532+ sys.exit(1)
533+ else: # in py3, we can simply use parser directly
534+ args = parser.parse_args()
503535 if 'show_version' in args and args.show_version:
504536 sys.stdout.write('aubio version ' + aubio.version + '\n')
505537 sys.exit(0)
python/lib/aubio/cut.py+3−2
@@ -101,7 +101,7 @@ def _cut_analyze(options): · 100 unmodified lines
101101
102102 s = source(source_uri, samplerate, hopsize)
103103 if samplerate == 0:
104− samplerate = s.get_samplerate()
104+ samplerate = s.samplerate
105105 options.samplerate = samplerate
106106
107107 if options.beat:
@@ -150,7 +150,8 @@ def _cut_slice(options, timestamps): · 42 unmodified lines
150150 slice_source_at_stamps(options.source_uri,
151151 timestamps, timestamps_end = timestamps_end,
152152 output_dir = options.output_directory,
153− samplerate = options.samplerate)
153+ samplerate = options.samplerate,
154+ create_first = options.create_first)
154155
155156 def main():
156157 parser = aubio_cut_parser()
python/lib/aubio/midiconv.py+38−9
@@ -1,9 +1,11 @@
11 # -*- coding: utf-8 -*-
22 """ utilities to convert midi note number to and from note names """
33
4−__all__ = ['note2midi', 'midi2note', 'freq2note']
4+__all__ = ['note2midi', 'midi2note', 'freq2note', 'note2freq']
55
66 import sys
7+from ._aubio import freqtomidi, miditofreq
8+
79 py3 = sys.version_info[0] == 3
810 if py3:
911 str_instances = str
@@ -24,10 +26,11 @@ def note2midi(note): · 14 unmodified lines
2426 }
2527 _valid_octaves = range(-1, 10)
2628 if not isinstance(note, str_instances):
27− raise TypeError("a string is required, got %s (%s)" % (note, str(type(note))))
29+ msg = "a string is required, got {:s} ({:s})"
30+ raise TypeError(msg.format(str(type(note)), repr(note)))
2831 if len(note) not in range(2, 5):
29− raise ValueError("string of 2 to 4 characters expected, got %d (%s)" \
30− % (len(note), note))
32+ msg = "string of 2 to 4 characters expected, got {:d} ({:s})"
33+ raise ValueError(msg.format(len(note), note))
3134 notename, modifier, octave = [None]*3
3235
3336 if len(note) == 4:
@@ -51,7 +54,8 @@ def note2midi(note): · 17 unmodified lines
5154 if octave not in _valid_octaves:
5255 raise ValueError("%s is not a valid octave" % octave)
5356
54− midi = 12 + octave * 12 + _valid_notenames[notename] + _valid_modifiers[modifier]
57+ midi = 12 + octave * 12 + _valid_notenames[notename] \
58+ + _valid_modifiers[modifier]
5559 if midi > 127:
5660 raise ValueError("%s is outside of the range C-2 to G8" % note)
5761 return midi
@@ -61,11 +65,36 @@ def midi2note(midi): · 3 unmodified lines
6165 if not isinstance(midi, int_instances):
6266 raise TypeError("an integer is required, got %s" % midi)
6367 if midi not in range(0, 128):
64− raise ValueError("an integer between 0 and 127 is excepted, got %d" % midi)
65− _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
68+ msg = "an integer between 0 and 127 is excepted, got {:d}"
69+ raise ValueError(msg.format(midi))
70+ _valid_notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#',
71+ 'A', 'A#', 'B']
6672 return _valid_notenames[midi % 12] + str(int(midi / 12) - 1)
6773
6874 def freq2note(freq):
6975 " convert frequency in Hz to nearest note name, e.g. [0, 22050.] -> [C-1, G9] "
70− from aubio import freqtomidi
71− return midi2note(int(freqtomidi(freq)))
76+ nearest_note = int(freqtomidi(freq) + .5)
77+ return midi2note(nearest_note)
78+
79+def note2freq(note):
80+ """Convert note name to corresponding frequency, in Hz.
81+
82+ Parameters
83+ ----------
84+ note : str
85+ input note name
86+
87+ Returns
88+ -------
89+ freq : float [0, 23000[
90+ frequency, in Hz
91+
92+ Example
93+ -------
94+ >>> aubio.note2freq('A4')
95+ 440
96+ >>> aubio.note2freq('A3')
97+ 220.1
98+ """
99+ midi = note2midi(note)
100+ return miditofreq(midi)
python/lib/aubio/slicing.py+11−5
@@ -6,19 +6,22 @@ from aubio import source, sink · 5 unmodified lines
66 _max_timestamp = 1e120
77
88 def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
9− output_dir=None, samplerate=0, hopsize=256):
9+ output_dir=None, samplerate=0, hopsize=256,
10+ create_first=False):
1011 """ slice a sound file at given timestamps """
1112
1213 if timestamps is None or len(timestamps) == 0:
1314 raise ValueError("no timestamps given")
1415
15− if timestamps[0] != 0:
16+ if timestamps[0] != 0 and create_first:
1617 timestamps = [0] + timestamps
1718 if timestamps_end is not None:
1819 timestamps_end = [timestamps[1] - 1] + timestamps_end
1920
2021 if timestamps_end is not None:
21− if len(timestamps_end) != len(timestamps):
22+ if len(timestamps_end) == len(timestamps) - 1:
23+ timestamps_end = timestamps_end + [_max_timestamp]
24+ elif len(timestamps_end) != len(timestamps):
2225 raise ValueError("len(timestamps_end) != len(timestamps)")
2326 else:
2427 timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
@@ -48,7 +51,7 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 23 unmodified lines
4851 # get hopsize new samples from source
4952 vec, read = _source.do_multi()
5053 # if the total number of frames read will exceed the next region start
51− if len(regions) and total_frames + read >= regions[0][0]:
54+ while len(regions) and total_frames + read >= regions[0][0]:
5255 #print "getting", regions[0], "at", total_frames
5356 # get next region
5457 start_stamp, end_stamp = regions.pop(0)
@@ -75,12 +78,15 @@ def slice_source_at_stamps(source_file, timestamps, timestamps_end=None, · 20 unmodified lines
7578 if remaining > start:
7679 # write remaining samples from current region
7780 _sink.do_multi(vec[:, start:remaining], remaining - start)
78− #print "closing region", "remaining", remaining
81+ #print("closing region", "remaining", remaining)
7982 # close this file
8083 _sink.close()
8184 elif read > start:
8285 # write all the samples
8386 _sink.do_multi(vec[:, start:read], read - start)
8487 total_frames += read
88+ # remove old slices
89+ slices = list(filter(lambda s: s['end_stamp'] > total_frames,
90+ slices))
8591 if read < hopsize:
8692 break
python/tests/test_aubio_cmd.py+6−3
@@ -19,13 +19,16 @@ class aubio_cmd(TestCase): · 18 unmodified lines
1919 class aubio_cmd_utils(TestCase):
2020
2121 def test_samples2seconds(self):
22− self.assertEqual(aubio.cmd.samples2seconds(3200, 32000), "0.100000\t")
22+ self.assertEqual(aubio.cmd.samples2seconds(3200, 32000),
23+ "0.100000\t")
2324
2425 def test_samples2milliseconds(self):
25− self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000), "100.000000\t")
26+ self.assertEqual(aubio.cmd.samples2milliseconds(3200, 32000),
27+ "100.000000\t")
2628
2729 def test_samples2samples(self):
28− self.assertEqual(aubio.cmd.samples2samples(3200, 32000), "3200\t")
30+ self.assertEqual(aubio.cmd.samples2samples(3200, 32000),
31+ "3200\t")
2932
3033 if __name__ == '__main__':
3134 main()
python/tests/test_note2midi.py+19−2
@@ -3,7 +3,7 @@ · 2 unmodified lines
33
44 from __future__ import unicode_literals
55
6−from aubio import note2midi, freq2note
6+from aubio import note2midi, freq2note, note2freq, float_type
77 from nose2.tools import params
88 import unittest
99
@@ -111,10 +111,27 @@ class note2midi_wrong_values(unittest.TestCase): · 101 unmodified lines
111111
112112 class freq2note_simple_test(unittest.TestCase):
113113
114− def test_freq2note(self):
114+ def test_freq2note_above(self):
115115 " make sure freq2note(441) == A4 "
116116 self.assertEqual("A4", freq2note(441))
117117
118+ def test_freq2note_under(self):
119+ " make sure freq2note(439) == A4 "
120+ self.assertEqual("A4", freq2note(439))
121+
122+class note2freq_simple_test(unittest.TestCase):
123+
124+ def test_note2freq(self):
125+ " make sure note2freq('A3') == 220"
126+ self.assertEqual(220, note2freq("A3"))
127+
128+ def test_note2freq_under(self):
129+ " make sure note2freq(A4) == 440"
130+ if float_type == 'float32':
131+ self.assertEqual(440, note2freq("A4"))
132+ else:
133+ self.assertLess(abs(note2freq("A4")-440), 1.e-12)
134+
118135 if __name__ == '__main__':
119136 import nose2
120137 nose2.main()
python/tests/test_notes.py+14−0
@@ -5,6 +5,7 @@ from numpy.testing import TestCase, assert_equal, assert_almost_equal · 4 unmodified lines
55 from aubio import notes
66
77 AUBIO_DEFAULT_NOTES_SILENCE = -70.
8+AUBIO_DEFAULT_NOTES_RELEASE_DROP = 10.
89 AUBIO_DEFAULT_NOTES_MINIOI_MS = 30.
910
1011 class aubio_notes_default(TestCase):
@@ -38,6 +39,19 @@ class aubio_notes_params(TestCase): · 27 unmodified lines
3839 self.o.set_silence(val)
3940 assert_equal (self.o.get_silence(), val)
4041
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+
4155 from .utils import list_all_sounds
4256 list_of_sounds = list_all_sounds('sounds')
4357
python/tests/test_slicing.py+25−4
@@ -23,19 +23,27 @@ class aubio_slicing_test_case(TestCase): · 22 unmodified lines
2323
2424 def test_slice_start_only_no_zero(self):
2525 regions_start = [i*1000 for i in range(1, n_slices)]
26− slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
26+ slice_source_at_stamps(self.source_file, regions_start,
27+ output_dir = self.output_dir, create_first=True)
2728
2829 def test_slice_start_beyond_end(self):
2930 regions_start = [i*1000 for i in range(1, n_slices)]
3031 regions_start += [count_samples_in_file(self.source_file) + 1000]
31− slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir)
32+ slice_source_at_stamps(self.source_file, regions_start,
33+ output_dir = self.output_dir, create_first=True)
3234
3335 def test_slice_start_every_blocksize(self):
3436 hopsize = 200
35− regions_start = [i*hopsize for i in range(1, n_slices)]
37+ regions_start = [i*hopsize for i in range(0, n_slices)]
3638 slice_source_at_stamps(self.source_file, regions_start, output_dir = self.output_dir,
3739 hopsize = 200)
3840
41+ def test_slice_start_every_half_blocksize(self):
42+ hopsize = 200
43+ regions_start = [i*hopsize//2 for i in range(0, n_slices)]
44+ slice_source_at_stamps(self.source_file, regions_start,
45+ output_dir = self.output_dir, hopsize = 200)
46+
3947 def tearDown(self):
4048 original_samples = count_samples_in_file(self.source_file)
4149 written_samples = count_samples_in_directory(self.output_dir)
@@ -91,6 +99,19 @@ class aubio_slicing_with_ends_test_case(TestCase): · 49 unmodified lines
9199 assert_equal(written_samples, expected_samples,
92100 "number of samples written different from number of original samples")
93101
102+ def test_slice_start_and_ends_with_missing_end(self):
103+ regions_start = [i*1000 for i in range(n_slices)]
104+ regions_ends = [r-1 for r in regions_start[1:]]
105+ slice_source_at_stamps(self.source_file, regions_start, regions_ends,
106+ output_dir = self.output_dir)
107+ written_samples = count_samples_in_directory(self.output_dir)
108+ original_samples = count_samples_in_file(self.source_file)
109+ total_files = count_files_in_directory(self.output_dir)
110+ assert_equal(n_slices, total_files,
111+ "number of slices created different from expected")
112+ assert_equal(written_samples, original_samples,
113+ "number of samples written different from number of original samples")
114+
94115 def tearDown(self):
95116 shutil.rmtree(self.output_dir)
96117
@@ -133,7 +154,7 @@ class aubio_slicing_wrong_ends_test_case(TestCase): · 36 unmodified lines
133154 regions_start = [i*1000 for i in range(1, n_slices)]
134155 regions_end = None
135156 slice_source_at_stamps (self.source_file, regions_start, regions_end,
136− output_dir = self.output_dir)
157+ output_dir = self.output_dir, create_first=True)
137158 total_files = count_files_in_directory(self.output_dir)
138159 assert_equal(n_slices, total_files,
139160 "number of slices created different from expected")

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