From: Florian Gross Date: 2005-01-16T07:41:10+09:00 Subject: Re: Ogg Vorbis 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