我目前正在使用此处的FFT代码: https://github.com/syedhali/EZAudio/tree/master/EZAudioExamples/iOS/EZAudioFFTExample
以下是2种相关方法的代码:
-(void)createFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// Setup the length
_log2n = log2f(bufferSize);
// Calculate the weights array. This is a one-off operation.
_FFTSetup = vDSP_create_fftsetup(_log2n, FFT_RADIX2);
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Populate *window with the values for a hamming window function
float *window = (float *)malloc(sizeof(float)*bufferSize);
vDSP_hamm_window(window, bufferSize, 0);
// Window the samples
vDSP_vmul(data, 1, window, 1, data, 1, bufferSize);
free(window);
// Define complex buffer
_A.realp = (float *) malloc(nOver2*sizeof(float));
_A.imagp = (float *) malloc(nOver2*sizeof(float));
}
-(void)updateFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Pack samples:
// C(re) -> A[n], C(im) -> A[n+1]
vDSP_ctoz((COMPLEX*)data, 2, &_A, 1, nOver2);
// Perform a forward FFT using fftSetup and A
// Results are returned in A
vDSP_fft_zrip(_FFTSetup, &_A, 1, _log2n, FFT_FORWARD);
// Convert COMPLEX_SPLIT A result to magnitudes
float amp[nOver2];
float maxMag = 0;
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
maxMag = mag > maxMag ? mag : maxMag;
}
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
// Bind the value to be less than 1.0 to fit in the graph
amp[i] = [EZAudio MAP:mag leftMin:0.0 leftMax:maxMag rightMin:0.0 rightMax:1.0];
}
我已经修改了上面的updateFFTWithBufferSize方法,这样我就可以得到这样的频率:
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
if(maxMag < mag) {
_i_max = i;
}
maxMag = mag > maxMag ? mag : maxMag;
}
float frequency = _i_max / bufferSize * 44100;
NSLog(@"FREQUENCY: %f", frequency);
我用不同频率的Audacity生成了一些纯正弦音,以便进行测试。我看到的问题是代码返回相同频率的两个不同的正弦音调,它们的价值相对接近。
例如: 在19255Hz产生的正弦音将从FFT显示为19293.750000Hz。因此,在19330Hz处产生正弦音。在计算中必须要做一些事情。
我非常感谢有关如何修改上述代码以获得纯正弦音调的更精确的FFT频率读数的任何帮助。 谢谢!
答案 0 :(得分:1)
您可以通过将抛物线曲线拟合到峰值幅度区域周围的3个FFT区间大小,然后找到该抛物线的极值来获得粗略的频率估计值。
通过将FFT窗口的变换用作插值核,并进行逐次逼近以细化插值点的最大值估计,可以创建更好的估计。 (零填充并使用更长的FFT将为您提供类似类型的插值估计。)
如果可能的话,静止信号的简单方法是使用更长的FFT,并且采用更长的时间间隔的样本。
答案 1 :(得分:1)
你在这里遇到了很多问题:
1)你的频率轴间距是f max / N,或大约80Hz,所以你不会得到比这更好的分辨率。
2)你的信号非常接近Nyquist frequency(即,20KHz / 44.1KHz几乎为0.5),当你接近奈奎斯特极限时,你需要非常小心想要准确的结果。 (也就是说,在20KHz时,每个完整振荡周期只记录两个数据点。)
3)由于20KHz处于人类听觉的边缘(对大多数人而言更高),许多麦克风并不真正担心它。 Here's iPhone的衡量标准。答案 2 :(得分:0)
也许您的采样频率不够高?
答案 3 :(得分:0)
如果您对输入一无所知,则FFT是获取频谱的一种非常好的方法。如果您知道输入是纯正弦波,那么您可以做得更好。首先计算FFT,以便大致了解正弦的位置。获得最小值和最大值来估计幅度[或从FFT获得 - 平方所有输入,加上它们,取平方根],在给定估计的频率和幅度的情况下得到开始和结束的相位。
一般情况下,您会发现阶段不匹配。那是因为结束时的相位偏离2 *Δ f * N. f - Δ f 是对频率的更好估计。请记住,这种方法对超级噪声敏感。该方法有效,因为输入是纯正弦波,噪声就是一切。使用这种方法迭代地快速爆炸;你甚至会遇到舍入错误(也不是正弦曲线)
另一个类似的技巧是减去估计波。两个正弦之间的差异是两个正弦的乘积,一个频率加上(在你的情况下,±38.5 kHz),另一个频率减去(Δ_f_,小于100 Hz)。另请参阅Heterodyne detection