C/C++/Obj-C实时算法,用于从声音输入中确定Note(而不是Pitch)

3
我希望检测的不是音高,而是唱出的音符的音级
因此,无论是C4还是C5都不重要:它们都必须被检测为C。
想象一下12个半音在时钟表面上排列,指针指向音级。这就是我想要的!理想情况下,我希望能够判断唱出的音符是否完美或略有偏差。
这不是以前提出的问题的重复,因为它引入了以下限制:
1.声源是一个单独的人声,希望背景干扰可以忽略(尽管我可能需要处理); 2.八度并不重要,只有音级
编辑--链接: 实时音高检测 使用苹果FFT和加速框架

是的,你正在“重复造轮子”。为什么PC上没有这个?它看起来比“Sing & See”好得多。 - Tamara Wijsman
离题一下:哎呀,刚发现这个新东西,可能更好用,唔...... - Tamara Wijsman
9个回答

7

查看我的答案,以获取平滑的频率检测:https://dev59.com/e3A75IYBdhLWcg3wP2gg#11042551

至于将此频率捕捉到最近的音符,这是我为我的调音器应用程序创建的一种方法:

- (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];
}

如果你需要一个关于音高检测的实现例子以及输出平滑处理,可以查看musicianskit.com/developer.php


在 int midiNote = (12*(log10(frequency/referenceA)/log10(2)) + 57) + 0.5; 中,referenceA 是什么? - some_id
1
440.0对于大多数管弦乐目的而言是标准音高。请查看维基百科关于音高标准A440的条目 - http://en.wikipedia.org/wiki/A440_(pitch_standard) - Kpmurphy91
谢谢,我正在查看你的自相关示例项目,它如何处理来自麦克风检测的噪声? - some_id

6
Pitch是一种人类心理感知现象。峰值频率内容与音高或音高类别不同。FFT和DFT方法无法直接提供音高,只能提供频率。零交叉测量对于人类语音源也效果不佳。尝试使用AMDF、ASDF、自相关或倒谱方法。此外,还有大量关于音高估计的学术论文。
这里还有另一个音高估计算法列表
编辑补充:苹果的SpeakHere和aurioTouch示例应用程序(可从其iOS开发中心获得)包含了从iPhone的麦克风获取PCM样本块的示例源代码。

5
大多数在其他回答中引用的频率检测算法对语音效果不佳。直观地看,考虑一下语言中的所有元音都可以在一个特定的音符上唱出来,尽管所有这些元音具有非常不同的频率内容,但它们都必须被检测为相同的音符。任何用于声音的音符检测算法都必须以某种方式考虑到这一点。此外,人类的语音和歌曲包含许多摩擦音,其中许多没有隐含的音高。
在通用(非语音)情况下,您要查找的功能称为色度特征,并且有相当大量的相关研究。 它也被称为和声音高类别轮廓。 该概念的原始参考文献是Tayuka Fujishima的“实时乐音和弦识别:使用Common Lisp Music的系统”。 维基百科条目概述了算法的更现代变体。 还有许多免费的论文和MATLAB实现可用于色度特征检测。
然而,由于您只关注人声,并且人声自然地包含大量泛音,在这种特定情况下,您实际上要寻找的是一种基频检测算法,或者f0检测算法。有几种这样的算法专门针对人声进行调整。此外,这里有一个广泛引用的算法可以同时处理多个声音。然后,您将检查检测到的频率与平均律音阶进行比较,找到最接近的匹配项。

我怀疑你正在尝试构建一个类似于Autotune的音高检测和/或校正器,因此你可能想使用M. Morise出色的WORLD实现,它可以快速而高质量地检测和修改语音流中的f0。

最后,请注意,只有少数几个声音音高检测器在声带颤音区间内能够很好地工作。其中几乎所有的音高检测器,包括WORLD,在声带颤音以及非常低的声音上都会失败。许多论文将声带颤音称为"creaky voice"并开发了特定算法,以帮助处理这种类型的声音输入。


