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_lengthn_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 + 1t = 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 的实现

posted @ 2023-03-26 00:04  gewy  阅读(266)  评论(0编辑  收藏  举报