好,我们来一一道来:
我正在寻找从声音信号中提取音高。
虽然我不是专家并且接受过最少的正式培训,但我想我知道这个问题的最佳答案。在过去的几年里,我做了很多搜索、阅读和实验。我的共识是,就准确性、复杂性、噪声鲁棒性和速度之间的权衡而言,自相关方法是迄今为止最好的音高检测器。除非您有一些非常具体的情况,否则我几乎总是建议使用自相关。稍后再详细介绍,让我回答您的其他问题。
您描述的是“倒谱分析”,这是一种主要用于从语音中提取音高的方法。倒谱分析完全依赖于信号泛音的丰富程度和强度。例如,如果你要通过倒谱分析传递一个纯正弦波,你会得到可怕的结果。然而,对于复杂信号的语音,存在大量泛音。(顺便说一下,泛音是信号的元素,它以基频的倍数振荡,即我们感知的音高)。倒谱分析在检测缺少基频的语音方面可以很稳健。也就是说,假设您绘制了函数罪(4x)+罪(6x)+罪(8x)+罪(10x)。如果你看一下,很明显它与函数 sin(2x) 具有相同的频率。但是,如果您对该函数应用傅立叶分析,则对应于 sin(2x) 的 bin 的量级为零。因此,该信号被认为具有“缺失的基频”,因为它不包含我们认为的频率的正弦曲线。因此,简单地选择傅立叶变换上的最大峰值对这个信号不起作用。
我以前使用过一种技术,该技术可以搜索单个 FFT 的频率区间以获得局部最大值。当它遇到一个时,它会使用一种巧妙的技术(自上次 FFT 以来的相位变化)来更准确地将实际峰值放置在 bin 内。
您所描述的是相位声码器技术,可以更准确地测量给定部分的频率。但是,如果您使用的信号缺少基频分量或基频分量较弱,那么挑选出最大分档的基本技术会给您带来麻烦。
我担心我在这里介绍的这种技术会失去这种精度。
首先,请记住相位声码器技术只能更准确地测量单个分音的频率。它忽略了较高部分中包含的关于基频的信息。其次,给定一个不错的 FFT 大小,您可以使用峰值插值获得非常好的精度。这里的其他人已将您指向抛物线插值。我也建议这样做。
如果您在 44100 Hz 处以大约 440 Hz 的间距对 4098 个样本数据块的 FFT 进行抛物线插值,这将意味着它将位于第 40 个(430.66 Hz)和第 41 个(441.430664064)bin 之间。假设这篇论文在一般情况下大致正确,它说抛物线插值将分辨率提高了一个数量级以上。这使得分辨率至少为 1 Hz,这是人类听觉的阈值。事实上,如果你使用理想的高斯窗,抛物线插值在峰值处是精确的(没错,精确。但是请记住,你永远不能使用真正的高斯窗,因为它在两个方向上永远延伸。)如果你是仍然担心获得更高的精度,您可以随时填充 FFT。这意味着在转换之前将零添加到 FFT 的末尾。结果表明,这相当于“sinc 插值”,它是频率受限信号的理想插值函数。
我想该技术可以在第二次 FFT 之后使用,以准确地获得基础。但看起来信息在第 2 步中丢失了。
那是对的。相位声码器技术依赖于连续帧连接并具有特定相位关系的事实。然而,连续帧的 FFT 的对数幅度在相位方面并没有显示出相同的关系,因此在第二个 FFT 中使用这种变换是没有用的。
是的,是的,我将在最后详细说明我对自相关的改进。
- 我有点担心 对数方分量;似乎有一个 vDSP 函数可以做到这一点:vDSP_vdbcon 然而,没有迹象表明它预先计算了一个日志表——我认为它没有,因为 FFT 函数需要调用并传递一个显式的预计算函数进去。而这个功能没有。
我不知道 vDSP 库的细节,抱歉。
在您原来的相位声码器峰值拾取技术中?是的。用倒谱法?不,不是真的,重点是它考虑了所有的谐波来获得它的频率估计。例如,假设我们的频率是 1。我们的泛音是 2,3,4,5,6,7,8,9 等 我们必须去掉所有的奇次谐波,即留下 2,4,6, 8等,并在基频开始与其泛音之一混淆之前将其删除。
- 有没有什么巧妙的方法可以让 vDSP 拉出最大值,最大的优先?
不知道 vDSP,但在一般情况下,您通常只需遍历所有这些并跟踪最大的。
我在评论中给你的链接 P. 看起来不错。
此外,该网站以理论和实践的方式对 DSP 主题提供了令人难以置信的深入和广泛的解释,包括各种音高提取、操作等。(这是指向网站索引的更一般的链接)。我总是发现自己回到了它。有时如果你跳到它的中间可能会有点不知所措,但你总是可以按照每一个解释回到基本的构建块。
现在是自相关。基本上,该技术是这样的:您获取(窗口)信号并对其进行不同数量的时间延迟。找到与您的原始信号最匹配的数量。那是基本时期。它有很多理论意义。您正在寻找信号的重复部分。
在实践中,与所有这些时间延迟的信号副本进行相关是很慢的。它通常以这种方式实现(这在数学上是等效的):
零填充它以使其原始长度加倍。以 FFT 为例。然后用它们的平方幅度替换所有系数,除了你设置为 0 的第一个系数。现在进行 IFFT。将每个元素除以第一个元素。这为您提供了自相关。在数学上,您正在使用循环卷积定理(查找它),并使用零填充将线性卷积问题转换为循环卷积问题,这可以有效地解决。
但是,要小心选择峰值。对于非常小的延迟,信号会很好地匹配自身,因为它是连续的。(我的意思是,如果你将它延迟为零,它与自身完全相关)相反,在第一次过零之后选择最大的峰值。您可以像使用其他技术一样对自相关函数进行抛物线插值,以获得更准确的值。
这本身将为您提供所有标准的非常好的音高检测但是,您有时可能会遇到音高减半和音高加倍的问题。基本上问题在于,如果一个信号每 1 秒重复一次,那么它也每两秒重复一次。同样,如果它具有非常强的泛音,您可能会得到音高减半。因此,最大的峰可能并不总是您想要的。这个问题的一个解决方案是 Phillip McLeod 的 MPM 算法。这个想法是这样的:
与其选择最大的峰,不如选择第一个足够大的峰以供考虑。您如何确定一个峰值是否足够大以供考虑?如果它至少与 A* 一样高,则为最大峰值,其中 A 是某个常数。Phillip 建议我认为 A 的值在 0.9 左右。实际上,他编写的程序 Tartini 允许您实时比较几种不同的音高检测算法。我强烈建议下载并试用它(它实现了倒谱、直接自相关和 MPM):(如果您在构建时遇到问题,请尝试此处的说明。
我应该注意的最后一件事是关于窗口。一般来说,任何平滑的窗口都可以。汉宁窗,汉明窗等希望你应该知道如何开窗。如果您想要更准确的时间测量,我还建议您进行重叠窗口。
顺便说一句,自相关的一个很酷的特性是,如果频率通过您正在测量的窗口部分呈线性变化,它将在窗口中心为您提供正确的频率。
还有一件事:我所描述的称为有偏自相关函数。这是因为对于更高的时滞,原始信号和时滞版本之间的重叠变得越来越少。例如,如果您查看已延迟 N-1 个样本的大小为 N 的窗口,您会看到只有一个样本重叠。所以这个延迟的相关性显然会非常接近于零。您可以通过将自相关函数的每个值除以样本重叠数得到它来补偿这一点。这称为无偏自相关。但是,一般来说,这样做会得到更糟糕的结果,因为自相关的较高延迟值非常嘈杂,因为它们仅基于少数样本,因此减少它们的权重是有意义的。
如果您正在寻找更多信息,一如既往,谷歌是您的朋友。好的搜索词:自相关、基音检测、基音跟踪、基音提取、基音估计、倒谱等。