PythonでWAVEファイルのバイナリデータを解析する

PythonでWAVEファイルのバイナリデータを解析する
PythonでWAVEファイルのバイナリデータを解析する

音データのファイルであるWAVEファイルの中身を調べてみました。WAVEファイルはバイナリで書き込まれてますが、Pythonやシェルを使えば比較的簡単にバイナリを解析できます。この技術は、WAVEファイル以外のバイナリファイルにも応用できるので役立つはずです。

WAVEファイルの構造

WAVEファイルの構造
WAVEファイルの構造

ヘッダー

パラメータbyte内容
chunkId4"RIFF"
chunkSize4size + 36
formType4"WAVE"

fmtチャンク

パラメータbyte内容
chunkID4"fmt "
chunkSize416
waveFormatType2PCM=1
channel2mono=1, stereo=2
samplePerSec4ex. 44100
bytePerSec2
blockSize2音データあたりのbyte数
bitsPerSample2量子化精度、8=8bit,16=16bit

dataチャンク

パラメータbyte内容
chunkID4"data"
chunkSize4size
datasize音データ

音データ

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

channelblockSize内容
118bitモノラル
218bitステレオ
2116bitモノラル
2216bitステレオ

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

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

od -j 44 -td2 raw/sin440.wav | more
sh
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を引いてあげる。

py
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 まで表現可能

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム

Python学習にオススメの本をご紹介!
Pandasでデータサイエンスはじめよう!
スクレイピングにオススメの書籍

▼ Beautiful Soup4を使ったWebクローリングをはじめ、表データをpandasやOpenPyXL、matplotでデータ解析、グラフ表示などのスクレイピングのやり方が分かりやすく説明されてます。図解が多いのでPython初心者の方でも読み進められる内容となってます。

関連記事