Class: WaveFile::Writer

Inherits:
Object
  • Object
show all
Defined in:
lib/wavefile/writer.rb

Overview

Provides the ability to write data to a wave file.

When a Writer is constructed it can be given a block. All samples should be written inside this block, and when the block exits the file will automatically be closed:

Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100)) do |writer|
  # Write sample data here
end

If no block is given, you’ll need to manually close the Writer when done. The underlaying file will not be valid or playable until close is called.

writer = Writer.new("my_file.wav", Format.new(:mono, :pcm_16, 44100))
# Write sample data here
writer.close

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file_name, format) ⇒ Writer

Returns a constructed Writer object which is available for writing sample data to the specified file (via the write method). When all sample data has been written, the Writer should be closed. Note that the wave file being written to will NOT be valid (and playable in other programs) until the Writer has been closed.

If a block is given to this method, sample data can be written inside the given block. When the block terminates, the Writer will be automatically closed (and no more sample data can be written).

If no block is given, then sample data can be written until the close method is called.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/wavefile/writer.rb', line 28

def initialize(file_name, format)
  @file_name = file_name
  @file = File.open(file_name, "wb")
  @format = format

  @total_sample_frames = 0
  @pack_code = PACK_CODES[format.sample_format][format.bits_per_sample]

  # Note that the correct sizes for the RIFF and data chunks can't be determined
  # until all samples have been written, so this header as written will be incorrect.
  # When close is called, the correct sizes will be re-written.
  write_header(0)

  if block_given?
    begin
      yield(self)
    ensure
      close
    end
  end
end

Instance Attribute Details

#file_nameObject (readonly)

Returns the name of the Wave file that is being written to



112
113
114
# File 'lib/wavefile/writer.rb', line 112

def file_name
  @file_name
end

#formatObject (readonly)

Returns a Format object describing the Wave file being written (number of channels, sample format and bits per sample, sample rate, etc.)



116
117
118
# File 'lib/wavefile/writer.rb', line 116

def format
  @format
end

#total_sample_framesObject (readonly)

Returns the number of samples (per channel) that have been written to the file so far. For example, if 1000 “left” samples and 1000 “right” samples have been written to a stereo file, this will return 1000.



121
122
123
# File 'lib/wavefile/writer.rb', line 121

def total_sample_frames
  @total_sample_frames
end

Instance Method Details

#closeObject

Closes the Writer. After a Writer is closed, no more sample data can be written to it.

Note that the wave file will NOT be valid until this method is called. The wave file format requires certain information about the amount of sample data, and this can’t be determined until all samples have been written. (This method doesn’t need to be called when passing a block to Writer.new, as this method will automatically be called when the block exits).

Returns nothing. Raises IOError if the Writer is already closed.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/wavefile/writer.rb', line 86

def close
  # The RIFF specification requires that each chunk be aligned to an even number of bytes,
  # even if the byte count is an odd number. Therefore if an odd number of bytes has been
  # written, write an empty padding byte.
  #
  # See http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/riffmci.pdf, page 11.
  bytes_written = @total_sample_frames * @format.block_align
  if bytes_written.odd?
    @file.syswrite(EMPTY_BYTE)
  end

  # We can't know what chunk sizes to write for the RIFF and data chunks until all
  # samples have been written, so go back to the beginning of the file and re-write
  # those chunk headers with the correct sizes.
  @file.sysseek(0)
  write_header(@total_sample_frames)

  @file.close
end

#closed?Boolean

Returns true if the Writer is closed, and false if it is open and available for writing.

Returns:

  • (Boolean)


71
72
73
# File 'lib/wavefile/writer.rb', line 71

def closed?
  @file.closed?
end

#total_durationObject

Returns a Duration instance for the number of sample frames that have been written so far



107
108
109
# File 'lib/wavefile/writer.rb', line 107

def total_duration
  Duration.new(@total_sample_frames, @format.sample_rate)
end

#write(buffer) ⇒ Object

Appends the sample data in the given Buffer to the end of the wave file.

Returns the number of sample frames that have been written to the file so far. Raises IOError if the Writer has been closed.



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/wavefile/writer.rb', line 55

def write(buffer)
  samples = buffer.convert(@format).samples

  if @format.bits_per_sample == 24 && @format.sample_format == :pcm
    samples.flatten.each do |sample|
      @file.syswrite([sample].pack("l<X"))
    end
  else
    @file.syswrite(samples.flatten.pack(@pack_code))
  end

  @total_sample_frames += samples.length
end