# $Id: Mp3info.pm,v 1.3 2003/10/24 19:00:46 ianb Exp $
package MP3::Archive::Lint::Tools::Mp3info;

use strict;
use warnings;

use vars qw(@ISA $VERSION);
@ISA = qw(MP3::Archive::Lint::Tool);
$VERSION = '0.01';

=head1 mp3info - Tool to check mp3 files using mp3info

Tests mp3 files using the mp3info command. If quick mode (-q) is
enabled, only does a full mp3 scan if the mp3 is variable bit rate (vbr).

There are two programs called mp3info available on the net. This
expects the one that is at L<http:E<sol>E<sol>ibiblio.orgE<sol>mp3info> It is also
the version that Debian users get if they type C<apt-get install
mp3info>

=head2 Tests

=over 4

=cut

sub new
{
	my $proto=shift;
	my $class=ref($proto) || $proto;
	my $opts=shift;
	my $self=$class->SUPER::new($opts);;
	bless($self,$class);


	$self->{cmd}=$self->findprogram("mp3info");
	unless (defined $self->{cmd})
	{
		$self->say("mp3info not found,test skipped");
		return $self;
	}
#	$self->settests("channels","samplerate","bitrate","framecheck");

	return $self;
}


sub initscan
{
	my $self=shift;

	return 0 unless $self->{filename}=~/\.mp[23]$/i;
	return 0 unless -f $self->{file};

	my $opencmd;
	if($self->config->quickscan)
	{
		$opencmd="$self->{cmd} -ra -p 'mode:%o\nsamplerate:%Q\nbitrate:%r\ncrc:%E\nlayer:%L\nmpegversion:%v\n' $self->{qfile} 2>/dev/null |";
	}
	else
	{
		$opencmd="$self->{cmd} -F -ra -p 'mode:%o\nsamplerate:%Q\nbadframes:%b\ngoodframes:%u\nbitrate:%r\ncrc:%E\nlayer:%L\nmpegversion:%v\n' $self->{qfile} 2>/dev/null |";
	}
	
	unless(open(I,$opencmd))
	{
		$self->say("cannot run mp3info:$!");
		return;
	}

	my %mp3info;
	my $totframes;
	
	while(<I>)
	{
		chomp;
		my ($key,$val)=split(/:/);
		$self->{info}{$key}=$val;
	}
	unless(close(I)) # mp3info returned nonzero
	{
		$self->{corrupt}=1;
		$self->settests("corrupt");
	}
	else
	{
		$self->{corrupt}=0;
		$self->settests("channels","samplerate","bitrate","framecheck");
	}
	return 1;
}

sub corrupt
{
	my $self=shift;
	#errors reported elsewhere 
	return if((-z $self->{file}) ||(!-e $self->{file}));
	if($self->{corrupt})
	{
		$self->say("mp3 corrupt");
	}
}


=item B<channels>

Tests mp3 is stereo.  

=cut

sub channels
{
	my $self=shift;
	unless($self->{info}{mode}=~/stereo/i)
	{
		if($self->{info}{mode}=~/mono/i)
		{
			$self->say("mono");
		}
		elsif($self->{info}{mode}!~/dual channel/i)
		{
			$self->say("not stereo:$self->{info}{mode}");
		}
	}
}

=item B<samplerate>

Tests mp3 samplerate is at least $lint_minsamplerate Hz.

Variables:

=over 4

=item $lint_minsamplerate

minimum samplerate to accept (number, default 44100)

=back

=cut

sub samplerate
{
	my $self=shift;
	my $minsamplerate=$self->config->get("lint_minsamplerate");
	if($self->{info}{samplerate} < $minsamplerate)
	{
		$self->say("low samplerate:$self->{info}{samplerate}Hz (min ${minsamplerate}Hz)");
	}
}

=item B<bitrate>

Tests mp3 is at least $lint_minbitrate_mp3 kbps. To allow for vbr mp3s with
an average bitrate slightly lower than the target, you may wish to lower it
slightly, eg for 128kbps, specify 120.  Variables:

=over 4

=item $lint_minbitrate_mp3

minimum bitrate to accept (number, default 120)

=back

=cut


sub bitrate
{
	my $self=shift;
	
	my $minbitrate=$self->config->get("lint_minbitrate_mp3");
		
	if($self->{info}{bitrate}<$minbitrate)
	{
		my $rounded=sprintf("%.0f",$self->{info}{bitrate});
		my $format;
		if($self->{info}{bitrate} == $rounded) # ie is integer, ie not VBR
		{
			$format="%s%.0fkbps (min %.0fkbps)";
		}
		else
		{
			$format="%s%.2fkbps (min %.1fkbps)";
		}
		$self->say(sprintf($format,"low bitrate:",$self->{info}{bitrate},$minbitrate));
	}
}

=item B<framecheck>

Tests for broken mp3s. As mp3info and mp3_check often give different
numbers of broken frames, both tests are run, and there are options to
specify a minimum number of broken frames before complaining.

Variables:

=over 4

=item $lint_maxbadframes_mp3info

number of bad frames to accept before complaining
(number, default 0)

=back

=cut

sub framecheck
{
	my $self=shift;
	
	unless($self->config->quickscan)
	{
		my $maxbadframes=$self->config->get("lint_maxbadframes_mp3info");
		
		my $totframes=$self->{info}{badframes}+$self->{info}{goodframes};
		
		if($self->{info}{badframes}>$maxbadframes) 
		{
			my $percentbad=sprintf("%.2f",($self->{info}{badframes}/$totframes)*100);
			if($self->{info}{goodframes}==0)
			{
				$self->say("mp3 corrupt (0 good frames)");
			}
			else
			{
				$self->say("bad frames:$self->{info}{badframes}/$totframes ($percentbad\%)");
			}
		}
	}
}
	
#mp3info -F -ra -p format
#%E CRC
#%L Layer
#%v MPEG version

#%o stereo/mono mode
#%Q sampling frequency in Hz
#%u good frames
#%b corrupt frames
#%r bit rate in kbps

=back

=cut	

1;
