Class: Riff::Builder

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

Overview

A Builder allows you to create a ‘de novo’ RIFF file recursively, similar in style to the XML Builder class. The client uses Builder.create to create an empty file at a given filesystem location, and yields a block, allowing the client to construct chunks and LISTs semantically with Ruby blocks.

In the following example, a new .wav file is created containing 1 second of 1 K tone and descriptive metadata.

require 'lib/riff/reader.rb'
require 'lib/riff/writer.rb'

include Riff
SAMPLES_PER_SECOND = 48000
FREQ = 1000
AMPLITUDE = 0x0fff

Builder.create(ARGV[0],"WAVE") do |w|
  w.list("INFO") { |info|
    info.chunk("ICOP") {|c|  # "copyright"
      c << "(c) 2007 Joe User.
    }
    info.chunk("INAM") {|c|  # "name" or "title"
      c << "1K tone with metadata"
    }
    info.chunk("ISFT") {|c|  # "software"
      c << "libRiff v" + "%i.%i" % [Riff::VERSION::MAJOR, Riff::VERSION::MINOR] 
    }
    info.chunk("IART") {|c|  # "artist" (maps to kMDItemAuthors in Spotlight)
      c << "Joe User"
    }
  }
  w.chunk("fmt ") { |c|
    c.putc(1);c.putc(0) #format 1, PCM
    c.putc(1);c.putc(0) #1 channel

    # samples per second
    Builder.int_to_four_little_endian_bytes(SAMPLES_PER_SECOND).each {|byte| c.putc(byte)}

    # avg. bytes per second
    Builder.int_to_four_little_endian_bytes(SAMPLES_PER_SECOND * 2).each {|byte| c.putc(byte)}
    c.putc(2);c.putc(0) #block alignment 2
    c.putc(16);c.putc(0) #16 bits per sample
  }
  w.chunk("data") { |c|
    factor = Math::PI / (SAMPLES_PER_SECOND / (FREQ * 2))
    1.upto(SAMPLES_PER_SECOND) do |i|
      val = ( Math::sin(  i * factor ) * AMPLITUDE ).floor
      Builder::int_to_four_little_endian_bytes(val)[0,2].each {|byte| c.putc(byte)}
    end
  }
end

Defined Under Namespace

Classes: IOProxy

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Builder

:nodoc:



151
152
153
# File 'lib/riff/writer.rb', line 151

def initialize(stream) # :nodoc:
  @io = stream
end

Class Method Details

.create(path, type) ⇒ Object

Create a new RIFF file at path, using type for the top-level chunk’s type or signature.

At this time, you must provide a block to this method to add chunks.

Raises:

  • (RuntimeError)


114
115
116
117
118
119
120
121
122
# File 'lib/riff/writer.rb', line 114

def create(path,type) #:yields: builder
  raise RuntimeError unless block_given?
  file = File.open(path,"w")
  w = Builder.new(file)
  w.container('RIFF',type) do |body_io|
    yield Builder.new(body_io)
  end
  file.close
end

Instance Method Details

#chunk(fourcc) {|IOProxy.new(@io)| ... } ⇒ Object

Add a data chunk to the Builder. The method yields a write-only pseudo-IO object that you write the chunk data to. Only write the payload of the chunk here; the fourcc and size will be written for you. Do not pad the data you write to an even boundary, either.

Yields:



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/riff/writer.rb', line 130

def chunk(fourcc) #:yields: stream
  @io.write( Util::sanitize_fourcc(fourcc) )
  4.times { @io.putc(0) }
  oldPos = @io.pos
  yield IOProxy.new(@io)
  length = @io.pos - oldPos
  @io.putc(0) if (length % 2) == 1
  @io.seek(oldPos-4,IO::SEEK_SET)
  bytes = Util::int_to_four_little_endian_bytes(length)
  bytes.each {|byte| @io.putc(byte)}
  @io.seek(0,IO::SEEK_END)
end

#container(fourcc, type) ⇒ Object

:nodoc:



155
156
157
158
159
160
# File 'lib/riff/writer.rb', line 155

def container(fourcc,type) # :nodoc:
  chunk(Util::sanitize_fourcc(fourcc)) do |io|
    io.write(Util::sanitize_fourcc(type))
    yield io
  end
end

#list(type) ⇒ Object

Add a RIFF “LIST” chunk. The method yields a builder, which you can add chunks and other lists to.



145
146
147
148
149
# File 'lib/riff/writer.rb', line 145

def list(type) #:yields: builder
  container("LIST",type) do |io|
    yield Builder.new(io)
  end
end