require 'audio' require 'sndfile.so' module Audio # libsndfile[http://www.mega-nerd.com/libsndfile/] # # = Synopsis # require 'audio/sndfile' # # Audio::Soundfile.open('chunky_bacon.wav') do |sf| # sound = sf.readf_float(sf.frames) # puts "Maximum amplitude: #{sound.abs.max}" # end # # = Details # Refer to the libsndfile api[http://www.mega-nerd.com/libsndfile/api.html]. # # Usage is quite straightforward: drop the +sf_+ prefix, omit the # SNDFILE* parameter, and use Sound or Numeric instead of # (pointer, size) pairs. So, if you have a Soundfile object named +sf+, then # sf_read_float(SNDFILE, float *ptr, sf_count_t items) # becomes # buf = Sound.float(items) # sf.read_float(buf) # or # buf = sf.read_float(items) # creates a new Sound # # Exceptions to this pattern are documented below. # # Constants are accessed as Soundfile::SF_FORMAT_WAV class Soundfile # SF_INFO attr :info attr_reader :mode # mode:: One of %w{r w rw} # info:: Instance of SF_INFO or nil def initialize(path, mode='r', info=nil) if info.nil? info = SF_INFO.new end modes = {:r => SFM_READ, :w => SFM_WRITE, :rw => SFM_RDWR} unless Numeric === mode mode = modes[mode.to_sym] end unless [SFM_READ, SFM_WRITE, SFM_RDWR].include? mode raise ArgumentError, "Invalid mode" end sf = Sndfile.sf_open(path.to_s, mode, info) @sf = sf @info = info @mode = modes.invert[mode] if block_given? yield self self.close end end class << self alias_method :open, :new end def frames @info.frames end def samplerate @info.samplerate end def channels @info.channels end def format @info.format end def sections @info.sections end def seekable @info.seekable end # The following are equivalent: # sf_format_check(info) /* C */ # sf.format_check # ruby def format_check Sndfile.sf_format_check(@info) end TYPES = [nil,:char,:short,:int,:float,:double] #:nodoc: # Automagic read method. Type is autodetected. def read(na) sym = "read_#{TYPES[na.typecode]}".to_sym self.send sym, na end # Automagic write method. Type is autodetected. def write(na) sym = "write_#{TYPES[na.typecode]}".to_sym self.send sym, na, na.size end %w{read readf}.each do |r| %w{short int float double}.each do |t| tc = TYPES.index(t.to_sym) c = ', channels' if r == 'readf' cmd = "#{r}_#{t}" eval <<-EOF def #{cmd}(arg) if Numeric === arg na = NArray.new(#{tc}, arg#{c}) n = Sndfile.sf_#{cmd}(@sf, na) Sound.deinterleave(na, channels) else na = arg n = Sndfile.sf_#{cmd}(@sf, na) na.deinterleave!(channels) n end end EOF end end %w{write writef}.each do |w| %w{short int float double}.each do |t| cmd = "#{w}_#{t}" eval <<-EOF def #{cmd}(sound) raise "Format check failed: \#{perror}" unless format_check Sndfile.sf_#{cmd}(@sf, sound.interleave) end EOF end end def method_missing(name, *args) #:nodoc: begin Sndfile.send "sf_#{name}".to_sym, @sf, *args rescue NameError super end end def self.const_missing(sym) #:nodoc: begin Sndfile.const_get(sym) rescue NameError super end end end end