WAVEフォーマット解析

どうもbchunkを騙して使うのはスマートでない感があるので、bin2wavくらいは自力で実装出来るようにWAVEフォーマットについて調べてみた。
http://ccrma.stanford.edu/courses/422/projects/WaveFormat/を参考にすると、ソースをCDDAに限定した場合はNumChannels=2、SampleRate=44100、BitsPerSample=16と定まるので、WAVEヘッダはChunkSizeとSubchunk2Size以外のフィールドが完全に固定された44バイトになることが分かる。
固定されていないChunkSizeとSubchunk2Sizeも、Subchunk2Size=(data=生PCM=binファイル)の大きさ、ChunkSize=32+Subchunk2Sizeのようなので、WAVEヘッダを生成するのに必要なデータはbinファイルの大きさだけということになる。
packを使うと、

$ ruby -e 's=File.size $*[0];print ["RIFF",36+s,"WAVE","fmt ",16,1,2,44100,44100*2*16/8,2*16/8,16,"data",s].pack("a4Va4a4Vv2V2v2a4V")' data.bin

ワンライナーでWAVEヘッダが出力できる。

$ ruby -e 's=File.size $*[0];print ["RIFF",36+s,"WAVEfmt",16,1,2,44100,176400,4,16,"data",s].pack("a4VA8Vv2V2v2a4V")' data.bin

までは短くなったけど、もっと短くする方法もあるだろう。data部分は前回書いたようにバイトオーダーを逆にする必要があったことを思い出してbin2wav.shを書くと、

#!/bin/sh
N="${1%.*}"
ruby -e 's=File.size $*[0];print ["RIFF",36+s,"WAVEfmt",16,1,2,44100,176400,4,16,"data",s].pack("a4VA8Vv2V2v2a4V")' "$1" > "$N.wav"
dd if="$1" conv=swab >> "$N.wav"

になる。md5sumでcdparanoiaが出力したwavファイルとの一致を確認。
しかし、bchunkで作ったwavファイルとは一致しない。何処が違うのか調べてみると、

$ ls -l data.bin
-rw-r--r-- 1 gama users 164240160 2007-03-04 11:33 data.bin
$ ruby -e 'p ARGF.read(44).unpack("V11")' data.wav
[1179011410, 164242548, 1163280727, 544501094, 16, 131073, 44100, 176400, 1048580, 1635017060, 164242512]

44バイトのヘッダを4バイトずつで区切ると最後がSubchunk2Sizeになるはずだが、数値がbinファイルのサイズよりも2352大きい。google:2352+wavなどして調べてみると、2352というのは1フレーム*1辺りのバイト数であるセクタ長に因るものであることが分かる。2352=176400/75だ。

Usage: bchunk [-v] [-r] [-p (PSX)] [-w (wav)] [-s (swabaudio)]

Example: bchunk foo.bin foo.cue foo
-v Verbose mode
-r Raw mode for MODE2/2352: write all 2352 bytes from offset 0 (VCD/MPEG)
-p PSX mode for MODE2/2352: write 2336 bytes from offset 24
(default MODE2/2352 mode writes 2048 bytes from offset 24)
-w Output audio files in WAV format
-s swabaudio: swap byte order in audio tracks

とかが怪しいとは思うけど、結局どういう妥当性がある挙動なのか分からず終い。RIFFに由来するヘッダなんて真面目に読んでるソフトウェアは少なそうだし気にすることないのかな?

*1:1/75秒