人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

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

このようにコマンドをパイプでつなげるだけなのでマジ簡単です。前から順に

  1. data.shortをSHORT型からFLOAT型に変換
  2. フレーム長400サンプル、フレーム周期80サンプルでフレームを抽出
  3. フレーム長を400サンプルとし、65フレーム目を切り出す
  4. 入力のフレーム長を400サンプル、出力のフレーム長を512サンプルとする窓関数をかける
  5. フレーム長512サンプルでFFTして振幅スペクトルを出力

となります。さっそく、data.fft前回(2012/7/16)作ったplot_fft.pyで表示してみます。

python plot_fft.py data.fft 512 16000

f:id:aidiary:20120801212608p:plain

縦軸は対数にすることが多いのでplot_fft.pyの下の部分を書き換えてみます。

plot(freqList[:N/2], fft[:N/2])
            ↓
plot(freqList[:N/2], np.log2(fft[:N/2]))

実行すると対数スペクトルになります。

f:id:aidiary:20120801212614p:plain

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

f:id:aidiary:20120801214054p:plain

先の結果と同じです。縦軸のスケールが違うけどこれは特に問題ないのかな?frameコマンドは、フーリエ変換以外にもMFCCやLPCの抽出でも頻繁に使うのでここで慣れておきたいな。