将 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 转换为频谱图的主要内容,如果未能解决你的问题,请参考以下文章