#!/usr/bin/perl

###
### Print the duration of an audio file in useful formats,
### including partial cdda frames (1/75 sec).
### Uses sndfile-info(1) to examine the audio file, so many
### sound formats are supported.
###

use Getopt::Std;

$sph = 60 * 60;
$cdrate = 44100; # scans per second
$cdfps = 75; # cdda frames per second
$cdscpf = $cdrate / $cdfps; # scans per frame: 44100/75 = 588
$head_printed = 0;

@ARGV or die "No file or #samples specified";

getopts('v');

if (@ARGV[0] =~ /^\d+$/ and ! -f $ARGV[0]) {
$scans = $ARGV[0];
die "Bad scans value: $scans" unless $scans > 0;
shift(@ARGV);
}
if (@ARGV and @ARGV[0] =~ /^\d+$/ and ! -f $ARGV[0]) {
$rate = $ARGV[0];
die "Bad data rate value: $rate" unless $rate > 0;
shift(@ARGV);
}

$rate ||= $cdrate;

foreach my $arg (@ARGV) {

my $quarg;

$arg =~ s/%=20/ /g; # unquote spaces

unless ( -f $arg and -r $arg) {
warn "file '$arg' doesn't exist or not readable";
next;
}
# quote funny chars in filename to protect from shell
($quarg = $arg) =~ s/([\W])/\\$1/g;

open INFO, "sndfile-info $quarg|" or
die "can't open file '$arg': $@";

while (<INFO>) {
if (/sample\s+rate/i) {
$rate = (split(/\s*\:\s*/))[1] || die "bad sample rate";
next;
}
if (/frames/i) {
$scans = (split(/\s*\:\s*/))[1] || die "bad scan count";
next;
}
if (/channels/i) {
$channels = (split(/\s*\:\s*/))[1] || die "bad channel count";
next;
}
if (/signal\s+max/i) {
$max = (split(/\s*\:\s*/))[1] || die "bad sample rate";
next;
}
if (/format not recognised/i) {
die "can't handle this type of sound file";
}
}

unless ($rate and $scans) {
warn "can't extract needed info from file '$arg'";
next;
}

{
use integer;

# total whole seconds
$isec = $scans / $rate;

$h = $isec / $sph;

# seconds, minutes beyond $h hours
$isec %= $sph;
$m = $isec / 60;

# whole seconds beyond $m minutes
$s = $isec %= 60;

# milliseconds (ms) beyond $isec (scale #'s for int calc)
$ms = (($scans * 10)/($rate/100)) % 1000;

# whole cdda frames past whole second
$cdf = ($scans % $rate) / $cdscpf;

# partial cdda frames (0-587 samples)
$xcdf = $scans % $cdscpf;
}

# seconds, 'n.m
$fsec = sprintf("%.3f", $scans/$rate);
# hh:mm:ss.sss
$fhms = sprintf("%02d:%02d.%03d", ($h*60 + $m), $s, $ms);
# cd audio mm:ss:cdf.fff
$fcdf = $rate == $cdrate ?
sprintf("%02d:%02d:%03d+%03d",
($h*60 + $m), $s, $cdf, $xcdf, $cdfps) :
sprintf("%02d:%02d:---+---",
($h*60 + $m), $s, $cdf, $xcdf, $cdfps);
print join("\t", 'seconds', 'hh:mm:s.s', 'mm:ss:cdf+s', 'file'), "\n"
unless $head_printed++;
print join("\t", $fsec, $fhms, $fcdf, $arg), "\n";

if ($opt_v) {

print "[$arg]\n";

printf "%g seconds\n", $scans/$rate;

printf "%02d:%02d.%03d (m:s.ms)\n", ($h*60 + $m), $s, $ms;

if ($rate == $cdrate) {
printf "%02d:%02d:%03d+%03d (m:s:frames+samples @%gfps)\n",
($h*60 + $m), $s, $cdf, $xcdf, $cdfps;

printf "%d+%03d (frames+samples @%gfps)\n",
int ($scans/$cdscpf),$scans % $cdscpf, $cdfps;
}
# max audio level 0-32768 n.m dB
printf "%s\n", $max;
}
}