我有一个样本保存在 DirectX 的缓冲区中。它是从乐器演奏和捕捉的音符样本。如何分析样本的频率(就像吉他调音器一样)?我相信涉及 FFT,但我没有指向 HOWTO。
7 回答
FFT 可以帮助您确定频率在哪里,但它不能准确地告诉您频率是多少。FFT 中的每个点都是频率的“bin”,因此如果 FFT 中有一个峰值,那么您所知道的就是您想要的频率在该 bin 或频率范围内的某个位置。
如果您希望它真正准确,则需要具有高分辨率和大量 bin(= 大量内存和大量计算)的长 FFT。您还可以使用对数标度频谱上的二次插值从低分辨率 FFT 中猜测真正的峰值,效果非常好。
如果计算成本最重要,您可以尝试将信号转换为可以计算过零的形式,然后计算的越多,测量就越准确。
但是,如果缺少基本要素,这些都将不起作用。:)
我在这里概述了一些不同的算法,插值 FFT 通常是最准确的(尽管这仅在基波是最强谐波时才有效- 否则你需要更聪明地找到它),过零紧随其后(尽管这只适用于每个周期有一次交叉的波形)。这些条件都不是典型的。
请记住,在许多乐器(如钢琴或吉他)中,基频以上的分音并不是完美的泛音。每个部分实际上都有点跑调,或不和谐。因此,FFT 中的高频峰值不会正好在基波的整数倍上,而且波形会从一个周期到下一个周期略有变化,这会消除自相关。
为了获得真正准确的频率读数,我会说使用自相关来猜测基频,然后使用二次插值找到真正的峰值。(您可以在频域中进行自相关以节省 CPU 周期。)有很多陷阱,使用正确的方法实际上取决于您的应用程序。
还有其他基于时间的算法,而不是基于频率的算法。自相关是一种相对简单的音高检测算法。参考:http ://cnx.org/content/m11714/latest/
我已经编写了自相关和其他可读算法的 c# 实现。查看http://code.google.com/p/yaalp/。
http://code.google.com/p/yaalp/source/browse/#svn/trunk/csaudio/WaveAudio/WaveAudio 列出文件,PitchDetection.cs 就是你想要的。
(该项目是 GPL;如果您使用代码,请理解这些条款)。
吉他调音器不使用 FFT 或 DFT。通常他们只计算零交叉。您可能无法获得基频,因为某些波形比其他波形具有更多的过零,但您通常可以通过这种方式获得基频的倍数。尽管您可能会偏离一个或多个八度音阶,但这足以得到音符。
在计算过零之前进行低通滤波通常可以去除多余的过零。调整低通滤波器需要一些您想要检测的频率范围的知识
确实会涉及 FFT(快速傅立叶变换)。FFT 允许您用固定频率和变化幅度的简单正弦波之和来近似任何模拟信号。您实际上要做的是获取样本并将其分解为幅度->频率对,然后获取与最高幅度相对应的频率。
希望另一个 SO 读者可以填补我在理论和代码之间留下的空白!
更具体一点:
如果您从输入阵列中的原始 PCM 开始,您基本上拥有的是波幅与时间的关系图。执行 FFT 会将其转换为频率从 0 到 1/2 输入采样率的频率直方图。结果数组中每个条目的值将是相应子频率的“强度”。
因此,要在给定以 S 个样本/秒采样的大小为 N 的输入数组的情况下找到根频率:
FFT(N, input, output);
max = max_i = 0;
for(i=0;i<N;i++)
if (output[i]>max) max_i = i;
root = S/2.0 * max_i/N ;
检索 PCM 音频信号中的基频是一项艰巨的任务,并且有很多话要说……
无论如何,通常基于时间的方法不适用于复音信号,因为由多个基频引起的不同谐波分量之和给出的复波具有仅取决于最低频率分量的过零率......也在频域 FFT 不是最合适的方法,因为音符之间的频率间隔遵循指数比例,而不是线性的。这意味着,如果时域中的分析窗口大小不够大,FFT 方法中使用的恒定频率分辨率可能不足以解析低频音符。
更合适的方法是常数 Q 变换,它是在对信号进行低通滤波和 2 抽取(即每步采样频率减半)的过程之后应用 DFT,以获得具有不同频率的不同子带解析度。通过这种方式优化了 DFT 的计算。问题是时间分辨率也是可变的,并且对于较低的子带会增加......
最后,如果我们试图估计单个音符的基频,FFT/DFT 方法是可以的。在复音环境中情况会发生变化,其中不同声音的部分重叠并根据它们的相位差求和/抵消它们的幅度,因此单个频谱峰值可能属于不同的谐波内容(属于不同的音符)。在这种情况下,相关性不会给出好的结果......
应用DFT,然后从结果中导出基频。谷歌搜索 DFT 信息将为您提供所需的信息——我会将您链接到一些信息,但它们对数学知识的期望差异很大。
祝你好运。