将FFT幅度归一化以模仿WMP

6
所以,我一直在为声音文件制作一个小型的可视化器,只是为了好玩。基本上我想模仿Windows Media Player中的“Scope”和“Ocean Mist”可视化器。Scope很容易,但是我在Ocean Mist方面遇到了问题。我相当确定它是某种频谱,但是当我对波形数据进行FFT时,我没有得到与Ocean Mist显示相对应的数据。频谱看起来确实正确,所以我知道FFT没有问题。我假设可视化器通过某种滤波器运行频谱,但是我不知道可能是什么。有任何想法吗?
编辑2: 我在这里发布了我的代码的编辑版本(编辑者注:链接已经失效)。通过编辑,我指删除了所有实验性评论,并仅留下活动代码。我还添加了一些描述性评论。现在可视化器看起来像this
编辑: 这里是图片。第一张是我的可视化器,第二张是Ocean Mist。

my visualizer

ocean mist


如果您能发布一个屏幕截图的链接,展示您想要实现的效果(例如海雾可视化的示例),这可能会对那些懒惰或非WMP用户有所帮助。 - davidtbernal
@Bevin - 我对你的代码进行了一些更改。它们未经测试,因此我无法保证语法,但我希望它们的精神是有意义的。我即将离开一段时间,但稍后会检查更新。另外,如果您可以发布FFT文档,那将非常有帮助。 - mtrw
嗯,你应该在保存后复制地址栏中的链接,因为 pastebin 实际上不会更改现有代码,而是创建一个新的“笔记本”。我可以等一下 :) - Bevin
时间有点晚了。无论如何,这是我得到FFT的地方。它不像FFTW那样大,但似乎可以工作。原始页面无法访问,因此这里是Google缓存页面。http://74.125.77.132/search?hl=en&q=cache:http://www.librow.com/articles/article-10&sourceid=navclient-ff&rlz=1B3GGGL_enSE346SE347&ie=UTF-8 - Bevin
另外,我想我应该以某种方式创建“虚假”行来填补间隙。 - Bevin
显示剩余6条评论
4个回答

6
这里是一些Octave代码,展示了我认为应该发生的事情。希望语法能够自说明:
%# First generate some test data
%# make a time domain waveform of sin + low level noise
N = 1024;
x = sin(2*pi*200.5*((0:1:(N-1))')/N) + 0.01*randn(N,1);

%# Now do the processing the way the visualizer should
%# first apply Hann window = 0.5*(1+cos)
xw = x.*hann(N, 'periodic');
%# Calculate FFT.  Octave returns double sided spectrum
Sw = fft(xw);
%# Calculate the magnitude of the first half of the spectrum
Sw = abs(Sw(1:(1+N/2))); %# abs is sqrt(real^2 + imag^2)

%# For comparison, also calculate the unwindowed spectrum
Sx = fft(x)
Sx = abs(Sx(1:(1+N/2)));

subplot(2,1,1);
plot([Sx Sw]); %# linear axes, blue is unwindowed version
subplot(2,1,2);
loglog([Sx Sw]); %# both axes logarithmic

以下是相应的图形: top: 常规频谱图,底部: 对数-对数频谱图 (蓝色为未经窗口处理) http://img710.imageshack.us/img710/3994/spectralplots.png 我让Octave处理从线性到对数x和y轴的缩放。 您是否会得到类似于正弦波这样的简单波形? 旧回答 我不熟悉您提到的可视化工具,但一般而言:
  • 频谱通常使用对数y轴(或色图)显示。
  • 你的FFT可能返回双边频谱,但你可能只想使用前半部分(看起来你已经在这样做了)。
  • 窗函数应用于时间数据可以通过减少泄漏使频谱峰变窄(看起来你也在这样做)。
  • 如果你关心绝对幅值,你可能需要除以变换块大小(我猜在你的情况下不重要)。
  • 看起来Ocean Mist可视化器也在使用对数x轴。它可能还在以集合方式平滑相邻的频率bin。

+1 表示注意到 x 和 y 轴都是对数尺度。其中,对数尺度的 x 轴解释了为什么顶部图中的第一个窄峰在下面的图中被拉伸到大约视图的 1/3。而对数尺度的 y 轴解释了为什么下面的图中峰值和平均值之间的变化被压缩了。 - the_mandrill
@Bevin - 两个轴都是对数轴。我通常使用Octave(Matlab的克隆版)进行绘图,所以我必须承认我自己不太擅长将数据映射到像素上。如果您有一个绘图库,请查找“loglog”绘图(请参见http://en.wikipedia.org/wiki/Logarithmic_scale#Log-log_plots)。如果您要自己做,请将显示高度与log(频谱幅度)成比例。如@Paul R所建议的那样。然后,使显示宽度与log(freq / FMin)成比例,其中FMin是您想要显示的最低频率。我建议从20 Hz开始,但更高的数字可能看起来更好。 - mtrw
@mtrw - 嗯,我(想我)实现了你说的,结果变成了这样:http://i41.tinypic.com/28jslj.jpg 不是我期望的。可能是我搞砸了。 - Bevin
@Bevin,当然可以。我将离线几个小时,但如果您不介意延迟,我很乐意查看,或者也许其他人会发现问题。 - mtrw
好的,我已经发布了。链接在帖子顶部。 - Bevin
显示剩余3条评论

3

通常在这种情况下,您需要将FFT输出转换为功率谱,通常使用对数(dB)幅度比例尺,例如,对于给定的输出bin:

p = 10.0 * log10 (re * re + im * im);


我需要对这个“p”进行归一化吗?比如说,之后将它除以n/2? - Bevin
这是一个分贝值 - 你可以添加或减去适当的分贝偏移量,使其进入你想要的任何范围。然后,你可以将这个分贝值转换为屏幕坐标或像素强度,或者适合你的可视化工具的任何内容。 - Paul R
好的,我尝试使用了你的公式,但是结果有点嘈杂。你看一下这个链接:http://i39.tinypic.com/15eig3s.jpg - Bevin
为了测试您的实现,您想从已知频谱的简单信号开始。例如,从1kHz的单一纯音(正弦波)开始,并查看其外观 - 您应该只获得一个大的峰值。如果没有,则您在FFT和/或绘图代码方面做错了些什么。 - Paul R
1
@Bevin - @Paul R的建议是正确的,可以对平方振幅取对数。从您的第二张图片来看,似乎需要添加一个窗口。将时间域数据乘以形式为0.5 *(1-cos(2 * pi * n / N))的函数,其中N是您的变换块大小。请参阅http://en.wikipedia.org/wiki/Window_function了解背景信息。 - mtrw

1

它确实看起来像海雾Y轴是对数的。


那么,我该如何实现Y对数刻度?将log(绝对值)用作y值? - Bevin

1

看起来不仅 y 轴,x 轴也是对数的。峰值之间的距离似乎随着频率增高而变小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接