如何分析PCM或WAV样本的基频?

26

我有一个来自DirectX缓冲区的样本。它是一个乐器演奏并被捕获的音符样本。如何分析样本的频率(就像吉他调音器一样)?我相信需要使用FFT,但我没有相关的HOWTO指针。

7个回答

22
FFT可以帮助您确定频率在哪里,但不能告诉您确切的频率。 FFT中的每个点都是频率的“bin”,因此如果FFT中有一个峰值,则您只知道您想要的频率在该bin或频率范围内的某个位置。
如果您需要真正准确的结果,则需要进行长时间高分辨率的FFT,并且需要大量的bin(=大量的内存和计算)。 您还可以通过对对数比例谱进行二次插值来从低分辨率FFT中猜测真实的峰值,这种方法效果出奇地好。
如果计算成本最重要,则可以尝试将信号转换为可以计算零交叉点的形式,然后您计算的越多,您的测量就越准确。
但是,如果基频缺失,则以上所有方法都不起作用。 :)
我在这里列举了几种不同的算法此处, 插值FFT通常是最准确的(尽管仅适用于基频为最强谐波的情况 - 否则需要更聪明地找到它),其次是零交叉(虽然仅适用于每个周期有一个交叉点的波形)。这两种条件都不是典型的。
请记住,许多乐器(如钢琴或吉他)中基频以上的偏振不是完美的谐波。每个偏极都实际上有一点走音,或者说是非谐波。因此,FFT中的高频峰将不会完全处于基频的整数倍上,并且波形将从一个周期到另一个周期略微变化,这会影响自相关。
为了获得真正准确的频率读数,我建议使用自相关来猜测基频,然后使用二次插值找到真正的峰值。(您可以在频域中执行自相关以节省CPU周期。)有很多陷阱,正确的方法取决于您的应用程序。

1
除了缺乏基础知识外,还存在共振问题需要处理弦乐器信号。特别是在吉他的乐器调音场景中,未调音的琴弦在任何给定时间都是开放的,如果接近调谐音高,则与演奏的琴弦具有相同的部分谐波。 - marko
1
算法概述的链接已经失效了。你是否有其他地方可以提供它们?先感谢了! - Rafael Beirigo
1
@RafaelB 谢谢,我已经修复了 https://gist.github.com/endolith/255291 - endolith

19

除了基于频率的算法外,还有其他基于时间的算法。

自相关是一种相对简单的音高检测算法。

参考链接:http://cnx.org/content/m11714/latest/

我编写了可读性较好的自相关算法和其他算法的C#实现。请查看http://code.google.com/p/yaalp/

http://code.google.com/p/yaalp/source/browse/#svn/trunk/csaudio/WaveAudio/WaveAudio上列出了文件列表,PitchDetection.cs是你想要的文件。

(该项目采用GPL协议,请了解使用代码的条款)。


3
我想多次给这个答案点赞。FFT对于这个问题来说是一个糟糕的解决方案,就像开坦克去杂货店一样,虽然很酷,但并不是最好的方法。自相关通常被认为是这个问题的正确解决方案,比如Autotune/Melodyne/Singstar等软件。基于零交叉的解决方案仅适用于具有特定谐波行为的情况。自相关方法也可以非常高效地实现。 - Dave Gamble
5
哇,什么?我不同意。频域解决方案绝对不是一个糟糕的解决方案,谐波乘积谱或倒谱法易于实现并且相当健壮。自相关并不一般被认为是“正确”的解决方案之一;它只是几种有效解决方案之一。当然,零交叉点不是音高的可靠指标。 - Steve Tjoa
1
自相关通常比傅立叶变换计算密集度高。实际上,我们经常使用傅里叶变换进行自相关计算,因为它更快。如果你本可以用 FFT 进行自相关计算而使用了朴素的自相关方法,就像是开坦克冲进杂货店而不是通过正门进去。 - endolith

