【问题标题】:Pitch recognition of musical notes on a smart phone智能手机上音符的音高识别
【发布时间】:2010-11-30 05:51:11
【问题描述】:

在 CPU、代码大小和 RAM 等资源有限的情况下,如何最好地检测音符的音高,类似于电子或软件调音器的工作方式?

我应该使用:

  • 亲吻 FFT
  • FFTW
  • 离散小波变换
  • 自相关
  • 过零分析
  • 倍频程过滤器

其他?

简而言之,我要做的是识别单个音符,在中音 C 以下两个八度到以上两个八度,在任何(合理的)乐器上演奏。我希望在半音的 20% 以内 - 换句话说,如果用户弹奏太平或太尖锐,我需要区分这一点。但是,我不需要调整所需的精度。

【问题讨论】:

    标签: audio signal-processing fft pitch-tracking


    【解决方案1】:

    如果您不需要那么高的精度,FFT 就足够了。 Window 首先是音频块,以便您获得明确定义的峰值,然后找到第一个重要的峰值。

    Bin 宽度 = 采样率 / FFT 大小:

    基础range from 20 Hz 到 7 kHz,因此 14 kHz 的采样率就足够了。下一个“标准”采样率是 22050 Hz。

    然后,FFT 大小由您想要的精度确定。 FFT 输出在频率上是线性的,而乐音在频率上是对数的,因此最坏情况下的精度将在低频下。对于 20 Hz 的 20% 半音,您需要 1.2 Hz 的宽度,这意味着 18545 的 FFT 长度。 2 的下一个幂是 215 = 32768。这是 1.5 秒的数据,需要我笔记本电脑的处理器 3 毫秒来计算。

    这不适用于具有“missing fundamental”的信号,并且找到“第一个重要”峰值有些困难(因为harmonics are often higher than the fundamental),但您可以找出适合您情况的方法。

    Autocorrelation and harmonic product spectrum 更擅长找到波的真正基波而不是谐波之一,但我认为它们处理 inharmonicity 的效果并不好,而且钢琴或吉他等大多数乐器都是不和谐的(谐波是比他们应该的稍微尖锐)。不过,这实际上取决于您的情况。

    此外,您可以使用Chirp-Z transform 仅在感兴趣的特定频带内进行计算,从而节省更多的处理器周期。

    为了比较,我写了a few different methods in Python

    【讨论】:

    • 这是非常有用的。谢谢你。我想现在我只关心 4 个八度,每边半个八度,总共 5 个八度。鉴于你告诉我的,我可能可以毫无困难地做 6 或 8 次。
    • endolith 的回答不是严重低估了所需的垃圾箱数量吗?频率在频率上是线性的,对吧?因此,要在频谱的低端(例如 100Hz)获得 20% 的半音精度,需要大约 1 赫兹的 bin 频率宽度。从 100Hz 向上四个八度音阶是 1600Hz,所以你需要 1500 个 bin。还是我错过了什么,完全有可能......?
    • @tml 嗯,是的。我怎么犯了这么愚蠢的错误,还得到了这么多赞?
    【解决方案2】:

    如果您想实时进行音高识别(精确到半音的 1/100 以内),您唯一真正的希望就是过零方法。这是一个微弱的希望,很抱歉。过零可以仅根据几个波长的数据来估计音调,并且可以通过智能手机的处理能力来完成,但它并不是特别准确,因为测量波长的微小误差会导致估计频率的大误差。诸如吉他合成器之类的设备(它只用几个波长从吉他弦中推断出音高)通过将测量值量化为音阶来工作。这可能适用于您的目的,但请注意,过零对简单波形的效果很好,但对更复杂的乐器声音效果却越来越差。

    在我的应用程序(在智能手机上运行的软件合成器)中,我使用单个乐器音符的录音作为波表合成的原材料,为了产生特定音高的音符,我需要知道一个乐器的基本音高录音,精确到半音的 1/1000 以内(我真的只需要 1/100 的精度,但我对此有强迫症)。过零方法非常不准确,而基于 FFT 的方法要么太不准确,要么太慢(有时两者兼而有之)。

    在这种情况下,我发现的最佳方法是使用自相关。通过自相关,您基本上可以猜测音高,然后测量样品在相应波长处的自相关。通过半音扫描合理的音高范围(比如 A = 55 Hz 到 A = 880 Hz),我找到最相关的音高,然后在该音高附近进行更细粒度的扫描以获得更准确的值。

    最适合您的方法完全取决于您尝试使用它的目的。

    【讨论】:

    • 有趣的答案。有一件事让我感到困惑......不是自相关O(N ^ 2)和FFT O(NlogN),也就是说,为了快速自相关人们通常使用FFT作为第一步,那么FFT如何自相关起作用时太慢且不准确?这是因为您可以有效地使用具有自相关性的较短数据段吗?
    • @tom10:通常相对性能计算是针对非音乐应用的。对于音高检测,您可以利用以下事实:音高可能在 55-880 Hz 的范围内,而不是 0-44100 Hz,因此您不必扫描/自相关完整的理论范围。此外,您还可以使用一个简单的数学技巧,使一组扫描一起完成的时间比单独完成的时间总和要少得多。
    • 可以使用 FFT 获得基波附近,然后扫描/自相关以获得更精确的测量结果。到目前为止,我发现我的方法(纯自相关)稍微快一些,但我最近(感谢一个 SO 问题)对加速我的 FFT 代码的方法有所了解,所以我自己可能很快就会转向这种方法.然而,最终 FFT 方法有一条明确的曲线,即速度会随着精度的提高而下降,因此在某些时候必须涉及自相关。
    • @MusiGenesis:感谢您提供的信息。很有趣!
    • @MusiGenesis:谢谢。你能告诉我你上面提到的简单的数学技巧是什么吗?
    【解决方案3】:

    我不熟悉您提到的所有方法,但您选择的方法主要取决于输入数据的性质。您是在分析纯音,还是您的输入源有多个音符?语音是您输入的一个特征吗?您必须对输入进行采样的时间长度是否有任何限制?您是否可以牺牲一些准确性来换取速度?

    在某种程度上,您的选择还取决于您是想在time 还是frequency space 中执行计算。将time series 转换为频率表示需要时间,但根据我的经验,往往会得到更好的结果。

    Autocorrelation 比较时域中的两个信号。一个简单的实现很简单,但计算起来相对昂贵,因为它需要在原始信号和时移信号中的所有点之间进行成对差分,然后进行差分以识别自相关函数中的转折点,然后选择对应于的最小值基频。有替代方法。例如,Average Magnitude Differencing 是一种非常便宜的自相关形式,但准确性会受到影响。所有自相关技术都存在倍频程误差的风险,因为函数中存在基波以外的峰值。

    测量zero-crossing points 简单明了,但如果信号中存在多个波形,则会遇到问题。

    在频率空间中,基于FFT 的技术可能对您的目的足够有效。一个例子是谐波乘积频谱技术,它将信号的功率谱与每个谐波的下采样版本进行比较,并通过将频谱相乘以产生清晰的峰值来识别音高。

    与以往一样,没有什么可以替代测试和分析多种技术,以凭经验确定最适合您的问题和限制的方法。

    这样的回答只能触及这个话题的皮毛。除了前面的链接,这里还有一些相关的参考资料供进一步阅读。

    【讨论】:

    • 简而言之,我想做的是识别一个音符,在任何(合理的)乐器上演奏。我希望在半音的 20% 以内 - 换句话说,如果用户弹奏太平或太尖锐,我需要区分这一点。但是,我不需要调整所需的精度。
    【解决方案4】:

    过零不起作用,因为典型声音的谐波和过零比基频多得多。

    我尝试过(作为家庭项目)是这样的:

    1. 使用 ADC 以您需要的任何采样率对声音进行采样。
    2. 检测波形的短期正负峰值电平(滑动窗口或类似)。 IE。包络检测器。
    3. 制作一个方波,当波形在正包络的 90%(左右)以内时变高,当波形在负包络的 90% 以内时变低。 IE。带滞后的跟踪方波。
    4. 使用直接计数/时间计算来测量方波的频率,使用尽可能多的样本来获得所需的精度。

    但是,我发现通过电子键盘的输入,对于某些乐器声音,它设法拾取了 2 倍的基本频率(下一个八度音阶)。这是一个附带项目,在开始做其他事情之前,我从来没有开始实施解决方案。但我认为它有望比 FFT 减少 CPU 负载。

    【讨论】:

      【解决方案5】:

      在我的项目danstuner 中,我从Audacity 获取代码。它基本上需要一个 FFT,然后通过在 FFT 上放置一条三次曲线并找到该曲线的峰值来找到峰值功率。效果很好,虽然我不得不提防八度跳跃。

      Spectrum.cpp

      【讨论】:

      • Audacity 现在可能有一些更漂亮的东西,那是很久以前的事了。
      • 实际上,我认为代码使用 FFT 进行了自相关。哇,好久不见了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多