将 FFT 转换为频谱图

Posted

技术标签:

【中文标题】将 FFT 转换为频谱图【英文标题】:Converting an FFT to a spectogram 【发布时间】:2009-11-05 11:33:37 【问题描述】:

我有一个音频文件,我正在遍历该文件并在每个步骤中采集 512 个样本,然后将它们通过 FFT。

我将数据作为块 514 浮动很长(使用 IPP 的 ippsFFTFwd_RToCCS_32f_I),实部和虚部交错。

我的问题是,一旦我有了这些复数,我该怎么办?目前我正在为每个值做的事情

const float realValue   = buffer[(y * 2) + 0];
const float imagValue   = buffer[(y * 2) + 1];
const float value       = sqrt( (realValue * realValue) + (imagValue * imagValue) );

这提供了一些稍微有用的东西,但我宁愿通过某种方式将值从 0 到 1 范围内。他上面的问题是峰值最终会回到 9 或更多左右。这意味着事情变得严重饱和,然后频谱图的其他部分几乎没有出现,尽管当我通过试听的频谱图运行音频时它们看起来非常强。我完全承认我不是 100% 确定 FFT 返回的数据是什么(除了它代表我传入的 512 个样本长块的频率值)。特别是我对复杂数字的确切含义缺乏了解。

任何建议和帮助将不胜感激!

编辑:只是为了澄清。我的大问题是,如果不知道比例是多少,返回的 FFT 值是没有意义的。有人可以指点我计算出这个比例吗?

Edit2:通过执行以下操作,我得到了非常漂亮的结果:

size_t count2   = 0;
size_t max2     = kFFTSize + 2;
while( count2 < max2 )

    const float realValue   = buffer[(count2) + 0];
    const float imagValue   = buffer[(count2) + 1];
    const float value   = (log10f( sqrtf( (realValue * realValue) + (imagValue * imagValue) ) * rcpVerticalZoom ) + 1.0f) * 0.5f;
    buffer[count2 >> 1] = value;
    count2 += 2;

在我看来,这甚至比我看过的大多数其他频谱图实现更好。

我正在做的事情有什么严重错误吗?

【问题讨论】:

您在获取复数的大小方面做对了。您只需要找出这些(复杂)数字(0-1、0-255、..?)的比例,请参阅 FFT 函数的文档。如果范围对您的喜好来说太大,那么按照下面的建议,取一个 log() 的量级应该会有所帮助。 可能对您的使用并不重要,但您也可以通过将频域值除以 FFT 宽度来标准化频域值(即从 FFT 获得的值)。 (即您的 FFT 越宽,各种频率桶中的值越大) 【参考方案1】:

使所有 FFT 可见的通常做法是取幅值的对数。

因此,输出缓冲区的位置告诉您检测到的频率。复数的幅度(L2 范数)告诉您检测到的频率有多强,而相位(反正切)为您提供在图像空间中比音频空间更重要的信息。因为 FFT 是离散的,所以频率从 0 到奈奎斯特频率。在图像中,第一项 (DC) 通常是最大的,因此如果您的目标是标准化,那么它是一个很好的候选者。我不知道音频是否也是如此(我对此表示怀疑)

【讨论】:

有趣的回应。请注意,在音频中,通常没有 DC 值(如果通过您的放大器会损坏您的扬声器),它纯粹是 AC。 无论如何,寻找最大值是一个非常短的操作(与 FFT 相比)。 同上使用对数刻度(并找到最大值) @Wim 我很高兴听到我的直觉并非完全混乱。 嗯 log10( sqrt( real^2 + imag^2 ) ) 肯定会给出更好看的结果...【参考方案2】:

对于 512 个样本的每个窗口,您可以像以前一样计算 FFT 的幅度。每个值代表信号中存在的相应频率的幅度。

mag
 /\
 |
 |      !         !
 |      !    !    !
 +--!---!----!----!---!--> freq
 0          Fs/2      Fs

现在我们需要弄清楚频率。

由于输入信号是实数值,FFT 围绕中间(奈奎斯特分量)对称,第一项是直流分量。已知信号采样频率Fs,奈奎斯特频率为Fs/2。因此对于索引k,对应的频率是k*Fs/512

因此,对于每个长度为 512 的窗口,我们得到指定频率的幅度。这些在连续窗口上的组形成频谱图。

【讨论】:

【参考方案3】:

只是为了让人们知道我在整个问题上做了很多工作。我发现的主要事情是 FFT 在完成后需要标准化。

为此,您将窗口向量的所有值平均在一起,以得到一个略小于 1 的值(如果您使用的是矩形窗口,则为 1)。然后,您将该数字除以 FFT 变换后的频率箱数。

最后,您将 FFT 返回的实际数字除以归一化数字。您的幅度值现在应该在 -Inf 到 1 的范围内。日志等,随意。您仍将使用已知范围。

【讨论】:

【参考方案4】:

我认为有几件事会对您有所帮助。

前向 FT 倾向于在输出中给出比在输入中更大的数字。您可以将其视为某个频率的所有强度都显示在一个位置,而不是通过数据集分布。这有关系吗?可能不是因为您可以随时扩展数据以满足您的需求。我曾经写过一个基于整数的 FFT/IFFT 对,每次通过都需要重新缩放以防止整数溢出。

作为您输入的真实数据将转换为几乎复杂的数据。事实证明,buffer[0] 和 buffer[n/2] 是真实且独立的。有一个很好的讨论here。

输入数据是随时间变化的声音强度值,间隔相等。据说它们在时域中是恰当的。 FT 的输出被称为频域,因为水平轴是频率。垂直刻度保持强度。虽然从输入数据中看不出来,但输入中也有相位信息。尽管所有的声音都是正弦的,但没有任何东西可以固定正弦波的相位。该相位信息作为单个复数的相位出现在频域中,但我们通常不关心它(而且我们也经常关心它!)。这仅取决于您在做什么。计算

const float value = sqrt((realValue * realValue) + (imagValue * imagValue));

检索强度信息但丢弃相位信息。取对数本质上只是抑制了大峰值。

希望这有帮助。

【讨论】:

那么我如何在不丢弃相位信息的情况下使用它?相位如何应用于频谱图?【参考方案5】:

如果您得到奇怪的结果,那么要检查的一件事是 FFT 库的文档,以查看输出是如何打包的。一些例程使用压缩格式,其中实/虚值交错,或者它们可能从 N/2 元素开始并环绕。

为了进行完整性检查,我建议创建具有已知特征的样本数据,例如 Fs/2、Fs/4(Fs = 采样频率),并将 FFT 例程的输出与您的预期进行比较。尝试在相同频率上创建正弦和余弦,因为它们在频谱中应该具有相同的幅度,但具有不同的相位(即 realValue/imagValue 会不同,但平方和应该相同。

如果您打算使用 FFT,那么您确实需要知道它在数学上是如何工作的,否则您可能会遇到其他奇怪的问题,例如混叠。

【讨论】:

我已经检查了个人资料。我的问题是,我从 FFT 得到的数字毫无意义,不知道规模代表什么。我会更新我原来的问题。

以上是关于将 FFT 转换为频谱图的主要内容,如果未能解决你的问题,请参考以下文章

在将数据输入 FFT 用于音频频谱分析仪之前,使用 python 将 wav 文件转换为 csv 文件 [关闭]

STM32实现FFT,求取幅度频谱

STM32实现FFT,求取幅度频谱

来自近乎完美的正弦曲线的奇怪 FFT 频谱

有没有办法直接将频谱图转换为 MFCC?

matlab 作出信号频谱图