Python3系でwavファイルをwaveライブラリでopenしたらヘッダ情報が消えていて詰んだ話

Yu Kato
5 min readSep 17, 2019

ローカルPCのディレクトリに保存したwavファイルを読み込んで、form/data形式のBodyに入れてPOSTのAPIを叩きたい時がありました。

wavファイルを開く方法は、ビルドインのopen()関数と、waveライブラリのopen()関数を使うかの2種類があります。

wavファイルの内部構造について、詳しくなかったので、特にどちらでも良いと思い、後者のwaveライブラリを使うことにしました。

wr = wave.open(file, 'rb')

すると、wavファイルをうまく再生することができない。。。

いざ調査

調査をすると、ヘッダーの情報が欠けているのが原因だということがわかりました。

waveライブラリを使った場合、ファイルを読み込む際に、音声データのみを抽出しているかもしれません。

wavファイルの構造は以下の通り

波形データ以外にこんなにヘッダー情報があるんですね。。なめてました。すいませんー

次にwaveライブラリでopen()が呼ばれたときに何が起こっているかソースコードを読みながら確認してみます。

def open(f, mode=None):  
if mode is None:
if hasattr(f, 'mode'):
mode = f.mode
else:
mode = 'rb'
if mode in ('r', 'rb'):
return Wave_read(f)

open(file_name, ‘rb’)で呼び出すので、Wave_readクラスがreturnされます。

class Wave_read:
====================
====================
def __init__(self, f):
self._i_opened_the_file = None
if isinstance(f, str):
f = builtins.open(f, 'rb')
self._i_opened_the_file = f
# else, assume it is an open file object already
try:
self.initfp(f)
====================
==================== def initfp(self, file):
self._convert = None
self._soundpos = 0
self._file = Chunk(file, bigendian = 0)
if self._file.getname() != b'RIFF':
raise Error('file does not start with RIFF id')
if self._file.read(4) != b'WAVE':
raise Error('not a WAVE file')
self._fmt_chunk_read = 0
self._data_chunk = None
while 1:
self._data_seek_needed = 1
try:
chunk = Chunk(self._file, bigendian = 0)
except EOFError:
break
chunkname = chunk.getname()
if chunkname == b'fmt ':
self._read_fmt_chunk(chunk)
self._fmt_chunk_read = 1
elif chunkname == b'data':
if not self._fmt_chunk_read:
raise Error('data chunk before fmt chunk')
self._data_chunk = chunk
self._nframes = chunk.chunksize // self._framesize
self._data_seek_needed = 0
break
chunk.skip()

やっぱりやってました!

initfpでは、whileでチャンクごとにファイルを読み込み、チャンクの名前が上記のデータ構造の項目がデータ(data)になると、self._data_chunk = chunkを行い波形データだけを抽出しています。

何がいけなかったのか

waveライブラリはwavファイル内に含まれている波形データを扱うためのライブラリであるので、waveファイルとして復元させたい場合(今回だと、APIサーバはwavファイルを受け取り、S3に保存する必要がある。)には適していないです。

こうしたら良い

ビルドインのopen()関数を使うと、ヘッダ情報も保持している状態のファイルクラスがreturnするので、そちらを使うと良きでした。

f = open(file, 'rb')

まとめ

pythonはライブラリがたくさんあるので、扱うデータの種類(今回だとwavファイル)を詳しく知っていないと、適切なライブラリの選定ができないなと感じました。

あと、もっとファイルの構造について詳しくなる必要があるなあと。

--

--

Yu Kato

Japanese web developer/1998’s/ i wiill write about my project(Go/React) https://github.com/yutify