ローカル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ファイル)を詳しく知っていないと、適切なライブラリの選定ができないなと感じました。
あと、もっとファイルの構造について詳しくなる必要があるなあと。