音遊び日記

ハードウェアとソフトウェアの両面から”音”で遊んだ事を備忘録として書いています。

pythonによるwavファイルの入出力

 

1. はじめに

今の時代簡単な機械学習とか自分で出来るようになっておきたいと思って、ニューラルネットワークpythonの勉強を始めました。pythonは信号処理用のライブラリも揃っていて、matlabとかの代わりとしても使いやすいようですので、せっかくなら音声処理させる事にします。今回はその第一歩としてwavファイルの入出力についてです。pythonの基本的な記述も合わせて勉強したことをまとめていきます。

pythonはライブラリが本当に豊富にあるので、色々方法がありそうですが、情報量の多さなどからwaveライブラリを使ってnumpy arrayでデータを扱えるようにする方法を採用しました。C言語と違ってライブラリを使うので、wavファイルの詳しい事はわからなくても記述できますが、一応下記にwavファイルの中身について書いてますので参考にして下さい。

hwswsgps.hatenablog.com

 

2. ソースコード

①ライブラリのインポート

import numpy as np
import sys
import wave

C言語でいう所の#includeですね。pythonでは「ライブラリ名.関数」みたいな記述をよくするのですが、毎回numpyって書くのは面倒臭いです。そういう時にasを付けると自由な文字列に変更できます。ここで「as *」と宣言するとライブラリ名を省略していきなり関数を記述する事が出来ますが、使いすぎてライブラリ同士で関数名が被ると思い通りの動作をしません。

numpyは三種の神器とも呼ばれる有名なライブラリの一つで、数値計算に必要な関数が入っています。その上位版の「sciPy」というライブラリもあります。

sysは今回はコマンドライン引数を利用するためにimportしています。

waveは音声の入出力用に使うライブラリで以下に情報が載っています。

https://docs.python.jp/3/library/wave.html#wave.Wave_write.setparams

 

②ファイルの読み込み

args = sys.argv
inFilename = args[1]
fIn = wave.open(inFilename, "rb")

1行目の記述だけでコマンドライン引数を取得してくれます。コマンドラインには「python ソースファイル名 音声ファイル名」と記述するため、C言語同様にコマンドライン引数はこの場合、args[0] = "ソースファイル名"、args[1] = "音声ファイル名"となります。

2行目でコマンドラインの音声ファイル名を取得し、3行目でファイルオープンします。①で書いたようにwave.openはwaveというライブラリのopenという関数を使うことを意味します。関数の引数と戻り値はC言語のfopenとほぼ同じと考えていいと思います。

 

③wavファイルパラメータ読み込み

ch = fIn.getnchannels() #チャンネル(モノラル or ステレオ)
fs = fIn.getframerate() #サンプリング周波数
nbits = fIn.getsampwidth() #量子化ビット
L = fIn.getnframes() #サンプル数

ここの情報も①で紹介したwaveライブラリのリンクに色々情報が載っています。wave.openで取得したfInオブジェクトに対しての処理なので「fIn.関数」のように記述します。処理をする時に使う情報としてはこれくらいかなと思うのでこれだけに絞って読み込みました。chは単純にモノラルなら1、ステレオなら2で返されます。

 

④wavファイル音声フレーム読み込み

x = np.frombuffer(fIn.readframes(L), dtype = "int16")
fIn.close()

fIn.readframesはfinで示すファイルから音声フレームを読み込んできます。np.frombufferはデータをndarrayと呼ばれるnumpyで使える配列に変換します。オプションでdtypeを16bit整数型に指定しています。

これでwavファイルの読み込みが完了したので、fInをクローズします。

 

⑤処理

n = 0
y = np.zeros(L, dtype = "int16")
while n < L:
  #実処理(今回は入力をそのまま出力)
  y[n] = x[n]
  n += 1

出力データ用の変数yをndarray型使用するためにnp.zeros()で初期化しています。

処理としては今回はファイルの入出力が目的なので、入力をそのまま出力するために、while文で配列の1要素ずつコピーしています。ただしpythonインタプリタ言語であり、処理が遅いです。なのでこういう書き方は本来はせず、配列全体として代入するべきです。

 

⑥ファイル出力

fOut = wave.open("out.wav", "wb")
fOut.setparams( (ch, nbits, fs, L, "NONE", "not compressed") )
fOut.writeframes(y)
fOut.close()

こちらも①のリンク先に詳細が載っています。1行目で出力ファイルをオープンし、2行目でパラメータを設定。ここで括弧が二重になっているのは、引数がタプルだからです。5個目と6個目の要素は圧縮形式を示しますが、先ほどのリンク先を見るとまだ実装されていないので"NONE"と"not compressed"で固定です。3行目でデータを書き込み、完了したらファイルをクローズします。

 

まとめ

これでpythonで音声処理をするための下地が出来ました。今後pythonでも音声処理するかもしれません。

pythonはライブラリがたくさんあります。今回は勉強のためにnumpyで処理をする前提で書いていますが、信号処理用のライブラリが用意されているのでそちらで処理できるようにファイルを取り込んだ方がいいのかもしれません。それは今後時間があったらよろうかな。