23

由于资源有限,例如较慢的 CPU、代码大小和 RAM,如何最好地检测音符的音高,类似于电子或软件调音器所做的事情?

我应该使用:

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

其他?

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

4

5 回答 5

15

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

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

基本频率范围从20 Hz 到 7 kHz,因此 14 kHz 的采样率就足够了。下一个“标准”采样率是 22050 Hz。

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

这不适用于具有“缺少基波”的信号,并且找到“第一个重要”峰值有些困难(因为谐波通常高于基波),但您可以找到适合您情况的方法。

自相关和谐波乘积谱更适合于找到波的真正基波而不是谐波之一,但我认为它们不能很好地处理不和谐,并且大多数乐器(如钢琴或吉他)都是不和谐的(谐波从他们应该是什么)。不过,这实际上取决于您的情况。

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

为了比较,我在 Python 中编写了一些不同的方法。

于 2009-11-28T08:35:23.943 回答
13

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

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

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

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

于 2009-09-21T23:45:00.547 回答
6

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

在某种程度上,您的选择还取决于您是想在时间空间还是在频率空间中进行计算。将时间序列转换为频率表示需要时间,但根据我的经验,往往会产生更好的结果。

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

测量零交叉点简单明了,但如果信号中存在多个波形,则会遇到问题。

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

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

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

于 2009-09-22T00:11:02.397 回答
5

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

请参阅Spectrum.cpp

于 2009-09-21T23:17:29.873 回答
5

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

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

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

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

于 2009-09-22T00:03:43.680 回答