Contents
はじめに
PythonでSciPyなどを利用してデータを読み込むと,wavファイルの形式によってデータの最大値が異なり扱いづらいことがあります.wavファイル形式の違いによらずデータの振幅値を[-1, 1]の範囲に正規化して,処理を行いたいときの便利関数を作りました.
具体的には,
wavファイルの形式によらず
- デフォルトで音データを正規化してくれる wavread() 関数
- 正規化されたデータを自分でもとに戻さなくても,音量を保って音データを書き込める wavwrite() 関数
- (関数から呼び出される正規化用関数 audio_normalize() 関数)
が入ったライブラリ matwavlib を作成しましたので,内容を簡単に説明します.
に役に立つのではないかと思います(少なくとも私は忘備録として使います).
対称とする読者
- 音・音声データをPythonで処理する方(データサイエンスなど)
- SciPyで時系列解析などを行う方
- wavファイルのフォーマットの違いを意識せずに音データを扱いたい方
- そのままコピーして使えるwav読み込み・書き込み関数を必要とする方
- 特定のwav formatに変換する必要のある方
目次
結論
以下のコードをコピー・ダウンロードして利用してください.
環境には,Numpy, SciPyがインストールされている必要があります(Anacondaを利用している場合,標準でインストールされています).
matwavlib.pyのコード(この行をクリックするとコードが展開されます)
gistfa0a56f9e5e59a4f3a80e375b84c4156
読み込み・書き込みに使える関数の説明
y, fs = wavread(wavfile, norm=True)
- 説明: 音データの読み込み(デフォルトでデータの正規化)
- パラメータ
- wavfile(str): 読み込みたいwavファイルのパスを入力
- norm(bool): 音データを正規化するかどうか(DefalutでTrue)
- 返り値
- y (np.ndarray): 音声データ
- fs (int): サンプリングレート
wavwrite(wavefile, data, fs, ftype=’float32′)
- 説明: 正規化されたデータの書き込み
- パラメータ
- wavefile(str): 保存したい場所・名前のパス
- data(np.ndarray): 保存したい音データ(正規化されていないデータも問題なく書き込めます).
- fs(int): サンプリング周波数
- ftype(str, parameters = [“float32”, “uint8”, “int16”, “int32”]): 保存するファイル形式の選択 (デフォルトで32bit floating-point format)
- int: integer PCM format
- float: floating-point format
使用例
wavread, wavwriteの間でyに対して好みの処理を行ってください.
from matwavlib import matwavlib as mw
infilepath = "input.wav"
outfilepath = "output.wav"
y, fs = mw.wavread(infilepath)
mw.wavwrite(outfilepath, y, fs)
print("input Datatype:", y.dtype)
GitHub
なにか変更やご指摘があればこちらで変更していきます.
解説
以下に細かい内容などを知りたい人向けの解説を書きます.特に形式を気にしない方は上をコピペしたりダウンロードして使ってもらえればと思います.
SciPy の仕様 -> MATLABの仕様へ
以下の表が,音データ(wav)を読み込む際の,SciPyとMATLABの仕様の違いです.
機能 | SciPy | MATLAB |
---|---|---|
読み込み | 正規化なし,wav形式によってデータの最大値異なる,読み込み後自分で正規化する必要あり | データ形式によらず最大値を正規化したデータを返す |
書き込み | 正規化した場合,書き込み前に元の音量に戻す処理が必要 | 正規化されたデータも自動で元の音量に戻す |
FFTやスペクトログラム.その他特徴抽出など,科学計算関連の関数を適用する場合には,データが正規化されていたほうが扱いやすいため,MATLABのようにデフォルトでは読み込み,書き込み関数が正規化・正規化解除をやってくれると手間が省けます.
wavread(), wavwrite()関数は,上の表のMATLABのように,関数で読み込めば勝手に正規化・正規化解除をしてくれるように作りました(normパラメータなどで正規化したくない場合はその旨を指定できます).
SciPyで読み込んだ場合・書き込んだ場合のデータ形式の違いについては,
ryoiijima.hatenablog.com
にまとめています.
正規化関数の説明 audio_normalize(y)
def audio_normalize(y):
"""
Audio data normalization between [-1, 1].
Parameters
----------
y: numpy.ndarray, audio data from SciPy
Returns
----------
norm_y: numpy.ndarray, normalized audio data the range is [-1, 1]
"""
print("y.dtype:", y.dtype)
if y.dtype == "float32" or y.dtype == "float64":
max_y = 1
elif y.dtype == "uint8":
y = y - 128 # convert unsigned to signed
max_y = 128
elif y.dtype == "int16":
max_y = np.abs(np.iinfo(np.int16).min)
elif y.dtype == "int32":
max_y = np.abs(np.iinfo(np.int32).min)
else:
raise ValueError("%s can't use datatype for audio normalization. \
Datatype must be [float32, float64, uint8, int16, int32]" % (y.dtype))
max_y = np.abs(np.iinfo(np.int16).min)
norm_y = y / max_y
norm_y = norm_y.astype(np.float32)
return norm_y
wavread(), wavwrite()から,正規化するための関数 audio_normalize() を呼び出すことによって,ファイル形式ごとに正規化する処理をしています.
最大値を取得して,データの各値を最大値で割ることで,[-1, 1]の間のデータ形式にしています.(scipyの音データ np.ndarrayを引数として渡せば使えます.)
同様にして,wavwrite()関数では,指定されたファイル形式に合わせて,値の範囲を[-1, 1]からもとに戻す処理を行っています.例えば,16bit integer PCMでは,[-32768, 32767]の範囲となるように最大値をかける処理を行います.この処理を忘れると,書き出されたファイルの音量が小さい・聞こえなくなってしまいます.
正規化をする際の注意点は,8 bit unsigned integer PCM 形式のファイルの場合,符号なしデータを0-255の範囲で扱っているため,他の形式と同じ用に扱うためには,128を引く・足すことによって半分ずらす必要があります.これらを考えるのが億劫なため,今後に備えてこのような関数を作りました.
参考文献
正規化の方法を参考にさせていただきました.