改进音高检测的技巧

信息处理 fft 傅里叶变换 声音的
2022-01-02 23:45:10

我正在开发一个简单的网络应用程序,允许用户调整他/她的吉他。我是信号处理方面的真正初学者,所以如果我的问题不恰当,请不要太严厉地评判我。

因此,我设法使用 FFT 算法获得了基频,此时应用程序在某种程度上可以正常工作。但是,还有改进的余地。现在,我将原始 PCM 发送到 FFT 算法,但我在想也许有一些前/后算法/过滤器可以改善检测。你能提出任何建议吗?

我的主要问题是,当它检测到某个频率时,它会显示该频率 1-2 秒,然后跳到其他随机频率并再次回来等等,即使声音是连续的。

如果有人对此类事情有经验,我也对任何其他类型的优化感兴趣。

3个回答

我猜它得到的其他频率是基波的谐波?就像您正在播放 100 Hz 并且它选择了 200 Hz 或 300 Hz?首先,您应该将搜索空间限制在吉他可能出现的频率范围内。找到你可能需要的最高基本面,并限制在这个范围内。

如果基波的幅度低于谐波(或完全丢失,但这不是吉他的问题),自相关在寻找基波方面会比 FFT 更有效:

在此处输入图像描述

您还可以尝试对较低频率进行加权以强调基频并最小化谐波,或者使用这样的峰值拾取算法,然后只选择最低频率。

此外,您应该在应用 FFT 之前对信号进行窗口化。您只需将它乘以一个窗口函数,它会逐渐减小波形的开头和结尾,以使频谱更清晰。然后你会得到频率分量的高窄尖峰,而不是宽尖峰。

您还可以使用插值来获得更准确的峰值。取光谱的对数,然后将抛物线拟合到峰值和两个相邻点,并找到抛物线的真实峰值。不过,您可能不需要这么高的准确性。

这是我所有这些的示例 Python 代码

音高与 FFT 的峰值幅度频率仓不同。音高是一种人类心理声学现象。音高可能有缺失或非常微弱的基音(在某些声音、钢琴和吉他声音中很常见)和/或在其频谱中存在大量强大的泛音,这些泛音会压倒音高频率(但人类仍能听到该音高音符) . 因此,任何 FFT 峰值频率检测器(甚至包括一些窗口和插值)都不是音高估计的稳健方法。

这个stackoverflow 问题包括一些估计音高的替代方法的列表,这些方法可能会产生更好的结果。

补充:如果您对吉他声音执行此操作,请注意最低的吉他弦实际上可能会产生轻微的不和谐泛音,这使得音高估计更加困难,因为人耳可能会听到与泛音的子倍数更密切相关的音高频率,而不是弦的实际基本振动频率。

添加#2:这个问题经常被问到,所以我写了一篇关于这个主题的更长的博客文章:http: //www.musingpaw.com/2012/04/musical-pitch-is-not-just-fft-frequency.html

我花了很多年研究复调音乐的音高检测——比如检测 mp3 录音中吉他独奏的音符。我还在 Wikipedia 上写了一个部分,简要描述了该过程(查看下面链接中的“音高检测”小节)。

当在钢琴上按下一个键时,我们听到的不仅仅是一个声音振动频率,而是在不同数学相关频率上发生的多个声音振动的复合。这种不同频率的振动复合的元素被称为谐波或分音。例如,如果我们按下钢琴上的中间 C 键,复合谐波的各个频率将从 261.6 Hz 作为基频开始,523 Hz 将是 2 次谐波,785 Hz 将是 3 次谐波,1046 Hz 将是 4 次谐波等。后面的谐波是基频 261.6 Hz 的整数倍(例如:2 x 261.6 = 523、3 x 261.6 = 785、4 x 261.6 = 1046)。

我使用修改后的 DFT 对数变换首先通过寻找具有峰值电平的频率来检测可能的谐波(见下图)。由于我为修改后的 Log DFT 收集数据的方式,我不必对信号应用 Windowing Function,也不必add 和 overlay我创建了 DFT,因此它的频率通道以对数方式定位,以便直接与吉他、萨克斯管等音符产生的谐波频率对齐。

现在退休了,我决定在一个名为PitchScope Player的免费演示应用程序中发布我的音高检测引擎的源代码。PitchScope Player 可在网络上获得,您可以下载适用于 Windows 的可执行文件,以查看我的算法在您选择的 mp3 文件上的运行情况。以下指向 GitHub.com 的链接将引导您访问我的完整源代码,您可以在其中查看我如何使用自定义对数 DFT 变换检测谐波,然后查找其频率满足定义了正确整数关系的部分(谐波)'沥青'。

我的音高检测算法实际上是一个两阶段的过程:a)首先检测到ScalePitch('ScalePitch' 有 12 个可能的音高值:{E、F、F#、G、G#、A、A#、B、C、C#、D , D#} ) b) 并且在确定 ScalePitch 之后,通过检查 4 个可能的 Octave-Candidate 音符的所有谐波来计算 Octave 该算法旨在检测和弦 MP3 文件中任何给定时刻的最主要音高(音符)。这通常对应于器乐独奏的音符。那些对我的 2 阶段音高检测算法的 C++ 源代码感兴趣的人可能希望从 GitHub.com 上的 SPitchCalc.cpp 文件中的 Estimate_ScalePitch() 函数开始。

https://github.com/CreativeDetectors/PitchScope_Player

https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection

下面是对数 DFT(由我的 C++ 软件创建)在复音 mp3 录音中吉他独奏 3 秒的图像。它显示了演奏独奏时吉他上各个音符的谐波如何出现。对于这个对数 DFT 上的每个音符,我们可以看到它的多个谐波垂直延伸,因为每个谐波将具有相同的时间宽度。确定音符的八度后,我们就知道了基本音的频率。

在此处输入图像描述

下图演示了八度检测算法,一旦确定了该音符的 ScalePitch,我开发该算法以选择正确的八度候选音符(即正确的基本音)。那些希望在 C++ 中看到该方法的人应该转到名为 FundCandidCalcer.cpp 的文件中的 Calc_Best_Octave_Candidate() 函数,该文件包含在我在 GitHub 的源代码中。

在此处输入图像描述