SPTKの使い方 (5) フレーム分割
SPTKの使い方 (4)(2012/7/16)の続き。
今回は、音声波形をフレーム単位に分割するSPTKのframeコマンドを使ってみます。音声でフレームというと、信号の複数のサンプルを一定幅でまとめたものを指すことが多いと思います。前に実装した短時間フーリエ変換(2011/7/16)では、連続する波形からフレームを切り出し、窓関数をかけてフーリエ変換というのをフレームを少しずつずらしながら繰り返し行うことでスペクトルの時間変化を求めました。
ここで、フレームを少しずつずらしながら音声を切り出すのがframeコマンドです。SPTKのマニュアルを見ると、引数で指定するのはフレームの長さLと周期Pになります。周期はフレームの中心をどれくらいずらしていくかを表すパラメータです。LよりPが小さいとオーバーラップしながらずらしていくことになります。
というわけで簡単なデータを作ってさっそく試してみます。結果がわかりやすいように0, 1, 2, 3, 4, 5, ...という連続した数値が入ったデータを作ります。
#coding:utf-8 # make_simple_data.py import struct fp = open("simple.short", "wb") for i in range(100): fp.write(struct.pack("h", i)) fp.close()
実行するとsimple.shortという0から99までのSHORT型データを書き込んだバイナリファイルができます。SPTKのdmpコマンドで見てみます。
> dmp +s simple.short 0 0 1 1 2 2 ... 97 97 98 98 99 99
OK。さっそくこのデータからフレーム長L=10、フレーム周期P=5でフレームを抽出してみます。frameの入力はfloat型しかダメなのでx2xでSHORT型からFLOAT型に変換してます。
x2x +sf < simple.short | frame -l 10 -p 5 > simple.frame dmp -l 10 +f simple.frame
dmpコマンドの-lオプションでフレーム長を渡すとフレーム単位でインデックスを割り振ってくれるため少し見やすくなります。もう少し整形すると
0 frame: 0 0 0 0 0 0 1 2 3 4 1 frame: 0 1 2 3 4 5 6 7 8 9 2 frame: 5 6 7 8 9 10 11 12 13 14 3 frame: 10 11 12 13 14 15 16 17 18 19 4 frame: 15 16 17 18 19 20 21 22 23 24 ... 18 frame: 85 86 87 88 89 90 91 92 93 94 19 frame: 90 91 92 93 94 95 96 97 98 99
このようにフレームが並んでいることがわかります。たしかにフレーム長は10でフレーム周期の5ずつずらしながらフレームを抽出していることがわかります。また、元のサンプル数が100で5ずつずらしながらフレームを抽出しているので全部で100/5=20フレームあることもわかります。
フレームを切り出す
次に、波形から任意の位置のフレームを切り出してみます。波形から任意のサンプルを切り出すコマンドはbcut(2012/7/4)でしたが、bcutに-lオプションを与えると単位がフレームになります。たとえば、18フレーム目を切り出したい時は、
> bcut +f -l 10 -s 18 -e 18 < data.frame | dmp +f 0 85 1 86 2 87 3 88 4 89 5 90 6 91 7 92 8 93 9 94
2から4フレーム目を切り出したい時は、
> bcut +f -l 10 -s 2 -e 4 < data.frame | dmp +f 0 5 1 6 2 7 3 8 ... 27 22 28 23 29 24
となります。
指定したフレームのスペクトルを求める
では、frame、bcutを使って音声の任意のフレームのスペクトルを求めてみます。音声はSPTKに付属のサンプル音声data.short(青い植木鉢)で試します。
x2x +sf < data.short | frame -l 400 -p 80 | \ bcut +f -l 400 -s 65 -e 65 | window -l 400 -L 512 | fftr -l 512 -A > data.fft
このようにコマンドをパイプでつなげるだけなのでマジ簡単です。前から順に
- data.shortをSHORT型からFLOAT型に変換
- フレーム長400サンプル、フレーム周期80サンプルでフレームを抽出
- フレーム長を400サンプルとし、65フレーム目を切り出す
- 入力のフレーム長を400サンプル、出力のフレーム長を512サンプルとする窓関数をかける
- フレーム長512サンプルでFFTして振幅スペクトルを出力
となります。さっそく、data.fftを前回(2012/7/16)作ったplot_fft.pyで表示してみます。
python plot_fft.py data.fft 512 16000
縦軸は対数にすることが多いのでplot_fft.pyの下の部分を書き換えてみます。
plot(freqList[:N/2], fft[:N/2]) ↓ plot(freqList[:N/2], np.log2(fft[:N/2]))
実行すると対数スペクトルになります。
SPTKで対数スペクトルを求める
SPTKにも対数スペクトルを求めるspecコマンドとそれを表示するglogspコマンドがあるので試してみます。
x2x +sf < data.short | frame -l 400 -p 80 | \ bcut +f -l 400 -s 65 -e 65 | window -l 400 -L 512 | \ spec -l 512 | glogsp -l 512 -x 8 -p 2 | xgr
先の結果と同じです。縦軸のスケールが違うけどこれは特に問題ないのかな?frameコマンドは、フーリエ変換以外にもMFCCやLPCの抽出でも頻繁に使うのでここで慣れておきたいな。