有限的资源,如较慢的CPU,代码大小和RAM,如何最好地检测音符的音高,类似于电子或软件调谐器会做什么?
我应该使用:
其它?
简而言之,我要做的就是识别单个音符,在中间C到两个八度音程之上的两个八度音阶,在任何(合理的)乐器上演奏。我想要在半音的20%范围内 - 换句话说,如果用户玩得太平或太尖锐,我需要区分它。但是,我不需要调整所需的准确度。
答案 0 :(得分:14)
如果您不需要那么高的准确度,那么FFT就足够了。首先Window音频块,以便获得明确定义的峰值,然后找到第一个重要峰值。
Bin宽度=采样率/ FFT大小:
基础知识range from 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毫秒来计算。
这不适用于具有“missing fundamental”的信号,并且发现“第一个重要”峰值有点困难(自harmonics are often higher than the fundamental以来),但你可以找到适合的方式你的情况。
Autocorrelation and harmonic product spectrum更善于找到波浪的真正基础而不是其中一种谐波,但我不认为它们与inharmonicity处理得很好,大多数乐器如钢琴或吉他都是不和谐(谐波与它们应该是略微尖锐的)。但这确实取决于你的情况。
此外,您可以使用Chirp-Z transform仅在特定感兴趣的频段内进行计算,从而节省更多的处理器周期。
为了进行比较,我写了a few different methods in Python。
答案 1 :(得分:13)
如果你想实时进行音高识别(并且准确到半音的1/100以内),你唯一真正的希望就是过零的方法。抱歉,这是一个微弱的希望。过零可以从几个波长的数据中估算出音调,并且它可以通过智能手机的处理能力来完成,但它并不是特别准确,因为测量波长的微小误差会导致估计频率出现大的误差。吉他合成器(通过几个波长从吉他弦中推断出音高)等设备的工作原理是将测量值量化为音阶的音符。这可能适用于您的目的,但请注意,零交叉可以很好地处理简单的波形,但对于更复杂的乐器声音,往往会越来越不能正常工作。
在我的应用程序(在智能手机上运行的软件合成器)中,我使用单个乐器音符的录音作为波表合成的原材料,并且为了以特定音高产生音符,我需要知道基音的基本音高。录音,精确到半音的1/1000之内(我真的只需要1/100的准确度,但我是关于此的OCD)。过零的方法很多太不准确了,基于FFT的方法要么太不准确,要么太慢(或者有时两者)。
我在这种情况下发现的最佳方法是使用自相关。通过自相关,您基本上可以猜测音高,然后测量样品在相应波长处的自相关性。通过半音调扫描似乎合理的音高范围(比如A = 55 Hz到A = 880 Hz),我找到最相关的音高,然后在该音高附近进行更精细的扫描,得到一个更准确的价值。
最适合您的方法完全取决于您尝试使用此方法。
答案 2 :(得分:6)
我不熟悉您提到的所有方法,但您选择的方法主要取决于输入数据的性质。您是在分析纯音,还是您的输入源有多个音符?演讲是你输入的一个特色吗?您必须对输入进行采样的时间长短是否有任何限制?您是否能够以一定的准确度换取速度?
在某种程度上,您选择的内容还取决于您是想在time还是frequency space中执行计算。将time series转换为频率表示需要时间,但根据我的经验,往往会产生更好的结果。
Autocorrelation比较时域中的两个信号。一个简单的实现很简单,但计算相对昂贵,因为它需要在原始和时移信号中的所有点之间进行成对差分,然后进行微分以识别自相关函数中的转折点,然后选择对应的最小值。基本频率。还有其他方法。例如,Average Magnitude Differencing是一种非常便宜的自相关形式,但准确性受到影响。所有自相关技术都存在八度音程错误的风险,因为函数中存在非基本峰值。
测量zero-crossing points简单明了,但如果信号中存在多个波形,则会遇到问题。
在频率空间中,基于FFT的技术可能足以满足您的需要。一个例子是谐波产品频谱技术,它将信号的功率谱与每个谐波处的下采样版本进行比较,并通过将频谱相乘以产生清晰的峰值来识别音调。
与以往一样,没有任何替代方法可以测试和分析几种技术,从而凭经验确定最适合您的问题和约束的方法。
这样的答案只能触及本主题的表面。除了早期的链接,这里还有一些相关的参考资料供进一步阅读。
答案 3 :(得分:5)
在我的项目danstuner中,我从Audacity获取了代码。它基本上采用FFT,然后通过在FFT上设置三次曲线并找到该曲线的峰值来找到峰值功率。工作得很好,虽然我不得不防止八度跳跃。
请参阅Spectrum.cpp。
答案 4 :(得分:5)
零交叉不起作用,因为典型的声音具有谐波和过零点,远远超过基频。
我尝试过的东西(作为家庭项目)是这样的:
然而我发现,通过我的电子键盘输入,对于某些乐器声音,它设法拾取了2倍的基频(下一个八度)。这是一个侧面项目,我从来没有开始实施解决方案,然后转向其他事情。但我认为它有望承担比CPU少得多的CPU负载。