You asked, and so you receive: some code I had 'lying around' for just this purpose.
my $last_block_flag = 1 << 31;
my $block_type_flag = 127 << 24;
my $m_len_flag = 255 + 256*255 + 256*256*255;
my $STREAMINFO=0;
my $PADDING=1;
my $APPLICATION=2;
my $SEEKTABLE=3;
my $VORBIS_COMMENT=4;
my $CUESHEET=5;
# Only argument is filename, with full path
sub read_flac
{
my $filename = shift;
my $fileinfo = {};
my ($flac_indicator,$tmp);
my ($meta_head,$meta_last,$meta_type,$meta_size,$meta_contents);
my ($meta_list,$numheads,$thr,@retlist);
# Check for existence & readability
-e $filename or return -1;
-r $filename or return -2;
$fileinfo->{filename} = $filename;
if (-w $filename)
{
$fileinfo->{mode} = "Read/Write";
}
else
{
$fileinfo->{mode} = "Read Only";
}
open(FLACFILE,$filename) or return -5;
binmode FLACFILE;
read FLACFILE, $flac_indicator,4 or return -3;
if ($flac_indicator ne "fLaC")
{
close FLACFILE;
return -4;
}
$meta_list=[];
$numheads = 0;
while(1)
{
read FLACFILE, $tmp, 4 or return -3;
$meta_head = unpack "N",$tmp;
# What's the info stored here?
$meta_last = ($last_block_flag & $meta_head)>>31;
$meta_type = ($block_type_flag & $meta_head)>>24;
$meta_size = $m_len_flag & $meta_head;
read FLACFILE,$meta_contents,$meta_size or return -3;
$thr = {Last => $meta_last,
Type => $meta_type,
Size => $meta_size,
Contents => $meta_contents};
$meta_list->[$numheads] = $thr;
$numheads++;
if ($meta_type==4)
{
# This is the Vorbis tag
$fileinfo->{tags} = parse_vorbis_header($meta_contents);
}
if ($meta_last)
{
last;
}
}
$fileinfo->{start} = tell FLACFILE;
close FLACFILE;
$fileinfo->{headers} = $meta_list;
return $fileinfo;
}
sub parse_vorbis_header
{
# All Vorbis tags are in little-endian format
# first, vendor length
my $tagstring = shift;
my ($vendor_length,$vendor_string);
my ($numtags,$i);
my ($lnlen,$lnstr);
my ($tag_key, $tag_val);
my $taghash = {};
my @list;
$vendor_length = substr($tagstring,0,4);
$vendor_length = unpack "L",$vendor_length;
$tagstring = substr($tagstring,4);
$vendor_string = substr($tagstring,0,$vendor_length);
$taghash->{Vendor} = $vendor_string;
$tagstring = substr($tagstring,$vendor_length);
$numtags = substr($tagstring,0,4);
$numtags = unpack "L",$numtags;
$tagstring = substr($tagstring,4);
for ($i=0;$i<$numtags;$i++)
{
$lnlen = substr($tagstring,0,4);
$tagstring = substr($tagstring,4);
$lnlen = unpack "L",$lnlen;
$lnstr = substr($tagstring,0,$lnlen);
$tagstring = substr($tagstring,$lnlen);
# parse for '='
if ($lnstr =~ /(.*?)=(.*)/)
{
$tag_key = $1;
$tag_key =~ tr/a-z/A-Z/;
$tag_val = $2;
$taghash->{$tag_key} = $tag_val;
}
}
return $taghash;
}