はじめに
Pythonで短時間フーリエ変換(STFT)を計算するためのツール(ライブラリ)の一つにscipyがある. scipyでSTFTを計算するための関数がstftであったが,最新版(v1.12.0)ではlegacyに位置づけられている.
scipy.signal.stft — SciPy v1.12.0 Manual
その代わりに ”ShortTimeFFT” クラスが新設され,stftはこのクラスのいちメソッドになった.
本記事はこの新しいstftを使ってみて実行結果を眺めてみよう,という主旨のもと,特に位相スペクトルに注目する.
新しいクラスと注目のパラメータ phase_shift
ここでShortTimeFFT クラスのパラメータを見てみよう.
パラメータ | 説明 |
---|---|
win | 分析窓(numpy 配列) |
hop | ホップ長(フレームシフト長) |
fft_mode | 両側FFTや片側FFTなどを指定 |
mfft | FFT長 |
dual_win | 双対窓 |
scale_to | STFT結果のスケーリングを指定 |
phase_shift | 線形位相シフトの量 |
本記事では最後のphase_shift
が重要である.
このパラメータを指定することで,STFT時の位相スペクトルに線形位相成分が付加される.デフォルト値は0,つまり線形位相成分を付加しない.Noneを指定すると,-mfft//2
に比例した線形位相成分が付加される.
このphase_shift
,初見では「一体何だこれは?」だろう.
細かい理論はすっ飛ばして,このパラメータの指定いかんで位相スペクトルがどのように変化するかを見ていこう.
スペクトルの描画
JSUTコーパスに含まれる onomatopee300 から 001.wavを選び,そのSTFTを計算して振幅スペクトルと位相スペクトルを描画する. 比較のため,旧stftでSTFTを計算した結果も示す. 以下,図1から図3まで振幅スペクトル(各図上段)と位相スペクトル(各図下段)を描画した結果を示している.
振幅スペクトルはどれも同じに見えるが,図2の位相スペクトルだけ見かけが大きく異なっていることが分かるだろう. 図1(旧stft)の位相スペクトルと図3(新stft)の位相スペクトルは,「何だかとても見づらい(構造が把握しづらい)」が,見かけは似ている.
描画に使ったコードは以下に置いた.
plot_amp_phase_spec.py · GitHub
仕掛け
STFTに伴って線形位相成分が付与される事情は矢田部先生の解説記事を読むのがよい(5.2節).
線形位相成分の議論は同じく矢田部先生よる以下の記事にも詳しい.
要するに,STFTでは分析窓の影響で音声本来の位相スペクトルに加えて窓由来の線形位相成分が乗ってしまう.ゆえに図1や図3のような位相スペクトルの見かけになっている. 新stftでphase_shift=0とすると,その線形位相成分が除去された状態の位相スペクトルが手に入る.phase_shift=Noneとすると,旧stftと同じような線形位相成分を乗せる.
線形位相成分が除去された状態の位相スペクトルを使うと分析しやすい事情は上記の記事にも書かれている.
細かい実装はソースコードを見れば分かるのだが,FFT計算の直前にサンプルのシフト(roll)を行っており,これにより線形位相成分をキャンセルしている. scipy/scipy/signal/_short_time_fft.py at v1.12.0 · scipy/scipy · GitHub
まとめ
新stftで位相スペクトルの線形位相成分の除去がやりやすくなったのは嬉しい.