12

我有一个音频文件,我正在遍历该文件并在每个步骤中采集 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;
}

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

我在做什么有什么重大问题吗?

4

5 回答 5

11

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

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

于 2009-11-05T14:28:09.073 回答
7

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

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

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

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

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

于 2009-11-05T15:09:22.360 回答
6

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

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

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

于 2011-12-17T00:30:57.190 回答
5

有几件事我认为你会发现有用的。

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

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

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

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

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

希望这会有所帮助。

于 2009-11-05T16:19:22.083 回答
1

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

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

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

于 2009-11-05T13:56:02.257 回答