WAVEファイルのバイナリ解析【Python・サウンドプログラミング】


画像の拡大

音データのファイルであるWAVEファイルの中身を調べてみました。WAVEファイルはバイナリで書き込まれていますが、Pythonやシェルを使えば比較的簡単にバイナリを解析することができます。この技術は、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('&ltB', wav_file.read(1))[0] で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 まで表現可能

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
Python学習にオススメの本
関連記事