归一化 FFT 幅度以模仿 WMP
Posted
技术标签:
【中文标题】归一化 FFT 幅度以模仿 WMP【英文标题】:Normalize FFT magnitude to imitate WMP 【发布时间】:2011-01-28 19:13:27 【问题描述】:所以,我一直在为声音文件制作一个小可视化工具,只是为了好玩。我基本上想模仿 Windows Media Player 中的“Scope”和“Ocean Mist”可视化工具。 Scope 很简单,但我在使用 Ocean Mist 时遇到了问题。我很确定这是某种频谱,但是当我对波形数据进行 FFT 时,我没有得到与 Ocean Mist 显示的数据相对应的数据。频谱实际上看起来是正确的,所以我知道 FFT 没有任何问题。我假设可视化器通过某种过滤器运行频谱,但我不知道它可能是什么。有什么想法吗?
编辑2: 我在这里发布了我的代码的编辑版本(编者注:链接不再起作用)。通过编辑,我的意思是我删除了所有的实验性 cmets,只留下了活动代码。我还添加了一些描述性的 cmets。可视化工具现在看起来像 this。
编辑: 这是图像。第一个是我的可视化器,第二个是 Ocean Mist。
【问题讨论】:
如果您为懒惰的\非 WMP 用户发布一个指向您尝试实现的目标(例如,海雾可视化示例)的屏幕截图的链接,这可能会有所帮助。 @Bevin - 我对您的代码进行了一些更改。它们未经测试,所以我不能保证语法,但我希望它们的精神是有意义的。我要出去一段时间,但稍后会检查更新。此外,如果您可以发布您正在使用的 FFT 的文档,将会很有帮助。 嗯,你应该在保存后复制地址栏中的链接,因为pastebin实际上并没有改变现有的代码,它是一个新的“pad”。我可以等:) 好吧,我迟到了。无论如何,这是我得到 FFT 的地方。它没有说的那么大,FFTW,但它似乎有效。无法访问原始页面,因此这里有一个 Google 缓存页面。 74.125.77.132/search?hl=en&q=cache:http://www.librow.com/… @Bevin - 我太傻了,抱歉。无论如何,我重建了这些变化。见pastebin.com/8WgaaAMY。确保当你传入一个正弦波时,你会得到类似于我之前发布的 loglog 图中的绿线。由于没有随机噪声,您的应该更平滑,但尖峰应该是大致相同的宽度和大致相同的水平位置。 【参考方案1】:这里有一些 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: regular spectral plot, bottom: loglog spectral plot (blue is unwindowed) http://img710.imageshack.us/img710/3994/spectralplots.png
我让 Octave 处理从线性到对数 x 和 y 轴的缩放。对于像正弦波这样的简单波形,你得到类似的东西吗?
老答案
我不熟悉你提到的可视化工具,但总的来说:
光谱通常使用对数 y 轴(或光谱图的颜色图)显示。 您的 FFT 可能会返回双面频谱,但您可能只想使用前半部分(看起来您已经这样做了)。 将window function 应用于您的时间数据可以通过减少泄漏使光谱峰变窄(看起来您也在这样做)。 如果您关心绝对幅度,您可能需要除以变换块大小(我想在您的情况下并不重要)。 看起来 Ocean Mist 可视化工具也在使用对数 x 轴。它也可能在集合中平滑相邻的频率区间或其他东西。【讨论】:
我假设您的意思是在那里记录 y 轴,还是有区别?我将如何实现它? +1 表示 x 和 y 轴都是对数的。 log-x 方面解释了为什么上图中的第一个窄峰被拉伸到下图中视图的 1/3 左右。 log-y 缩放解释了为什么峰值和平均值之间的变化在下部图中被压缩。 @Bevin - 两个轴都是对数的。我通常使用 Octave(一个 Matlab 克隆)进行绘图,所以我不得不承认我自己并不擅长将数据映射到像素。如果您有绘图库,请查找loglog
绘图(请参阅en.wikipedia.org/wiki/Logarithmic_scale#Log-log_plots)。如果您自己做,请按照@Paul R 的建议使显示高度与对数(频谱幅度)成正比。然后使显示宽度与 log(freq/FMin) 成比例,其中 FMin 是您要显示的最低频率。我建议从 20 Hz 开始,但更高的数字可能会更好看。
@mtrw - 好吧,我(我想我)实现了你所说的,结果是这样的:i41.tinypic.com/28jslj.jpg 并不是我所期望的。不过我可能搞砸了。
@Bevin - 这绝对看起来不对。给我几分钟,我会做一些我认为应该发生的事情的图表。【参考方案2】:
通常对于这种事情,您希望将 FFT 输出转换为功率谱,通常具有对数 (dB) 幅度标度,例如对于给定的输出箱:
p = 10.0 * log10 (re * re + im * im);
【讨论】:
我必须标准化这个“p”吗?比如,之后除以 n/2? 这是一个 dB 值 - 您可以添加或减去一个合适的 dB 偏移,以使其进入您想要的任何范围。然后,您可以将此 dB 值转换为屏幕坐标或像素强度或适合您的可视化器的任何值。 嗯,我试过用你的公式,结果有点吵。来看看吧:i39.tinypic.com/15eig3s.jpg 为了测试您的实现,您希望从具有已知频谱的简单信号开始。从例如开始一个单一的纯音(正弦波),比如 1 kHz,看看它是什么样子 - 你应该只得到一个大峰值。如果不是,那么您的 FFT 和/或绘图代码有问题。 @Bevin - @Paul R 建议采用平方幅度的对数是正确的。查看您的第二张图片,您似乎需要添加一个窗口。将时域数据乘以 0.5*(1 - cos(2*pi*n/N)) 形式的函数,其中 N 是变换块大小。背景见en.wikipedia.org/wiki/Window_function。【参考方案3】:看起来海雾 Y 轴是对数的。
【讨论】:
那么,我将如何实现 Y-log 量表?使用对数(绝对幅度)作为 y 值?【参考方案4】:似乎不仅y轴,而且x轴也是对数的。峰值之间的距离似乎在较高频率下较低。
【讨论】:
以上是关于归一化 FFT 幅度以模仿 WMP的主要内容,如果未能解决你的问题,请参考以下文章