我想检测的不是音高,而是歌唱音符的音级。
因此,是 C4 还是 C5 并不重要:它们必须都被检测为 C。
想象一下排列在钟面上的 12 个半音,指针指向音高等级。这就是我所追求的!理想情况下,我希望能够分辨出演唱的音符是准确的还是略微偏离的。
这不是先前提出的问题的重复,因为它引入了以下约束:
声源是单一的人声,希望背景干扰可以忽略不计(尽管我可能需要处理这个)
八度不重要,只有音阶
我想检测的不是音高,而是歌唱音符的音级。
因此,是 C4 还是 C5 并不重要:它们必须都被检测为 C。
想象一下排列在钟面上的 12 个半音,指针指向音高等级。这就是我所追求的!理想情况下,我希望能够分辨出演唱的音符是准确的还是略微偏离的。
这不是先前提出的问题的重复,因为它引入了以下约束:
声源是单一的人声,希望背景干扰可以忽略不计(尽管我可能需要处理这个)
八度不重要,只有音阶
在这里查看我的答案以获得平滑的频率检测:https ://stackoverflow.com/a/11042551/1457445
至于将这个频率捕捉到最近的音符——这是我为我的调谐器应用程序创建的一种方法:
- (int) snapFreqToMIDI: (float) frequencyy {
int midiNote = (12*(log10(frequencyy/referenceA)/log10(2)) + 57) + 0.5;
return midiNote;
}
这将返回 MIDI 音符值(http://www.phys.unsw.edu.au/jw/notes.html)
为了从此 MIDI 音符值中获取字符串:
- (NSString*) midiToString: (int) midiNote {
NSArray *noteStrings = [[NSArray alloc] initWithObjects:@"C", @"C#", @"D", @"D#", @"E", @"F", @"F#", @"G", @"G#", @"A", @"A#", @"B", nil];
return [noteStrings objectAtIndex:midiNote%12];
}
有关使用输出平滑的音高检测的示例实现,请查看 musicerskit.com/developer.php
音高是人类的一种心理感知现象。峰值频率内容与音高或音级不同。FFT 和 DFT 方法不会直接提供音高,只提供频率。过零测量也不适用于人声源。尝试 AMDF、ASDF、自相关或倒谱方法。还有很多关于音高估计的学术论文。
这里还有一长串音高估计算法。
编辑补充:Apple 的 SpeakHere 和 aurioTouch 示例应用程序(可从他们的 iOS 开发中心获得)包含用于从 iPhone 麦克风获取 PCM 示例块的示例源代码。
其他答案中引用的大多数频率检测算法都不适用于语音。要了解为什么如此直观,请考虑一种语言中的所有元音都可以在一个特定的音符上演唱。即使所有这些元音都有非常不同的频率内容,它们都必须被检测为同一个音符。任何语音的音符检测算法都必须以某种方式考虑到这一点。此外,人类的语音和歌曲包含许多擦音,其中许多擦音中没有隐含的音高。
在通用(非语音案例)中,您正在寻找的功能称为色度功能,并且在该主题上有相当多的工作。它等效地称为谐波音高等级轮廓。关于这个概念的原始参考论文是 Tayuka Fujishima 的“ Real-Time Chord Recognition of Musical Sound: A System Using Common Lisp Music ”。Wikipedia 条目概述了该算法的更现代变体。有很多免费的论文和色度特征检测的 MATLAB 实现。
但是,由于您只关注人声,并且由于人声自然包含大量泛音,因此在此特定场景中您实际上要寻找的是基频检测算法,或f0 检测算法。有几种这样的算法专门针对语音进行了调整。此外,这是一种被广泛引用的算法,可以同时处理多个声音。然后,您将根据等律音阶检查检测到的频率,然后找到最接近的匹配。
由于我怀疑您正在尝试构建一个音调检测器和/或校正器,如自动调谐,您可能想要使用 M. Morise 出色的WORLD实现,它允许快速和高质量地检测和修改语音流上的 f0。
最后,请注意,只有少数声高检测器在声带中运行良好。几乎所有人,包括 WORLD,在声乐和非常低的声音方面都失败了。许多论文将人声炒作称为“吱吱作响的声音”,并开发了特定的算法来专门帮助这种类型的语音输入。
如果您正在寻找音高课程,您应该查看色谱图(http://labrosa.ee.columbia.edu/matlab/chroma-ansyn/)
您也可以简单地检测 f0 (使用类似YIN 算法)并返回适当的半音,大多数基频估计算法都会受到倍频程误差的影响
对输入波形中的样本执行离散傅立叶变换,然后将对应于不同八度音阶的等效音符的值相加。取最大值作为主频。
您可能会在 Objective C 中找到一些适合您需要的现有 DFT 代码。
把我找到的信息放上来...
维基百科上的音高检测算法是一个很好的起点。它列出了一些无法确定八度音阶的方法,这对我的目的来说是可以的。
可以在此处找到对自相关的一个很好的解释(为什么维基百科不能像那样简单地描述事物??)。
多亏了DSP Dimension的这篇文章,我终于结束了这一点
文章包含源代码。
基本上,他执行 FFT。然后他解释说,与它们落入的垃圾箱中心不重合的频率会以一种钟形曲线涂抹在附近的垃圾箱上。他解释了如何在第二遍(FFT 是第一遍)中从这些数据中提取准确的频率。
然后文章进一步讨论了音调变化;我可以简单地删除代码。
请注意,他们提供了一个商业图书馆,它做同样的事情(甚至更多),只是超级优化。有一个免费版本的库可能会做我需要的一切,虽然因为我已经使用过 iOS 音频子系统,我还不如自己实现它。
作为记录,我找到了另一种方法来提取精确频率,方法是在bin 及其两个邻居上逼近二次曲线。我不知道这两种方法之间的相对准确性是多少。
正如其他人所提到的,您应该使用音高检测算法。既然这个基础很好,我将解决你问题的一些细节。你说你正在寻找音符的音高等级。但是,找到它的方法是计算音符的频率,然后使用表格将其转换为音级、八度和音分。我不知道在没有找到基频的情况下获得音高等级的任何方法。
您将需要一个实时音高检测算法。在评估算法时,请注意每个算法隐含的延迟,与您期望的准确性相比。尽管有些算法比其他算法更好,但从根本上说,您必须以一种换取另一种,并且不能确定地知道两者——有点像海森堡不确定性原理。(当只听到一个周期的一小部分时,你怎么知道这个音符是 C4?)
你的“平滑”方法相当于一个数字滤波器,它会改变声音的频率特性。简而言之,它可能会干扰您估计音高的尝试。如果您对数字音频感兴趣,那么数字滤波器是该领域的基本且有用的工具,而且是一个引人入胜的主题。拥有强大的数学背景有助于理解它们,但您不一定需要它来获得基本概念。
此外,您的过零方法是估计波形周期和音高的基本技术。可以通过这种方式完成,但只能进行大量启发式和微调。(本质上,开发一些“候选”音高并尝试推断占主导地位的音高。会出现很多特殊情况,这会混淆这一点。快速的一个是较少的's'。)你会发现它更容易从频域音高检测算法开始。