From: Paul Brossier Date: Thu, 23 Mar 2017 13:56:50 +0000 (+0100) Subject: setup.py: move cut and cmd inside aubio module, use entry_points to generate scripts X-Git-Tag: 0.4.5~44^2~10 X-Git-Url: https://git.aubio.org/?a=commitdiff_plain;h=8e2f36a607e19683241bdd45894905346c9f9dea;p=aubio.git setup.py: move cut and cmd inside aubio module, use entry_points to generate scripts --- diff --git a/python/lib/aubio/cmd.py b/python/lib/aubio/cmd.py new file mode 100644 index 00000000..672b845d --- /dev/null +++ b/python/lib/aubio/cmd.py @@ -0,0 +1,370 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +"""aubio command line tool + +This file was written by Paul Brossier and is released under +the GNU/GPL v3. + +Note: this script is mostly about parsing command line arguments. For more +readable code examples, check out the `python/demos` folder.""" + +import sys +import argparse +import aubio + +def aubio_parser(): + epilog = 'use "%(prog)s --help" for more info about each command' + parser = argparse.ArgumentParser(epilog=epilog) + parser.add_argument('-V', '--version', help="show version", + action="store_true", dest="show_version") + + subparsers = parser.add_subparsers(dest='command', + description="", metavar="") + + # onset subcommand + subparser = subparsers.add_parser('onset', + help='get onset times', + formatter_class = argparse.ArgumentDefaultsHelpFormatter) + parser_add_input(subparser) + parser_add_buf_hop_size(subparser) + helpstr = "onset novelty function" + helpstr += " " + parser_add_method(subparser, helpstr=helpstr) + parser_add_threshold(subparser) + parser_add_silence(subparser) + parser_add_minioi(subparser) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_onset) + + # pitch subcommand + subparser = subparsers.add_parser('pitch', + help='extract fundamental frequency') + parser_add_input(subparser) + parser_add_buf_hop_size(subparser, buf_size=2048) + helpstr = "pitch detection method " + parser_add_method(subparser, helpstr=helpstr) + parser_add_threshold(subparser) + parser_add_silence(subparser) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_pitch) + + # tempo subcommand + subparser = subparsers.add_parser('beat', + help='get locations of beats') + parser_add_input(subparser) + parser_add_buf_hop_size(subparser, buf_size=1024, hop_size=512) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_tempo) + + # notes subcommand + subparser = subparsers.add_parser('notes', + help='get midi-like notes') + parser_add_input(subparser) + parser_add_buf_hop_size(subparser) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_notes) + + # mfcc subcommand + subparser = subparsers.add_parser('mfcc', + help='extract mel-frequency cepstrum coefficients') + parser_add_input(subparser) + parser_add_buf_hop_size(subparser) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_mfcc) + + # melbands subcommand + subparser = subparsers.add_parser('melbands', + help='extract mel-frequency energies per band') + parser_add_input(subparser) + parser_add_buf_hop_size(subparser) + parser_add_time_format(subparser) + parser_add_verbose_help(subparser) + subparser.set_defaults(process=process_melbands) + + return parser + +def parser_add_input(parser): + parser.add_argument("source_uri", default=None, nargs='?', + help="input sound file to analyse", metavar = "") + parser.add_argument("-i", "--input", dest = "source_uri2", + help="input sound file to analyse", metavar = "") + parser.add_argument("-r", "--samplerate", + metavar = "", type=int, + action="store", dest="samplerate", default=0, + help="samplerate at which the file should be represented") + +def parser_add_verbose_help(parser): + parser.add_argument("-v","--verbose", + action="count", dest="verbose", default=1, + help="make lots of noise [default]") + parser.add_argument("-q","--quiet", + action="store_const", dest="verbose", const=0, + help="be quiet") + +def parser_add_buf_hop_size(parser, buf_size=512, hop_size=256): + parser.add_argument("-B","--bufsize", + action="store", dest="buf_size", default=buf_size, + metavar = "", type=int, + help="buffer size [default=%d]" % buf_size) + parser.add_argument("-H","--hopsize", + metavar = "", type=int, + action="store", dest="hop_size", default=hop_size, + help="overlap size [default=%d]" % hop_size) + +def parser_add_method(parser, method='default', helpstr='method'): + parser.add_argument("-m","--method", + metavar = "", type=str, + action="store", dest="method", default=method, + help="%s [default=%s]" % (helpstr, method)) + +def parser_add_threshold(parser, default=None): + parser.add_argument("-t","--threshold", + metavar = "", type=float, + action="store", dest="threshold", default=default, + help="threshold [default=%s]" % default) + +def parser_add_silence(parser): + parser.add_argument("-s", "--silence", + metavar = "", type=float, + action="store", dest="silence", default=-70, + help="silence threshold") + +def parser_add_minioi(parser): + parser.add_argument("-M", "--minioi", + metavar = "", type=str, + action="store", dest="minioi", default="12ms", + help="minimum Inter-Onset Interval") + +def parser_add_time_format(parser): + helpstr = "select time values output format (samples, ms, seconds)" + helpstr += " [default=seconds]" + parser.add_argument("-T", "--time-format", + metavar='format', + dest="time_format", + default=None, + help=helpstr) + +# some utilities + +def parse_options(args, valid_opts): + options = {k :v for k,v in vars(args).items() if k in valid_opts} + return options + +def remap_pvoc_options(options): + # remap buf_size to win_s, hop_size to hop_s + # FIXME: adjust python/ext/py-phasevoc.c to understand buf_size/hop_size + options['win_s'] = options['buf_size'] + del options['buf_size'] + options['hop_s'] = options['hop_size'] + del options['hop_size'] + return options + +def samples2seconds(n_frames, samplerate): + return "%f\t" % (n_frames / float(a_source.samplerate)) + +def samples2milliseconds(n_frames, samplerate): + return "%f\t" % (1000. * n_frames / float(a_source.samplerate)) + +def samples2samples(n_frames, samplerate): + return "%d\t" % n_frames + +def timefunc(mode): + if mode is None or mode == 'seconds' or mode == 's': + return samples2seconds + elif mode == 'ms' or mode == 'milliseconds': + return samples2milliseconds + elif mode == 'samples': + return samples2samples + else: + raise ValueError('invalid time format %s' % mode) + +# definition of processing classes + +class default_process(object): + def __init__(self, args): + if 'time_format' in args: + self.time2string = timefunc(args.time_format) + if args.verbose > 2 and hasattr(self, 'options'): + name = type(self).__name__.split('_')[1] + optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n']) + sys.stderr.write(optstr) + +class process_onset(default_process): + valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] + def __init__(self, args): + self.options = parse_options(args, self.valid_opts) + self.onset = aubio.onset(**self.options) + if args.threshold is not None: + self.onset.set_threshold(args.threshold) + if args.minioi: + if args.minioi.endswith('ms'): + self.onset.set_minioi_ms(float(args.minioi[:-2])) + elif args.minioi.endswith('s'): + self.onset.set_minioi_s(float(args.minioi[:-1])) + else: + self.onset.set_minioi(int(args.minioi)) + if args.silence: + self.onset.set_silence(args.silence) + super(process_onset, self).__init__(args) + def __call__(self, block): + return self.onset(block) + def repr_res(self, res, frames_read, a_source): + if res[0] != 0: + outstr = self.time2string(self.onset.get_last(), a_source.samplerate) + sys.stdout.write(outstr + '\n') + +class process_pitch(default_process): + valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] + def __init__(self, args): + self.options = parse_options(args, self.valid_opts) + self.pitch = aubio.pitch(**self.options) + if args.threshold is not None: + self.pitch.set_tolerance(args.threshold) + if args.silence is not None: + self.pitch.set_silence(args.silence) + super(process_pitch, self).__init__(args) + def __call__(self, block): + return self.pitch(block) + def repr_res(self, res, frames_read, a_source): + fmt_out = self.time2string(frames_read, a_source.samplerate) + sys.stdout.write(fmt_out + "%.6f\n" % res[0]) + +class process_tempo(default_process): + valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] + def __init__(self, args): + self.options = parse_options(args, self.valid_opts) + self.tempo = aubio.tempo(**self.options) + super(process_tempo, self).__init__(args) + def __call__(self, block): + return self.tempo(block) + def repr_res(self, res, frames_read, a_source): + if res[0] != 0: + outstr = self.time2string(self.tempo.get_last(), a_source.samplerate) + sys.stdout.write(outstr + '\n') + +class process_notes(default_process): + valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] + def __init__(self, args): + self.options = parse_options(args, self.valid_opts) + self.notes = aubio.notes(**self.options) + super(process_notes, self).__init__(args) + def __call__(self, block): + return self.notes(block) + def repr_res(self, res, frames_read, a_source): + if res[2] != 0: # note off + fmt_out = self.time2string(frames_read, a_source.samplerate) + sys.stdout.write(fmt_out + '\n') + if res[0] != 0: # note on + lastmidi = res[0] + fmt_out = "%f\t" % lastmidi + fmt_out += self.time2string(frames_read, a_source.samplerate) + sys.stdout.write(fmt_out) # + '\t') + +class process_mfcc(default_process): + def __init__(self, args): + valid_opts = ['hop_size', 'buf_size'] + options = parse_options(args, valid_opts) + self.options = remap_pvoc_options(options) + self.pv = aubio.pvoc(**options) + + valid_opts = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate'] + options = parse_options(args, valid_opts) + self.mfcc = aubio.mfcc(**options) + self.options.update(options) + + super(process_mfcc, self).__init__(args) + + def __call__(self, block): + fftgrain = self.pv(block) + return self.mfcc(fftgrain) + def repr_res(self, res, frames_read, a_source): + fmt_out = self.time2string(frames_read, a_source.samplerate) + fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) + sys.stdout.write(fmt_out + '\n') + +class process_melbands(default_process): + def __init__(self, args): + self.args = args + valid_opts = ['hop_size', 'buf_size'] + options = parse_options(args, valid_opts) + options = remap_pvoc_options(options) + self.pv = aubio.pvoc(**options) + + valid_opts = ['buf_size', 'n_filters'] + options = {k :v for k,v in vars(args).items() if k in valid_opts} + # FIXME + options['win_s'] = options['buf_size'] + del options['buf_size'] + self.filterbank = aubio.filterbank(**options) + self.filterbank.set_mel_coeffs_slaney(args.samplerate) + + super(process_melbands, self).__init__(args) + def __call__(self, block): + fftgrain = self.pv(block) + return self.filterbank(fftgrain) + def repr_res(self, res, frames_read, a_source): + fmt_out = self.time2string(frames_read, a_source.samplerate) + fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) + sys.stdout.write(fmt_out + '\n') + +def main(): + parser = aubio_parser() + args = parser.parse_args() + if args.show_version or ('verbose' in args and args.verbose > 3): + sys.stdout.write('aubio version ' + aubio.version + '\n') + if args.show_version and args.command is None: + sys.exit(0) + if args.command is None: + sys.stderr.write("Error: a command is required\n") + parser.print_help() + sys.exit(1) + elif not args.source_uri and not args.source_uri2: + sys.stderr.write("Error: a source is required\n") + parser.print_help() + sys.exit(1) + elif args.source_uri2 is not None: + args.source_uri = args.source_uri2 + try: + # open source_uri + with aubio.source(args.source_uri, hop_size=args.hop_size, + samplerate=args.samplerate) as a_source: + args.samplerate = a_source.samplerate + # create the processor for this subcommand + processor = args.process(args) + frames_read = 0 + while True: + # read new block from source + block, read = a_source() + # execute processor on this block + res = processor(block) + # print results for this block + if args.verbose > 0: + processor.repr_res(res, frames_read, a_source) + # increment total number of frames read + frames_read += read + # exit loop at end of file + if read < a_source.hop_size: break + # special case for notes + if args.command == 'notes': + eof = processor.time2string(frames_read, a_source.samplerate) + sys.stdout.write(eof + '\n') + sys.stdout.flush() + if args.verbose > 1: + fmt_string = "read {:.2f}s" + fmt_string += " ({:d} samples in {:d} blocks of {:d})" + fmt_string += " from {:s} at {:d}Hz\n" + sys.stderr.write(fmt_string.format( + frames_read/float(a_source.samplerate), + frames_read, + frames_read // a_source.hop_size + 1, + a_source.hop_size, + a_source.uri, + a_source.samplerate)) + except KeyboardInterrupt as e: + sys.exit(1) diff --git a/python/lib/aubio/cut.py b/python/lib/aubio/cut.py new file mode 100644 index 00000000..f8bbef44 --- /dev/null +++ b/python/lib/aubio/cut.py @@ -0,0 +1,211 @@ +#! /usr/bin/env python + +""" this file was written by Paul Brossier + it is released under the GNU/GPL license. +""" + +import sys + +def parse_args(): + from optparse import OptionParser + usage = "usage: %s [options] -i soundfile" % sys.argv[0] + usage += "\n help: %s -h" % sys.argv[0] + parser = OptionParser(usage=usage) + parser.add_option("-i", "--input", action = "store", dest = "source_file", + help="input sound file to analyse", metavar = "") + parser.add_option("-O","--onset-method", + action="store", dest="onset_method", default='default', + metavar = "", + help="onset detection method [default=default] \ + complexdomain|hfc|phase|specdiff|energy|kl|mkl") + # cutting methods + parser.add_option("-b","--beat", + action="store_true", dest="beat", default=False, + help="use beat locations") + """ + parser.add_option("-S","--silencecut", + action="store_true", dest="silencecut", default=False, + help="use silence locations") + parser.add_option("-s","--silence", + metavar = "", + action="store", dest="silence", default=-70, + help="silence threshold [default=-70]") + """ + # algorithm parameters + parser.add_option("-r", "--samplerate", + metavar = "", type='int', + action="store", dest="samplerate", default=0, + help="samplerate at which the file should be represented") + parser.add_option("-B","--bufsize", + action="store", dest="bufsize", default=512, + metavar = "", type='int', + help="buffer size [default=512]") + parser.add_option("-H","--hopsize", + metavar = "", type='int', + action="store", dest="hopsize", default=256, + help="overlap size [default=256]") + parser.add_option("-t","--onset-threshold", + metavar = "", type="float", + action="store", dest="threshold", default=0.3, + help="onset peak picking threshold [default=0.3]") + parser.add_option("-c","--cut", + action="store_true", dest="cut", default=False, + help="cut input sound file at detected labels \ + best used with option -L") + + # minioi + parser.add_option("-M","--minioi", + metavar = "", type='string', + action="store", dest="minioi", default="12ms", + help="minimum inter onset interval [default=12ms]") + + """ + parser.add_option("-D","--delay", + action = "store", dest = "delay", type = "float", + metavar = "", default=0, + help="number of seconds to take back [default=system]\ + default system delay is 3*hopsize/samplerate") + parser.add_option("-C","--dcthreshold", + metavar = "", + action="store", dest="dcthreshold", default=1., + help="onset peak picking DC component [default=1.]") + parser.add_option("-L","--localmin", + action="store_true", dest="localmin", default=False, + help="use local minima after peak detection") + parser.add_option("-d","--derivate", + action="store_true", dest="derivate", default=False, + help="derivate onset detection function") + parser.add_option("-z","--zerocross", + metavar = "", + action="store", dest="zerothres", default=0.008, + help="zero-crossing threshold for slicing [default=0.00008]") + """ + # plotting functions + """ + parser.add_option("-p","--plot", + action="store_true", dest="plot", default=False, + help="draw plot") + parser.add_option("-x","--xsize", + metavar = "", + action="store", dest="xsize", default=1., + type='float', help="define xsize for plot") + parser.add_option("-y","--ysize", + metavar = "", + action="store", dest="ysize", default=1., + type='float', help="define ysize for plot") + parser.add_option("-f","--function", + action="store_true", dest="func", default=False, + help="print detection function") + parser.add_option("-n","--no-onsets", + action="store_true", dest="nplot", default=False, + help="do not plot detected onsets") + parser.add_option("-O","--outplot", + metavar = "", + action="store", dest="outplot", default=None, + help="save plot to output.{ps,png}") + parser.add_option("-F","--spectrogram", + action="store_true", dest="spectro", default=False, + help="add spectrogram to the plot") + """ + parser.add_option("-o","--output", type = str, + metavar = "", + action="store", dest="output_directory", default=None, + help="specify path where slices of the original file should be created") + parser.add_option("--cut-until-nsamples", type = int, + metavar = "", + action = "store", dest = "cut_until_nsamples", default = None, + help="how many extra samples should be added at the end of each slice") + parser.add_option("--cut-every-nslices", type = int, + metavar = "", + action = "store", dest = "cut_every_nslices", default = None, + help="how many slices should be groupped together at each cut") + parser.add_option("--cut-until-nslices", type = int, + metavar = "", + action = "store", dest = "cut_until_nslices", default = None, + help="how many extra slices should be added at the end of each slice") + + parser.add_option("-v","--verbose", + action="store_true", dest="verbose", default=True, + help="make lots of noise [default]") + parser.add_option("-q","--quiet", + action="store_false", dest="verbose", default=True, + help="be quiet") + (options, args) = parser.parse_args() + if not options.source_file: + import os.path + if len(args) == 1: + options.source_file = args[0] + else: + print ("no file name given\n" + usage) + sys.exit(1) + return options, args + +def main(): + options, args = parse_args() + + hopsize = options.hopsize + bufsize = options.bufsize + samplerate = options.samplerate + source_file = options.source_file + + from aubio import onset, tempo, source, sink + + s = source(source_file, samplerate, hopsize) + if samplerate == 0: samplerate = s.get_samplerate() + + if options.beat: + o = tempo(options.onset_method, bufsize, hopsize) + else: + o = onset(options.onset_method, bufsize, hopsize) + if options.minioi: + if options.minioi.endswith('ms'): + o.set_minioi_ms(int(options.minioi[:-2])) + elif options.minioi.endswith('s'): + o.set_minioi_s(int(options.minioi[:-1])) + else: + o.set_minioi(int(options.minioi)) + o.set_threshold(options.threshold) + + timestamps = [] + total_frames = 0 + # analyze pass + while True: + samples, read = s() + if o(samples): + timestamps.append (o.get_last()) + if options.verbose: print ("%.4f" % o.get_last_s()) + total_frames += read + if read < hopsize: break + del s + # print some info + nstamps = len(timestamps) + duration = float (total_frames) / float(samplerate) + info = 'found %(nstamps)d timestamps in %(source_file)s' % locals() + info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() + sys.stderr.write(info) + + # cutting pass + if options.cut and nstamps > 0: + # generate output files + from aubio.slicing import slice_source_at_stamps + timestamps_end = None + if options.cut_every_nslices: + timestamps = timestamps[::options.cut_every_nslices] + nstamps = len(timestamps) + if options.cut_until_nslices and options.cut_until_nsamples: + print ("warning: using cut_until_nslices, but cut_until_nsamples is set") + if options.cut_until_nsamples: + timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]] + timestamps_end += [ 1e120 ] + if options.cut_until_nslices: + timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]] + timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1) + slice_source_at_stamps(source_file, timestamps, timestamps_end = timestamps_end, + output_dir = options.output_directory, + samplerate = samplerate) + + # print some info + duration = float (total_frames) / float(samplerate) + info = 'created %(nstamps)d slices from %(source_file)s' % locals() + info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() + sys.stderr.write(info) diff --git a/python/scripts/aubio b/python/scripts/aubio deleted file mode 100755 index b1e2ba70..00000000 --- a/python/scripts/aubio +++ /dev/null @@ -1,370 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -"""aubio command line tool - -This file was written by Paul Brossier and is released under -the GNU/GPL v3. - -Note: this script is mostly about parsing command line arguments. For more -readable code examples, check out the `python/demos` folder.""" - -import sys -import argparse -import aubio - -def aubio_parser(): - epilog = 'use "%(prog)s --help" for more info about each command' - parser = argparse.ArgumentParser(epilog=epilog) - parser.add_argument('-V', '--version', help="show version", - action="store_true", dest="show_version") - - subparsers = parser.add_subparsers(dest='command', - description="", metavar="") - - # onset subcommand - subparser = subparsers.add_parser('onset', - help='get onset times', - formatter_class = argparse.ArgumentDefaultsHelpFormatter) - parser_add_input(subparser) - parser_add_buf_hop_size(subparser) - helpstr = "onset novelty function" - helpstr += " " - parser_add_method(subparser, helpstr=helpstr) - parser_add_threshold(subparser) - parser_add_silence(subparser) - parser_add_minioi(subparser) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_onset) - - # pitch subcommand - subparser = subparsers.add_parser('pitch', - help='extract fundamental frequency') - parser_add_input(subparser) - parser_add_buf_hop_size(subparser, buf_size=2048) - helpstr = "pitch detection method " - parser_add_method(subparser, helpstr=helpstr) - parser_add_threshold(subparser) - parser_add_silence(subparser) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_pitch) - - # tempo subcommand - subparser = subparsers.add_parser('beat', - help='get locations of beats') - parser_add_input(subparser) - parser_add_buf_hop_size(subparser, buf_size=1024, hop_size=512) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_tempo) - - # notes subcommand - subparser = subparsers.add_parser('notes', - help='get midi-like notes') - parser_add_input(subparser) - parser_add_buf_hop_size(subparser) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_notes) - - # mfcc subcommand - subparser = subparsers.add_parser('mfcc', - help='extract mel-frequency cepstrum coefficients') - parser_add_input(subparser) - parser_add_buf_hop_size(subparser) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_mfcc) - - # melbands subcommand - subparser = subparsers.add_parser('melbands', - help='extract mel-frequency energies per band') - parser_add_input(subparser) - parser_add_buf_hop_size(subparser) - parser_add_time_format(subparser) - parser_add_verbose_help(subparser) - subparser.set_defaults(process=process_melbands) - - return parser - -def parser_add_input(parser): - parser.add_argument("source_uri", default=None, nargs='?', - help="input sound file to analyse", metavar = "") - parser.add_argument("-i", "--input", dest = "source_uri2", - help="input sound file to analyse", metavar = "") - parser.add_argument("-r", "--samplerate", - metavar = "", type=int, - action="store", dest="samplerate", default=0, - help="samplerate at which the file should be represented") - -def parser_add_verbose_help(parser): - parser.add_argument("-v","--verbose", - action="count", dest="verbose", default=1, - help="make lots of noise [default]") - parser.add_argument("-q","--quiet", - action="store_const", dest="verbose", const=0, - help="be quiet") - -def parser_add_buf_hop_size(parser, buf_size=512, hop_size=256): - parser.add_argument("-B","--bufsize", - action="store", dest="buf_size", default=buf_size, - metavar = "", type=int, - help="buffer size [default=%d]" % buf_size) - parser.add_argument("-H","--hopsize", - metavar = "", type=int, - action="store", dest="hop_size", default=hop_size, - help="overlap size [default=%d]" % hop_size) - -def parser_add_method(parser, method='default', helpstr='method'): - parser.add_argument("-m","--method", - metavar = "", type=str, - action="store", dest="method", default=method, - help="%s [default=%s]" % (helpstr, method)) - -def parser_add_threshold(parser, default=None): - parser.add_argument("-t","--threshold", - metavar = "", type=float, - action="store", dest="threshold", default=default, - help="threshold [default=%s]" % default) - -def parser_add_silence(parser): - parser.add_argument("-s", "--silence", - metavar = "", type=float, - action="store", dest="silence", default=-70, - help="silence threshold") - -def parser_add_minioi(parser): - parser.add_argument("-M", "--minioi", - metavar = "", type=str, - action="store", dest="minioi", default="12ms", - help="minimum Inter-Onset Interval") - -def parser_add_time_format(parser): - helpstr = "select time values output format (samples, ms, seconds)" - helpstr += " [default=seconds]" - parser.add_argument("-T", "--time-format", - metavar='format', - dest="time_format", - default=None, - help=helpstr) - -# some utilities - -def parse_options(args, valid_opts): - options = {k :v for k,v in vars(args).items() if k in valid_opts} - return options - -def remap_pvoc_options(options): - # remap buf_size to win_s, hop_size to hop_s - # FIXME: adjust python/ext/py-phasevoc.c to understand buf_size/hop_size - options['win_s'] = options['buf_size'] - del options['buf_size'] - options['hop_s'] = options['hop_size'] - del options['hop_size'] - return options - -def samples2seconds(n_frames, samplerate): - return "%f\t" % (n_frames / float(a_source.samplerate)) - -def samples2milliseconds(n_frames, samplerate): - return "%f\t" % (1000. * n_frames / float(a_source.samplerate)) - -def samples2samples(n_frames, samplerate): - return "%d\t" % n_frames - -def timefunc(mode): - if mode is None or mode == 'seconds' or mode == 's': - return samples2seconds - elif mode == 'ms' or mode == 'milliseconds': - return samples2milliseconds - elif mode == 'samples': - return samples2samples - else: - raise ValueError('invalid time format %s' % mode) - -# definition of processing classes - -class default_process(object): - def __init__(self, args): - if 'time_format' in args: - self.time2string = timefunc(args.time_format) - if args.verbose > 2 and hasattr(self, 'options'): - name = type(self).__name__.split('_')[1] - optstr = ' '.join(['running', name, 'with options', repr(self.options), '\n']) - sys.stderr.write(optstr) - -class process_onset(default_process): - valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] - def __init__(self, args): - self.options = parse_options(args, self.valid_opts) - self.onset = aubio.onset(**self.options) - if args.threshold is not None: - self.onset.set_threshold(args.threshold) - if args.minioi: - if args.minioi.endswith('ms'): - self.onset.set_minioi_ms(float(args.minioi[:-2])) - elif args.minioi.endswith('s'): - self.onset.set_minioi_s(float(args.minioi[:-1])) - else: - self.onset.set_minioi(int(args.minioi)) - if args.silence: - self.onset.set_silence(args.silence) - super(process_onset, self).__init__(args) - def __call__(self, block): - return self.onset(block) - def repr_res(self, res, frames_read, a_source): - if res[0] != 0: - outstr = self.time2string(self.onset.get_last(), a_source.samplerate) - sys.stdout.write(outstr + '\n') - -class process_pitch(default_process): - valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] - def __init__(self, args): - self.options = parse_options(args, self.valid_opts) - self.pitch = aubio.pitch(**self.options) - if args.threshold is not None: - self.pitch.set_tolerance(args.threshold) - if args.silence is not None: - self.pitch.set_silence(args.silence) - super(process_pitch, self).__init__(args) - def __call__(self, block): - return self.pitch(block) - def repr_res(self, res, frames_read, a_source): - fmt_out = self.time2string(frames_read, a_source.samplerate) - sys.stdout.write(fmt_out + "%.6f\n" % res[0]) - -class process_tempo(default_process): - valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] - def __init__(self, args): - self.options = parse_options(args, self.valid_opts) - self.tempo = aubio.tempo(**self.options) - super(process_tempo, self).__init__(args) - def __call__(self, block): - return self.tempo(block) - def repr_res(self, res, frames_read, a_source): - if res[0] != 0: - outstr = self.time2string(self.tempo.get_last(), a_source.samplerate) - sys.stdout.write(outstr + '\n') - -class process_notes(default_process): - valid_opts = ['method', 'hop_size', 'buf_size', 'samplerate'] - def __init__(self, args): - self.options = parse_options(args, self.valid_opts) - self.notes = aubio.notes(**self.options) - super(process_notes, self).__init__(args) - def __call__(self, block): - return self.notes(block) - def repr_res(self, res, frames_read, a_source): - if res[2] != 0: # note off - fmt_out = self.time2string(frames_read, a_source.samplerate) - sys.stdout.write(fmt_out + '\n') - if res[0] != 0: # note on - lastmidi = res[0] - fmt_out = "%f\t" % lastmidi - fmt_out += self.time2string(frames_read, a_source.samplerate) - sys.stdout.write(fmt_out) # + '\t') - -class process_mfcc(default_process): - def __init__(self, args): - valid_opts = ['hop_size', 'buf_size'] - options = parse_options(args, valid_opts) - self.options = remap_pvoc_options(options) - self.pv = aubio.pvoc(**options) - - valid_opts = ['buf_size', 'n_filters', 'n_coeffs', 'samplerate'] - options = parse_options(args, valid_opts) - self.mfcc = aubio.mfcc(**options) - self.options.update(options) - - super(process_mfcc, self).__init__(args) - - def __call__(self, block): - fftgrain = self.pv(block) - return self.mfcc(fftgrain) - def repr_res(self, res, frames_read, a_source): - fmt_out = self.time2string(frames_read, a_source.samplerate) - fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) - sys.stdout.write(fmt_out + '\n') - -class process_melbands(default_process): - def __init__(self, args): - self.args = args - valid_opts = ['hop_size', 'buf_size'] - options = parse_options(args, valid_opts) - options = remap_pvoc_options(options) - self.pv = aubio.pvoc(**options) - - valid_opts = ['buf_size', 'n_filters'] - options = {k :v for k,v in vars(args).items() if k in valid_opts} - # FIXME - options['win_s'] = options['buf_size'] - del options['buf_size'] - self.filterbank = aubio.filterbank(**options) - self.filterbank.set_mel_coeffs_slaney(args.samplerate) - - super(process_melbands, self).__init__(args) - def __call__(self, block): - fftgrain = self.pv(block) - return self.filterbank(fftgrain) - def repr_res(self, res, frames_read, a_source): - fmt_out = self.time2string(frames_read, a_source.samplerate) - fmt_out += ' '.join(["% 9.7f" % f for f in res.tolist()]) - sys.stdout.write(fmt_out + '\n') - -if __name__ == '__main__': - parser = aubio_parser() - args = parser.parse_args() - if args.show_version or ('verbose' in args and args.verbose > 3): - sys.stdout.write('aubio version ' + aubio.version + '\n') - if args.show_version and args.command is None: - sys.exit(0) - if args.command is None: - sys.stderr.write("Error: a command is required\n") - parser.print_help() - sys.exit(1) - elif not args.source_uri and not args.source_uri2: - sys.stderr.write("Error: a source is required\n") - parser.print_help() - sys.exit(1) - elif args.source_uri2 is not None: - args.source_uri = args.source_uri2 - try: - # open source_uri - with aubio.source(args.source_uri, hop_size=args.hop_size, - samplerate=args.samplerate) as a_source: - args.samplerate = a_source.samplerate - # create the processor for this subcommand - processor = args.process(args) - frames_read = 0 - while True: - # read new block from source - block, read = a_source() - # execute processor on this block - res = processor(block) - # print results for this block - if args.verbose > 0: - processor.repr_res(res, frames_read, a_source) - # increment total number of frames read - frames_read += read - # exit loop at end of file - if read < a_source.hop_size: break - # special case for notes - if args.command == 'notes': - eof = processor.time2string(frames_read, a_source.samplerate) - sys.stdout.write(eof + '\n') - sys.stdout.flush() - if args.verbose > 1: - fmt_string = "read {:.2f}s" - fmt_string += " ({:d} samples in {:d} blocks of {:d})" - fmt_string += " from {:s} at {:d}Hz\n" - sys.stderr.write(fmt_string.format( - frames_read/float(a_source.samplerate), - frames_read, - frames_read // a_source.hop_size + 1, - a_source.hop_size, - a_source.uri, - a_source.samplerate)) - except KeyboardInterrupt as e: - sys.exit(1) diff --git a/python/scripts/aubiocut b/python/scripts/aubiocut deleted file mode 100755 index e348665c..00000000 --- a/python/scripts/aubiocut +++ /dev/null @@ -1,211 +0,0 @@ -#! /usr/bin/env python - -""" this file was written by Paul Brossier - it is released under the GNU/GPL license. -""" - -import sys - -def parse_args(): - from optparse import OptionParser - usage = "usage: %s [options] -i soundfile" % sys.argv[0] - usage += "\n help: %s -h" % sys.argv[0] - parser = OptionParser(usage=usage) - parser.add_option("-i", "--input", action = "store", dest = "source_file", - help="input sound file to analyse", metavar = "") - parser.add_option("-O","--onset-method", - action="store", dest="onset_method", default='default', - metavar = "", - help="onset detection method [default=default] \ - complexdomain|hfc|phase|specdiff|energy|kl|mkl") - # cutting methods - parser.add_option("-b","--beat", - action="store_true", dest="beat", default=False, - help="use beat locations") - """ - parser.add_option("-S","--silencecut", - action="store_true", dest="silencecut", default=False, - help="use silence locations") - parser.add_option("-s","--silence", - metavar = "", - action="store", dest="silence", default=-70, - help="silence threshold [default=-70]") - """ - # algorithm parameters - parser.add_option("-r", "--samplerate", - metavar = "", type='int', - action="store", dest="samplerate", default=0, - help="samplerate at which the file should be represented") - parser.add_option("-B","--bufsize", - action="store", dest="bufsize", default=512, - metavar = "", type='int', - help="buffer size [default=512]") - parser.add_option("-H","--hopsize", - metavar = "", type='int', - action="store", dest="hopsize", default=256, - help="overlap size [default=256]") - parser.add_option("-t","--onset-threshold", - metavar = "", type="float", - action="store", dest="threshold", default=0.3, - help="onset peak picking threshold [default=0.3]") - parser.add_option("-c","--cut", - action="store_true", dest="cut", default=False, - help="cut input sound file at detected labels \ - best used with option -L") - - # minioi - parser.add_option("-M","--minioi", - metavar = "", type='string', - action="store", dest="minioi", default="12ms", - help="minimum inter onset interval [default=12ms]") - - """ - parser.add_option("-D","--delay", - action = "store", dest = "delay", type = "float", - metavar = "", default=0, - help="number of seconds to take back [default=system]\ - default system delay is 3*hopsize/samplerate") - parser.add_option("-C","--dcthreshold", - metavar = "", - action="store", dest="dcthreshold", default=1., - help="onset peak picking DC component [default=1.]") - parser.add_option("-L","--localmin", - action="store_true", dest="localmin", default=False, - help="use local minima after peak detection") - parser.add_option("-d","--derivate", - action="store_true", dest="derivate", default=False, - help="derivate onset detection function") - parser.add_option("-z","--zerocross", - metavar = "", - action="store", dest="zerothres", default=0.008, - help="zero-crossing threshold for slicing [default=0.00008]") - """ - # plotting functions - """ - parser.add_option("-p","--plot", - action="store_true", dest="plot", default=False, - help="draw plot") - parser.add_option("-x","--xsize", - metavar = "", - action="store", dest="xsize", default=1., - type='float', help="define xsize for plot") - parser.add_option("-y","--ysize", - metavar = "", - action="store", dest="ysize", default=1., - type='float', help="define ysize for plot") - parser.add_option("-f","--function", - action="store_true", dest="func", default=False, - help="print detection function") - parser.add_option("-n","--no-onsets", - action="store_true", dest="nplot", default=False, - help="do not plot detected onsets") - parser.add_option("-O","--outplot", - metavar = "", - action="store", dest="outplot", default=None, - help="save plot to output.{ps,png}") - parser.add_option("-F","--spectrogram", - action="store_true", dest="spectro", default=False, - help="add spectrogram to the plot") - """ - parser.add_option("-o","--output", type = str, - metavar = "", - action="store", dest="output_directory", default=None, - help="specify path where slices of the original file should be created") - parser.add_option("--cut-until-nsamples", type = int, - metavar = "", - action = "store", dest = "cut_until_nsamples", default = None, - help="how many extra samples should be added at the end of each slice") - parser.add_option("--cut-every-nslices", type = int, - metavar = "", - action = "store", dest = "cut_every_nslices", default = None, - help="how many slices should be groupped together at each cut") - parser.add_option("--cut-until-nslices", type = int, - metavar = "", - action = "store", dest = "cut_until_nslices", default = None, - help="how many extra slices should be added at the end of each slice") - - parser.add_option("-v","--verbose", - action="store_true", dest="verbose", default=True, - help="make lots of noise [default]") - parser.add_option("-q","--quiet", - action="store_false", dest="verbose", default=True, - help="be quiet") - (options, args) = parser.parse_args() - if not options.source_file: - import os.path - if len(args) == 1: - options.source_file = args[0] - else: - print ("no file name given\n" + usage) - sys.exit(1) - return options, args - -if __name__ == '__main__': - options, args = parse_args() - - hopsize = options.hopsize - bufsize = options.bufsize - samplerate = options.samplerate - source_file = options.source_file - - from aubio import onset, tempo, source, sink - - s = source(source_file, samplerate, hopsize) - if samplerate == 0: samplerate = s.get_samplerate() - - if options.beat: - o = tempo(options.onset_method, bufsize, hopsize) - else: - o = onset(options.onset_method, bufsize, hopsize) - if options.minioi: - if options.minioi.endswith('ms'): - o.set_minioi_ms(int(options.minioi[:-2])) - elif options.minioi.endswith('s'): - o.set_minioi_s(int(options.minioi[:-1])) - else: - o.set_minioi(int(options.minioi)) - o.set_threshold(options.threshold) - - timestamps = [] - total_frames = 0 - # analyze pass - while True: - samples, read = s() - if o(samples): - timestamps.append (o.get_last()) - if options.verbose: print ("%.4f" % o.get_last_s()) - total_frames += read - if read < hopsize: break - del s - # print some info - nstamps = len(timestamps) - duration = float (total_frames) / float(samplerate) - info = 'found %(nstamps)d timestamps in %(source_file)s' % locals() - info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() - sys.stderr.write(info) - - # cutting pass - if options.cut and nstamps > 0: - # generate output files - from aubio.slicing import slice_source_at_stamps - timestamps_end = None - if options.cut_every_nslices: - timestamps = timestamps[::options.cut_every_nslices] - nstamps = len(timestamps) - if options.cut_until_nslices and options.cut_until_nsamples: - print ("warning: using cut_until_nslices, but cut_until_nsamples is set") - if options.cut_until_nsamples: - timestamps_end = [t + options.cut_until_nsamples for t in timestamps[1:]] - timestamps_end += [ 1e120 ] - if options.cut_until_nslices: - timestamps_end = [t for t in timestamps[1 + options.cut_until_nslices:]] - timestamps_end += [ 1e120 ] * (options.cut_until_nslices + 1) - slice_source_at_stamps(source_file, timestamps, timestamps_end = timestamps_end, - output_dir = options.output_directory, - samplerate = samplerate) - - # print some info - duration = float (total_frames) / float(samplerate) - info = 'created %(nstamps)d slices from %(source_file)s' % locals() - info += ' (total %(duration).2fs at %(samplerate)dHz)\n' % locals() - sys.stderr.write(info) diff --git a/setup.py b/setup.py index 0c93bb97..987ff4cc 100755 --- a/setup.py +++ b/setup.py @@ -57,7 +57,6 @@ distrib = setup(name='aubio', version = __version__, packages = ['aubio'], package_dir = {'aubio':'python/lib/aubio'}, - scripts = ['python/scripts/aubiocut', 'python/scripts/aubio'], ext_modules = [aubio_extension], description = 'a collection of tools for music analysis', long_description = 'a collection of tools for music analysis', @@ -75,6 +74,12 @@ distrib = setup(name='aubio', 'clean': CleanGenerated, 'build_ext': build_ext, }, + entry_points = { + 'console_scripts': [ + 'aubio = aubio.cmd:main', + 'aubiocut = aubio.cut:main', + ], + }, test_suite = 'nose2.collector.collector', extras_require = { 'tests': ['numpy'],