2 # -*- coding: utf-8 -*-
4 """aubio command line tool
6 This file was written by Paul Brossier <piem@aubio.org> and is released under
9 Note: this script is mostly about parsing command line arguments. For more
10 readable code examples, check out the `python/demos` folder."""
17 epilog = 'use "%(prog)s <command> --help" for more info about each command'
18 parser = argparse.ArgumentParser(epilog=epilog)
19 parser.add_argument('-V', '--version', help="show version",
20 action="store_true", dest="show_version")
22 subparsers = parser.add_subparsers(dest='command',
23 description="", metavar="<command>")
26 subparser = subparsers.add_parser('onset',
27 help='estimate time of onsets (beginning of sound event)',
28 formatter_class = argparse.ArgumentDefaultsHelpFormatter)
29 parser_add_input(subparser)
30 parser_add_buf_hop_size(subparser)
31 helpstr = "onset novelty function"
32 helpstr += " <default|energy|hfc|complex|phase|specdiff|kl|mkl|specflux>"
33 parser_add_method(subparser, helpstr=helpstr)
34 parser_add_threshold(subparser)
35 parser_add_silence(subparser)
36 parser_add_minioi(subparser)
37 parser_add_time_format(subparser)
38 parser_add_verbose_help(subparser)
39 subparser.set_defaults(process=process_onset)
42 subparser = subparsers.add_parser('pitch',
43 help='estimate fundamental frequency (monophonic)')
44 parser_add_input(subparser)
45 parser_add_buf_hop_size(subparser, buf_size=2048)
46 helpstr = "pitch detection method <default|yinfft|yin|mcomb|fcomb|schmitt>"
47 parser_add_method(subparser, helpstr=helpstr)
48 parser_add_threshold(subparser)
49 parser_add_silence(subparser)
50 parser_add_time_format(subparser)
51 parser_add_verbose_help(subparser)
52 subparser.set_defaults(process=process_pitch)
55 subparser = subparsers.add_parser('beat',
56 help='estimate location of beats')
57 parser_add_input(subparser)
58 parser_add_buf_hop_size(subparser, buf_size=1024, hop_size=512)
59 parser_add_time_format(subparser)
60 parser_add_verbose_help(subparser)
61 subparser.set_defaults(process=process_beat)
64 subparser = subparsers.add_parser('tempo',
65 help='estimate overall tempo in bpm')
66 parser_add_input(subparser)
67 parser_add_buf_hop_size(subparser, buf_size=1024, hop_size=512)
68 parser_add_time_format(subparser)
69 parser_add_verbose_help(subparser)
70 subparser.set_defaults(process=process_tempo)
73 subparser = subparsers.add_parser('notes',
74 help='estimate midi-like notes (monophonic)')
75 parser_add_input(subparser)
76 parser_add_buf_hop_size(subparser)
77 parser_add_time_format(subparser)
78 parser_add_verbose_help(subparser)
79 subparser.set_defaults(process=process_notes)
82 subparser = subparsers.add_parser('mfcc',
83 help='extract Mel-Frequency Cepstrum Coefficients')
84 parser_add_input(subparser)
85 parser_add_buf_hop_size(subparser)
86 parser_add_time_format(subparser)
87 parser_add_verbose_help(subparser)
88 subparser.set_defaults(process=process_mfcc)
91 subparser = subparsers.add_parser('melbands',
92 help='extract energies in Mel-frequency bands')
93 parser_add_input(subparser)
94 parser_add_buf_hop_size(subparser)
95 parser_add_time_format(subparser)
96 parser_add_verbose_help(subparser)
97 subparser.set_defaults(process=process_melbands)
101 def parser_add_input(parser):
102 parser.add_argument("source_uri", default=None, nargs='?',
103 help="input sound file to analyse", metavar = "<source_uri>")
104 parser.add_argument("-i", "--input", dest = "source_uri2",
105 help="input sound file to analyse", metavar = "<source_uri>")
106 parser.add_argument("-r", "--samplerate",
107 metavar = "<freq>", type=int,
108 action="store", dest="samplerate", default=0,
109 help="samplerate at which the file should be represented")
111 def parser_add_verbose_help(parser):
112 parser.add_argument("-v","--verbose",
113 action="count", dest="verbose", default=1,
114 help="make lots of noise [default]")
115 parser.add_argument("-q","--quiet",
116 action="store_const", dest="verbose", const=0,
119 def parser_add_buf_hop_size(parser, buf_size=512, hop_size=256):
120 parser.add_argument("-B","--bufsize",
121 action="store", dest="buf_size", default=buf_size,
122 metavar = "<size>", type=int,
123 help="buffer size [default=%d]" % buf_size)
124 parser.add_argument("-H","--hopsize",
125 metavar = "<size>", type=int,
126 action="store", dest="hop_size", default=hop_size,
127 help="overlap size [default=%d]" % hop_size)
129 def parser_add_method(parser, method='default', helpstr='method'):
130 parser.add_argument("-m","--method",
131 metavar = "<method>", type=str,
132 action="store", dest="method", default=method,
133 help="%s [default=%s]" % (helpstr, method))
135 def parser_add_threshold(parser, default=None):
136 parser.add_argument("-t","--threshold",
137 metavar = "<threshold>", type=float,
138 action="store", dest="threshold", default=default,
139 help="threshold [default=%s]" % default)
141 def parser_add_silence(parser):
142 parser.add_argument("-s", "--silence",
143 metavar = "<value>", type=float,
144 action="store", dest="silence", default=-70,
145 help="silence threshold")
147 def parser_add_minioi(parser):
148 parser.add_argument("-M", "--minioi",
149 metavar = "<value>", type=str,
150 action="store", dest="minioi", default="12ms",
151 help="minimum Inter-Onset Interval")
153 def parser_add_time_format(parser):
154 helpstr = "select time values output format (samples, ms, seconds)"
155 helpstr += " [default=seconds]"
156 parser.add_argument("-T", "--time-format",
164 def samples2seconds(n_frames, samplerate):
165 return "%f\t" % (n_frames / float(samplerate))
167 def samples2milliseconds(n_frames, samplerate):
168 return "%f\t" % (1000. * n_frames / float(samplerate))
170 def samples2samples(n_frames, samplerate):
171 return "%d\t" % n_frames
174 if mode is None or mode == 'seconds' or mode == 's':
175 return samples2seconds
176 elif mode == 'ms' or mode == 'milliseconds':
177 return samples2milliseconds
178 elif mode == 'samples':
179 return samples2samples
181 raise ValueError('invalid time format %s' % mode)
183 # definition of processing classes
185 class default_process(object):
186 def __init__(self, args):
187 if 'time_format' in args:
188 self.time2string = timefunc(args.time_format)
189 if args.verbose > 2 and hasattr(self, 'options'):
190 name = type(self).__name__.split('_')[1]
191 optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n'])
192 sys.stderr.write(optstr)
193 def flush(self, n_frames, samplerate):
194 # optionally called at the end of process
197 def parse_options(self, args, valid_opts):
198 # get any valid options found in a dictionnary of arguments
199 options = {k :v for k,v in vars(args).items() if k in valid_opts}
200 self.options = options
202 def remap_pvoc_options(self, options):
203 # FIXME: we need to remap buf_size to win_s, hop_size to hop_s
204 # adjust python/ext/py-phasevoc.c to understand buf_size/hop_size
205 if 'buf_size' in options:
206 options['win_s'] = options['buf_size']
207 del options['buf_size']
208 if 'hop_size' in options:
209 options['hop_s'] = options['hop_size']
210 del options['hop_size']
211 self.options = options
213 class process_onset(default_process):
214 valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
215 def __init__(self, args):
216 self.parse_options(args, self.valid_opts)
217 self.onset = aubio.onset(**self.options)
218 if args.threshold is not None:
219 self.onset.set_threshold(args.threshold)
221 if args.minioi.endswith('ms'):
222 self.onset.set_minioi_ms(float(args.minioi[:-2]))
223 elif args.minioi.endswith('s'):
224 self.onset.set_minioi_s(float(args.minioi[:-1]))
226 self.onset.set_minioi(int(args.minioi))
228 self.onset.set_silence(args.silence)
229 super(process_onset, self).__init__(args)
230 def __call__(self, block):
231 return self.onset(block)
232 def repr_res(self, res, frames_read, samplerate):
234 outstr = self.time2string(self.onset.get_last(), samplerate)
235 sys.stdout.write(outstr + '\n')
237 class process_pitch(default_process):
238 valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
239 def __init__(self, args):
240 self.parse_options(args, self.valid_opts)
241 self.pitch = aubio.pitch(**self.options)
242 if args.threshold is not None:
243 self.pitch.set_tolerance(args.threshold)
244 if args.silence is not None:
245 self.pitch.set_silence(args.silence)
246 super(process_pitch, self).__init__(args)
247 def __call__(self, block):
248 return self.pitch(block)
249 def repr_res(self, res, frames_read, samplerate):
250 fmt_out = self.time2string(frames_read, samplerate)
251 sys.stdout.write(fmt_out + "%.6f\n" % res[0])
253 class process_beat(default_process):
254 valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
255 def __init__(self, args):
256 self.parse_options(args, self.valid_opts)
257 self.tempo = aubio.tempo(**self.options)
258 super(process_beat, self).__init__(args)
259 def __call__(self, block):
260 return self.tempo(block)
261 def repr_res(self, res, frames_read, samplerate):
263 outstr = self.time2string(self.tempo.get_last(), samplerate)
264 sys.stdout.write(outstr + '\n')
266 class process_tempo(process_beat):
267 def __init__(self, args):
268 super(process_tempo, self).__init__(args)
269 self.beat_locations = []
270 def repr_res(self, res, frames_read, samplerate):
272 self.beat_locations.append(self.tempo.get_last_s())
273 def flush(self, frames_read, samplerate):
275 bpms = 60./ np.diff(self.beat_locations)
276 median_bpm = np.mean(bpms)
277 sys.stdout.write('%.2f bpm' % median_bpm + '\n')
279 class process_notes(default_process):
280 valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate']
281 def __init__(self, args):
282 self.parse_options(args, self.valid_opts)
283 self.notes = aubio.notes(**self.options)
284 super(process_notes, self).__init__(args)
285 def __call__(self, block):
286 return self.notes(block)
287 def repr_res(self, res, frames_read, samplerate):
288 if res[2] != 0: # note off
289 fmt_out = self.time2string(frames_read, samplerate)
290 sys.stdout.write(fmt_out + '\n')
291 if res[0] != 0: # note on
293 fmt_out = "%f\t" % lastmidi
294 fmt_out += self.time2string(frames_read, samplerate)
295 sys.stdout.write(fmt_out) # + '\t')
296 def flush(self, frames_read, samplerate):
297 eof = self.time2string(frames_read, samplerate)
298 sys.stdout.write(eof + '\n')
300 class process_mfcc(default_process):
301 def __init__(self, args):
302 valid_opts1 = ['hop_size', 'buf_size']
303 self.parse_options(args, valid_opts1)
304 self.remap_pvoc_options(self.options)
305 self.pv = aubio.pvoc(**self.options)
307 valid_opts2 = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate']
308 self.parse_options(args, valid_opts2)
309 self.mfcc = aubio.mfcc(**self.options)
311 # remember all options
312 self.parse_options(args, list(set(valid_opts1 + valid_opts2)))
314 super(process_mfcc, self).__init__(args)
316 def __call__(self, block):
317 fftgrain = self.pv(block)
318 return self.mfcc(fftgrain)
319 def repr_res(self, res, frames_read, samplerate):
320 fmt_out = self.time2string(frames_read, samplerate)
321 fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()])
322 sys.stdout.write(fmt_out + '\n')
324 class process_melbands(default_process):
325 def __init__(self, args):
327 valid_opts = ['hop_size', 'buf_size']
328 self.parse_options(args, valid_opts)
329 self.remap_pvoc_options(self.options)
330 self.pv = aubio.pvoc(**self.options)
332 valid_opts = ['buf_size', 'n_filters']
333 self.parse_options(args, valid_opts)
334 self.remap_pvoc_options(self.options)
335 self.filterbank = aubio.filterbank(**self.options)
336 self.filterbank.set_mel_coeffs_slaney(args.samplerate)
338 super(process_melbands, self).__init__(args)
339 def __call__(self, block):
340 fftgrain = self.pv(block)
341 return self.filterbank(fftgrain)
342 def repr_res(self, res, frames_read, samplerate):
343 fmt_out = self.time2string(frames_read, samplerate)
344 fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()])
345 sys.stdout.write(fmt_out + '\n')
348 parser = aubio_parser()
349 args = parser.parse_args()
350 if args.show_version or ('verbose' in args and args.verbose > 3):
351 sys.stdout.write('aubio version ' + aubio.version + '\n')
352 if args.show_version and args.command is None:
354 if args.command is None:
355 sys.stderr.write("Error: a command is required\n")
358 elif not args.source_uri and not args.source_uri2:
359 sys.stderr.write("Error: a source is required\n")
362 elif args.source_uri2 is not None:
363 args.source_uri = args.source_uri2
366 with aubio.source(args.source_uri, hop_size=args.hop_size,
367 samplerate=args.samplerate) as a_source:
368 # always update args.samplerate to native samplerate, in case
369 # source was opened with args.samplerate=0
370 args.samplerate = a_source.samplerate
371 # create the processor for this subcommand
372 processor = args.process(args)
375 # read new block from source
376 block, read = a_source()
377 # execute processor on this block
378 res = processor(block)
379 # print results for this block
381 processor.repr_res(res, frames_read, a_source.samplerate)
382 # increment total number of frames read
384 # exit loop at end of file
385 if read < a_source.hop_size: break
386 # flush the processor if needed
387 processor.flush(frames_read, a_source.samplerate)
389 fmt_string = "read {:.2f}s"
390 fmt_string += " ({:d} samples in {:d} blocks of {:d})"
391 fmt_string += " from {:s} at {:d}Hz\n"
392 sys.stderr.write(fmt_string.format(
393 frames_read/float(a_source.samplerate),
395 frames_read // a_source.hop_size + 1,
398 a_source.samplerate))
399 except KeyboardInterrupt as e: