Reading a Wave File With a Block

The example below shows the preferred way of reading an entire wave file:

require 'wavefile'
include WaveFile 

Reader.new("my_file.wav").each_buffer do |buffer|
  puts "Read #{buffer.samples.length} samples."
end

First construct a Reader object, then call each_buffer() on it. Successive sample buffers will be read from the file and passed to the given block, until all sample data in the file has been read. Finally, the Reader will automatically be closed. (Note that this is essentially the same as how IO.open works if you pass it a block).

Note: When calling each_buffer() (and read()), the number you pass indicates the number of samples to read for each channel. That is, if you call each_buffer(1024) on a stereo/16-bit file, then the returned buffer will contain 1024 samples for the left channel, and 1024 samples for the right channel.

Reading a Wave File (Manual File Closing Edition)

Alternately, you can manually call read() to control exactly how much of the file to read. When doing this, make sure to close the Reader when you're done.

require 'wavefile'
include WaveFile

SAMPLES_PER_BUFFER = 4096

reader = Reader.new("my_file.wav")
begin
  while true do
    buffer = reader.read(SAMPLES_PER_BUFFER)
    puts "Read #{buffer.samples.length} samples."
  end
rescue EOFError
  reader.close
end

Reading a Wave File Into an Arbitrary Format

It's easy to read sample data out of a file in whatever format you need, regardless of what format is used inside the file. For example, suppose that my_file.wav is stereo/16-bit, but you need mono normalized floating point samples. No problem, as shown below.

require 'wavefile'
include WaveFile

# Samples will be read as monophonic floating point,
# regardless of the actual sample format on disk
format = Format.new(:mono, :float, 44100)
reader = Reader.new("my_file.wav", format).each_buffer do |buffer|
  puts "Read #{buffer.samples.length} samples."
end

Getting Metadata About a Wave File

require 'wavefile'
include WaveFile

file_name = ARGV[0]
puts "Metadata for #{file_name}:"

begin
  reader = Reader.new(file_name)

  puts "  Readable by this gem?  #{reader.readable_format? ? 'Yes' : 'No'}"
  puts "  Audio Format:          #{reader.native_format.audio_format}"
  puts "  Channels:              #{reader.native_format.channels}"
  puts "  Bits per sample:       #{reader.native_format.bits_per_sample}"
  puts "  Samples per second:    #{reader.native_format.sample_rate}"
  puts "  Bytes per second:      #{reader.native_format.byte_rate}"
  puts "  Block align:           #{reader.native_format.block_align}"
  puts "  Sample frame count:    #{reader.total_sample_frames}"

  duration = reader.total_duration
  formatted_duration = duration.hours.to_s.rjust(2, "0") << ":" <<
                       duration.minutes.to_s.rjust(2, "0") << ":" <<
                       duration.seconds.to_s.rjust(2, "0") << ":" <<
                       duration.milliseconds.to_s.rjust(3, "0")
  puts "  Play time:             #{formatted_duration}"
rescue InvalidFormatError
  puts "  Not a valid Wave file!"
end

Writing a Wave File With A Block

The Writer object is used to write data to a wave file. When constructing the Writer, one can pass a block inside which the writing will occur. When the block exits the file will automatically be closed. The example below shows how to write a basic square wave to a file.

require 'wavefile'
include WaveFile

# Write a 440Hz square wave beep
cycle = ([0.5] * 50) + ([-0.5] * 50)
buffer = Buffer.new(cycle, Format.new(:mono, :float, 44100))
Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100)) do |writer|
  100.times { writer.write(buffer) }
end

Writing a Wave File (Manual File Close Edition)

Alternately, one can manually control when to close the file, as shown below. Note that a file won't be valid for playback until it is closed. This is because some data required for playback (such as the total number of samples) isn't written to the file until it is closed.

require 'wavefile'
include WaveFile

writer = Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100))

# Write a 440Hz square wave beep
cycle = ([0.5] * 50) + ([-0.5] * 50)
buffer = Buffer.new(cycle, Format.new(:mono, :float, 44100))
100.times do
  writer.write(buffer)
end

writer.close

Copying a Wave File to Different Format

In this example, the sample data in the file original.wav will be written to copy.wav as stereo/16-bit with a 44,100Hz sample rate, regardless of what format the sample data in original.wav is stored in.

This example also shows that you can also pass a block to Writer.new(), and the Writer will automatically be closed when the block exits.

require 'wavefile'
include WaveFile

Writer.new("copy.wav", Format.new(:stereo, :pcm_16, 44100)) do |writer|
  Reader.new("original.wav").each_buffer do |buffer|
    writer.write(buffer)
  end
end

Appending Wave Files

This example will take 3 Wave files, and write them to a single file containing each input file played one after another. Note that the individual files can be in different formats.

require 'wavefile'
include WaveFile

FILES_TO_APPEND = ["file1.wav", "file2.wav", "file3.wav"]

Writer.new("append.wav", Format.new(:stereo, :pcm_16, 44100)) do |writer|
  FILES_TO_APPEND.each do |file_name|
    Reader.new(file_name).each_buffer do |buffer|
      writer.write(buffer)
    end
  end
end