Ogg Vorbis

Hello All,

      I am looking for Ogg Vorbis support in Ruby. Mostly encode, file
      information and decode.

      There is already one lib out there, ruby-vorbisfile, that does decoding.
      However I want more! :slight_smile:

      I've worked for a little bit on extending this initial work into a more
      thorough lib that supports a wide range of Ogg Vorbis features. However,
      I am not a very good programmer, especially when it comes to extending
      Ruby with C. I get some of the ideas, but is a bit over my head. I've
      been using the ldap library as a reference, simply b/c it is quite
      extensive in its support.

      Mostly what I've been trying to do is create Ruby libs out of
      vorbis/vorbisfile.h (already done, now integrating that with)
      vorbis/vorbisenc.h and any other features.

      Is there anyone willing to work with me on this? Granted, I can offer
      some help, but I would need some tutoring in moving forward. I really
      would like to learn more, but need help in grasping some of the ideas.

      I understand this wouldn't be a top priority for most, but it might be a
      good project.

      If you would like to help me out, and are patient with a n00b, please
      let me know. Thank you.

···

--
<<< Asenchi >>>

asenchi@asenchi.com wrote:

Hello All,

Moin.

      I am looking for Ogg Vorbis support in Ruby. Mostly encode, file
      information and decode.

      There is already one lib out there, ruby-vorbisfile, that does decoding.
      However I want more! :slight_smile:

[...]

      Mostly what I've been trying to do is create Ruby libs out of
      vorbis/vorbisfile.h (already done, now integrating that with)
      vorbis/vorbisenc.h and any other features.

Did you have a look at Ruby/DL already? IMHO using it is very frequently easier than writing a C extension.

Maybe it would also be an option to just drive external applications that already do those jobs well via the command line.

Florian Gross spewed:

Moin.

Did you have a look at Ruby/DL already? IMHO using it is very frequently
easier than writing a C extension.

environment.

Maybe it would also be an option to just drive external applications
that already do those jobs well via the command line.

I've consider using external apps, but I would really like the comment
manipluation to happen in Ruby. I figure if I start there, why not move
forward and get encoding ability...

···

From my limited understanding, the DL library is for Windows? I am in a linux

--
<<< Asenchi >>>

asenchi@asenchi.com wrote:

From my limited understanding, the DL library is for Windows? I am in a linux
environment.

Works generally everywhere. It can be used for WinAPI or for writing bindings to C libraries etc. It even comes bundled with Ruby.

I think manipulating the OGG tags ought to be possible fairly easily in pure Ruby. (If I'm not totally wrong about the format it should just be simple .pack and .unpack and maybe computing checksums.)

Encoding and decoding OTOH sounds like something that would need C bindings to work nicely...

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

0..2, surely

martin

···

Florian Gross <flgr@ccan.de> wrote:

>
> module PacketTypes
> Identification, Comment, Setup = *1 .. 2