Audio-Mad

 view release on metacpan or  search on metacpan

lib/Audio/Mad/Util.pm  view on Meta::CPAN

package Audio::Mad::Util;
require 5.6.0;

use strict;
use warnings;

use Audio::Mad qw(:all);

use Exporter;
our @ISA = ('Exporter');

our @EXPORT_OK   = qw(mad_stream_info mad_parse_xing mad_cbr_seek mad_xing_seek);
our %EXPORT_TAGS = ( all => [@EXPORT_OK] );

sub mad_stream_info {
	my ($fh, $do_toc, $do_ex) = @_;
	my ($h, $buf) = ({}, '');

	$do_toc = 0 unless (defined($do_toc) && $do_toc ne '');
	$do_ex  = 1 unless (defined($do_ex)  && $do_ex  ne '');
	
	## fail if file is zero bytes in length
	return undef if (($h->{f_size} = (stat($fh))[7]) == 0);
	
	## get a new stream object and turn off crc 
	## checking,  as many encoders are broken
	## and it isn't too terribly useful in decoding but 
	## works nicely for checking stream validity.

	## creates a mad_stream structure
	my $stream = new Audio::Mad::Stream(MAD_OPTION_IGNORECRC);

	## creates a mad_frame structure
	my $frame  = new Audio::Mad::Frame;
	
	## setup some initial counter type things.
	$h->{s_frames}   = 0;
	$h->{s_vbr}      = 0;

	## s_size is the size of the stream,  and we attempt to
	## correct this key for ID3 tags and other extraneous
	## data -- in the case of a xing header,  we use it's
	## size variable if possible..
	$h->{s_size}     = $h->{f_size};

	## create a mad_timer structure
	$h->{s_duration} = new Audio::Mad::Timer;

	## we use these variables to calaculate a toc strucutre
	## when requested by the do_toc argument to this sub,
	## it provides an easy index for searching through the
	## stream.
	my $scale = $h->{s_size} / 100;
	my @toc   = ();
	
	## to start,  we cycle on a frame-by-frame basis..
	FRAME: while(1) {

		## calls mad_frame_decode,  and returns the
		## same value (-1 for error);
		if ($frame->decode_header($stream) == -1) {
			## corresponds to MAD_RECOVERABLE(stream->error),
			## if it is recoverable,  just skip to the next
			## frame.
			next FRAME if ($stream->err_ok());

			## otherwise,  if we get an error due to and
			## end of buffer condition (BUFLEN),  or because 
			## the buffer was never set (BUFPTR) take action
			## to fill the buffer.
			if (
			    $stream->error == MAD_ERROR_BUFLEN || 
			    $stream->error == MAD_ERROR_BUFPTR
			) {
				## this is to capture the frame fragment at
				## the end of the buffer,  if we don't do this
				## we lose the frame.
				$buf = substr($buf, $stream->next_frame);
			
				## attempt to read more data onto the end of
				## the buffer,  we drop out of our loop at
				## the end of the file.
				last FRAME if (sysread($fh, $buf, 128000, length($buf)) == 0);
				
				## call mad_stream_buffer on the newly created buffer.
				$stream->buffer($buf);
				
				## from here,  we restart our loop on the frame
				## we just failed to decode.
				redo FRAME;
			}
			
			## otherwise,  it's a fatal error that we're not 
			## prepared to deal with..
			return undef;
		}
	
		## keep a tally of the frames processed during 
		## this loop,  and evaluate this block only
		## during the first frame processed..
		unless ($h->{s_frames}++) {
			
			## this is some fairly obvious setup..
			
			$h->{s_bitrate}    = 
			$h->{s_avgrate}    = $frame->bitrate();



( run in 2.602 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )