C++ 将FFT转换为spectrogram
我有一个音频文件,我在文件中迭代,每一步采集512个样本,然后通过FFT传递它们 我将数据输出为块514长浮动(使用IPP的IPPSFFTFFWD_RToCCS_32f_I),实部和虚部交错 我的问题是,一旦我有了这些复数,我该怎么处理它们呢?目前我正在为每一个值做准备C++ 将FFT转换为spectrogram,c++,fft,spectrogram,intel-ipp,C++,Fft,Spectrogram,Intel Ipp,我有一个音频文件,我在文件中迭代,每一步采集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
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个样本长块的频率值)。尤其是我对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;
}
size\u t count2=0;
大小\u t max2=kffsize+2;
while(count2>1]=值;
count2+=2;
}
在我看来,这甚至比我看过的大多数其他光谱图实现都要好
我所做的有什么主要错误吗?如果你得到奇怪的结果,那么需要检查的一件事是FFT库的文档,以查看输出是如何打包的。一些例程使用压缩格式,其中实/虚值交错,或者它们可能从N/2元素开始并环绕 对于健全性检查,我建议创建具有已知特征的样本数据,例如Fs/2、Fs/4(Fs=样本频率),并将FFT例程的输出与您期望的结果进行比较。尝试以相同的频率创建正弦和余弦,因为它们在频谱中应具有相同的幅值,但具有不同的相位(即realValue/imagValue将不同,但平方和应相同)
如果您打算使用FFT,那么您真的需要知道它在数学上是如何工作的,否则您可能会遇到其他奇怪的问题,例如混叠。要使所有FFT可见,通常要做的是取幅值的对数
因此,输出缓冲区的位置告诉您检测到的频率。复数的大小(L2范数)告诉您检测到的频率有多强,相位(反正切)给你的信息在图像空间比音频空间重要得多。因为FFT是离散的,频率从0到奈奎斯特频率。在图像中,第一项(DC)通常是最大的,因此如果这是你的目标,它是用于标准化的一个很好的候选者。我不知道这是否也适用于音频(我对此表示怀疑)对于512个样本的每个窗口,您都会像计算一样计算FFT的幅度。每个值表示信号中存在的相应频率的幅度
mag
/\
|
| ! !
| ! ! !
+--!---!----!----!---!--> freq
0 Fs/2 Fs
现在我们需要计算出频率
由于输入信号为实数,FFT在中间(奈奎斯特分量)对称,第一项为直流分量。已知信号采样频率Fs
,奈奎斯特频率为Fs/2。因此对于索引k
,相应的频率为k*Fs/512
因此,对于长度为512的每个窗口,我们得到指定频率下的震级。连续窗口上的一组震级构成了频谱图。我认为有一些东西对您会有所帮助 前向FT在输出中给出的数字往往比在输入中给出的数字大。你可以将其视为某个频率下的所有强度都显示在一个位置,而不是分布在数据集中。这有关系吗?可能不是因为你总是可以根据需要缩放数据。我曾经写过一篇基于整数的文章FFT/IFFT对和每个过程都需要重新缩放以防止整数溢出 您输入的真实数据被转换为几乎复杂的数据。事实证明,缓冲区[0]和缓冲区[n/2]是真实的、独立的。对此有很好的讨论 输入数据是随时间变化的等间距声强值。它们被称为在时域中。FT的输出被称为在频域中,因为水平轴是频率。垂直标度保持强度。虽然从输入数据看不明显,但存在相位信息输入中也存在相位。虽然所有声音都是正弦的,但没有任何东西可以固定正弦波的相位。该相位信息在频域中显示为单个复数的相位,但我们通常不关心它(我们也经常关心!)。它只取决于你在做什么。计算
const float value = sqrt((realValue * realValue) + (imagValue * imagValue));
检索强度信息,但丢弃相位信息。取对数基本上只是抑制大峰值
希望这会有帮助。让人们知道我在整个问题上做了很多工作。我发现的主要问题是FFT在完成后需要归一化 要做到这一点,您需要平均窗口向量的所有值