class WaveInfo

Constants

VERSION

Attributes

debug[RW]

Public Class Methods

new(file) click to toggle source

Create a new WaveInfo object to get information and metadata about a Wave file (.wav). 'file' can either be a filename or an IO object.

# File lib/waveinfo.rb, line 13
def initialize(file)

  # Set default values
  @audio_format_id = 0
  @bits_per_sample = nil
  @block_align = nil
  @byte_rate = nil
  @channels = nil
  @data_size = nil
  @sample_rate = nil
  @samples = nil

  # What was passed in to us?
  if file.is_a?(String)
    @io = File.new(file, 'rb')
    @filepath = @io.path
    read_headers
    @io.close
  else
    @io = file
    @filepath = @io.path if @io.respond_to?(:path)
    read_headers
  end
end

Public Instance Methods

audio_format() click to toggle source

Get the name of the audio codec (for example 'PCM').

# File lib/waveinfo.rb, line 49
def audio_format
  case @audio_format_id
    when 0x01 then
      "PCM"
    when 0x02 then
      "Microsoft ADPCM"
    when 0x06 then
      "a-law"
    when 0x07 then
      "u-law"
    when 0x11 then
      "IMA ADPCM"
    when 0x14 then
      "G.723"
    when 0x31 then
      "GSM"
    when 0x40 then
      "G.721"
    when 0x50 then
      "MPEG-1 Audio"
    when 0x55 then
      "MPEG Audio Layer 3"
    when 0xFFFE
      "Extensible wave format"
    else
      sprintf("Unknown (0x%2.2x)",@audio_format_id)
  end
end
audio_format_id() click to toggle source

Get the identifier of the audio codec (for example PCM would be 1).

# File lib/waveinfo.rb, line 44
def audio_format_id
  @audio_format_id
end
bits_per_sample() click to toggle source

Get the number of bits per sample.

# File lib/waveinfo.rb, line 99
def bits_per_sample
  @bits_per_sample
end
block_align() click to toggle source

Get the number of bytes per sample slice.

# File lib/waveinfo.rb, line 94
def block_align
  @block_align
end
byte_rate() click to toggle source

Get the average number of bytes per second.

# File lib/waveinfo.rb, line 89
def byte_rate
  @byte_rate
end
channels() click to toggle source

Get the number of channels.

# File lib/waveinfo.rb, line 79
def channels
  @channels
end
duration() click to toggle source

Get the duration of the audio (in seconds).

# File lib/waveinfo.rb, line 120
def duration
  if sample_rate.to_f.nonzero?
    sample_rate ? samples.to_f / sample_rate.to_f : 0.0
  else
    0.0
  end
end
filename() click to toggle source

Return the name of the input file.

# File lib/waveinfo.rb, line 39
def filename
  File.basename(@filepath)
end
sample_rate() click to toggle source

Get the sample rate (in Hz).

# File lib/waveinfo.rb, line 84
def sample_rate
  @sample_rate
end
samples() click to toggle source

Get the total number of samples.

# File lib/waveinfo.rb, line 104
def samples
  if @samples
    @samples
  elsif @block_align.to_f.nonzero?
    @data_size && @block_align ? @data_size / @block_align : 0.0
  else 
    0.0
  end
end
size() click to toggle source

Get the length of the audio data (in bytes).

# File lib/waveinfo.rb, line 115
def size
  @data_size
end

Private Instance Methods

read_ds64_chunk() click to toggle source
# File lib/waveinfo.rb, line 204
def read_ds64_chunk
  subchunk_id = read_fourchar
  raise FileFormatError.new("First sub-chunk of RF64 file is not 'ds64'") unless subchunk_id == 'ds64'
  subchunk_size = read_longint

  riff_size_low = read_longint
  riff_size_high = read_longint
  data_size_low = read_longint
  data_size_high = read_longint
  sample_count_low = read_longint
  sample_count_high = read_longint

  @data_size = (data_size_high << 32) + data_size_low
  @samples = (sample_count_high << 32) + sample_count_low
  @chunk_size = (riff_size_high << 32) + riff_size_low

  # Skip any extra data
  @io.seek(subchunk_size-24,IO::SEEK_CUR) if subchunk_size > 24
end
read_fact_chunk(size) click to toggle source
# File lib/waveinfo.rb, line 193
def read_fact_chunk(size)
  # Read in the number of samples
  sample_count = read_longint
  unless sample_count == 0xFFFFFFFF
    @samples = sample_count
  end

  # Skip any extra data
  @io.seek(size-4,IO::SEEK_CUR) if size > 4
end
read_fmt_chunk(size) click to toggle source
# File lib/waveinfo.rb, line 181
def read_fmt_chunk(size)
  @audio_format_id = read_shortint
  @channels = read_shortint
  @sample_rate = read_longint
  @byte_rate = read_longint
  @block_align = read_shortint
  @bits_per_sample = read_shortint

  # Skip any extra parameters
  @io.seek(size-16,IO::SEEK_CUR) if size > 16
end
read_fourchar() click to toggle source
# File lib/waveinfo.rb, line 224
def read_fourchar
  @io.read(4)
end
read_headers() click to toggle source
# File lib/waveinfo.rb, line 131
def read_headers
  # Read in the chunk header
  @chunk_id = read_fourchar
  @chunk_size = read_longint
  @chunk_format = read_fourchar
  raise FileFormatError.new("Chunk format is not 'WAVE'") unless @chunk_format == 'WAVE'

  if @chunk_id == 'RIFF'
    position = 0xC
  elsif @chunk_id == 'RF64'
    # Next sub-chunk *has* to be a 'ds64'
    subchunk_size = read_ds64_chunk
    position = 0xC + subchunk_size
  else
    raise FileFormatError.new("Primary chunk id is not 'RIFF' or 'RF64'")
  end

  read_subchunks(position)
end
read_longint() click to toggle source
# File lib/waveinfo.rb, line 228
def read_longint
  bytes = @io.read(4)
  bytes ? bytes.unpack('V').first : nil
end
read_shortint() click to toggle source
# File lib/waveinfo.rb, line 233
def read_shortint
  bytes = @io.read(2)
  bytes ? bytes.unpack('v').first : nil
end
read_subchunks(position) click to toggle source
# File lib/waveinfo.rb, line 151
def read_subchunks(position)
  # Read in each of the sub-chunks
  while(@chunk_size - position) > 0 do
    subchunk_id = read_fourchar
    subchunk_size = read_longint
    case subchunk_id
      when 'fmt '
        read_fmt_chunk(subchunk_size)
      when 'fact'
        read_fact_chunk(subchunk_size)
      when 'data'
        unless subchunk_size == 0xFFFFFFFF
        @data_size = subchunk_size
        end
        # Skip over the wave data
        @io.seek(@data_size,IO::SEEK_CUR)
      when nil
        # Give up if read fails
        $stderr.puts "Warning: read error before reaching end of file" if WaveInfo.debug
        break
      else
        pos = sprintf("0x%x", position)
        $stderr.puts "Warning: unsupported sub-chunk at #{pos}: #{subchunk_id}" if WaveInfo.debug
        @io.seek(subchunk_size,IO::SEEK_CUR)
    end
    position += subchunk_size + 8
  end

end