[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 " 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 } } <>Sound | File