c - FFT - 在 PCM 数据上应用窗口

标签 c signal-processing fft fmod kissfft

我目前正在尝试重现 FMOD 音频库的 getSpectrum 函数。该函数读取当前播放缓冲区的 PCM 数据,对该数据应用窗口并应用 FFT 来获取频谱。

它返回一个 float 组,其中每个浮点介于 0 到 1 dB 之间 (10.0f * ( float)log10(val) * 2.0f )。

我不确定我做什么是我应该做什么,所以我会解释一下:

首先,我在4096字节缓冲区中获取PCM数据,根据文档,PCM数据是由左右一对数据的样本组成的。

data

就我而言,我正在使用 16 位样本,如上图所示。因此,如果我只想使用左声道,我会将左 PCM 数据保存在一个短数组中,执行以下操作:

short *data = malloc(4096);
FMOD_Sound_ReadData(sound, (void *)data, 4096, &read);  

因此,如果样本 = 4 个字节,则我有 1024 个样本,即 1024 个代表左声道的 Shorts 和 1024 个代表右声道的 Shorts。

为了执行 FFT,我需要一个 float 组并对数据应用一个窗口 (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)));
}

whew in 是输入,i 是数组中的位置,s 是数组的大小 (1024)。

仅获取左 channel :

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

以 dB 为单位计算:

amp = 10.0f * (float)log10(amp) * 2.0f;

amp 不在 0 和 1 之间。我不知道必须在哪里标准化我的数据(在 PCM 数据上或末尾)。另外,我不确定我在 PCM 数据上应用窗口的方式。

这是我从 0 到 20kHz 歌曲中得到的结果与 getSpectrum 函数的结果进行比较。 (对于矩形窗口)

results

             My Result                         getSpectrum Result

如何才能达到相同的结果?

最佳答案

您对 log (dB) 刻度有点困惑 - 您没有得到 0 - 1 dB 的范围,对于 16 位音频,您得到的范围通常为 96 dB,其中上限和下限有些许差异任意的,例如0 至 -96 dB,或 96 dB 至 0 dB,或您喜欢的任何其他范围,具体取决于各种因素。您可能只需要通过合适的偏移量和因子来移动和缩放频谱图即可解决此问题。

(注:96 dB 的范围来自公式20 * log10(2^16),其中 16 是位数。)

关于c - FFT - 在 PCM 数据上应用窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9645983/

相关文章:

java - 将音频立体声转换为音频字节

c - 当int具有十六进制时如何在C中将二维数组转换为一维数组

c++ - 共享内存仅在第一次起作用

python - 为什么提取出来的水印和嵌入的水印不一样?

python - 实际加速度计数据的功率谱密度显示 0 Hz 处的异常值

java - 你如何创建一个音调生成器,其音调可以在 "live"或在 java 中即时操作?

audio - WAV 文件的 FFT 和输出以绘制频谱

java - 从麦克风或音频文件中识别拨号音

C 程序接收到信号 SIGSEGV,矩阵乘法时出现段错误?

c++ - 函数仅声明,但仍然有效