STFT
这篇文章讲下短时傅里叶变换(Short-Time Fourier Transform, STFT)
为什么要对语音信号进行STFT?
首先,语音信号由随时间变化的各种频率和振幅组成。我五秒前说过的字和我现在正在说的字组成了我要表达的信息,由于发声器官的变化,每个字都具有不同的频率特性。对语音中的频率成分进行分析有利于了解、处理、修改该信号。
然而,语音信号一种非平稳信号,其频率特性随时间而变化。简单地对语音信号进行一次FFT并不能够捕捉到这种时变特性,反而是在整个信号持续时间内的频率分布。
但同时,人或乐器发音时,在一个音素发音区间内(通常为20-30 \(ms\)),信号是有周期规律的\(^{[1]}\),其特性基本保持不变即相对稳定。那么,我们可以对短时间内的语音信号进行FFT并将结果按时间顺序排列,就能捕捉到语音信号的随时间变化的频率特性。
怎么进行STFT?
- 分帧:将语音信号分为一段段持续30 \(ms\)的帧,且相邻帧之间会互相有重叠的部分。需要帧之间保持重叠的原因有很多,比如上回说到的加窗,窗函数会使信号的左右两边平滑以减小频谱泄漏,但同时边缘部分信号的能量会减弱,因此需要使用重叠的帧来确保,在某一帧里被影响的边缘信号在其他帧里处在非边缘的位置。同时,重叠的分帧也有利于对原始信号进行重构。
- 加窗:使每一帧的信号在时域内更加平稳,从而能够更准确地分析信号。上篇说过了。
- FFT,并将结果按帧顺序排列
librosa.stft()
下面来看下librosa.stft()函数里各项参数的意义,来对STFT有更实际的了解。
librosa.stft(y, *, n_fft=2048, hop_length=None, win_length=None, window='hann', center=True, dtype=None, pad_mode='constant', out=None)
其中,
y
:要处理的语音信号;n_fft
:FFT点数,默认为2048。在选取时尽量保证和窗函数的长度win_length
一样,同时选取长于win_length
的n_fft
并不能得到更大的频域波形分辨率\(^{[2]}\);hop_length
:帧移,即下一帧是从上一帧向右移动了多少点,默认是win_length // 4
。需要注意的是,帧移的选取是有讲究的,需要满足Constant Overlap-Add (COLA)\(^{[3-5]}\)以确保在逆变换是能够完美重建信号。这个COLA我在上次写博客的时候研究过,印象挺深的。这次就只贴几个参考文献吧。。。win_length
:窗长,默认为n_fft
。需要选取小于等于n_fft
的值,当小于时,会对每一帧信号进行补零至n_fft
长度。window
:窗函数类型,默认为Hann窗。center
:如果选择center=True
,那么会对原信号的左右进行补零,个数为n_fft// 2
。这样做会分帧后D[:, t]
以y[t * hop_length]
为中心。如果选择center=False
,那么D[:, t]
以y[t * hop_length]
为起点。
输出:
一个复矩阵\(D[f, t]\),其中f = n_fft // 2 + 1
,t = audio_length // hop_length + 1
。
使用np.abs( D[f, t] )
得到幅度谱;
使用np.angle( D[f, t] )
得到相位谱。
[1] 语音处理:音频信号短时平稳性分析
[2] 快速傅里叶变换(FFT)中为什么要“补零”?
[3] Constant Overlap Add (COLA)
[4] Overlap-Add (OLA) STFT Processing
[5] librosa 语音库(二)STFT 的实现