Skip to content

Instantly share code, notes, and snippets.

@xavriley
Last active April 27, 2016 20:14
Show Gist options
  • Save xavriley/68ce8a7f1b79d6737ac8c78d65c852ed to your computer and use it in GitHub Desktop.
Save xavriley/68ce8a7f1b79d6737ac8c78d65c852ed to your computer and use it in GitHub Desktop.
Ruby binding for aubio (work in progress)

Ruby bindings for Aubio

Prerequisites

  • OSX
  • homebrew
  • Sonic Pi (for example to work)

Setup

$ brew install aubio --with-libsndfile --with-fftw --with-libsamplerate
$ gem install ffi
$ ruby aubio.rb

This is ripped off from the work already done by @grayleonard in the node-aubio repo https://github.com/aubio/node-aubio

require 'ffi'
module Aubio
extend FFI::Library
ffi_lib '/usr/local/Cellar/aubio/0.4.2/lib/libaubio.4.2.2.dylib'
# tempo
attach_function :new_aubio_tempo, [ :string, :int, :int, :int ], :pointer
attach_function :aubio_tempo_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_tempo_get_last, [:pointer], :int
attach_function :aubio_tempo_get_last_s, [:pointer], :float
attach_function :aubio_tempo_get_last_ms, [:pointer], :float
attach_function :aubio_tempo_set_silence, [:pointer, :float], :int
attach_function :aubio_tempo_get_silence, [:pointer], :float
attach_function :aubio_tempo_set_threshold, [:pointer, :float], :int
attach_function :aubio_tempo_get_threshold, [:pointer], :float
attach_function :aubio_tempo_get_bpm, [:pointer], :float
attach_function :aubio_tempo_get_confidence, [:pointer], :float
attach_function :del_aubio_tempo, [:pointer], :void
# beattracking / misc
attach_function :new_aubio_beattracking, [:int, :int, :int], :pointer
attach_function :aubio_beattracking_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_beattracking_get_bpm, [:pointer], :float
attach_function :aubio_filter_do, [:pointer, :pointer], :void
attach_function :new_aubio_filter_a_weighting, [:int], :pointer
# source
attach_function :new_aubio_source, [:string, :int, :int], :pointer
attach_function :aubio_source_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_source_do_multi, [:pointer, :pointer, :pointer], :void
attach_function :aubio_source_get_samplerate, [:pointer], :int
attach_function :aubio_source_get_channels, [:pointer], :int
attach_function :aubio_source_seek, [:pointer, :int], :int
attach_function :aubio_source_close, [:pointer], :int
attach_function :del_aubio_source, [:pointer], :void
# sink
attach_function :new_aubio_sink, [:string, :int], :pointer
attach_function :aubio_sink_preset_samplerate, [:pointer, :int], :void
attach_function :aubio_sink_preset_channels, [:pointer, :int], :void
attach_function :aubio_sink_get_samplerate, [:pointer], :int
attach_function :aubio_sink_get_channels, [:pointer], :int
attach_function :aubio_sink_do, [:pointer, :pointer, :int], :void
attach_function :aubio_sink_do_multi, [:pointer, :pointer, :int], :void
attach_function :aubio_sink_close, [:pointer], :int
attach_function :del_aubio_sink, [:pointer], :void
# onset
attach_function :new_aubio_onset, [:string, :int, :int, :int], :pointer
attach_function :aubio_onset_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_onset_get_last, [:pointer], :int
attach_function :aubio_onset_get_last_s, [:pointer], :float
attach_function :aubio_onset_get_last_ms, [:pointer], :float
attach_function :aubio_onset_set_silence, [:pointer, :float], :int
attach_function :aubio_onset_get_silence, [:pointer], :float
attach_function :aubio_onset_get_descriptor, [:pointer], :float
attach_function :aubio_onset_get_thresholded_descriptor, [:pointer], :float
attach_function :aubio_onset_set_threshold, [:pointer, :float], :int
attach_function :aubio_onset_set_minioi, [:pointer, :int], :int
attach_function :aubio_onset_set_minioi_s, [:pointer, :int], :int
attach_function :aubio_onset_set_minioi_ms, [:pointer, :float], :int
attach_function :aubio_onset_set_delay, [:pointer, :int], :int
attach_function :aubio_onset_set_delay_s, [:pointer, :int], :int
attach_function :aubio_onset_set_delay_ms, [:pointer, :float], :int
attach_function :aubio_onset_get_minioi, [:pointer], :int
attach_function :aubio_onset_get_minioi_s, [:pointer], :float
attach_function :aubio_onset_get_minioi_ms, [:pointer], :float
attach_function :aubio_onset_get_delay, [:pointer], :int
attach_function :aubio_onset_get_delay_s, [:pointer], :float
attach_function :aubio_onset_get_delay_ms, [:pointer], :float
attach_function :aubio_onset_get_threshold, [:pointer], :float
attach_function :del_aubio_onset, [:pointer], :void
# pitch
attach_function :new_aubio_pitch, [:string, :int, :int, :int], :pointer
attach_function :aubio_pitch_do, [:pointer, :pointer, :pointer], :void
attach_function :aubio_pitch_set_tolerance, [:pointer, :int], :int
attach_function :aubio_pitch_set_unit, [:pointer, :string], :int
attach_function :aubio_pitch_set_silence, [:pointer, :float], :int
attach_function :aubio_pitch_get_silence, [:pointer], :float
attach_function :aubio_pitch_get_confidence, [:pointer], :float
attach_function :del_aubio_pitch, [:pointer], :void
# new fvec
attach_function :new_fvec, [:int], :pointer
attach_function :del_fvec, [:pointer], :void
attach_function :fvec_get_sample, [:pointer, :int], :float
attach_function :fvec_set_sample, [:pointer, :float, :int], :void
attach_function :fvec_get_data, [:pointer], :float
attach_function :fvec_print, [:pointer], :void
attach_function :fvec_set_all, [:pointer, :float], :void
attach_function :fvec_zeros, [:pointer], :void
attach_function :fvec_rev, [:pointer], :void
attach_function :fvec_weight, [:pointer, :pointer], :void
attach_function :fvec_copy, [:pointer, :pointer], :void
attach_function :fvec_ones, [:pointer], :void
def self.onsets(path, params={})
sample_rate = params[:sample_rate] || 44100
window_size = params[:window_size] || 1024
hop_size = params[:hop_size] || 512
# parser.add_option("-O","--onset-method",
# action="store", dest="onset_method", default='default',
# metavar = "<onset_method>",
# help="onset detection method [default=default] \
# complexdomain|hfc|phase|specdiff|energy|kl|mkl")
onset_method = params[:onset_method] || "default"
# # cutting methods
# parser.add_option("-b","--beat",
# action="store_true", dest="beat", default=False,
# help="use beat locations")
beat = params[:beat] || false
# """
# parser.add_option("-S","--silencecut",
# action="store_true", dest="silencecut", default=False,
# help="use silence locations")
silencecut = params[:silencecut] || false
# parser.add_option("-s","--silence",
# metavar = "<value>",
# action="store", dest="silence", default=-70,
# help="silence threshold [default=-70]")
silence = params[:silence] || -70
# """
# # algorithm parameters
# parser.add_option("-r", "--samplerate",
# metavar = "<freq>", 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 = "<size>", type='int',
# help="buffer size [default=512]")
# parser.add_option("-H","--hopsize",
# metavar = "<size>", type='int',
# action="store", dest="hopsize", default=256,
# help="overlap size [default=256]")
# parser.add_option("-t","--onset-threshold",
# metavar = "<value>", 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 = "<value>", 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 = "<seconds>", default=0,
# help="number of seconds to take back [default=system]\
# default system delay is 3*hopsize/samplerate")
# parser.add_option("-C","--dcthreshold",
# metavar = "<value>",
# 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 = "<value>",
# 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 = "<size>",
# action="store", dest="xsize", default=1.,
# type='float', help="define xsize for plot")
# parser.add_option("-y","--ysize",
# metavar = "<size>",
# 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 = "<output_image>",
# 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 = "<outputdir>",
# 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 = "<samples>",
# 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-until-nslices", type = int,
# metavar = "<slices>",
# 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
source = new_aubio_source(path, sample_rate, hop_size)
onset = new_aubio_onset("default", 512, hop_size)
aubio_onset_set_minioi_ms(onset, 12.0)
aubio_onset_set_threshold(onset, 0.3)
timestamps = []
total_frames = 0
# create output for source
samples = new_fvec(hop_size)
total_frames_counter = 0
tmp_read = FFI::MemoryPointer.new(:int)
loop do
aubio_source_do(source, samples, tmp_read)
end
# 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_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)
end
def self.get_features(path, params={})
sample_rate = params[:sample_rate] || 44100
window_size = params[:window_size] || 1024
hop_size = params[:hop_size] || 512
source = new_aubio_source(path, sample_rate, hop_size)
calculated_sample_rate = aubio_source_get_samplerate(source)
puts "samplerate: #{calculated_sample_rate}"
onset = new_aubio_onset('default', window_size, hop_size, sample_rate)
aubio_onset_set_minioi_ms(onset, 12.0)
aubio_onset_set_threshold(onset, 0.3)
onsets = []
pitch = new_aubio_pitch('default', window_size, hop_size, sample_rate)
aubio_pitch_set_unit(pitch, 'Hz')
# create output for source
samples = new_fvec(hop_size)
# create output for pitch and beat
out_fvec = new_fvec(1)
total_frames_counter = 0
tmp_read = FFI::MemoryPointer.new(:int)
loop do
aubio_source_do(source, samples, tmp_read)
aubio_pitch_do(pitch, samples, out_fvec)
cur_time = total_frames_counter / sample_rate
last_pitch = fvec_get_sample(out_fvec, 0);
#puts "pitch at #{cur_time} seconds: #{last_pitch} Hz"
aubio_onset_do(onset, samples, out_fvec)
is_onset = fvec_get_sample(out_fvec, 0)
if is_onset > 0.0
last_onset = aubio_onset_get_last_s(onset)
onsets << last_onset
puts "onset at #{last_onset}"
end
read = tmp_read.read_int
total_frames_counter += read
if(read != hop_size) then
break
end
end
cur_time = total_frames_counter.to_f / sample_rate;
puts "total time : #{cur_time} seconds (#{total_frames_counter} frames)"
puts "found #{onsets.length} onsets total"
# cleanup
del_aubio_source(source);
del_aubio_onset(onset);
del_aubio_pitch(pitch);
onsets
end
# # change comments, swap args, convert to sym
# intPtr = 'int'
# stringPtr = "string" #ref.refType(ref.types.CString);
# {
# "aubio_tempo_do": [ "void", [ "pointer", "pointer", "pointer"]],
# "aubio_tempo_get_last": [ "int", ["pointer"]],
# "aubio_tempo_get_last_s": [ "float", ["pointer"]],
# "aubio_tempo_get_last_ms": [ "float", ["pointer"]],
# "aubio_tempo_set_silence": [ "int", ["pointer", "float"]],
# "aubio_tempo_get_silence": [ "float", ["pointer"]],
# "aubio_tempo_set_threshold": [ "int", ["pointer", "float"]],
# "aubio_tempo_get_threshold": [ "float", ["pointer"]],
# "aubio_tempo_get_bpm": [ "float", ["pointer"]],
# "aubio_tempo_get_confidence": [ "float", ["pointer"]],
# "del_aubio_tempo": [ "void", ["pointer"]],
# # beattracking / misc
# "new_aubio_beattracking": [ "pointer", [ "int", "int", "int"]],
# "aubio_beattracking_do": [ "void", [ "pointer", "pointer", "pointer"]],
# "aubio_beattracking_get_bpm": [ "float", ["pointer"]],
# "aubio_filter_do": [ "void", [ "pointer", "pointer" ]],
# "new_aubio_filter_a_weighting": [ "pointer", [ "int" ]],
# # source
# "new_aubio_source": [ "pointer", [ "string", "int", "int" ]],
# "aubio_source_do": [ "void", [ "pointer", "pointer", intPtr ]],
# "aubio_source_do_multi": [ "void", [ "pointer", "pointer", intPtr ]],
# "aubio_source_get_samplerate": [ "int", [ "pointer" ]],
# "aubio_source_get_channels": [ "int", [ "pointer" ]],
# "aubio_source_seek": [ "int", [ "pointer", "int" ]],
# "aubio_source_close": [ "int", [ "pointer" ]],
# "del_aubio_source": [ "void", [ "pointer" ]],
# # sink
# "new_aubio_sink": [ "pointer", [ "string", "int" ]],
# "aubio_sink_preset_samplerate": [ "void", [ "pointer", "int" ]],
# "aubio_sink_preset_channels": [ "void", [ "pointer", "int" ]],
# "aubio_sink_get_samplerate": [ "int", [ "pointer" ]],
# "aubio_sink_get_channels": [ "int", [ "pointer" ]],
# "aubio_sink_do": ["void", ["pointer", "pointer", "int"]],
# "aubio_sink_do_multi": ["void", ["pointer", "pointer", "int"]],
# "aubio_sink_close": [ "int", [ "pointer" ]],
# "del_aubio_sink": [ "void", [ "pointer" ]],
# # onset
# "new_aubio_onset": [ "pointer", [ "string", "int", "int", "int"]],
# "aubio_onset_do": [ "void", [ "pointer", "pointer", "pointer"]],
# "aubio_onset_get_last": [ "int", ["pointer"]],
# "aubio_onset_get_last_s": [ "float", ["pointer"]],
# "aubio_onset_get_last_ms": [ "float", ["pointer"]],
# "aubio_onset_set_silence": [ "int", ["pointer", "float"]],
# "aubio_onset_get_silence": [ "float", ["pointer"]],
# "aubio_onset_get_descriptor": [ "float", ["pointer"]],
# "aubio_onset_get_thresholded_descriptor": [ "float", ["pointer"]],
# "aubio_onset_set_threshold": [ "int", ["pointer", "float"]],
# "aubio_onset_set_minioi": [ "int", ["pointer", "int"]],
# "aubio_onset_set_minioi_s": [ "int", ["pointer", "int"]],
# "aubio_onset_set_minioi_ms": [ "int", ["pointer", "float"]],
# "aubio_onset_set_delay": [ "int", ["pointer", "int"]],
# "aubio_onset_set_delay_s": [ "int", ["pointer", "int"]],
# "aubio_onset_set_delay_ms": [ "int", ["pointer", "float"]],
# "aubio_onset_get_minioi": [ "int", ["pointer"]],
# "aubio_onset_get_minioi_s": [ "float", ["pointer"]],
# "aubio_onset_get_minioi_ms": [ "float", ["pointer"]],
# "aubio_onset_get_delay": [ "int", ["pointer"]],
# "aubio_onset_get_delay_s": [ "float", ["pointer"]],
# "aubio_onset_get_delay_ms": [ "float", ["pointer"]],
# "aubio_onset_get_threshold": [ "float", ["pointer"]],
# "del_aubio_onset": [ "void", ["pointer"]],
# # pitch
# "new_aubio_pitch": [ "pointer", [ "string", "int", "int", "int"]],
# "aubio_pitch_do": ["void", ["pointer", "pointer", "pointer"]],
# "aubio_pitch_set_tolerance": [ "int", ["pointer", "int"]],
# "aubio_pitch_set_unit": ["int", ["pointer", "string"]],
# "aubio_pitch_set_silence": ["int", ["pointer", "float"]],
# "aubio_pitch_get_silence": ["float", ["pointer"]],
# "aubio_pitch_get_confidence": ["float", ["pointer"]],
# "del_aubio_pitch": [ "void", ["pointer"]],
# # fvec
# "new_fvec": [ "pointer", [ "int" ]],
# "del_fvec": [ "void", [ "pointer" ]],
# "fvec_get_sample": [ "float", [ "pointer", "int" ]],
# "fvec_set_sample": [ "void", [ "pointer", "float", "int" ]],
# "fvec_get_data": [ "float", [ "pointer" ]],
# "fvec_print": [ "void", [ "pointer" ]],
# "fvec_set_all": [ "void", [ "pointer", "float" ]],
# "fvec_zeros": [ "void", [ "pointer" ]],
# "fvec_rev": [ "void", [ "pointer" ]],
# "fvec_weight": [ "void", [ "pointer", "pointer" ]],
# "fvec_copy": [ "void", [ "pointer", "pointer" ]],
# "fvec_ones": [ "void", [ "pointer" ]],
# }.each do |k,v|
# puts "attach_function :#{k.to_sym}, #{v.last.map(&:to_sym)}, :#{v.first.to_sym}"
# end
end
puts Aubio.get_features("/Applications/Sonic Pi.app/etc/samples/loop_amen.flac", hop_size: 256, sample_rate: 44100).inspect
AUBIO ERROR: source_apple_audio: Failed opening /Applications/Sonic Pi.app/etc/samples/loop_amen.flac, error in ExtAudioFileOpenURL ('typ?')
samplerate: 44100
onset at 0.0
onset at 0.2196371853351593
onset at 0.4431746006011963
onset at 0.6746258735656738
onset at 0.7988208532333374
onset at 0.8954194784164429
onset at 1.0019954442977905
onset at 1.102018117904663
onset at 1.3155782222747803
onset at 1.5457369089126587
onset at 1.6655101776123047
total time : 1.753310657596372 seconds (77321 frames)
found 11 onsets total
[0.0, 0.2196371853351593, 0.4431746006011963, 0.6746258735656738, 0.7988208532333374, 0.8954194784164429, 1.0019954442977905, 1.102018117904663, 1.3155782222747803, 1.5457369089126587, 1.6655101776123047]
onsets = [0.0, 0.2196371853351593, 0.4431746006011963, 0.6746258735656738, 0.7988208532333374, 0.8954194784164429, 1.0019954442977905, 1.102018117904663, 1.3155782222747803, 1.5457369089126587, 1.6655101776123047, sample_duration(:loop_amen)]
scaled_onsets = onsets.map {|o| o / sample_duration(:loop_amen) }
pattern = scaled_onsets.each_cons(2).to_a.shuffle.shuffle
live_loop :foo do
pattern.each.with_index do |(s,f), i|
sample :bd_haus if (i % 4) == 0
sample :loop_amen, start: s, finish: f
sleep (f-s) * 2
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment