将FFT转换为谱图

Goz*_*Goz 12 c++ fft spectrogram intel-ipp

我有一个音频文件,我正在遍历文件,每步取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) );
Run Code Online (Sandbox Code Playgroud)

这提供了一些稍微有用的东西,但我更倾向于在0到1的范围内获得值.上面的问题是峰值最终会回到9或更多.这意味着事情会恶化饱和,然后光谱图的其他部分几乎不会出现,尽管当我通过试听的频谱图运行音频时它们看起来非常强大.我完全承认我不是100%确定FFT返回的数据是什么(除了它代表我传入的512个样本长块的频率值).特别是我的理解是缺乏compex数字代表什么.

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

编辑:只是为了澄清.我的一个大问题是,如果不知道比例是什么,返回的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;
}
Run Code Online (Sandbox Code Playgroud)

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

我正在做的事情有什么大不了的吗?

McB*_*eth 11

使所有FFT可见的通常做法是采用幅度的对数.

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


Amr*_*mro 7

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

mag
 /\
 |
 |      !         !
 |      !    !    !
 +--!---!----!----!---!--> freq
 0          Fs/2      Fs
Run Code Online (Sandbox Code Playgroud)

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

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

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


Goz*_*Goz 6

正因如此,人们都知道我在整个问题上做了很多工作.我发现的主要事情是FFT在完成后需要归一化.

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

最后,将FFT返回的实际数除以归一化数.您的振幅值现在应该在-Inf到1范围内.记录等,请随意.您仍将使用已知范围.


Tim*_*man 5

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

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

您输入的真实数据将转换为几乎复杂的内容.事实证明,缓冲区[0]和缓冲区[n/2]是真实且独立的.这里是它的一个很好的讨论在这里.

输入数据是随时间变化的声强度值,等间隔.据说它们在时域中足够恰当.据说FT的输出在频域,因为水平轴是频率.垂直刻度仍然是强度.虽然输入数据不明显,但输入中也存在相位信息.尽管所有的声音都是正弦的,但没有任何东西可以修复正弦波的相位.此阶段信息在频域中显示为单个复数的阶段,但通常我们不关心它(通常我们也会这样做!).这取决于你在做什么.计算

const float value = sqrt((realValue * realValue) + (imagValue * imagValue));
Run Code Online (Sandbox Code Playgroud)

检索强度信息但丢弃相位信息.取对数基本上只会抑制大峰值.

希望这是有帮助的.