从FFT带有纯正弦音调的更精确的频率



我目前正在使用此处的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方法,以便我可以在这样的hz中获得频率:

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); 

我已经在不同的频率测试时生成了一些纯粹的正弦音调。我看到的问题是,对于两个不同的正弦音调,代码返回相同的频率,这些音调相对接近。

例如:19255Hz产生的正弦音色将从FFT出现为19293.750000Hz。19330Hz产生的正弦音调也会因此。计算中必须关闭某些东西。

在如何修改上述代码以获取更精确的FFT频率读取纯正弦音调方面的任何帮助。谢谢!

您可以通过将抛物线曲线拟合到峰值幅度bin周围的3 fft bin尺寸,然后找到该抛物线的极端。

,可以获得粗略的频率估计。

可以通过使用FFT窗口的转换作为插值内核来创建更好的估计,并进行连续的近似以完善插值点的最大值的估计值。(零填充和使用更长的FFT将为您提供类似类型的插值估计。)

固定信号的简便方法是,如果可能的话,只需使用更长的fft,其中包含更多跨越时间间隔的样本。

您在这里遇到了许多问题:

1)您的频率轴间距是f 最大/n或大约80Hz,因此您不会获得比这更好的分辨率。

2)您的信号非常接近Nyquist频率(即20KHz/44.1kHz几乎是0.5),当您接近Nyquist限制时,如果需要准确的结果,您需要非常小心。(也就是说,在20kHz时,您只记录每个完整振荡周期的两个数据点。)

3)由于20KHz处于人类听力的边缘(对于大多数人来说),因此许多麦克风并不真正担心。这是iPhone的测量。

也许您的采样频率不够高?

如果您对输入一无所知,则FFT是获得频谱的一个很好的方法。如果您知道输入是纯正弦波,那么您可以做得更好。首先计算FFT,以了解正弦的位置。获取最小值和最大值以估计幅度[或从FFT - 平方所有输入中获取幅度,添加它们,取平方根],考虑到估计的频率和振幅,在开始和结束时获得相位。

通常,您会发现阶段不匹配。那是因为末端的相位被2 *Δ f * N. f - Δ f 是对频率的更好估计。请记住,这种方法对超级噪声敏感。该方法之所以起作用,是因为输入是纯正弦波,而噪声只是一切。使用此方法迭代迅速炸毁;您甚至遇到了四舍五入错误(也不是正弦)

另一个类似的技巧是减去估计的波。两种罪之间的差异是两种罪的乘积,一个添加了频率(在您的情况下,±38.5 kHz),一个频率减去频率(Δ_F_,小于100 Hz)。另请参见杂差检测

最新更新