我目前正在尝试重现FMOD音频库的getSpectrum
功能此函数读取当前正在播放的缓冲区的PCM数据,在此数据上应用一个窗口并应用FFT来获取频谱。
它返回一个浮点数组,其中每个浮点在0和1db之间(10.0f * ( float)log10(val) * 2.0f
)。
我不确定我该做什么,所以我要解释一下:
首先,我在4096字节的缓冲区中得到PCM数据,根据文档,PCM数据由一对左右数据组成。
在我的例子中,我使用的是16位的样本,如上图所示因此,如果我只想使用左声道,我将左PCM数据保存在一个短数组中,执行以下操作:
short *data = malloc(4096);
FMOD_Sound_ReadData(sound, (void *)data, 4096, &read);
所以如果一个样本=4字节,我有1024个样本,即1024个代表左通道的短路和1024个代表右通道的短路。
为了执行FFT,我需要一个浮点数组并对我的数据应用一个窗口(Hanning):
float hanningWindow(short in, size_t i, size_t s)
{
return in*0.5f*(1.0f-cos(2.0f*M_PI*(float)(i)/(float)(s-1.0f)));
}
当
in
是输入时,i
是数组中的位置,s
是数组的大小(1024)。要只获取左声道:
float *input = malloc(1024*sizeof(float));
for (i = 0; i < 1024; i++)
input[i] = hanningWindow(data[i*2], i, 1024);
然后我通过kiss-FFT(从真实到复杂)来执行FFT我得到一个大小为1024/2+1=513的
kiss_fft_cpx *ouput
(复数数组)。我用以下公式计算每个频率的振幅:
kiss_fft_cpx c = output[i];
float amp = sqrt(c.r*c.r + c.i*c.i);
以分贝计算:
amp = 10.0f * (float)log10(amp) * 2.0f;
amp
不在0和1之间我不知道在哪里我必须规范我的数据(在PCM数据或在最后)另外,我也不确定如何将窗口应用于PCM数据。这是我从一首0到20kHz的歌曲中得到的结果,与getSpectrum函数的结果相比(对于矩形窗)
My Result getSpectrum Result
我怎样才能达到同样的效果呢?
最佳答案
你对对数(dB)刻度有点困惑-你没有得到0-1db的范围,16位音频通常得到96db的范围,其中上下端有些任意,例如0到-96db,或96db到0db,或任何其他你喜欢的范围,取决于各种因素你可能只需要通过适当的偏移量和因子来改变和缩放你的光谱图就可以了。
(注:96分贝的范围来自公式20 * log10(2^16)
,其中16是位的数目。)