[Slim-Checkins] r10247 - in /trunk/server: Slim/Formats/MP3.pm Slim/Player/Source.pm lib/MP3/Info.pm

andy at svn.slimdevices.com andy at svn.slimdevices.com
Fri Oct 6 12:25:27 PDT 2006


Author: andy
Date: Fri Oct  6 12:25:25 2006
New Revision: 10247

URL: http://svn.slimdevices.com?rev=10247&view=rev
Log:
Fix a bug where gapless mp3 files with no ID3 tags weren't scanned properly.  Display information on whether or not a track will play gapless when using d_source

Modified:
    trunk/server/Slim/Formats/MP3.pm
    trunk/server/Slim/Player/Source.pm
    trunk/server/lib/MP3/Info.pm

Modified: trunk/server/Slim/Formats/MP3.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Formats/MP3.pm?rev=10247&r1=10246&r2=10247&view=diff
==============================================================================
--- trunk/server/Slim/Formats/MP3.pm (original)
+++ trunk/server/Slim/Formats/MP3.pm Fri Oct  6 12:25:25 2006
@@ -182,7 +182,7 @@
 
 	my ($start, $end) = $class->findFrameBoundaries($fh, 0, $info->{'SIZE'});
 
-	if ($start) {
+	if ( defined $start ) {
 		$info->{'OFFSET'} = $start;
 
 		if ($end) {
@@ -298,6 +298,8 @@
 
 sub findFrameBoundaries {
 	my ($class, $fh, $offset, $seek) = @_;
+	
+	binmode $fh;
 
 	my ($start, $end) = (0, 0);
 
@@ -350,6 +352,35 @@
 	} else {
 		return wantarray ? ($start, $end) : $start;
 	}
+}
+
+=head2 getFrame( $fh )
+
+Returns the first MPEG::Audio::Frame object found in the given filehandle.
+
+=cut
+
+sub getFrame {
+	my ( $class, $fh ) = @_;
+
+	binmode $fh;
+	
+	my $offset = tell $fh;
+		
+	# dup the filehandle, as MPEG::Audio::Frame uses read(), and not sysread()
+	open(my $mpeg, '<&=', $fh) or do {
+		errorMsg("getFrames: Couldn't dup filehandle!\n");
+		return;
+	};
+	
+	my $frame = MPEG::Audio::Frame->read($mpeg);
+	
+	# Seek back to where we started
+	seek $fh, $offset, 0;
+	
+	close $mpeg;
+	
+	return $frame;
 }
 
 =head2 scanBitrate( $fh )

Modified: trunk/server/Slim/Player/Source.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Player/Source.pm?rev=10247&r1=10246&r2=10247&view=diff
==============================================================================
--- trunk/server/Slim/Player/Source.pm (original)
+++ trunk/server/Slim/Player/Source.pm Fri Oct  6 12:25:25 2006
@@ -1493,6 +1493,35 @@
 					};
 					$offset -= $seekoffset;
 				}
