代码之家  ›  专栏  ›  技术社区  ›  Goz

将FFT转换为频谱图

  •  12
  • Goz  · 技术社区  · 15 年前

    我有一个音频文件,我正在迭代该文件,在每一步采集512个样本,然后通过一个FFT传递它们。

    我把数据作为一个514块长浮出来(使用ipp的ippsfftffwd_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点或更多。这意味着事情变得非常饱和,然后频谱图中的其他部分几乎没有出现,尽管当我通过Audition的频谱图运行音频时,它们看起来相当强。我完全承认,我不完全确定由FFT返回的数据是什么(除了它代表我传入的512个长样本块的频率值)。尤其是我对compex的确切数字所代表的东西缺乏理解。

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

    编辑:只是为了澄清。我的大问题是,如果不知道尺度是什么,返回的FFT值是没有意义的。有人能给我指一下这个比例吗?

    伊迪丝2:我通过以下操作得到了非常漂亮的结果:

    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;
    }
    

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

    我做的事有什么大问题吗?

    5 回复  |  直到 13 年前
        1
  •  11
  •   McBeth    15 年前

    要使所有的FFT都可见,通常要做的就是取其大小的对数。

    所以,输出缓冲区的位置告诉你检测到了什么频率。复数的大小(l2范数)告诉你检测到的频率有多强,相位(arcTangent)给你的信息在图像空间比音频空间更重要。因为FFT是离散的,所以频率从0到奈奎斯特频率。在图像中,第一个术语(dc)通常是最大的,因此如果这是您的目标,那么它是用于标准化的一个很好的候选者。我不知道音频是否也是这样(我怀疑)

        2
  •  7
  •   Amro    15 年前

    对于512个样本的每个窗口,您可以像计算一样计算FFT的大小。每个值表示信号中相应频率的大小。

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

    现在我们需要计算出频率。

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

    因此,对于长度为512的每个窗口,我们得到指定频率下的大小。连续窗口上的一组构成了光谱图。

        3
  •  6
  •   Goz    13 年前

    只是为了让人们知道我在这个问题上做了很多工作。我发现的主要问题是,做了FFT之后,它需要归一化。

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

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

        4
  •  5
  •   Tim Allman    15 年前

    我认为你会发现一些有用的东西。

    向前的ft将倾向于在输出中给出比输入中更大的数字。您可以将其视为某个频率下的所有强度都显示在一个位置,而不是通过数据集分布。这有关系吗?可能不是因为你总是可以根据自己的需要调整数据。我曾经写过一个基于整数的fft/i fft对,每次传递都需要重新缩放以防止整数溢出。

    您输入的真实数据被转换成几乎很复杂的东西。事实证明,缓冲区[0]和缓冲区[N/2]是真实和独立的。有一个很好的讨论 here .

    输入数据是经过一段时间后,平均间隔的声强值。他们被认为是,适当地足够,在时间领域。因为横轴是频率,所以FT的输出被称为频率域。垂直比例保持强度。虽然从输入数据看不明显,但输入中也有相位信息。尽管所有的声音都是正弦的,但没有什么能固定正弦波的相位。这个相位信息作为单个复数的相位出现在频域中,但我们通常并不关心它(我们也经常这样做!)这取决于你在做什么。计算

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

    检索强度信息,但丢弃相位信息。取对数本质上只是减弱了大峰。

    希望这是有帮助的。

        5
  •  1
  •   the_mandrill    15 年前

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

    对于健全性检查,我建议创建具有已知特征的样本数据,例如fs/2、fs/4(fs=样本频率),并将fft例程的输出与您期望的结果进行比较。尝试在同一频率下创建正弦和余弦,因为它们在频谱中的大小应该相同,但具有不同的相位(即realvalue/imagvalue将不同,但平方和应该相同)。

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