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