forked from len0rd/rockbox
re-indented to look normal ;-)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5582 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
7396d99027
commit
e24a528499
1 changed files with 411 additions and 411 deletions
816
tools/songdb.pl
816
tools/songdb.pl
|
@ -885,226 +885,226 @@ data (if TAGVERSION argument is C<0>, may contain two versions).
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub get_mp3tag {
|
sub get_mp3tag {
|
||||||
my($file, $ver, $raw_v2) = @_;
|
my($file, $ver, $raw_v2) = @_;
|
||||||
my($tag, $v1, $v2, $v2h, %info, @array, $fh);
|
my($tag, $v1, $v2, $v2h, %info, @array, $fh);
|
||||||
$raw_v2 ||= 0;
|
$raw_v2 ||= 0;
|
||||||
$ver = !$ver ? 0 : ($ver == 2 || $ver == 1) ? $ver : 0;
|
$ver = !$ver ? 0 : ($ver == 2 || $ver == 1) ? $ver : 0;
|
||||||
|
|
||||||
if (not (defined $file && $file ne '')) {
|
if (not (defined $file && $file ne '')) {
|
||||||
$@ = "No file specified";
|
$@ = "No file specified";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not -s $file) {
|
if (not -s $file) {
|
||||||
$@ = "File is empty";
|
$@ = "File is empty";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref $file) { # filehandle passed
|
if (ref $file) { # filehandle passed
|
||||||
$fh = $file;
|
$fh = $file;
|
||||||
} else {
|
} else {
|
||||||
$fh = gensym;
|
$fh = gensym;
|
||||||
if (not open $fh, "< $file\0") {
|
if (not open $fh, "< $file\0") {
|
||||||
$@ = "Can't open $file: $!";
|
$@ = "Can't open $file: $!";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binmode $fh;
|
binmode $fh;
|
||||||
|
|
||||||
if ($ver < 2) {
|
if ($ver < 2) {
|
||||||
seek $fh, -128, 2;
|
seek $fh, -128, 2;
|
||||||
while(defined(my $line = <$fh>)) { $tag .= $line }
|
while(defined(my $line = <$fh>)) { $tag .= $line }
|
||||||
|
|
||||||
if ($tag =~ /^TAG/) {
|
if ($tag =~ /^TAG/) {
|
||||||
$v1 = 1;
|
$v1 = 1;
|
||||||
if (substr($tag, -3, 2) =~ /\000[^\000]/) {
|
if (substr($tag, -3, 2) =~ /\000[^\000]/) {
|
||||||
(undef, @info{@v1_tag_names}) =
|
(undef, @info{@v1_tag_names}) =
|
||||||
(unpack('a3a30a30a30a4a28', $tag),
|
(unpack('a3a30a30a30a4a28', $tag),
|
||||||
ord(substr($tag, -2, 1)),
|
ord(substr($tag, -2, 1)),
|
||||||
$mp3_genres[ord(substr $tag, -1)]);
|
$mp3_genres[ord(substr $tag, -1)]);
|
||||||
$info{TAGVERSION} = 'ID3v1.1';
|
$info{TAGVERSION} = 'ID3v1.1';
|
||||||
} else {
|
} else {
|
||||||
(undef, @info{@v1_tag_names[0..4, 6]}) =
|
(undef, @info{@v1_tag_names[0..4, 6]}) =
|
||||||
(unpack('a3a30a30a30a4a30', $tag),
|
(unpack('a3a30a30a30a4a30', $tag),
|
||||||
$mp3_genres[ord(substr $tag, -1)]);
|
$mp3_genres[ord(substr $tag, -1)]);
|
||||||
$info{TAGVERSION} = 'ID3v1';
|
$info{TAGVERSION} = 'ID3v1';
|
||||||
}
|
}
|
||||||
if ($UNICODE) {
|
if ($UNICODE) {
|
||||||
for my $key (keys %info) {
|
for my $key (keys %info) {
|
||||||
next unless $info{$key};
|
next unless $info{$key};
|
||||||
my $u = Unicode::String::latin1($info{$key});
|
my $u = Unicode::String::latin1($info{$key});
|
||||||
$info{$key} = $u->utf8;
|
$info{$key} = $u->utf8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elsif ($ver == 1) {
|
} elsif ($ver == 1) {
|
||||||
_close($file, $fh);
|
_close($file, $fh);
|
||||||
$@ = "No ID3v1 tag found";
|
$@ = "No ID3v1 tag found";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
($v2, $v2h) = _get_v2tag($fh);
|
($v2, $v2h) = _get_v2tag($fh);
|
||||||
|
|
||||||
unless ($v1 || $v2) {
|
unless ($v1 || $v2) {
|
||||||
_close($file, $fh);
|
_close($file, $fh);
|
||||||
$@ = "No ID3 tag found";
|
$@ = "No ID3 tag found";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($ver == 0 || $ver == 2) && $v2) {
|
if (($ver == 0 || $ver == 2) && $v2) {
|
||||||
if ($raw_v2 == 1 && $ver == 2) {
|
if ($raw_v2 == 1 && $ver == 2) {
|
||||||
%info = %$v2;
|
%info = %$v2;
|
||||||
$info{TAGVERSION} = $v2h->{version};
|
$info{TAGVERSION} = $v2h->{version};
|
||||||
} else {
|
} else {
|
||||||
my $hash = $raw_v2 == 2 ? { map { ($_, $_) } keys %v2_tag_names } : \%v2_to_v1_names;
|
my $hash = $raw_v2 == 2 ? { map { ($_, $_) } keys %v2_tag_names } : \%v2_to_v1_names;
|
||||||
for my $id (keys %$hash) {
|
for my $id (keys %$hash) {
|
||||||
if (exists $v2->{$id}) {
|
if (exists $v2->{$id}) {
|
||||||
if ($id =~ /^TCON?$/ && $v2->{$id} =~ /^.?\((\d+)\)/) {
|
if ($id =~ /^TCON?$/ && $v2->{$id} =~ /^.?\((\d+)\)/) {
|
||||||
$info{$hash->{$id}} = $mp3_genres[$1];
|
$info{$hash->{$id}} = $mp3_genres[$1];
|
||||||
} else {
|
} else {
|
||||||
my $data1 = $v2->{$id};
|
my $data1 = $v2->{$id};
|
||||||
|
|
||||||
# this is tricky ... if this is an arrayref,
|
# this is tricky ... if this is an arrayref, we want
|
||||||
# we want to only return one, so we pick the
|
# to only return one, so we pick the first one. but
|
||||||
# first one. but if it is a comment, we pick
|
# if it is a comment, we pick the first one where the
|
||||||
# the first one where the first charcter after
|
# first charcter after the language is NULL and not an
|
||||||
# the language is NULL and not an additional
|
# additional sub-comment, because that is most likely
|
||||||
# sub-comment, because that is most likely to be
|
# to be the user-supplied comment
|
||||||
# the user-supplied comment
|
|
||||||
if (ref $data1 && !$raw_v2) {
|
|
||||||
if ($id =~ /^COMM?$/) {
|
|
||||||
my($newdata) = grep /^(....\000)/, @{$data1};
|
|
||||||
$data1 = $newdata || $data1->[0];
|
|
||||||
} else {
|
|
||||||
$data1 = $data1->[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data1 = [ $data1 ] if ! ref $data1;
|
if (ref $data1 && !$raw_v2) {
|
||||||
|
if ($id =~ /^COMM?$/) {
|
||||||
|
my($newdata) = grep /^(....\000)/, @{$data1};
|
||||||
|
$data1 = $newdata || $data1->[0];
|
||||||
|
} else {
|
||||||
|
$data1 = $data1->[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for my $data (@$data1) {
|
$data1 = [ $data1 ] if ! ref $data1;
|
||||||
$data =~ s/^(.)//; # strip first char (text encoding)
|
|
||||||
my $encoding = $1;
|
|
||||||
my $desc;
|
|
||||||
if ($id =~ /^COM[M ]?$/) {
|
|
||||||
$data =~ s/^(?:...)//; # strip language
|
|
||||||
$data =~ s/^(.*?)\000+//; # strip up to first NULL(s),
|
|
||||||
# for sub-comment
|
|
||||||
$desc = $1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($UNICODE) {
|
for my $data (@$data1) {
|
||||||
if ($encoding eq "\001" || $encoding eq "\002") { # UTF-16, UTF-16BE
|
$data =~ s/^(.)//; # strip first char (text encoding)
|
||||||
my $u = Unicode::String::utf16($data);
|
my $encoding = $1;
|
||||||
$data = $u->utf8;
|
my $desc;
|
||||||
$data =~ s/^\xEF\xBB\xBF//; # strip BOM
|
if ($id =~ /^COM[M ]?$/) {
|
||||||
} elsif ($encoding eq "\000") {
|
$data =~ s/^(?:...)//; # strip language
|
||||||
my $u = Unicode::String::latin1($data);
|
$data =~ s/^(.*?)\000+//; # strip up to first NULL(s),
|
||||||
$data = $u->utf8;
|
# for sub-comment
|
||||||
}
|
$desc = $1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($raw_v2 == 2 && $desc) {
|
if ($UNICODE) {
|
||||||
$data = { $desc => $data };
|
if ($encoding eq "\001" || $encoding eq "\002") { # UTF-16, UTF-16BE
|
||||||
}
|
my $u = Unicode::String::utf16($data);
|
||||||
|
$data = $u->utf8;
|
||||||
|
$data =~ s/^\xEF\xBB\xBF//; # strip BOM
|
||||||
|
} elsif ($encoding eq "\000") {
|
||||||
|
my $u = Unicode::String::latin1($data);
|
||||||
|
$data = $u->utf8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($raw_v2 == 2 && exists $info{$hash->{$id}}) {
|
if ($raw_v2 == 2 && $desc) {
|
||||||
if (ref $info{$hash->{$id}} eq 'ARRAY') {
|
$data = { $desc => $data };
|
||||||
push @{$info{$hash->{$id}}}, $data;
|
}
|
||||||
} else {
|
|
||||||
$info{$hash->{$id}} = [ $info{$hash->{$id}}, $data ];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$info{$hash->{$id}} = $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($ver == 0 && $info{TAGVERSION}) {
|
|
||||||
$info{TAGVERSION} .= ' / ' . $v2h->{version};
|
|
||||||
} else {
|
|
||||||
$info{TAGVERSION} = $v2h->{version};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unless ($raw_v2 && $ver == 2) {
|
if ($raw_v2 == 2 && exists $info{$hash->{$id}}) {
|
||||||
foreach my $key (keys %info) {
|
if (ref $info{$hash->{$id}} eq 'ARRAY') {
|
||||||
if (defined $info{$key}) {
|
push @{$info{$hash->{$id}}}, $data;
|
||||||
$info{$key} =~ s/\000+.*//g;
|
} else {
|
||||||
$info{$key} =~ s/\s+$//;
|
$info{$hash->{$id}} = [ $info{$hash->{$id}}, $data ];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
$info{$hash->{$id}} = $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($ver == 0 && $info{TAGVERSION}) {
|
||||||
|
$info{TAGVERSION} .= ' / ' . $v2h->{version};
|
||||||
|
} else {
|
||||||
|
$info{TAGVERSION} = $v2h->{version};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (@v1_tag_names) {
|
unless ($raw_v2 && $ver == 2) {
|
||||||
$info{$_} = '' unless defined $info{$_};
|
foreach my $key (keys %info) {
|
||||||
}
|
if (defined $info{$key}) {
|
||||||
}
|
$info{$key} =~ s/\000+.*//g;
|
||||||
|
$info{$key} =~ s/\s+$//;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (keys %info && exists $info{GENRE} && ! defined $info{GENRE}) {
|
for (@v1_tag_names) {
|
||||||
$info{GENRE} = '';
|
$info{$_} = '' unless defined $info{$_};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_close($file, $fh);
|
if (keys %info && exists $info{GENRE} && ! defined $info{GENRE}) {
|
||||||
|
$info{GENRE} = '';
|
||||||
|
}
|
||||||
|
|
||||||
return keys %info ? {%info} : undef;
|
_close($file, $fh);
|
||||||
|
|
||||||
|
return keys %info ? {%info} : undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_v2tag {
|
sub _get_v2tag {
|
||||||
my($fh) = @_;
|
my($fh) = @_;
|
||||||
my($off, $myseek, $myseek_22, $myseek_23, $v2, $h, $hlen, $num);
|
my($off, $myseek, $myseek_22, $myseek_23, $v2, $h, $hlen, $num);
|
||||||
$h = {};
|
$h = {};
|
||||||
|
|
||||||
$v2 = _get_v2head($fh) or return;
|
$v2 = _get_v2head($fh) or return;
|
||||||
if ($v2->{major_version} < 2) {
|
if ($v2->{major_version} < 2) {
|
||||||
warn "This is $v2->{version}; " .
|
warn "This is $v2->{version}; " .
|
||||||
"ID3v2 versions older than ID3v2.2.0 not supported\n"
|
"ID3v2 versions older than ID3v2.2.0 not supported\n"
|
||||||
if $^W;
|
if $^W;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($v2->{major_version} == 2) {
|
if ($v2->{major_version} == 2) {
|
||||||
$hlen = 6;
|
$hlen = 6;
|
||||||
$num = 3;
|
$num = 3;
|
||||||
} else {
|
} else {
|
||||||
$hlen = 10;
|
$hlen = 10;
|
||||||
$num = 4;
|
$num = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
$myseek = sub {
|
$myseek = sub {
|
||||||
seek $fh, $off, 0;
|
seek $fh, $off, 0;
|
||||||
read $fh, my($bytes), $hlen;
|
read $fh, my($bytes), $hlen;
|
||||||
return unless $bytes =~ /^([A-Z0-9]{$num})/
|
return unless $bytes =~ /^([A-Z0-9]{$num})/
|
||||||
|| ($num == 4 && $bytes =~ /^(COM )/); # stupid iTunes
|
|| ($num == 4 && $bytes =~ /^(COM )/); # stupid iTunes
|
||||||
my($id, $size) = ($1, $hlen);
|
my($id, $size) = ($1, $hlen);
|
||||||
my @bytes = reverse unpack "C$num", substr($bytes, $num, $num);
|
my @bytes = reverse unpack "C$num", substr($bytes, $num, $num);
|
||||||
for my $i (0 .. ($num - 1)) {
|
for my $i (0 .. ($num - 1)) {
|
||||||
$size += $bytes[$i] * 256 ** $i;
|
$size += $bytes[$i] * 256 ** $i;
|
||||||
}
|
}
|
||||||
return($id, $size);
|
return($id, $size);
|
||||||
};
|
};
|
||||||
|
|
||||||
$off = $v2->{ext_header_size} + 10;
|
$off = $v2->{ext_header_size} + 10;
|
||||||
|
|
||||||
while ($off < $v2->{tag_size}) {
|
while ($off < $v2->{tag_size}) {
|
||||||
my($id, $size) = &$myseek or last;
|
my($id, $size) = &$myseek or last;
|
||||||
seek $fh, $off + $hlen, 0;
|
seek $fh, $off + $hlen, 0;
|
||||||
read $fh, my($bytes), $size - $hlen;
|
read $fh, my($bytes), $size - $hlen;
|
||||||
if (exists $h->{$id}) {
|
if (exists $h->{$id}) {
|
||||||
if (ref $h->{$id} eq 'ARRAY') {
|
if (ref $h->{$id} eq 'ARRAY') {
|
||||||
push @{$h->{$id}}, $bytes;
|
push @{$h->{$id}}, $bytes;
|
||||||
} else {
|
} else {
|
||||||
$h->{$id} = [$h->{$id}, $bytes];
|
$h->{$id} = [$h->{$id}, $bytes];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$h->{$id} = $bytes;
|
$h->{$id} = $bytes;
|
||||||
}
|
}
|
||||||
$off += $size;
|
$off += $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return($h, $v2);
|
return($h, $v2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1143,292 +1143,292 @@ On error, returns nothing and sets C<$@>.
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub get_mp3info {
|
sub get_mp3info {
|
||||||
my($file) = @_;
|
my($file) = @_;
|
||||||
my($off, $myseek, $byte, $eof, $h, $tot, $fh);
|
my($off, $myseek, $byte, $eof, $h, $tot, $fh);
|
||||||
|
|
||||||
if (not (defined $file && $file ne '')) {
|
if (not (defined $file && $file ne '')) {
|
||||||
$@ = "No file specified";
|
$@ = "No file specified";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not -s $file) {
|
if (not -s $file) {
|
||||||
$@ = "File is empty";
|
$@ = "File is empty";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref $file) { # filehandle passed
|
if (ref $file) { # filehandle passed
|
||||||
$fh = $file;
|
$fh = $file;
|
||||||
} else {
|
} else {
|
||||||
$fh = gensym;
|
$fh = gensym;
|
||||||
if (not open $fh, "< $file\0") {
|
if (not open $fh, "< $file\0") {
|
||||||
$@ = "Can't open $file: $!";
|
$@ = "Can't open $file: $!";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$off = 0;
|
$off = 0;
|
||||||
$tot = 4096;
|
$tot = 4096;
|
||||||
|
|
||||||
$myseek = sub {
|
$myseek = sub {
|
||||||
seek $fh, $off, 0;
|
seek $fh, $off, 0;
|
||||||
read $fh, $byte, 4;
|
read $fh, $byte, 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
binmode $fh;
|
binmode $fh;
|
||||||
&$myseek;
|
&$myseek;
|
||||||
|
|
||||||
if ($off == 0) {
|
if ($off == 0) {
|
||||||
if (my $id3v2 = _get_v2head($fh)) {
|
if (my $id3v2 = _get_v2head($fh)) {
|
||||||
$tot += $off += $id3v2->{tag_size};
|
$tot += $off += $id3v2->{tag_size};
|
||||||
&$myseek;
|
&$myseek;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$h = _get_head($byte);
|
$h = _get_head($byte);
|
||||||
until (_is_mp3($h)) {
|
until (_is_mp3($h)) {
|
||||||
$off++;
|
$off++;
|
||||||
&$myseek;
|
&$myseek;
|
||||||
$h = _get_head($byte);
|
$h = _get_head($byte);
|
||||||
if ($off > $tot && !$try_harder) {
|
if ($off > $tot && !$try_harder) {
|
||||||
_close($file, $fh);
|
_close($file, $fh);
|
||||||
$@ = "Couldn't find MP3 header (perhaps set " .
|
$@ = "Couldn't find MP3 header (perhaps set " .
|
||||||
'$MP3::Info::try_harder and retry)';
|
'$MP3::Info::try_harder and retry)';
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $vbr = _get_vbr($fh, $h, \$off);
|
my $vbr = _get_vbr($fh, $h, \$off);
|
||||||
|
|
||||||
seek $fh, 0, 2;
|
seek $fh, 0, 2;
|
||||||
$eof = tell $fh;
|
$eof = tell $fh;
|
||||||
seek $fh, -128, 2;
|
seek $fh, -128, 2;
|
||||||
$off += 128 if <$fh> =~ /^TAG/ ? 1 : 0;
|
$off += 128 if <$fh> =~ /^TAG/ ? 1 : 0;
|
||||||
|
|
||||||
_close($file, $fh);
|
_close($file, $fh);
|
||||||
|
|
||||||
$h->{size} = $eof - $off;
|
$h->{size} = $eof - $off;
|
||||||
|
|
||||||
return _get_info($h, $vbr);
|
return _get_info($h, $vbr);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_info {
|
sub _get_info {
|
||||||
my($h, $vbr) = @_;
|
my($h, $vbr) = @_;
|
||||||
my $i;
|
my $i;
|
||||||
|
|
||||||
$i->{VERSION} = $h->{IDR} == 2 ? 2 : $h->{IDR} == 3 ? 1 :
|
$i->{VERSION} = $h->{IDR} == 2 ? 2 : $h->{IDR} == 3 ? 1 :
|
||||||
$h->{IDR} == 0 ? 2.5 : 0;
|
$h->{IDR} == 0 ? 2.5 : 0;
|
||||||
$i->{LAYER} = 4 - $h->{layer};
|
$i->{LAYER} = 4 - $h->{layer};
|
||||||
$i->{VBR} = defined $vbr ? 1 : 0;
|
$i->{VBR} = defined $vbr ? 1 : 0;
|
||||||
|
|
||||||
$i->{COPYRIGHT} = $h->{copyright} ? 1 : 0;
|
$i->{COPYRIGHT} = $h->{copyright} ? 1 : 0;
|
||||||
$i->{PADDING} = $h->{padding_bit} ? 1 : 0;
|
$i->{PADDING} = $h->{padding_bit} ? 1 : 0;
|
||||||
$i->{STEREO} = $h->{mode} == 3 ? 0 : 1;
|
$i->{STEREO} = $h->{mode} == 3 ? 0 : 1;
|
||||||
$i->{MODE} = $h->{mode};
|
$i->{MODE} = $h->{mode};
|
||||||
|
|
||||||
$i->{SIZE} = $vbr && $vbr->{bytes} ? $vbr->{bytes} : $h->{size};
|
$i->{SIZE} = $vbr && $vbr->{bytes} ? $vbr->{bytes} : $h->{size};
|
||||||
|
|
||||||
my $mfs = $h->{fs} / ($h->{ID} ? 144000 : 72000);
|
my $mfs = $h->{fs} / ($h->{ID} ? 144000 : 72000);
|
||||||
$i->{FRAMES} = int($vbr && $vbr->{frames}
|
$i->{FRAMES} = int($vbr && $vbr->{frames}
|
||||||
? $vbr->{frames}
|
? $vbr->{frames}
|
||||||
: $i->{SIZE} / $h->{bitrate} / $mfs
|
: $i->{SIZE} / $h->{bitrate} / $mfs
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($vbr) {
|
if ($vbr) {
|
||||||
$i->{VBR_SCALE} = $vbr->{scale} if $vbr->{scale};
|
$i->{VBR_SCALE} = $vbr->{scale} if $vbr->{scale};
|
||||||
$h->{bitrate} = $i->{SIZE} / $i->{FRAMES} * $mfs;
|
$h->{bitrate} = $i->{SIZE} / $i->{FRAMES} * $mfs;
|
||||||
if (not $h->{bitrate}) {
|
if (not $h->{bitrate}) {
|
||||||
$@ = "Couldn't determine VBR bitrate";
|
$@ = "Couldn't determine VBR bitrate";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$h->{'length'} = ($i->{SIZE} * 8) / $h->{bitrate} / 10;
|
$h->{'length'} = ($i->{SIZE} * 8) / $h->{bitrate} / 10;
|
||||||
$i->{SECS} = $h->{'length'} / 100;
|
$i->{SECS} = $h->{'length'} / 100;
|
||||||
$i->{MM} = int $i->{SECS} / 60;
|
$i->{MM} = int $i->{SECS} / 60;
|
||||||
$i->{SS} = int $i->{SECS} % 60;
|
$i->{SS} = int $i->{SECS} % 60;
|
||||||
$i->{MS} = (($i->{SECS} - ($i->{MM} * 60) - $i->{SS}) * 1000);
|
$i->{MS} = (($i->{SECS} - ($i->{MM} * 60) - $i->{SS}) * 1000);
|
||||||
# $i->{LF} = ($i->{MS} / 1000) * ($i->{FRAMES} / $i->{SECS});
|
# $i->{LF} = ($i->{MS} / 1000) * ($i->{FRAMES} / $i->{SECS});
|
||||||
# int($i->{MS} / 100 * 75); # is this right?
|
# int($i->{MS} / 100 * 75); # is this right?
|
||||||
$i->{TIME} = sprintf "%.2d:%.2d", @{$i}{'MM', 'SS'};
|
$i->{TIME} = sprintf "%.2d:%.2d", @{$i}{'MM', 'SS'};
|
||||||
|
|
||||||
$i->{BITRATE} = int $h->{bitrate};
|
$i->{BITRATE} = int $h->{bitrate};
|
||||||
# should we just return if ! FRAMES?
|
# should we just return if ! FRAMES?
|
||||||
$i->{FRAME_LENGTH} = int($h->{size} / $i->{FRAMES}) if $i->{FRAMES};
|
$i->{FRAME_LENGTH} = int($h->{size} / $i->{FRAMES}) if $i->{FRAMES};
|
||||||
$i->{FREQUENCY} = $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}];
|
$i->{FREQUENCY} = $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}];
|
||||||
|
|
||||||
return $i;
|
return $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_head {
|
sub _get_head {
|
||||||
my($byte) = @_;
|
my($byte) = @_;
|
||||||
my($bytes, $h);
|
my($bytes, $h);
|
||||||
|
|
||||||
$bytes = _unpack_head($byte);
|
$bytes = _unpack_head($byte);
|
||||||
@$h{qw(IDR ID layer protection_bit
|
@$h{qw(IDR ID layer protection_bit
|
||||||
bitrate_index sampling_freq padding_bit private_bit
|
bitrate_index sampling_freq padding_bit private_bit
|
||||||
mode mode_extension copyright original
|
mode mode_extension copyright original
|
||||||
emphasis version_index bytes)} = (
|
emphasis version_index bytes)} = (
|
||||||
($bytes>>19)&3, ($bytes>>19)&1, ($bytes>>17)&3, ($bytes>>16)&1,
|
($bytes>>19)&3, ($bytes>>19)&1, ($bytes>>17)&3, ($bytes>>16)&1,
|
||||||
($bytes>>12)&15, ($bytes>>10)&3, ($bytes>>9)&1, ($bytes>>8)&1,
|
($bytes>>12)&15, ($bytes>>10)&3, ($bytes>>9)&1, ($bytes>>8)&1,
|
||||||
($bytes>>6)&3, ($bytes>>4)&3, ($bytes>>3)&1, ($bytes>>2)&1,
|
($bytes>>6)&3, ($bytes>>4)&3, ($bytes>>3)&1, ($bytes>>2)&1,
|
||||||
$bytes&3, ($bytes>>19)&3, $bytes
|
$bytes&3, ($bytes>>19)&3, $bytes
|
||||||
);
|
);
|
||||||
|
|
||||||
$h->{bitrate} = $t_bitrate[$h->{ID}][3 - $h->{layer}][$h->{bitrate_index}];
|
$h->{bitrate} = $t_bitrate[$h->{ID}][3 - $h->{layer}][$h->{bitrate_index}];
|
||||||
$h->{fs} = $t_sampling_freq[$h->{IDR}][$h->{sampling_freq}];
|
$h->{fs} = $t_sampling_freq[$h->{IDR}][$h->{sampling_freq}];
|
||||||
|
|
||||||
return $h;
|
return $h;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _is_mp3 {
|
sub _is_mp3 {
|
||||||
my $h = $_[0] or return undef;
|
my $h = $_[0] or return undef;
|
||||||
return ! ( # all below must be false
|
return ! ( # all below must be false
|
||||||
$h->{bitrate_index} == 0
|
$h->{bitrate_index} == 0
|
||||||
||
|
||
|
||||||
$h->{version_index} == 1
|
$h->{version_index} == 1
|
||||||
||
|
||
|
||||||
($h->{bytes} & 0xFFE00000) != 0xFFE00000
|
($h->{bytes} & 0xFFE00000) != 0xFFE00000
|
||||||
||
|
||
|
||||||
!$h->{fs}
|
!$h->{fs}
|
||||||
||
|
||
|
||||||
!$h->{bitrate}
|
!$h->{bitrate}
|
||||||
||
|
||
|
||||||
$h->{bitrate_index} == 15
|
$h->{bitrate_index} == 15
|
||||||
||
|
||
|
||||||
!$h->{layer}
|
!$h->{layer}
|
||||||
||
|
||
|
||||||
$h->{sampling_freq} == 3
|
$h->{sampling_freq} == 3
|
||||||
||
|
||
|
||||||
$h->{emphasis} == 2
|
$h->{emphasis} == 2
|
||||||
||
|
||
|
||||||
!$h->{bitrate_index}
|
!$h->{bitrate_index}
|
||||||
||
|
||
|
||||||
($h->{bytes} & 0xFFFF0000) == 0xFFFE0000
|
($h->{bytes} & 0xFFFF0000) == 0xFFFE0000
|
||||||
||
|
||
|
||||||
($h->{ID} == 1 && $h->{layer} == 3 && $h->{protection_bit} == 1)
|
($h->{ID} == 1 && $h->{layer} == 3 && $h->{protection_bit} == 1)
|
||||||
||
|
||
|
||||||
($h->{mode_extension} != 0 && $h->{mode} != 1)
|
($h->{mode_extension} != 0 && $h->{mode} != 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_vbr {
|
sub _get_vbr {
|
||||||
my($fh, $h, $roff) = @_;
|
my($fh, $h, $roff) = @_;
|
||||||
my($off, $bytes, @bytes, $myseek, %vbr);
|
my($off, $bytes, @bytes, $myseek, %vbr);
|
||||||
|
|
||||||
$off = $$roff;
|
$off = $$roff;
|
||||||
@_ = (); # closure confused if we don't do this
|
@_ = (); # closure confused if we don't do this
|
||||||
|
|
||||||
$myseek = sub {
|
$myseek = sub {
|
||||||
my $n = $_[0] || 4;
|
my $n = $_[0] || 4;
|
||||||
seek $fh, $off, 0;
|
seek $fh, $off, 0;
|
||||||
read $fh, $bytes, $n;
|
read $fh, $bytes, $n;
|
||||||
$off += $n;
|
$off += $n;
|
||||||
};
|
};
|
||||||
|
|
||||||
$off += 4;
|
$off += 4;
|
||||||
|
|
||||||
if ($h->{ID}) { # MPEG1
|
if ($h->{ID}) { # MPEG1
|
||||||
$off += $h->{mode} == 3 ? 17 : 32;
|
$off += $h->{mode} == 3 ? 17 : 32;
|
||||||
} else { # MPEG2
|
} else { # MPEG2
|
||||||
$off += $h->{mode} == 3 ? 9 : 17;
|
$off += $h->{mode} == 3 ? 9 : 17;
|
||||||
}
|
}
|
||||||
|
|
||||||
&$myseek;
|
&$myseek;
|
||||||
return unless $bytes eq 'Xing';
|
return unless $bytes eq 'Xing';
|
||||||
|
|
||||||
&$myseek;
|
&$myseek;
|
||||||
$vbr{flags} = _unpack_head($bytes);
|
$vbr{flags} = _unpack_head($bytes);
|
||||||
|
|
||||||
if ($vbr{flags} & 1) {
|
if ($vbr{flags} & 1) {
|
||||||
&$myseek;
|
&$myseek;
|
||||||
$vbr{frames} = _unpack_head($bytes);
|
$vbr{frames} = _unpack_head($bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($vbr{flags} & 2) {
|
if ($vbr{flags} & 2) {
|
||||||
&$myseek;
|
&$myseek;
|
||||||
$vbr{bytes} = _unpack_head($bytes);
|
$vbr{bytes} = _unpack_head($bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($vbr{flags} & 4) {
|
if ($vbr{flags} & 4) {
|
||||||
$myseek->(100);
|
$myseek->(100);
|
||||||
# Not used right now ...
|
# Not used right now ...
|
||||||
# $vbr{toc} = _unpack_head($bytes);
|
# $vbr{toc} = _unpack_head($bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($vbr{flags} & 8) { # (quality ind., 0=best 100=worst)
|
if ($vbr{flags} & 8) { # (quality ind., 0=best 100=worst)
|
||||||
&$myseek;
|
&$myseek;
|
||||||
$vbr{scale} = _unpack_head($bytes);
|
$vbr{scale} = _unpack_head($bytes);
|
||||||
} else {
|
} else {
|
||||||
$vbr{scale} = -1;
|
$vbr{scale} = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$$roff = $off;
|
$$roff = $off;
|
||||||
return \%vbr;
|
return \%vbr;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _get_v2head {
|
sub _get_v2head {
|
||||||
my $fh = $_[0] or return;
|
my $fh = $_[0] or return;
|
||||||
my($h, $bytes, @bytes);
|
my($h, $bytes, @bytes);
|
||||||
|
|
||||||
# check first three bytes for 'ID3'
|
# check first three bytes for 'ID3'
|
||||||
seek $fh, 0, 0;
|
seek $fh, 0, 0;
|
||||||
read $fh, $bytes, 3;
|
read $fh, $bytes, 3;
|
||||||
return unless $bytes eq 'ID3';
|
return unless $bytes eq 'ID3';
|
||||||
|
|
||||||
# get version
|
# get version
|
||||||
read $fh, $bytes, 2;
|
read $fh, $bytes, 2;
|
||||||
$h->{version} = sprintf "ID3v2.%d.%d",
|
$h->{version} = sprintf "ID3v2.%d.%d",
|
||||||
@$h{qw[major_version minor_version]} =
|
@$h{qw[major_version minor_version]} =
|
||||||
unpack 'c2', $bytes;
|
unpack 'c2', $bytes;
|
||||||
|
|
||||||
# get flags
|
# get flags
|
||||||
read $fh, $bytes, 1;
|
read $fh, $bytes, 1;
|
||||||
if ($h->{major_version} == 2) {
|
if ($h->{major_version} == 2) {
|
||||||
@$h{qw[unsync compression]} =
|
@$h{qw[unsync compression]} =
|
||||||
(unpack 'b8', $bytes)[7, 6];
|
(unpack 'b8', $bytes)[7, 6];
|
||||||
$h->{ext_header} = 0;
|
$h->{ext_header} = 0;
|
||||||
$h->{experimental} = 0;
|
$h->{experimental} = 0;
|
||||||
} else {
|
} else {
|
||||||
@$h{qw[unsync ext_header experimental]} =
|
@$h{qw[unsync ext_header experimental]} =
|
||||||
(unpack 'b8', $bytes)[7, 6, 5];
|
(unpack 'b8', $bytes)[7, 6, 5];
|
||||||
}
|
}
|
||||||
|
|
||||||
# get ID3v2 tag length from bytes 7-10
|
# get ID3v2 tag length from bytes 7-10
|
||||||
$h->{tag_size} = 10; # include ID3v2 header size
|
$h->{tag_size} = 10; # include ID3v2 header size
|
||||||
read $fh, $bytes, 4;
|
read $fh, $bytes, 4;
|
||||||
@bytes = reverse unpack 'C4', $bytes;
|
@bytes = reverse unpack 'C4', $bytes;
|
||||||
foreach my $i (0 .. 3) {
|
foreach my $i (0 .. 3) {
|
||||||
# whoaaaaaa nellllllyyyyyy!
|
# whoaaaaaa nellllllyyyyyy!
|
||||||
$h->{tag_size} += $bytes[$i] * 128 ** $i;
|
$h->{tag_size} += $bytes[$i] * 128 ** $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
# get extended header size
|
# get extended header size
|
||||||
$h->{ext_header_size} = 0;
|
$h->{ext_header_size} = 0;
|
||||||
if ($h->{ext_header}) {
|
if ($h->{ext_header}) {
|
||||||
$h->{ext_header_size} += 10;
|
$h->{ext_header_size} += 10;
|
||||||
read $fh, $bytes, 4;
|
read $fh, $bytes, 4;
|
||||||
@bytes = reverse unpack 'C4', $bytes;
|
@bytes = reverse unpack 'C4', $bytes;
|
||||||
for my $i (0..3) {
|
for my $i (0..3) {
|
||||||
$h->{ext_header_size} += $bytes[$i] * 256 ** $i;
|
$h->{ext_header_size} += $bytes[$i] * 256 ** $i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $h;
|
return $h;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _unpack_head {
|
sub _unpack_head {
|
||||||
unpack('l', pack('L', unpack('N', $_[0])));
|
unpack('l', pack('L', unpack('N', $_[0])));
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _close {
|
sub _close {
|
||||||
my($file, $fh) = @_;
|
my($file, $fh) = @_;
|
||||||
unless (ref $file) { # filehandle not passed
|
unless (ref $file) { # filehandle not passed
|
||||||
close $fh or warn "Problem closing '$file': $!";
|
close $fh or warn "Problem closing '$file': $!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
@mp3_genres = (
|
@mp3_genres = (
|
||||||
'Blues',
|
'Blues',
|
||||||
'Classic Rock',
|
'Classic Rock',
|
||||||
'Country',
|
'Country',
|
||||||
|
@ -1583,7 +1583,7 @@ BEGIN {
|
||||||
'Synthpop',
|
'Synthpop',
|
||||||
);
|
);
|
||||||
|
|
||||||
@t_bitrate = ([
|
@t_bitrate = ([
|
||||||
[0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
|
[0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
|
||||||
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
|
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
|
||||||
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
|
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
|
||||||
|
@ -1593,17 +1593,17 @@ BEGIN {
|
||||||
[0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
|
[0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@t_sampling_freq = (
|
@t_sampling_freq = (
|
||||||
[11025, 12000, 8000],
|
[11025, 12000, 8000],
|
||||||
[undef, undef, undef], # reserved
|
[undef, undef, undef], # reserved
|
||||||
[22050, 24000, 16000],
|
[22050, 24000, 16000],
|
||||||
[44100, 48000, 32000]
|
[44100, 48000, 32000]
|
||||||
);
|
);
|
||||||
|
|
||||||
@frequency_tbl = map { $_ ? eval "${_}e-3" : 0 }
|
@frequency_tbl = map { $_ ? eval "${_}e-3" : 0 }
|
||||||
map { @$_ } @t_sampling_freq;
|
map { @$_ } @t_sampling_freq;
|
||||||
|
|
||||||
@mp3_info_fields = qw(
|
@mp3_info_fields = qw(
|
||||||
VERSION
|
VERSION
|
||||||
LAYER
|
LAYER
|
||||||
STEREO
|
STEREO
|
||||||
|
@ -1624,12 +1624,12 @@ BEGIN {
|
||||||
VBR_SCALE
|
VBR_SCALE
|
||||||
);
|
);
|
||||||
|
|
||||||
%v1_tag_fields =
|
%v1_tag_fields =
|
||||||
(TITLE => 30, ARTIST => 30, ALBUM => 30, COMMENT => 30, YEAR => 4);
|
(TITLE => 30, ARTIST => 30, ALBUM => 30, COMMENT => 30, YEAR => 4);
|
||||||
|
|
||||||
@v1_tag_names = qw(TITLE ARTIST ALBUM YEAR COMMENT TRACKNUM GENRE);
|
@v1_tag_names = qw(TITLE ARTIST ALBUM YEAR COMMENT TRACKNUM GENRE);
|
||||||
|
|
||||||
%v2_to_v1_names = (
|
%v2_to_v1_names = (
|
||||||
# v2.2 tags
|
# v2.2 tags
|
||||||
'TT2' => 'TITLE',
|
'TT2' => 'TITLE',
|
||||||
'TP1' => 'ARTIST',
|
'TP1' => 'ARTIST',
|
||||||
|
@ -1648,7 +1648,7 @@ BEGIN {
|
||||||
'TCON' => 'GENRE',
|
'TCON' => 'GENRE',
|
||||||
);
|
);
|
||||||
|
|
||||||
%v2_tag_names = (
|
%v2_tag_names = (
|
||||||
# v2.2 tags
|
# v2.2 tags
|
||||||
'BUF' => 'Recommended buffer size',
|
'BUF' => 'Recommended buffer size',
|
||||||
'CNT' => 'Play counter',
|
'CNT' => 'Play counter',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue