7

我已经为iPhone实施了Demetri的Pitch Detector项目,并解决了两个问题。 1)任何种类的背景噪音都会传送频率读数香蕉,2)低频率的声音没有正确传送。我试着调整我的吉他,而较高的琴弦工作 - 调谐器不能正确辨别低E.iPhone上的FFT忽略背景噪音并找到较低音高

基音检测代码位于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); 

德米特里然后继续确定为“显性”频率如下:

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_ctoz功能之前插入

vDSP_vmul(outputBuffer, 1, window, 1, transferBuffer, 1, windowSize); 

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

+0

我记得几年前玩过这些东西的两件事:你可以通过从预定量中减去原始输入数据来设置背景声音的静噪水平(你现在可以猜到,直到你的数字出了一个好的算法),设置任何低于0到0的值,并且由于某些原因,我记不起来了,所以不要记录为更大的声音,因此在比较之前需要按指数规模放大低频率音量到更高的主导频率。 –

+0

在FFT之前,您似乎没有应用合适的窗口函数,因此在功率谱中将会出现很多伪影,这可能会扰乱任何尝试进行基音检测的尝试。 –

+2

这里有一个类似的答案几乎重复的问题:http://stackoverflow.com/questions/4227420/matlab-missing-fundamental-from-an-fft – hotpaw2

回答

7

音高与峰值幅度频率bin不同(这是Accelerate框架中的FFT可能会直接提供的)。所以任何峰值频率检测器对于音调估计都不可靠。当音符有缺失或非常弱的基音(在某些声音,钢琴和吉他声音中很常见)和/或其频谱中有很多强大的泛音时,低通滤波器将无济于事。

看看你的音乐声音的宽带谱或光谱仪,你会看到问题。

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

+0

谢谢@ hotpaw2。我将不得不深入一些你在这里列出的技术。感谢您的建议。 –

+0

参考其他一些沥青估计方法的描述是在我的博客上:http://www.nicholson.com/rhn/dsp.html#1 – hotpaw2

3

至少您需要在计算FFT之前将window function应用于您的时域数据。如果没有这一步,功率谱将包含人为因素(请参阅:spectral leakage),这会干扰您提取音调信息的尝试。

一个简单的Hann(又名汉宁)窗口应该足够了。

+2

好的答案和不错的头像图片:D – jarryd

+0

不能真正同意这个评论。您需要在采样模拟信号之前应用一个窗口滤波器_before_。一旦采样(即,通过麦克风或WAV格式记录一次),您不需要过滤任何东西 - 除非您正在寻找其他效果 –

+0

@ZeJibe:我认为您可能在应用[窗口函数](https://en.wikipedia.org/wiki/Window_function)并使用[anti-aliasing filter](https://en.wikipedia.org/wiki/Anti-aliasing_filter)? –

1

什么是您的采样频率和块大小?低E大约是80赫兹,所以你需要确保你的捕获块足够长以在这个频率捕获很多周期。这是因为傅里叶变换将频谱分成几个频段,每个频段宽。如果你44岁抽样。1 kHz,并具有1024点时域采样,例如,每个bin将为44100/1024 = 43.07 Hz宽。因此,低E将在第二个仓中。出于一系列原因(涉及频谱泄漏和有限时间块的性质),实际上,您应该考虑FFT结果中的前三个或四个数据单元,并带有极度怀疑。

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

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

+0

我试过了22050和44100.我也试过1024,2048和4096块大小的循环缓冲区,它可以保存每个回调的数据,直到获得“块大小”。我的确理解这个想法,即固定块大小的情况下,较低的采样率会给我稍微更宽的箱子,并且使得听到低E更容易一些。不幸的是,我注意到一旦我达到295赫兹左右,调谐器开始读取883和901这样的数字。非常接近真实音调的2和3的倍数。 –

+0

我正在用另一个iPhone坐在旁边生成铃声。我已经注意到,当我把它打开时,我的纯正的正弦正弦发出尖锐的声音,调谐器失去了正在显示的正确值并开始偏离其他频率 - 不一定是2或3的倍数我正在寻找的基调。 –

+0

对于任何音调估计器来说,较长的音程大小都是一个很好的开始,但FFT幅度峰值仍然不适用于任何基音缺失或较弱的声音(即使通过话筒滚降)。 – hotpaw2

1

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

如果您尝试检测低吉他弦的基本模式,则麦克风可能充当过滤器并抑制您感兴趣的频率。如果您有兴趣使用fft数据,有几个选项您可以得到 - 您可以在频率范围内显示您想要检测的音符的数据,以便您可以看到的是第一个模式,即使它的幅度低于高级模式(即有一个切换键可以调整第一个模式字符串并将其置于此模式)。

或者您可以对声音数据进行低通滤波 - 您可以在时域或更简单的情况下执行此操作,因为您已经在频域中拥有频域数据。一个非常简单的时域低通滤波器就是做一个时移移动平均滤波器。一个非常简单的频域低通滤波器就是将你的fft幅度乘以一个低频范围内的1和一个更高频率下的线性(或者甚至是一个阶梯)斜坡。