Keith Vetter 2005-02-28 : Recently I've had to dig deep into various audio and video file formats. Here's a little tool I wrote to parse and dump out information about
WAV audio files. (There's also code on the
WAV page to do this but it's not very robust. It assumes that there are only two chunks and that the first chunk is always 'fmt '. It also can't handle the bizarre big-endian format, also known as RIFX files--but then again RealPlayer can't handle them either.)
##+##########################################################################
#
# wavDump.tsh -- Parses and dumps wav audio files
# by Keith Vetter - Feb 28, 2005
#
##+##########################################################################
#############################################################################
proc comma { num } {
while {[regsub {^([-+]?[0-9]+)([0-9][0-9][0-9])} $num {\1,\2} num]} {}
return $num
}
##+##########################################################################
#
# fmtChunk -- parses and dumps the fmt chunk
#
proc fmtChunk {len} {
global bytesSec scanFmt fin
binary scan [read $fin 16] $scanFmt(fmt) \
fmt chan sampleSec bytesSec align bitsSample
set line " fmt: $fmt "
append line [expr {$chan == 1 ? "mono" : $chan == 2 ? "stereo" : "$chan channels"}]
append line " [comma $sampleSec]Hz "
append line "[comma $bytesSec] bytes/sec "
append line "[comma $align] bytes/sample "
append line "[comma $bitsSample] bits/sample "
if {$fmt != 1} { ;# Extra header stuff
binary scan [read $fin 2] $scanFmt(16) extra
append line "extra: $extra"
}
puts $line
}
##+##########################################################################
#
# DoFile -- Parse one wave file
#
proc DoFile {fname} {
global fin scanFmt fmtScan
puts $fname
set fin [open $fname "r"]
fconfigure $fin -translation binary
set magic [read $fin 4]
array set scanFmt {16 s 32 i fmt "ssiiss"} ;# Little endian by default
if {$magic eq "RIFX"} { ;# This is big-endian format
puts " big-endian format"
array set scanFmt {16 S 32 I fmt "SSIISS"}
} elseif {$magic ne "RIFF"} {
puts "Bad magic number: '$magic'"
exit
}
binary scan [read $fin 4] $scanFmt(32) len ;# Should be file length - 8
set type [read $fin 4]
if {$type ne "WAVE"} {
puts "Not a WAVE file: '$type'"
exit
}
set dataLen 0
while {1} { ;# Loop through every chunk
set chunk [read $fin 4] ;# Chunk type
if {[eof $fin]} break
binary scan [read $fin 4] $scanFmt(32) len ;# Chunk length
set eoc [expr {[tell $fin] + $len}] ;# End of chunk
set proc "[string tolower [string trim $chunk]]Chunk"
if {[info procs $proc] ne ""} {
$proc $len
} else {
puts " $chunk: length [comma $len]"
if {$chunk eq "data"} { ;# Possibly multiple data chunks
incr dataLen $len
}
}
seek $fin $eoc start
}
set duration [expr {double($dataLen) / $::bytesSec}]
puts " => duration: [format %.3g $duration] sec"
close $fin
puts ""
}
################################################################
################################################################
if {$argc == 0} {
puts stderr "usage: wavDump.tsh <wave files>"
exit
}
foreach arg $argv {
set arg [file normalize $arg] ;# Make ok for glob
foreach fname [glob -nocomplain $arg] { ;# Do wildcard expansion if any
DoFile $fname
}
}