Florian Gross
1/15/2005 10:39:00 PM
asenchi@asenchi.com wrote:
> I've consider using external apps, but I would really like the comment
> manipluation to happen in Ruby.
See below. (I could not attach this because my news server complained
about it being an attachment, sorry.) It should be a good starting point...
It would probably be a good idea to refactor the repetitive parts into
private methods and being able to actually manipulate the data would
also be nifty.
----
> class OggFile
> class OggError < IOError; end
> class MagicError < OggError; end
> class VersionError < OggError; end
> class PageTypeError < OggError; end
> class PageOrderError < OggError; end
> class PacketTypeError < OggError; end
> class BlocksizeError < OggError; end
> class ValueError < OggError; end
>
> class << self
> alias :open :new
> end
>
> module HeaderFlags
> Continued, First, Last = *(0 .. 2).map { |i| 1 << i }
> end
>
> module PacketTypes
> Identification, Comment, Setup = *1 .. 2
> end
>
> AllowedBlocksizes = [64, 128, 256, 512, 1024, 2048, 4096, 8192]
>
> attr_accessor :vorbis_version, :audio_channels, :audio_sample_rate,
> :bitrate_maximum, :bitrate_nominal, :bitrate_minimum, :blocksize_0,
> :blocksize_1, :comments
>
> def initialize(filename)
> data = File.open(filename, "rb") { |file| file.read }
>
> magic, version, header_type, granule_position, serial_number,
> page_number, checksum, segment_size, rest = *data.unpack("a4CCQLLLCa*")
>
> raise(MagicError, "File magic bytes are not 'OggS'") if magic != "OggS"
> raise(VersionError, "Unknown structure version") if version != 0
> raise(PageOrderError, "Out of order page") if page_number != 0
> # Checksum is ignored for now
>
> if (header_type & HeaderFlags::First).zero? then
> raise(PageTypeError, "Unexpected non-first page at file beginning")
> end
>
> # Rest of initial page, identification header
> segment, id_packet_type, id_magic, @vorbis_version, @audio_channels,
> @audio_sample_rate, @bitrate_maximum, @bitrate_nominal, @bitrate_minimum,
> blocksize, framing_bit, rest = *rest.unpack(
> "a#{segment_size}Ca6LCLlllCCa*")
>
> @blocksize_0 = 2 ** (blocksize & 0b1111) # first 4 bits are 2 exponent
> @blocksize_1 = 2 ** (blocksize >> 4) # last 4 bits are 2 exponent
>
> #if id_packet_type != PacketTypes::Identification then
> # raise(PacketTypeError, "Out of order packet. Expected Identification, " +
> # "but got #{id_packet_type}")
> #end
>
> if id_magic != "vorbis" then
> raise(MagicError, "Header magic needs to be 'vorbis'")
> end
>
> raise(VersionError, "Unknown vorbis version") if @vorbis_version != 0
> raise(ValueError, "Audio channels need to be > 0") if @audio_channels == 0
>
> if @audio_sample_rate == 0 then
> raise(ValueError, "Audio sample rate needs to be > 0")
> end
>
> [@blocksize_0, @blocksize_1].each do |bs|
> unless AllowedBlocksizes.include?(bs)
> raise(ValueError, "Unallowed blocksize #{bs}")
> end
> end
>
> if @blocksize_0 > @blocksize_1 then
> raise(ValueError, "Blocksize 0 has to be > blocksize 1")
> end
>
> raise(ValueError, "Framing bit has to be non-zero") if framing_bit.zero?
>
> # Second page
> magic, version, header_type, granule_position, serial_number,
> page_number, checksum, segment_size, rest = *rest.unpack("a4CCQLLLCa*")
>
> raise(MagicError, "File magic bytes are not 'OggS'") if magic != "OggS"
> raise(VersionError, "Unknown structure version") if version != 0
> raise(PageOrderError, "Out of order page") if page_number != 1
> # Checksum is ignored for now
>
> # Rest of second page, comment header
> segment, cmt_packet_type, cmt_magic, vendor_length, rest = *rest.unpack(
> "a#{segment_size}Ca6LA*")
>
> #if cmt_packet_type != PacketTypes::Comment then
> # raise(PacketTypeError, "Out of order packet. Expected Comment, " +
> # "but got #{id_packet_type}")
> #end
>
> if cmt_magic != "vorbis" then
> raise(MagicError, "Header magic needs to be 'vorbis'")
> end
>
> vendor, comment_count, rest = *rest.unpack("a#{vendor_length}LA*")
>
> @comments = Hash.new { |hash, key| hash[key] = Array.new }
> comment_count.times do
> length, rest = *rest.unpack("La*")
> string, rest = *rest.unpack("a#{length}a*")
> key, value = string.split("=", 2)
> @comments[key.upcase] << value
> end
>
> framing_bit, rest = *rest.unpack("Ca*")
>
> raise(ValueError, "Framing bit has to be non-zero") if framing_bit.zero?
> end
> end