+				
+				if ( $::d_source && $format eq 'mp3' ) {
+					# report whether the track should play back gapless or not
+					
+					my $streamClass = streamClassForFormat($client, 'mp3');
+					my $frame       = $streamClass->getFrame( $client->audioFilehandle );
+					
+					# Look for the LAME header and delay data in the frame
+					my $io = IO::String->new( \$frame->asbin );
+					
+					if ( my $info = MP3::Info::get_mp3info($io) ) {
+						if ( $info->{LAME} ) {
+							$::d_source && msg('MP3 file was encoded with ' . $info->{LAME}->{encoder_version} . "\n");
+							
+							if ( $info->{LAME}->{start_delay} ) {
+								$::d_source && msgf("MP3 file contains encoder delay information (%d/%d), will be played gapless\n",
+									$info->{LAME}->{start_delay},
+									$info->{LAME}->{end_padding},
+								);
+							}
+							else {
+								$::d_source && msg("MP3 file does not contain encoder delay information, will not play back gapless\n");
+							}
+						}
+						else {
+							$::d_source && msg("MP3 file was not encoded with LAME, will not play back gapless\n");
+						}
+					}
+				}
 
 				# pipe is a socket
 				if (-p $filepath) {
@@ -1838,9 +1867,9 @@
 }
 
 sub streamClassForFormat {
-	my $client = shift;
-
-	my $streamFormat = $client->streamformat;
+	my ( $client, $streamFormat ) = @_;
+
+	$streamFormat ||= $client->streamformat;
 
 	if (Slim::Formats->loadTagFormatForType($streamFormat)) {
 

Modified: trunk/server/lib/MP3/Info.pm
URL: http://svn.slimdevices.com/trunk/server/lib/MP3/Info.pm?rev=10247&r1=10246&r2=10247&view=diff
==============================================================================
--- trunk/server/lib/MP3/Info.pm (original)
+++ trunk/server/lib/MP3/Info.pm Fri Oct  6 12:25:25 2006
@@ -1476,14 +1476,14 @@
 		return undef;
 	}
 
-	if (not -s $file) {
-		$@ = "File is empty";
-		return undef;
-	}
-
 	if (ref $file) { # filehandle passed
 		$fh = $file;
 	} else {
+		if (not -s $file) {
+			$@ = "File is empty";
+			return undef;
+		}
+		
 		if (not open $fh, '<', $file) {
 			$@ = "Can't open $file: $!";
 			return undef;
@@ -1547,7 +1547,9 @@
 	}
 
 	my $vbr = _get_vbr($fh, $h, \$off);
-
+	
+	my $lame = _get_lame($fh, $h, \$off);
+	
 	seek $fh, 0, SEEK_END;
 	$eof = tell $fh;
 	seek $fh, -128, SEEK_END;
@@ -1566,11 +1568,11 @@
 	$h->{size} = $eof - $off;
 	$h->{offset} = $off;
 
-	return _get_info($h, $vbr);
+	return _get_info($h, $vbr, $lame);
 }
 
 sub _get_info {
-	my($h, $vbr) = @_;
+	my($h, $vbr, $lame) = @_;
 	my $i;
 
 	# No bitrate or sample rate? Something's wrong.
@@ -1619,6 +1621,10 @@
 	# should we just return if ! FRAMES?
 	$i->{FRAME_LENGTH}	= int($h->{size} / $i->{FRAMES}) if $i->{FRAMES};
 	$i->{FREQUENCY}		= $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}];
+	
+	if ($lame) {
+		$i->{LAME} = $lame;
+	}
 
 	return $i;
 }
@@ -1704,7 +1710,7 @@
 	}
 
 	_vbr_seek($fh, \$off, \$bytes);
-	return unless $bytes eq 'Xing';
+	return unless $bytes =~ /(?:Xing|Info)/;
 
 	_vbr_seek($fh, \$off, \$bytes);
 	$vbr{flags} = _unpack_head($bytes);
@@ -1734,6 +1740,33 @@
 
 	$$roff = $off;
 	return \%vbr;
+}
+
+# Read LAME info tag
+# http://gabriel.mp3-tech.org/mp3infotag.html
+sub _get_lame {
+	my($fh, $h, $roff) = @_;
+	
+	my($off, $bytes, @bytes, %lame);
+
+	$off = $$roff;
+	
+	# Encode version, 9 bytes
+	_vbr_seek($fh, \$off, \$bytes, 9);
+	$lame{encoder_version} = $bytes;
+
+	return unless $bytes =~ /^LAME/;
+
+	# There's some stuff here but it's not too useful
+	_vbr_seek($fh, \$off, \$bytes, 12);
+	
+	# Encoder delays (used for gapless decoding)
+	_vbr_seek($fh, \$off, \$bytes, 3);
+	my $bin = unpack 'B*', $bytes;
+	$lame{start_delay} = unpack('N', pack('B32', substr('0' x 32 . substr($bin, 0, 12), -32)));
+	$lame{end_padding} = unpack('N', pack('B32', substr('0' x 32 . substr($bin, 12, 12), -32)));
+	
+	return \%lame;
 }
 
 # _get_v2head(file handle, start offset in file);



More information about the checkins mailing list