Permalink
Browse files

add basic support for mpc SV8

  • Loading branch information...
squentin committed May 18, 2014
1 parent 1f4ce77 commit 48e030ec676b5b19baad2be4cfafa0d9fc34835e
Showing with 63 additions and 1 deletion.
  1. +63 −1 mpcheader.pm
View
@@ -47,7 +47,14 @@ sub _ReadHeader
my $offset=$self->{startaudio};
seek $fh,$offset,0;
read $fh,my$buf,11;
if ($buf=~m/^MP\+/) #SV7, SV7.1 or SV8
if ($buf=~m/^MPCK/) #SV8
{ seek $fh,$offset+4,0;
$self->readV8packets;
my $info= $self->{info};
$info->{bitrate}=( $self->{endaudio}-$self->{startaudio} )*8/$info->{seconds} if $info && $info->{seconds};
return;
}
elsif ($buf=~m/^MP\+/) #SV7, SV7.1 or SV8? (I've found doc describing SV8 format like that (MP+ instead of MPCK), but not sure such files exist)
{ my ($v,$nbframes,$pf)=unpack 'x3CVxxC',$buf;
$info{version}=($v & 0x0f).'.'.($v>>4);
if (($v & 0x0f)>8) { warn "Version of mpc not supported\n";return; }
@@ -69,4 +76,59 @@ sub _ReadHeader
$self->{info}=\%info;
}
sub readV8packets
{ my $self=shift;
my $fh=$self->{fileHandle};
my %info;
eval { # in eval block to avoid error in case of invalid BER value in unpack
while (my $read=read($fh,my$buf,12))
{ last if $read<3;
my ($id,$size,$notheader)=unpack 'A2wa*',$buf; #size is BER compressed integer
$notheader=length $notheader; #number of bytes read that are not from the header
if ($id!~m/^[A-Z][A-Z]$/) { warn "mpcV8: invalid packet id ".unpack("H*",$id)."\n"; return }
warn "mpcV8 packet=$id size=$size\n" if $::debug;
if ($id eq 'AP') { return } # currently stop when the first audio packet is found
#FIXME very unlikely to happen, especially with audio files, but $size could be too big to be kept as an integer, max possible value is 2**70-1
# not sure if a too big value would cause problem with seek/read
# ok for now as it should only affect audio packets, and we stop at the first one
return if $id eq 'SE'; #stream end packet
$size-= $read-$notheader; # $size is now size of packet without header
seek $fh,-$notheader,1; #position at end of packet header
if ($id eq 'SH' || $id eq 'RG')
{ my $read=read $fh,$buf,$size;
if ($read!=$size) { warn "mpcv8: packet $id too short\n"; return }
if ($id eq 'SH') # stream header packet
{ my ($crc,$version,$samples,$silence,$freq_bands,$chan_MS_frames)= unpack 'NCwwCC',$buf; # count and silence are BER compressed integer
# $crc is ignored for now
warn "mpcV8: unknown bitstream version $version\n" if $version!=8;
my $freq= $freq_bands>>5;
$info{rate}= $freq<4 ? $freq[$freq] : 0; #freq can be 0 to 7, but only defined up to 3
$info{channels}= 1+($chan_MS_frames>>4);
$info{max_bands}= $chan_MS_frames & 0b11111;
$info{mid_side_stereo}= $chan_MS_frames & 0b1000 ? 1 : 0;
$info{frames_per_audio_packet}= 4**($chan_MS_frames & 0b111);
$info{samples}=$samples;
$info{silence_samples}=$silence;
$info{version}=$version;
$info{seconds}= $info{rate} ? ($samples-$silence)/$info{rate} : 0;
$self->{info}=\%info;
}
elsif ($id eq 'RG') # replaygain packet
{ my ($version,$tgain,$tpeak,$again,$apeak)= unpack 'Cs>4',$buf; # "s>": signed big-endian 16bit
$info{replaygain_version}=$version;
$info{track_gain}= (10 ** ($tgain/256/20) / 65535) if $tgain;#formula taken from mutagen
$info{album_gain}= (10 ** ($again/256/20) / 65535) if $again;
$info{track_peak}= 64.82-$tpeak/256 if $tpeak;#formula taken from mutagen
$info{album_peak}= 64.82-$apeak/256 if $apeak;
#ignored for now
}
}
else { seek $fh,$size,1 } #skip
}
};
}
1;

0 comments on commit 48e030e

Please sign in to comment.