关于FFT和音调检测,Stackoverflow及其他方面已经进行了无数次讨论。
人们普遍认为,虽然快速,但对于许多应用来说,FFT并不是非常准确,但它并没有经常解释原因。
我想解释一下为什么会出现这种情况的理解,希望比我聪明的人能够纠正我并填补我无法做到的空白。
FFT将输入数据从时域转换到频域。
最初,我们从一系列数据开始,如果我们要在图表上绘图,那么在Y轴上给定时间点的声音幅度和沿X轴的时间。这是时域。
FFT将这些时间点的振幅值转换为不同频率的振幅。
从FFT输出的数据数量是SAME作为数据输入的数量
如果我们输入10个时间点的幅度(10个样本),FFT将输出这些样本中10个不同频率的幅度(在乘以虚数和实数的sqrt之后)。
哪些频率由以下因素确定:
我们将FFT的输出称为 bin ,每个bin的宽度通过将采样率除以FFT中的采样数来计算:
bin width = Sample Rate(Hz)/FFT Length (n samples)
有了一些真正的价值观,那可能是:
bin_width = 44100/512 = 86.132
因此,我们的FFT中有512个二进制位(请记住它与输出数据相同),每个都超过86.132 Hz。
因此,对于给定的bin,我们可以计算它代表的频率:
Bin Freq (Hz) = Bin number (n) * bin width (Hz)
使用上面的值,FFT输出中的第3个bin表示258.398Hz的幅度:
Bin Freq (Hz) = 3 * 86.132 = 258.396Hz
这意味着对于给定的采样率和缓冲区大小,FFT输出不能比±86.132Hz更精确。
如果您需要更高的精度(例如1Hz),您必须降低采样率或增加缓冲区大小(或两者)。
desired bin width: 1Hz = 44100 / 44100 # A buffer size of 44100 would work in this instance
随着缓冲区大小越来越接近采样率,延迟问题变得更加严重。
FFT Results per second = Sample Rate / Buffer Size = 44100/44100 = 1 FFT per second
(每秒44100个样本,填充44100个样本缓冲区=每秒1个完整缓冲区)。
我意识到FFT不仅仅是计算基频(具有最高振幅的bin),而且到目前为止我对Pitch Detection中FFT的理解是否正确?
有没有办法在不牺牲延迟的情况下提高FFT的准确度?
答案 0 :(得分:3)
关于你的第一个问题(“到目前为止,我对Pitch Detection中FFT的理解是否正确?”)我会说是的,但我想指出一个陷阱:
使用上面的值,FFT输出中的第3个bin表示258.398Hz的幅度:
Bin Freq(Hz)= 3 * 86.132 = 258.396Hz
请注意第0个bin表示0 Hz。这意味着代表3 * 86.132 = 258.396Hz的bin位于结果数组的第4个位置。
要完成此索引陷阱,如果您的FFT为512点(= fftsize),则索引值256表示奈奎斯特频率(=采样频率/ 2)。这意味着你总是得到fftsize / 2 + 1箱代表真实的频谱,即在你的情况下是257箱。
关于你的第二个问题,有两种广泛而简单的方法可以提高频率检测精度:
抛物线插值(参见例如the 1st answer)
最后,一个未被问及的回答:绝对建议应用窗口函数,不仅因为它是抛物线插值的前提,还因为它减少了显着的人工旁瓣的振幅。
答案 1 :(得分:3)
除了@HartmutPfitzinger建议零填充和插值的好答案之外,值得指出的是,对于时间限制信号提取的傅里叶变换可以获得的信息存在重要的基本限制。
考虑零填充的限制情况 - 例如,取一个样本,然后将其填充到1秒持续时间,以便以1 Hz的分辨率进行傅里叶变换。很明显,一个非常短的信号片段根本不包含有关周期性的信息。直觉上,我们需要一个比问题周期更长的片段,以便能够说明信号在那个时期是否真的重复。
如果我们对周期信号的形状有限制,我们可以做得更好。例如,如果我们只寻找单个正弦波(即,我们知道我们的信号是s(t)= A * cos(w * t + phi)),那么我们可以求解未知振幅A,频率w和相位phi使用少至三个s(t)样本。然而,我们很难看到完全符合该配方的信号。至少我们期望增加噪声,但通常我们有很多谐波,即未知的非正弦周期波形。
如果您尝试实现上面建议的插值峰值拾取和/或零填充,然后查看您获得的结果,因为您使信号摘录更短(同时保持FFT长度相同),您将看到当片段变短时,不确定性(误差)会增加 - 当片段短于您尝试测量的周期长度的两倍左右时,您可能会得到无用的结果。
这说明了一个有点反直觉但非常基本的限制:根据短于T秒的观察,很难确定信号的频率优于1 / T Hz。这有时被称为不确定性原理,并且在数学上它与量子力学中的海森堡不确定性原理相同。
最后,我用来改善离散傅立叶变换分辨率的另一种技术是瞬时频率,如下所述:
Toshihiko Abe,Takao Kobayashi,Satoshi Imai:基于瞬时频率在嘈杂环境中进行谐波增强的稳健音高估计。 ICSLP 1996 (你可以在网上找到PDF,我的链接余额已经用完了。)
频率只是相位相对于时间的导数;事实证明,你可以使用"衍生部分"通过使用不同的窗函数(一个是另一个的导数)组合两个FFT的实部和虚部,直接计算每个FFT区中的瞬时频率。有关Matlab的实现,请参阅
http://labrosa.ee.columbia.edu/matlab/chroma-ansyn/ifgram.m
或在Python中:
https://github.com/bmcfee/librosa/blob/master/librosa/core.py#L343
答案 2 :(得分:2)
您需要先了解' 投球'真的是。当在吉他或钢琴上制作单个音符时,我们听到的不仅仅是声音振动的一个频率,而是在不同的数学相关频率上发生的多个声音振动的复合。这种不同频率的振动合成元素被称为谐波或部分。例如,如果我们按下钢琴上的中间C键,复合波的各个频率将以261.6 Hz开始作为基频,523 Hz将是2次谐波,785 Hz将是3次谐波,1046 Hz是第四次谐波等。后来的谐波是基频的整数倍,261.6 Hz(例如:2 x 261.6 = 523,3 x 261.6 = 785,4 x 261.6 = 1046)。
下面,在GitHub.com上,我设计了一个不寻常的两阶段算法的C ++源代码,它可以在Windows上播放时对复音MP3文件进行实时音高检测。这种免费应用程序(PitchScope Player,可在网上获得)经常用于在MP3录制时检测吉他或萨克斯独奏的音符。您可以下载Windows的可执行文件,以便在您选择的mp3文件上查看我的算法。该算法被设计成在MP3或WAV音乐文件中的任何给定时刻检测最主要的音调(音符)。通过在MP3录制期间的任何给定时刻的最主要音高(音符)的变化来准确地推断音符开始。
我使用改进的DFT对数变换(类似于FFT)来首先通过查找具有峰值电平的频率来检测这些可能的谐波(参见下图)。由于我为修改后的Log DFT收集数据的方式,我不必对信号应用窗口函数,也不必添加和重叠。我创建了DFT,因此它的频道以对数方式定位,以便直接与吉他,萨克斯管等音符创建谐波的频率对齐。
我的音高检测算法实际上是一个两阶段过程:a)首先检测到ScalePitch(' ScalePitch'有12个可能的音高值:{E,F,F#,G,G#,A,A# ,B,C,C#,D,D#})b)并且在确定ScalePitch之后,通过检查4种可能的Octave-Candidate音符的所有谐波来计算Octave。该算法旨在检测复音MP3文件中任何给定时刻的最主要音高(音符)。这通常对应于乐器独奏的音符。那些对我的Two Stage Pitch Detection算法的C ++源代码感兴趣的人可能想从GitHub.com的SPitchCalc.cpp文件中的Estimate_ScalePitch()函数开始。
https://github.com/CreativeDetectors/PitchScope_Player
https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection
答案 3 :(得分:1)
通常,增加FFT的长度不仅会增加延迟,还会使检测频率变得困难,因为它可能不是常数。
使用重叠和窗口的常见做法是:http://en.wikipedia.org/wiki/Spectral_density_estimation
在峰值检测之后,有一些方法可以提高估计频率的准确度。例如,通过傅里叶系数的插值 请查看第2部分here或第1.3节here