在iPhone上进行FFT以忽略背景噪音并找到较低的音高

时间:2011-08-24 20:09:15

标签: iphone signal-processing fft pitch-tracking accelerate-framework

我已经为iPhone实施了Demetri的Pitch Detector项目,并遇到了两个问题。 1)任何类型的背景噪声发送频率读取香蕉和2)较低频率的声音没有正确调整。我试着调整我的吉他,而当更高的琴弦工作时 - 调音师无法正确辨别低音。

音高检测代码位于RIOInterface.mm中,就像这样......

// get the data
AudioUnitRender(...);

// convert int16 to float
Convert(...);

// divide the signal into even-odd configuration
vDSP_ctoz((COMPLEX*)outputBuffer, 2, &A, 1, nOver2);

// apply the fft
vDSP_fft_zrip(fftSetup, &A, stride, log2n, FFT_FORWARD);

// convert split real form to split vector
vDSP_ztoc(&A, 1, (COMPLEX *)outputBuffer, 2, nOver2);
然后,Demetri继续确定“主导”频率如下:

float dominantFrequency = 0;
int bin = -1;
for (int i=0; i<n; i+=2) {
    float curFreq = MagnitudeSquared(outputBuffer[i], outputBuffer[i+1]);
    if (curFreq > dominantFrequency) {
        dominantFrequency = curFreq;
        bin = (i+1)/2;
    }
}
memset(outputBuffer, 0, n*sizeof(SInt16));

// Update the UI with our newly acquired frequency value.
[THIS->listener frequencyChangedWithValue:bin*(THIS->sampleRate/bufferCapacity)];

首先,我相信我需要应用低通过滤波器...但我不是FFT专家,并且不确定在何处或如何对vDSP功能返回的数据执行此操作。我也不确定如何在较低频率下提高代码的准确性。似乎还有其他算法可以确定主导频率 - 但同样,在使用Apple的Accelerate框架返回的数据时,正在寻找正确方向的优势。

更新:

加速框架实际上有一些窗口函数。我设置了一个像这样的基本窗口

windowSize = maxFrames;
transferBuffer = (float*)malloc(sizeof(float)*windowSize);
window = (float*)malloc(sizeof(float)*windowSize);
memset(window, 0, sizeof(float)*windowSize);
vDSP_hann_window(window, windowSize, vDSP_HANN_NORM); 

然后我通过插入

来申请
vDSP_vmul(outputBuffer, 1, window, 1, transferBuffer, 1, windowSize); 
在vDSP_ctoz函数之前

。然后我将其余代码更改为使用'transferBuffer'而不是outputBuffer ...但到目前为止,还没有注意到最终音高猜测的任何显着变化。

4 个答案:

答案 0 :(得分:7)

Pitch与峰值频率bin不同(这是Accelerate框架中的FFT可能直接给你的)。因此任何峰值频率检测器对于音调估计都不可靠。当音符具有缺失或非常弱的基音(在某些声音,钢琴和吉他声中很常见)和/或其频谱中有许多强大的泛音时,低通滤波器将无法帮助。

查看音乐声的宽带频谱或光谱仪,您将看到问题。

通常需要其他方法来更可靠地估计音高。其中一些包括自相关方法(AMDF,ASDF),倒谱/倒谱分析,谐波产品频谱,相位声码器和/或复合算法,例如RAPT(用于音高跟踪的鲁棒算法)和YAAPT。 FFT仅作为上述某些方法的子部分有用。

答案 1 :(得分:3)

在计算FFT之前,您至少需要在时域数据中应用window function。如果没有这一步,功率谱将包含人工制品(参见:spectral leakage),这将干扰您提取音高信息的尝试。

一个简单的Hann(又名 Hanning )窗口就足够了。

答案 2 :(得分:1)

您的采样频率和块大小是多少?低E大约为80 Hz,因此您需要确保捕获块足够长,以便在此频率下捕获多个周期。这是因为傅立叶变换将频谱划分为多个频段,每个频宽几赫兹。例如,如果以44.1 kHz采样并且具有1024点时域采样,则每个箱将为44100/1024 = 43.07 Hz宽。因此,低E将在第二个箱中。由于一系列原因(与频谱泄漏和有限时间块的性质有关),实际上你应该考虑FFT结果中的前三个或四个数据库而非常怀疑。

如果将采样率降至8 kHz,则相同的blockize会为您提供宽度为7.8125 Hz的容器。现在低E将在第10或第11箱中,这要好得多。您也可以使用更长的块大小。

作为Paul R points out,你必须使用一个窗口来减少频谱泄漏。

答案 3 :(得分:1)

iPhone的频率响应功能降至100 - 200 Hz以下(例如,见http://blog.faberacoustical.com/2009/ios/iphone/iphone-microphone-frequency-response-comparison/)。

如果您尝试检测低吉他弦的基本模式,麦克风可能会充当滤镜并抑制您感兴趣的频率。如果您对使用fft数据感兴趣,可以选择几种方法可以得到 - 你可以在你试图检测的音符周围的频域中窗口数据,这样你就可以看到所有你看到的第一种模式,即使它的幅度低于高音模式(即有一个切换来调整第一个字符串)并把它放在这种模式)。

或者您可以对声音数据进行低通滤波 - 您可以在时域中执行此操作,或者甚至更容易,因为您已经在频域中拥有频域数据。一个非常简单的时域低通滤波器是做一个时移平均滤波器。一个非常简单的频域低通滤波器是将你的fft幅度乘以一个在低频范围内为1的向量,在较高频率上用一个线性(甚至是一步)斜降。