WAVEファイルのバイナリ解析


WAVEファイルの構造


ヘッダー

パラメータ byte 内容
chunkId 4 "RIFF"
chunkSize 4 size + 36
formType 4 "WAVE"

fmtチャンク

パラメータ byte 内容
chunkID 4 "fmt "
chunkSize 4 16
waveFormatType 2 PCM=1
channel 2 mono=1, stereo=2
samplePerSec 4 ex. 44100
bytePerSec 2
blockSize 2 音データあたりのbyte数
bitsPerSample 2 量子化精度、8=8bit,16=16bit

dataチャンク

パラメータ byte 内容
chunkID 4 "data"
chunkSize 4 size
data size 音データ



音データ


WAVEファイルの構造より、つまりは44byte目からが音データとなる。音データはsigned int(例外もあり)で表現されている。



channel blockSize 内容
1 1 8bitモノラル
2 1 8bitステレオ
2 1 16bitモノラル
2 2 16bitステレオ


例えば、16bitモノラルでは音データ単位あたりの数値が2byteで表現される。 つまり1サンプルは-32768〜32768の範囲内の値となる。

つぎはaudacityで作った440Hzの正弦波をodコマンドを使って音データを表示した場合の例だ。

od -j 44 -td2 raw/sin440.wav | more

0000054        -1    1644    3276    4904    6501    8087    9625   11143
0000074     12597   14023   15375   16679   17913   19075   20166   21175
0000114     22100   22943   23689   24349   24907   25372   25734   25997
0000134     26156   26215   26169   26019   25770   25418   24964   24418
0000154     23768   23033   22201   21282   20287   19201   18052   16818
0000174     15531   14174   12768   11305    9803    8261    6685    5085
0000214      3463    1828     187   -1455   -3094   -4716   -6325   -7904
0000234     -9456  -10969  -12438  -13860  -15226  -16534  -17776  -18947
0000254    -20046  -21063  -22002  -22849  -23611  -24277  -24850  -25323
0000274    -25699  -25971  -26144  -26214  -26178  -26043  -25803  -25461
0000314    -25025  -24480  -23851  -23119  -22299  -21394  -20401  -19331
0000334    -18183  -16964  -15680  -14331  -12930  -11474   -9976   -8438



Pythonで音バイナリデータを数値に変換する



open(filename, 'rb')でwavファイルを読み込み44byte移行の音データをstruct.unpack('でsamplesという配列に格納する。
unpackBオブションは1byteをunsigned charとして読み込むという意味である。

しかしグラフ表示にしたり、フィルター処理をしたい場合にはこのままだと不便なので-32768〜32768の数値データに変換する。ここで注意したいのが
16bitモノラルの場合、1サンプル(2byte)はリトルエンディアンで並んでいる。つまり次のようにして2byteの数値を格納している。

1byte目 2byte目
下の桁 上の桁

このデータ配列を変換するために次のようなプログラムを作った。

1. 配列内のunsigned char値を2進数に変換し、2byte目を8bit分シフトする。
2. それを1byte目と足し合わせる。32767より大きい値は負の値であるので65536を引いてあげる。

def convert_int16_samples(samples):
    data = []
    for i in range(0, int(len(samples) / 2), 2):
        s1 = samples[i]
        s2 = samples[i + 1]
        left_b = s2 << 8
        right_b = s1
        s = (left_b | right_b)
        if s > 32767:
            s = s - 65536
        data.append(s)
    return data

※numpyのnp.array(samples, dtype=np.int16)を使えばもっと簡単に書くことができてしまうが、WAVEの構造を学ぶためには上記のようなプログラムが役に立つだろう。

この配列データであれば、pyplotなどで簡単にグラフ表示ができる。





その他メモ

1byte (8bit) -> 256 まで表現可能

2byte (16bit) -> 65,536 まで表現可能

4byte (32bit) -> 4,294,967,296 まで表現可能




参考