# $Id: Au.pm,v 1.3 2003/12/14 09:43:56 ianb Exp $
package MP3::Archive::Lint::Tools::Au;

use strict;
use warnings;

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

=head1 au - Tool to check .au files

Checks au files (Sun/NeXT/DEC audio) by parsing the header.

=head2 Tests

=over 4

=item B<data>

Tests if file is a valid au file.

=cut

# data is not actually a test, message generated by parseauheader
# called from initscan, but indistinguishable to the user

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

	$self->settests("samplerate","samplesize","channels"); 

	%{$self->{auformats}}=(
						   1 =>  { name=>"8-bit ISDN mu-law",
								   bits=>8, },
						   2 =>  { name=>"8-bit linear PCM [REF-PCM]",
								   bits=>8, },
						   3 =>  { name=>"16-bit linear PCM",
								   bits=>16, },
						   4 =>  { name=>"24-bit linear PCM",
								   bits=>24, },
						   5 =>  { name=>"32-bit linear PCM",
								   bits=>32, },
						   6 =>  { name=>"32-bit IEEE floating point",
								   bits=>32, },
						   7 =>  { name=>"64-bit IEEE floating point",
								   bits=>64, },
						   23 => { name=>"8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice data encoding)",
								   bits=>8, },
						   24 => { name=>"compressed (8-bit G.722 ADPCM)",
								   bits=>8, },
						   25 => { name=>"compressed (3-bit G.723 ADPCM)",
								   bits=>3, },
						   26 => { name=>"compressed (5-bit G.723 ADPCM)",
								   bits=>5, },
						   27 => { name=>"8-bit A-law",
								   bits=>8, },);
	return $self;
}


sub initscan
{
	my $self=shift;

	return 0 unless ($self->{filename}=~/\.au$/i);
	return 0 if (-z $self->{file});	
	return 0 if (-d $self->{file});	

	my @ret;
	@ret=$self->parseauheader($self->{file});
	unless(scalar(@ret))
	{
		return 0;
	}

	($self->{magic},
	 $self->{format},
	 $self->{channels},
	 $self->{samplerate}) = @ret;

	return 1;
}

=item B<samplerate>

Tests sample rate of au file is at least $lint_minsamplerate Hz.

Variables:

=over 4

=item $lint_minsamplerate

Minimum sample rate in Hz (number, default 44100)

=back

=cut


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

=item B<samplesize>

Test samplesize of au file is at least $lint_minbitsize bits.

Variables:

=over 4

=item $lint_minbitsize

(number, default 16)

=back

=cut


sub samplesize
{
	my $self=shift;
	my $min=$self->config->get("lint_minbitsize");
	my $bitsize=$self->{auformats}{$self->{format}}{bits};
	if($bitsize < $min)
	{
		$self->say("low bitsize:$bitsize (should be $min)");
	}
}

=item B<channels>

Tests file is stereo.

=cut

sub channels
{
	my $self=shift;
	if($self->{channels} == 1)
	{
			$self->say("mono");
	}
	elsif($self->{channels} > 2)
	{
		$self->say("not stereo:$self->{channels} channels");
	}
}

sub parseauheader
{
	my ($self,$file)=@_;
	unless(open(AU,$file))
	{
		print "cannot open:$file:$!\n";
		return(());
	}
	my $len=24; # how much header we need
	my $buf;
	my $readlen=read(AU,$buf,$len,0);
	close AU;
	return(()) unless($readlen == $len);

	#             magic undef format samplerate channels
	my $beunpack="A4    A8    N      N          N";
	my $leunpack="A4    A8    V      V          V";
	my $bemagic=".snd";
	my $lemagic=0x0064732E;
	my $unpackstr;
	my $snd=substr($buf,0,4);

	if($snd =~ /^$bemagic$/i)
	{
		$unpackstr=$beunpack;
	}
	elsif(unpack("V",$snd) == $lemagic)
	{
		$unpackstr=$leunpack;
	}
	else
	{
		# fake test name, we are called from initscan so one is not set
		$self->say("data:not an au file or header corrupt/missing");
		return(());
	}

	my($magic,undef,$format,$samplerate,$channels)=unpack($unpackstr,$buf);

#	print "magic: $magic\n";
#	print "format: ",$self->{auformats}{$format}{name}," channels: $channels samplerate: $samplerate\n";
	
	return($magic,$format,$channels,$samplerate);
}

# au file format
# Extracted from magic(5) database
## Sun/NeXT audio data
#0	string		.snd		Sun/NeXT audio data:
#>12	belong		1		8-bit ISDN mu-law,
#>12	belong		2		8-bit linear PCM [REF-PCM],
#>12	belong		3		16-bit linear PCM,
#>12	belong		4		24-bit linear PCM,
#>12	belong		5		32-bit linear PCM,
#>12	belong		6		32-bit IEEE floating point,
#>12	belong		7		64-bit IEEE floating point,
#>12	belong		23		8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice data encoding),
#>12	belong		24		compressed (8-bit G.722 ADPCM)
#>12	belong		25		compressed (3-bit G.723 ADPCM),
#>12	belong		26		compressed (5-bit G.723 ADPCM),
#>12	belong		27		8-bit A-law,
#>20	belong		1		mono,
#>20	belong		2		stereo,
#>20	belong		4		quad,
#>16	belong		>0		%d Hz
## DEC systems (e.g. DECstation 5000) use a variant of the Sun/NeXT format
## that uses little-endian encoding and has a different magic number
#0	lelong		0x0064732E	DEC audio data:
#>12	lelong		1		8-bit ISDN mu-law,
#>12	lelong		2		8-bit linear PCM [REF-PCM],
#>12	lelong		3		16-bit linear PCM,
#>12	lelong		4		24-bit linear PCM,
#>12	lelong		5		32-bit linear PCM,
#>12	lelong		6		32-bit IEEE floating point,
#>12	lelong		7		64-bit IEEE floating point,
#>12	lelong		23		8-bit ISDN mu-law compressed (CCITT G.721 ADPCM voice data encoding),
#>20	lelong		1		mono,
#>20	lelong		2		stereo,
#>20	lelong		4		quad,
#>16	lelong		>0		%d Hz

=back

=cut

1; 
