如何使用傅里叶变换从WAV文件中提取半精确频率

我们说我有一个WAV文件。 在此文件中,是精确1秒间隔的一系列正弦音。 我想使用FFTW库按顺序提取这些音调。 这特别难吗? 我怎么会这样呢?

另外,将这种音调写入WAV文件的最佳方法是什么? 我假设我只需要一个简单的音频库来输出。

我选择的语言是C.

要获得文件的一部分的功率谱 :

  • 收集N个样本,其中N是2的幂 – 如果您的采样率是44.1 kHz,并且您想要大约每秒采样,那么请说N = 32768个样本。

  • 对样本应用合适的窗函数 ,例如汉宁

  • 将窗口样本传递给FFT例程 – 理想情况下,您需要一个实际到复数的FFT,但如果您只有一个是复杂到复杂的FFT,则为所有虚拟输入部分传递0

  • 计算FFT输出区的平方幅度(re * re + im * im)

  • (可选)计算每个幅度平方输出仓的10 * log10,得到以dB为单位的幅度值

现在您已经拥有了功率谱,您只需要确定峰值,如果您有合理的信噪比,这应该非常简单。 注意,频率分辨率随着N的增大而提高。对于上述44.1kHz采样率和N = 32768的示例,每个箱的频率分辨率为44100/32768 = 1.35Hz。

您基本上对估计频谱感兴趣 – 假设您已经超过了读取WAV并将其转换为离散时间信号的阶段。

在各种方法中,最基本的是周期图,相当于采用窗口离散傅立叶变换(带FFT)并保持其平方幅度。 这符合保罗的答案。 您需要一个窗口,该窗口跨越您想要检测的最低频率的几个周期。 例如:如果你的正弦曲线可以低至10赫兹(周期= 100毫秒),你应该采取200毫秒或300毫秒左右(或更多)的窗口。 然而,周期图有一些缺点 ,虽然计算简单,但如果不需要高精度,它就足够了:

由于频谱偏差以及给定频率的方差随着计算中使用的样本数量的增加而减小的事实,原始周期图不是良好的频谱估计。

通过平均几个窗口可以更好地执行周期图,并且可以明智地选择宽度( Bartlet方法 )。 还有许多其他估算频谱的方法(AR建模)。

实际上,您并不完全对估计全频谱感兴趣,而只对单个频率的位置感兴趣。 这可以通过寻找估计光谱的峰值(如所解释的那样)来完成,但也可以通过更具体和更强大(和复杂)的方法 (Pisarenko,MUSIC算法)来完成。 在你的情况下,他们可能会有点矫枉过正。

WAV文件包含线性脉冲编码调制(LPCM)数据。 这只意味着它是固定采样率的一系列振幅值。 文件开头包含一个RIFF标头 ,用于传送采样率和每个样本的比特(例如,8 kHz带符号的16位)等信息。

格式非常简单,您可以轻松自己滚动。 但是,有几个库可用于加速进程,例如libsndfile 。 简单的直接媒体层(SDL) / SDL_mixer和PortAudio是两个很好的播放库。

至于将数据馈送到FFTW,您需要缓冲1秒的块(通过采样率和每个样本的位确定大小)。 然后将所有样本转换为IEEE浮点(即floatdouble float ,具体取决于FFTW配置 – libsndfile可以为您执行此操作)。 接下来创建另一个数组来保存频域输出。 最后,通过将两个缓冲区传递给fftw_plan_dft_r2c_1d并使用返回的fftw_plan句柄调用fftw_execute来创建并执行FFTW计划。