forked from len0rd/rockbox
use tag cache
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9734 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
f9bfd73a24
commit
3215ed2e85
2 changed files with 0 additions and 2855 deletions
2123
tools/songdb.pl
2123
tools/songdb.pl
File diff suppressed because it is too large
Load diff
|
@ -1,732 +0,0 @@
|
||||||
#############################################################################
|
|
||||||
# This is
|
|
||||||
# http://search.cpan.org/~amolloy/Ogg-Vorbis-Header-PurePerl-0.07/PurePerl.pm
|
|
||||||
# written by Andrew Molloy
|
|
||||||
# Code under GNU GENERAL PUBLIC LICENCE v2
|
|
||||||
# $Id$
|
|
||||||
#############################################################################
|
|
||||||
|
|
||||||
package vorbiscomm;
|
|
||||||
|
|
||||||
use 5.005;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
|
|
||||||
use Fcntl qw/SEEK_END/;
|
|
||||||
|
|
||||||
our $VERSION = '0.07';
|
|
||||||
|
|
||||||
sub new
|
|
||||||
{
|
|
||||||
my $class = shift;
|
|
||||||
my $file = shift;
|
|
||||||
|
|
||||||
return load($class, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub load
|
|
||||||
{
|
|
||||||
my $class = shift;
|
|
||||||
my $file = shift;
|
|
||||||
my $from_new = shift;
|
|
||||||
my %data;
|
|
||||||
my $self;
|
|
||||||
|
|
||||||
# there must be a better way...
|
|
||||||
if ($class eq 'vorbiscomm')
|
|
||||||
{
|
|
||||||
$self = bless \%data, $class;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$self = $class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($self->{'FILE_LOADED'})
|
|
||||||
{
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
$self->{'FILE_LOADED'} = 1;
|
|
||||||
|
|
||||||
# check that the file exists and is readable
|
|
||||||
unless ( -e $file && -r _ )
|
|
||||||
{
|
|
||||||
warn "File does not exist or cannot be read.";
|
|
||||||
# file does not exist, can't do anything
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
# open up the file
|
|
||||||
open FILE, $file;
|
|
||||||
# make sure dos-type systems can handle it...
|
|
||||||
binmode FILE;
|
|
||||||
|
|
||||||
$data{'filename'} = $file;
|
|
||||||
$data{'fileHandle'} = \*FILE;
|
|
||||||
|
|
||||||
if (_init(\%data)) {
|
|
||||||
_loadInfo(\%data);
|
|
||||||
_loadComments(\%data);
|
|
||||||
_calculateTrackLength(\%data);
|
|
||||||
}
|
|
||||||
|
|
||||||
close FILE;
|
|
||||||
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub info
|
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $key = shift;
|
|
||||||
|
|
||||||
# if the user did not supply a key, return the entire hash
|
|
||||||
unless ($key)
|
|
||||||
{
|
|
||||||
return $self->{'INFO'};
|
|
||||||
}
|
|
||||||
|
|
||||||
# otherwise, return the value for the given key
|
|
||||||
return $self->{'INFO'}{lc $key};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub comment_tags
|
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
if ( $self && $self->{'COMMENT_KEYS'} ) {
|
|
||||||
return @{$self->{'COMMENT_KEYS'}};
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub comment
|
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
my $key = shift;
|
|
||||||
|
|
||||||
# if the user supplied key does not exist, return undef
|
|
||||||
unless($self->{'COMMENTS'}{lc $key})
|
|
||||||
{
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
return @{$self->{'COMMENTS'}{lc $key}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub add_comments
|
|
||||||
{
|
|
||||||
warn "Ogg::Vorbis::Header::PurePerl add_comments() unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub edit_comment
|
|
||||||
{
|
|
||||||
warn "Ogg::Vorbis::Header::PurePerl edit_comment() unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub delete_comment
|
|
||||||
{
|
|
||||||
warn "Ogg::Vorbis::Header::PurePerl delete_comment() unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub clear_comments
|
|
||||||
{
|
|
||||||
warn "Ogg::Vorbis::Header::PurePerl clear_comments() unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
sub path
|
|
||||||
{
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
return $self->{'fileName'};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub write_vorbis
|
|
||||||
{
|
|
||||||
warn "Ogg::Vorbis::Header::PurePerl write_vorbis unimplemented.";
|
|
||||||
}
|
|
||||||
|
|
||||||
# "private" methods
|
|
||||||
|
|
||||||
sub _init
|
|
||||||
{
|
|
||||||
my $data = shift;
|
|
||||||
my $fh = $data->{'fileHandle'};
|
|
||||||
my $byteCount = 0;
|
|
||||||
|
|
||||||
# check the header to make sure this is actually an Ogg-Vorbis file
|
|
||||||
$byteCount = _checkHeader($data);
|
|
||||||
|
|
||||||
unless($byteCount)
|
|
||||||
{
|
|
||||||
# if it's not, we can't do anything
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data->{'startInfoHeader'} = $byteCount;
|
|
||||||
return 1; # Success
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _checkHeader
|
|
||||||
{
|
|
||||||
my $data = shift;
|
|
||||||
my $fh = $data->{'fileHandle'};
|
|
||||||
my $buffer;
|
|
||||||
my $pageSegCount;
|
|
||||||
my $byteCount = 0; # stores how far into the file we've read,
|
|
||||||
# so later reads into the file can skip right
|
|
||||||
# past all of the header stuff
|
|
||||||
|
|
||||||
# check that the first four bytes are 'OggS'
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
if ($buffer ne 'OggS')
|
|
||||||
{
|
|
||||||
warn "This is not an Ogg bitstream (no OggS header).";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# check the stream structure version (1 byte, should be 0x00)
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x00)
|
|
||||||
{
|
|
||||||
warn "This is not an Ogg bitstream (invalid structure version).";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# check the header type flag
|
|
||||||
# This is a bitfield, so technically we should check all of the bits
|
|
||||||
# that could potentially be set. However, the only value this should
|
|
||||||
# possibly have at the beginning of a proper Ogg-Vorbis file is 0x02,
|
|
||||||
# so we just check for that. If it's not that, we go on anyway, but
|
|
||||||
# give a warning (this behavior may (should?) be modified in the future.
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x02)
|
|
||||||
{
|
|
||||||
warn "Invalid header type flag (trying to go ahead anyway).";
|
|
||||||
}
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# skip to the page_segments count
|
|
||||||
read($fh, $buffer, 20);
|
|
||||||
$byteCount += 20;
|
|
||||||
# we do nothing with this data
|
|
||||||
|
|
||||||
# read the number of page segments
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$pageSegCount = ord($buffer);
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# read $pageSegCount bytes, then throw 'em out
|
|
||||||
read($fh, $buffer, $pageSegCount);
|
|
||||||
$byteCount += $pageSegCount;
|
|
||||||
|
|
||||||
# check packet type. Should be 0x01 (for indentification header)
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x01)
|
|
||||||
{
|
|
||||||
warn "Wrong vorbis header type, giving up.";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# check that the packet identifies itself as 'vorbis'
|
|
||||||
read($fh, $buffer, 6);
|
|
||||||
if ($buffer ne 'vorbis')
|
|
||||||
{
|
|
||||||
warn "This does not appear to be a vorbis stream, giving up.";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
$byteCount += 6;
|
|
||||||
|
|
||||||
# at this point, we assume the bitstream is valid
|
|
||||||
return $byteCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _loadInfo
|
|
||||||
{
|
|
||||||
my $data = shift;
|
|
||||||
my $start = $data->{'startInfoHeader'};
|
|
||||||
my $fh = $data->{'fileHandle'};
|
|
||||||
my $buffer;
|
|
||||||
my $byteCount = $start;
|
|
||||||
my %info;
|
|
||||||
|
|
||||||
seek $fh, $start, 0;
|
|
||||||
|
|
||||||
# read the vorbis version
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$info{'version'} = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the number of audio channels
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$info{'channels'} = ord($buffer);
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# read the sample rate
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$info{'rate'} = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the bitrate maximum
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$info{'bitrate_upper'} = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the bitrate nominal
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$info{'bitrate_nominal'} = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the bitrate minimal
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$info{'bitrate_lower'} = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the blocksize_0 and blocksize_1
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
# these are each 4 bit fields, whose actual value is 2 to the power
|
|
||||||
# of the value of the field
|
|
||||||
$info{'blocksize_0'} = 2 << ((ord($buffer) & 0xF0) >> 4);
|
|
||||||
$info{'blocksize_1'} = 2 << (ord($buffer) & 0x0F);
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# read the framing_flag
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$info{'framing_flag'} = ord($buffer);
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# bitrate_window is -1 in the current version of vorbisfile
|
|
||||||
$info{'bitrate_window'} = -1;
|
|
||||||
|
|
||||||
$data->{'startCommentHeader'} = $byteCount;
|
|
||||||
|
|
||||||
$data->{'INFO'} = \%info;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _loadComments
|
|
||||||
{
|
|
||||||
my $data = shift;
|
|
||||||
my $fh = $data->{'fileHandle'};
|
|
||||||
my $start = $data->{'startCommentHeader'};
|
|
||||||
my $buffer;
|
|
||||||
my $page_segments;
|
|
||||||
my $vendor_length;
|
|
||||||
my $user_comment_count;
|
|
||||||
my $byteCount = $start;
|
|
||||||
my %comments;
|
|
||||||
|
|
||||||
seek $fh, $start, 0;
|
|
||||||
|
|
||||||
# check that the first four bytes are 'OggS'
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
if ($buffer ne 'OggS')
|
|
||||||
{
|
|
||||||
warn "No comment header?";
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# skip over next ten bytes
|
|
||||||
read($fh, $buffer, 10);
|
|
||||||
$byteCount += 10;
|
|
||||||
|
|
||||||
# read the stream serial number
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
push @{$data->{'commentSerialNumber'}}, _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read the page sequence number (should be 0x01)
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
if (_decodeInt($buffer) != 0x01)
|
|
||||||
{
|
|
||||||
warn "Comment header page sequence number is not 0x01: " +
|
|
||||||
_decodeInt($buffer);
|
|
||||||
warn "Going to keep going anyway.";
|
|
||||||
}
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# and ignore the page checksum for now
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# get the number of entries in the segment_table...
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$page_segments = _decodeInt($buffer);
|
|
||||||
$byteCount += 1;
|
|
||||||
# then skip on past it
|
|
||||||
read($fh, $buffer, $page_segments);
|
|
||||||
$byteCount += $page_segments;
|
|
||||||
|
|
||||||
# check the header type (should be 0x03)
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x03)
|
|
||||||
{
|
|
||||||
warn "Wrong header type: " . ord($buffer);
|
|
||||||
}
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
# now we should see 'vorbis'
|
|
||||||
read($fh, $buffer, 6);
|
|
||||||
if ($buffer ne 'vorbis')
|
|
||||||
{
|
|
||||||
warn "Missing comment header. Should have found 'vorbis', found " .
|
|
||||||
$buffer;
|
|
||||||
}
|
|
||||||
$byteCount += 6;
|
|
||||||
|
|
||||||
# get the vendor length
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$vendor_length = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# read in the vendor
|
|
||||||
read($fh, $buffer, $vendor_length);
|
|
||||||
$comments{'vendor'} = $buffer;
|
|
||||||
$byteCount += $vendor_length;
|
|
||||||
|
|
||||||
# read in the number of user comments
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
$user_comment_count = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
$data->{'COMMENT_KEYS'} = [];
|
|
||||||
|
|
||||||
# finally, read the comments
|
|
||||||
for (my $i = 0; $i < $user_comment_count; $i++)
|
|
||||||
{
|
|
||||||
# first read the length
|
|
||||||
read($fh, $buffer, 4);
|
|
||||||
my $comment_length = _decodeInt($buffer);
|
|
||||||
$byteCount += 4;
|
|
||||||
|
|
||||||
# then the comment itself
|
|
||||||
read($fh, $buffer, $comment_length);
|
|
||||||
$byteCount += $comment_length;
|
|
||||||
|
|
||||||
my ($key) = $buffer =~ /^([^=]+)/;
|
|
||||||
my ($value) = $buffer =~ /=(.*)$/;
|
|
||||||
|
|
||||||
push @{$comments{lc $key}}, $value;
|
|
||||||
push @{$data->{'COMMENT_KEYS'}}, lc $key;
|
|
||||||
}
|
|
||||||
|
|
||||||
# read past the framing_bit
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$byteCount += 1;
|
|
||||||
|
|
||||||
$data->{'INFO'}{'offset'} = $byteCount;
|
|
||||||
|
|
||||||
$data->{'COMMENTS'} = \%comments;
|
|
||||||
|
|
||||||
# Now find the offset of the first page
|
|
||||||
# with audio data.
|
|
||||||
while(_findPage($fh))
|
|
||||||
{
|
|
||||||
$byteCount = tell($fh) - 4;
|
|
||||||
|
|
||||||
# version flag
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x00)
|
|
||||||
{
|
|
||||||
warn "Invalid stream structure version: " .
|
|
||||||
sprintf("%x", ord($buffer));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
# header type flag
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
# Audio data starts as a fresh packet on a new page, so
|
|
||||||
# if header_type is odd it's not a fresh packet
|
|
||||||
next if ( ord($buffer) % 2 );
|
|
||||||
|
|
||||||
# skip past granule position, stream_serial_number,
|
|
||||||
# page_sequence_number, and crc
|
|
||||||
read($fh, $buffer, 20);
|
|
||||||
|
|
||||||
# page_segments
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
my $page_segments = ord($buffer);
|
|
||||||
|
|
||||||
# skip past the segment table
|
|
||||||
read($fh, $buffer, $page_segments);
|
|
||||||
|
|
||||||
# read packet_type byte
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
|
|
||||||
# Not an audio packet. All audio packet numbers are even
|
|
||||||
next if ( ord($buffer) % 2 );
|
|
||||||
|
|
||||||
# Found the first audio packet
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data->{'INFO'}{'audio_offset'} = $byteCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _calculateTrackLength
|
|
||||||
{
|
|
||||||
my $data = shift;
|
|
||||||
my $fh = $data->{'fileHandle'};
|
|
||||||
my $buffer;
|
|
||||||
my $pageSize;
|
|
||||||
my $granule_position;
|
|
||||||
|
|
||||||
seek($fh,-8500,SEEK_END); # that magic number is from vorbisfile.c
|
|
||||||
# in the constant CHUNKSIZE, which comes
|
|
||||||
# with the comment /* a shade over 8k;
|
|
||||||
# anyone using pages well over 8k gets
|
|
||||||
# what they deserve */
|
|
||||||
|
|
||||||
# we just keep looking through the headers until we get to the last one
|
|
||||||
# (there might be a couple of blocks here)
|
|
||||||
while(_findPage($fh))
|
|
||||||
{
|
|
||||||
# stream structure version - must be 0x00
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
if (ord($buffer) != 0x00)
|
|
||||||
{
|
|
||||||
warn "Invalid stream structure version: " .
|
|
||||||
sprintf("%x", ord($buffer));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
# header type flag
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
# we should check this, but for now we'll just ignore it
|
|
||||||
|
|
||||||
# absolute granule position - this is what we need!
|
|
||||||
read($fh, $buffer, 8);
|
|
||||||
$granule_position = _decodeInt($buffer);
|
|
||||||
|
|
||||||
# skip past stream_serial_number, page_sequence_number, and crc
|
|
||||||
read($fh, $buffer, 12);
|
|
||||||
|
|
||||||
# page_segments
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
my $page_segments = ord($buffer);
|
|
||||||
|
|
||||||
# reset pageSize
|
|
||||||
$pageSize = 0;
|
|
||||||
|
|
||||||
# calculate approx. page size
|
|
||||||
for (my $i = 0; $i < $page_segments; $i++)
|
|
||||||
{
|
|
||||||
read($fh, $buffer, 1);
|
|
||||||
$pageSize += ord($buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
seek $fh, $pageSize, 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data->{'INFO'}{'length'} =
|
|
||||||
int($granule_position / $data->{'INFO'}{'rate'});
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _findPage
|
|
||||||
{
|
|
||||||
# search forward in the file for the 'OggS' page header
|
|
||||||
my $fh = shift;
|
|
||||||
my $char;
|
|
||||||
my $curStr = '';
|
|
||||||
|
|
||||||
while (read($fh, $char, 1))
|
|
||||||
{
|
|
||||||
$curStr = $char . $curStr;
|
|
||||||
$curStr = substr($curStr, 0, 4);
|
|
||||||
|
|
||||||
# we are actually looking for the string 'SggO' because we
|
|
||||||
# tack character on to our test string backwards, to make
|
|
||||||
# trimming it to 4 characters easier.
|
|
||||||
if ($curStr eq 'SggO')
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _decodeInt
|
|
||||||
{
|
|
||||||
my $bytes = shift;
|
|
||||||
my $num = 0;
|
|
||||||
my @byteList = split //, $bytes;
|
|
||||||
my $numBytes = @byteList;
|
|
||||||
my $mult = 1;
|
|
||||||
|
|
||||||
for (my $i = 0; $i < $numBytes; $i ++)
|
|
||||||
{
|
|
||||||
$num += ord($byteList[$i]) * $mult;
|
|
||||||
$mult *= 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $num;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _decodeInt5Bit
|
|
||||||
{
|
|
||||||
my $byte = ord(shift);
|
|
||||||
|
|
||||||
$byte = $byte & 0xF8; # clear out the bottm 3 bits
|
|
||||||
$byte = $byte >> 3; # and shifted down to where it belongs
|
|
||||||
|
|
||||||
return $byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _decodeInt4Bit
|
|
||||||
{
|
|
||||||
my $byte = ord(shift);
|
|
||||||
|
|
||||||
$byte = $byte & 0xFC; # clear out the bottm 4 bits
|
|
||||||
$byte = $byte >> 4; # and shifted down to where it belongs
|
|
||||||
|
|
||||||
return $byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _ilog
|
|
||||||
{
|
|
||||||
my $x = shift;
|
|
||||||
my $ret = 0;
|
|
||||||
|
|
||||||
unless ($x > 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($x > 0)
|
|
||||||
{
|
|
||||||
$ret++;
|
|
||||||
$x = $x >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
__DATA__
|
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
Ogg::Vorbis::Header::PurePerl - An object-oriented interface to Ogg Vorbis
|
|
||||||
information and comment fields, implemented entirely in Perl. Intended to be
|
|
||||||
a drop in replacement for Ogg::Vobis::Header.
|
|
||||||
|
|
||||||
Unlike Ogg::Vorbis::Header, this module will go ahead and fill in all of the
|
|
||||||
information fields as soon as you construct the object. In other words,
|
|
||||||
the C<new> and C<load> constructors have identical behavior.
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
use Ogg::Vorbis::Header::PurePerl;
|
|
||||||
my $ogg = Ogg::Vorbis::Header::PurePerl->new("song.ogg");
|
|
||||||
while (my ($k, $v) = each %{$ogg->info}) {
|
|
||||||
print "$k: $v\n";
|
|
||||||
}
|
|
||||||
foreach my $com ($ogg->comment_tags) {
|
|
||||||
print "$com: $_\n" foreach $ogg->comment($com);
|
|
||||||
}
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
This module is intended to be a drop in replacement for Ogg::Vorbis::Header,
|
|
||||||
implemented entirely in Perl. It provides an object-oriented interface to
|
|
||||||
Ogg Vorbis information and comment fields. (NOTE: This module currently
|
|
||||||
supports only read operations).
|
|
||||||
|
|
||||||
=head1 CONSTRUCTORS
|
|
||||||
|
|
||||||
=head2 C<new ($filename)>
|
|
||||||
|
|
||||||
Opens an Ogg Vorbis file, ensuring that it exists and is actually an
|
|
||||||
Ogg Vorbis stream. This method does not actually read any of the
|
|
||||||
information or comment fields, and closes the file immediately.
|
|
||||||
|
|
||||||
=head2 C<load ([$filename])>
|
|
||||||
|
|
||||||
Opens an Ogg Vorbis file, ensuring that it exists and is actually an
|
|
||||||
Ogg Vorbis stream, then loads the information and comment fields. This
|
|
||||||
method can also be used without a filename to load the information
|
|
||||||
and fields of an already constructed instance.
|
|
||||||
|
|
||||||
=head1 INSTANCE METHODS
|
|
||||||
|
|
||||||
=head2 C<info ([$key])>
|
|
||||||
|
|
||||||
Returns a hashref containing information about the Ogg Vorbis file from
|
|
||||||
the file's information header. Hash fields are: version, channels, rate,
|
|
||||||
bitrate_upper, bitrate_nominal, bitrate_lower, bitrate_window, and length.
|
|
||||||
The bitrate_window value is not currently used by the vorbis codec, and
|
|
||||||
will always be -1.
|
|
||||||
|
|
||||||
The optional parameter, key, allows you to retrieve a single value from
|
|
||||||
the object's hash. Returns C<undef> if the key is not found.
|
|
||||||
|
|
||||||
=head2 C<comment_tags ()>
|
|
||||||
|
|
||||||
Returns an array containing the key values for the comment fields.
|
|
||||||
These values can then be passed to C<comment> to retrieve their values.
|
|
||||||
|
|
||||||
=head2 C<comment ($key)>
|
|
||||||
|
|
||||||
Returns an array of comment values associated with the given key.
|
|
||||||
|
|
||||||
=head2 C<add_comments ($key, $value, [$key, $value, ...])>
|
|
||||||
|
|
||||||
Unimplemented.
|
|
||||||
|
|
||||||
=head2 C<edit_comment ($key, $value, [$num])>
|
|
||||||
|
|
||||||
Unimplemented.
|
|
||||||
|
|
||||||
=head2 C<delete_comment ($key, [$num])>
|
|
||||||
|
|
||||||
Unimplemented.
|
|
||||||
|
|
||||||
=head2 C<clear_comments ([@keys])>
|
|
||||||
|
|
||||||
Unimplemented.
|
|
||||||
|
|
||||||
=head2 C<write_vorbis ()>
|
|
||||||
|
|
||||||
Unimplemented.
|
|
||||||
|
|
||||||
=head2 C<path ()>
|
|
||||||
|
|
||||||
Returns the path/filename of the file the object represents.
|
|
||||||
|
|
||||||
=head1 NOTE
|
|
||||||
|
|
||||||
This is ALPHA SOFTWARE. It may very well be very broken. Do not use it in
|
|
||||||
a production environment. You have been warned.
|
|
||||||
|
|
||||||
=head1 ACKNOWLEDGEMENTS
|
|
||||||
|
|
||||||
Dave Brown <cpan@dagbrown.com> made this module significantly faster
|
|
||||||
at calculating the length of ogg files.
|
|
||||||
|
|
||||||
Robert Moser II <rlmoser@earthlink.net> fixed a problem with files that
|
|
||||||
have no comments.
|
|
||||||
|
|
||||||
=head1 AUTHOR
|
|
||||||
|
|
||||||
Andrew Molloy E<lt>amolloy@kaizolabs.comE<gt>
|
|
||||||
|
|
||||||
=head1 COPYRIGHT
|
|
||||||
|
|
||||||
Copyright (c) 2003, Andrew Molloy. All Rights Reserved.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it
|
|
||||||
under the terms of the GNU General Public License as published by the
|
|
||||||
Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
your option) any later version. A copy of this license is included
|
|
||||||
with this module (LICENSE.GPL).
|
|
||||||
|
|
||||||
=head1 SEE ALSO
|
|
||||||
|
|
||||||
L<Ogg::Vorbis::Header>, L<Ogg::Vorbis::Decoder>
|
|
||||||
|
|
||||||
=cut
|
|
Loading…
Add table
Add a link
Reference in a new issue