6
吉他调音器不使用FFT或DFT。通常它们只是计算零交叉点。您可能无法获得基频,因为某些波形具有比其他波形更多的零交叉点,但通常可以以此方式获得基频的倍数。这已足够获得音符,尽管您可能会偏离一个或多个八度。
在计算零交叉之前进行低通滤波通常可以消除多余的零交叉。调整低通滤波器需要一些对要检测的频率范围的了解。

3
我怀疑他们只是计算零交叉。在典型的吉他波形中,每个周期有许多零交叉。他们可能会做一个简单的自相关。 - endolith
1
更多的零交叉对于一个简单的调谐器来说并不重要。记住,调谐器不需要精确的基频率,它只需要知道音符。通过每个周期计算更多的零交叉可能会锁定到更高的八度,但Cb仍然是Cb,两个cents的偏高仍然是两个cents的偏高。自相关在更高级的处理中非常好用,但对于调谐器来说有点过头了。 - Mendelt

5

FFTs(快速傅里叶变换)确实会被涉及。FFT允许你用一组固定频率和变幅的简单正弦波来近似任何模拟信号。你将要做的就是对样本进行分解,得到振幅->频率对,然后取与最高振幅相对应的频率。

希望其他SO读者能填补我在理论和代码之间留下的空白!


3
这种方法在音乐环境下存在严重的准确性问题。正如endolith所指出的那样,FFT可以给出一定频率范围内的强度;频率范围随着FFT窗口大小的缩小(和加快)而变大。更糟糕的是,整个范围是0到44100 Hz(对于红色书籍音频),而典型的音符几乎总是低于1000Hz,因此你拥有的大部分分辨率都被浪费在更高的频带上。 - MusiGenesis

4

更具体地说:

如果您从输入数组中的原始 PCM 开始,您基本上拥有的是波幅与时间的图表。做 FFT 将会将其转换为从0到输入采样率的1/2的频率直方图。结果数组中每个条目的值将是对应子频率的“强度”。

因此,要在给定大小为 N、以 S 次/秒采样的输入数组中找到根频率:

FFT(N, input, output);
max = max_i = 0;
for(i=0;i<N;i++)
  if (output[i]>max) max_i = i;
root = S/2.0 * max_i/N ;

我喜欢简洁明了的风格,但怀疑for循环存在问题。输出结果肯定不会与输入大小相同。 - Waslap
它的大小完全相同。进行FFT是一种权衡 - 只有通过分析较长时间跨度,才能获得精细的频率分辨率(许多输出bin)。如果将分析限制在非常精确的时间范围内(小的输入样本大小),则在频率域中会获得低分辨率。 - AShelly

3
在PCM音频信号中检索基本频率是一项困难的任务,需要对其进行大量讨论... 通常基于时间的方法不适用于复音信号,因为由多个基本频率引起的不同谐波成分之和形成的复杂波具有仅取决于最低频率分量的零交叉率... 在频域中,FFT也不是最合适的方法,因为音符之间的频率间隔遵循指数比例而非线性。这意味着在FFT方法中使用的恒定频率分辨率,如果时间域中的分析窗口大小不足够大,则可能无法解析出较低频率的音符。
更适合的方法是常Q变换,它是在对信号进行低通滤波和2倍抽取(即每步减半采样频率)处理后应用DFT,以获得具有不同频率分辨率的不同子带。通过这种方式,DFT的计算被优化了。问题在于时间分辨率也是可变的,并且随着较低子带而增加...
最后,如果我们正在尝试估算单个音符的基本频率,则FFT/DFT方法是可以的。但在复音环境中情况就不同了,其中不同声音的部分重叠并根据它们的相位差异相加/相消,因此单个频谱峰可能属于不同的谐波内容(属于不同的音符)。在这种情况下,相关性不能给出良好的结果...

1
应用DFT,然后从结果中推导出基频。在谷歌上搜索DFT信息将为您提供所需的信息--我可以为您提供一些链接,但它们对数学知识的要求差异很大。
祝你好运。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接