3

2
LabRosa的Chromagram是哥伦比亚大学的Dan Ellis的杰作。这段代码适用于Matlab。 - loretoparisi
我认为他没有反转Chromagram,但他确实在他的网页上提供了Matlab的免费实现。 - lizzie

2
最终,我通过this来自DSP Dimension的文章得到了解决。
这篇文章包含源代码。
基本上,他执行了一个FFT。然后他解释说,与bin中心不完全重合的频率会在附近的bin上呈钟形分布。他还解释了如何在第二次处理(FFT是第一次处理)中从这些数据中提取精确的频率。
文章进一步介绍了音高转换;我可以简单地删除这段代码。
请注意,他们提供了一个商业库,可以做同样的事情(而且功能更多),只是超级优化了。虽然有一个免费版本的库可能会做到我需要的一切,但既然我已经通过iOS音频子系统工作过了,我可能会自己实现它。
记录一下,我找到了一种替代方法来通过近似二次曲线在频率分量及其相邻两个 bin 上的值here来提取精确频率。我不知道这两种方法之间的相对精确度。

那是一个声码器。声码器是一种经典的音调缩放方法。这与原始问题有什么关系? - johnwbyrd

2
对输入波形的样本执行离散傅里叶变换,然后将对应于不同八度的等效音符的值相加。取最大值作为主频率。
您很可能会在Objective C中找到一些现有的DFT代码,以满足您的需求。

1
这对于人类的声音,特别是男性的声音来说并不适用,因为许多频率值更可能属于完全不同“音符”的泛音,而不是从频率到音符表中明显的选择。 - hotpaw2
DFT是许多音高检测算法中使用的构建模块,但它不能替代真正的音高检测算法。 - jbarlow
hotpaw2,您能详细说明一下您的评论吗?我已经查看了女性声音的频谱图,它似乎由基频和其谐波组成。现在我有些困惑... - P i

2

我会尽力帮助您翻译以下内容,涉及IT技术方面:

随着我发现信息,我会把它放在这里...

维基百科上的音高检测算法是一个很好的起点。它列出了一些方法,用于确定八度,这对我的目的来说是可以的。

有关自相关的良好解释可以在这里找到(为什么维基百科不能像这样简单明了呢?)。


1

如其他人提到的,你应该使用音高检测算法。由于这个领域已经被广泛覆盖,我会介绍你问题的一些细节。你说你正在寻找这个音符的音高类别。然而,找到这个方法是计算音符的频率,然后使用一个表格将其转换为音高类别、八度和cents。我不知道有任何方法可以获得音高类别而不找到基频。

你将需要一个实时音高检测算法。在评估算法时,要注意每个算法所暗示的延迟时间,与你所希望的精度相比较。虽然有些算法比其他算法更好,但基本上你必须在一个和另一个之间进行权衡,并且不能确定两者都正确 - 就像海森堡测不准原理一样。(当只听到了一个周期的一小部分时,怎么能知道音符是C4呢?)

你的“平滑”方法相当于数字滤波器,它会改变声音的频率特性。简而言之,它可能会干扰你估计音高的尝试。如果你对数字音频有兴趣,数字滤波器是该领域基本且有用的工具,也是一个迷人的主题。理解它们需要强大的数学背景,但你不一定需要这个来获得基本的想法。

此外,你的零交叉方法是一种估计波形周期和音高的基本技术。可以用这种方式完成,但需要很多启发式和微调。(基本上,开发许多“候选”音高并尝试推断出主导音高。将出现许多特殊情况,这将使其混淆。一个快速的例子是较少的's'。)你会发现使用频率域音高检测算法更容易入手。


ZCR 真的不能帮助你估计波形的周期。如果你懒得使用更好的算法来检测熵,它只是一种贫穷人的熵检测器。 - johnwbyrd

1

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