0%

MATLAB 中的 fft 与 stft

前言

又鸽了一个月的博客,忙于实验也没有刷论文,没有刷论文就莫得博客了……

最近在用 MATLAB 做信号处理,有一些小坑需要注意,尤其是 fft 与 stft 的输出让我走了很多弯路……简要来说,fft 和 stft 的输出是不一致的,不止是因为 stft 的输出有时间这一维度,两者即使抛去时间,输出的内容也是平移的关系:stft 是 fft 沿频率正方向循环平移一半的采样频率的结果。

好像还是没说清楚,再从另一个角度说明可能会清晰一点: fft (对于单一信号)输出是一个 N (采样点数)维向量(设它为 s),其中 s(1) 就是直流分量,也就是频率为 0 的部分,之后一直到 N/2 *都是 (0, fs/2) 的频率分量,剩余右半部分则是(-fs/2, -fs/N) 的频率分量。

* 而具体是 到 N/2 还是 N/2 +- 1 我也不是很确定……待之后需要非常精确的时候考证吧。因为 N 的取值为了加速 fft 通常都是 2 的幂,比如说 1024。 那这 1024 维中,有一个直流分量频率为 0 ,剩余的正频谱和负频谱必然不是对称的,必然是一个比另一个多一个数据。至于哪个多哪个少我还没有找到具体的文档,也许和 FFT 的数学原理关系比较大,但是我并没有下功夫看 FFT 的数学原理……

而对于 stft 来说,如果 N = 1024,他的 s(512) 就是直流分量,大于 512 的索引表示正频谱,小于的表示负频谱。

结论:如果只取非负的频谱,对于 fft 是取 (0, N/2),对于 stft 是取 (N/2, N) 。

具体可以参见正文的 MATLAB 代码与输出。

正文

下面是测试的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
y = audio_data;  % 你可以使用 audioread 读取一段音频
y(all(y==0,2),:) = []; % 这一步不是必要的,但是清除没有声音的部分可以减少误差
fs = 48000;
N = 1024;
window_size = fs * 0.02;
s = stft(y, fs, 'Window', hamming(window_size),'OverlapLength',window_size * 0.5, 'FFTLength', N);
p1_spectrum = abs(s(:,:,1));
y2_fft = y(1:window_size,:);
s2 = fft(y2_fft, N);
pp1_spectrum = abs(s2(:,1));
figure;
subplot(2,1,1)
plot(pp1_spectrum);
subplot(2,1,2)
plot(p1_spectrum(:,1));

结果如下:

image-20201209100527613

可以明显看到两张图之间的关系,stft 自己做了频谱的搬移。