Ogg Vorbis

A

asenchi

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! :)

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.
 
F

Florian Gross

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! :)

[...]

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.
 
A

asenchi

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.

From my limited understanding, the DL library is for Windows? I am in a linux
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...
 
F

Florian Gross

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...
 
F

Florian Gross

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 :eek:pen :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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top