【问题标题】:Is my understanding of FFT and Pitch Detection correct here?我对 FFT 和音高检测的理解在这里正确吗?
【发布时间】:2014-07-11 19:52:47
【问题描述】:

关于 FFT 和音高检测的 Stackoverflow 及其他领域的讨论数不胜数。

人们普遍认为,FFT 虽然速度很快,但对于许多应用程序来说并不是很准确,但通常不会解释原因。

我想解释一下我对为什么会这样的理解,希望比我聪明的人可以纠正我并填补我无法填补的空白。


FFT 将输入数据从时域转换到频域。

最初,我们从一系列数据开始,如果我们要在图表上绘制这些数据,则 Y 轴为给定时间点的声音幅度,X 轴为时间。这是在时域中。

FFT 将这些时间点的幅度值转换为不同频率的幅度。

FFT输出的数据数量与输入的数据数量相同

如果我们输入 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 个 bin(请记住,输入和输出的数据数量相同),每个 bin 的频率跨度为 86.132 Hz。

所以对于给定的 bin,我们可以通过以下方式计算它所代表的频率:

Bin Freq (Hz) = Bin number (n) * bin width (Hz)

使用上面的值,FFT 输出中的第三个 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),但到目前为止我对音高检测中的 FFT 的理解是否正确?

有什么方法可以在不牺牲延迟的情况下提高 FFT 的准确性?

【问题讨论】:

  • 请注意,FFT 最适用于大小为 2 次方的缓冲区。
  • @nalply:一些 FFT 实现就是这种情况,但肯定不是全部。 FFT 实现递归地工作,并且要求在每一步中剩余的数据都被划分为N 相等的块。根据实现的不同,N 的不同选择是允许的,但实际上总是允许235 不太常见。 4 是多余的(这只是 2x2),6 (2x3) 也是如此。一个因子7 开始变得不那么有用了,因为你最多可以填充 8 个。

标签: signal-processing fft pitch-detection


【解决方案1】:

您需要首先了解 'pitch' 的真正含义。当在吉他或钢琴上发出一个音符时,我们听到的不仅仅是一个声音振动频率,而是在不同数学相关频率上发生的多个声音振动的复合。这种不同频率的振动复合的元素被称为谐波或分音。例如,如果我们按下钢琴上的中间 C 键,复合谐波的各个频率将从 261.6 Hz 作为基频开始,523 Hz 将是 2 次谐波,785 Hz 将是 3 次谐波,1046 Hz 将是 4 次谐波等。后面的谐波是基频 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 文件中任何给定时刻的最主要音高(音符)。这通常对应于乐器独奏的音符。对我的两阶段音高检测算法的 C++ 源代码感兴趣的人可能希望从 GitHub.com 上 SPitchCalc.cpp 文件中的 Estimate_ScalePitch() 函数开始。

https://github.com/CreativeDetectors/PitchScope_Player

https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection

【讨论】:

    【解决方案2】:

    除了@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

    【讨论】:

    • 您的评论指向频谱和频率分析的深度。别忘了提到 Wigner Ville 变换,当然还有线性预测编码。但问题是关于对 FFT 的理解。实际上,具有抛物线插值的 FFT 比您建议的要好得多。关键是“插值”。两个 bin 之间的频率可以通过相邻 bin 的幅度来重建。当然,傅里叶频谱中只有正弦曲线。但是,是的,如果 FFT 和 p.i. 是下一步。高阶光谱分析是不够的。
    • “每个 FFT bin 中的瞬时频率”非常有趣,但 Python 链接已损坏。请介意更新它。谢谢
    【解决方案3】:

    关于您的第一个问题(“到目前为止,我对音高检测中的 FFT 的理解是否正确?”)我会说是的,但我想指出一个陷阱:

    使用上面的值,FFT 输出中的第 3 个 bin 将表示 258.398Hz 处的幅度:

    分频 (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 个 bins 代表真实的频谱,即在你的例子中是 257 个 bins。

    关于您的第二个问题,有两种广泛而简单的方法可以提高频率检测精度:

    1. 零填充(参见例如some answers why zero-padding is used

    2. 抛物线插值(参见例如the 1st answer

    最后,一个不问回答:绝对推荐应用窗函数,不仅因为它是抛物线插值的前提,而且因为它降低显着人工旁瓣的幅度。

    【讨论】:

    • 太棒了!谢谢!您知道上述相关方法可以将准确性提高多少吗?我需要 1Hz 范围内但每秒 4+ FFT 的东西。
    • 与零填充(将 bin 数量增加一个整数)相比,抛物线插值产生浮点结果,因此如果您选择高斯窗口函数,即使窗口大小只有 50 毫秒,有效性非常适合合成信号。窗口大小和窗口函数是重要参数。
    • 准确地说,bin 0 为 0 Hz +/- 43.132 Hz。 Bin 3 的中心频率为 258 Hz,但它代表的频率范围为 215 到 301 Hz。
    • 第 0 和 (fftsiz/2+1) 个 bin 的宽度确实是其余 bin 宽度的一半。好点子。但实际宽度取决于零填充的数量和选择的窗口函数。它几乎永远不会是 bin 距离的 +-一半。
    【解决方案4】:

    通常,增加 FFT 的长度不仅会增加延迟,而且还会使检测频率变得困难,因为它可能不是恒定的。
    一个常见的做法是使用重叠和窗口,看看这个:http://en.wikipedia.org/wiki/Spectral_density_estimation

    在峰值检测之后,有几种方法可以提高估计频率的准确性。例如,通过对傅立叶系数进行插值。
    看看第 2 节 here 或第 1.3 节 here

    【讨论】:

      猜你喜欢
      • 2011-09-23
      • 2011-05-28
      • 1970-01-01
      • 2023-03-19
      • 2011-01-15
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      相关资源
      最近更新 更多