2011-11-30 57 views
4

我一直在研究Android项目一段时间,显示输入信号的基本频率(充当调谐器)。我已经成功实现了AudioRecord类并从中获取数据。但是,我很难对这些数据执行FFT以获得输入信号的基频。我一直在寻找here的帖子,并且我正在使用FFT in JavaComplex class安卓音频FFT显示基本频率

我已经成功地使用了Java中的FFT中找到的FFT函数,但我不确定我是否获得了正确的结果。对于FFT的幅度(sqrt [sqrt [re re + im im im]),我得到的值从15000赫兹左右开始很高,然后缓慢下降到大约300赫兹。看起来不正确。

另外,就麦克风的原始数据而言,数据看起来很好,除非前50个值总是数字3,除非再次在应用程序中再次点击调谐按钮,然后我只有15岁左右。这是正常的吗?

这是我的一些代码。

首先,我使用以下代码将post I have been looking at中的短数据(从麦克风获得)转换为双精度数据。这段代码我不完全理解,但我认为它的工作原理。

//Conversion from short to double 
double[] micBufferData = new double[bufferSizeInBytes];//size may need to change 
final int bytesPerSample = 2; // As it is 16bit PCM 
final double amplification = 1.0; // choose a number as you like 
for (int index = 0, floatIndex = 0; index < bufferSizeInBytes - bytesPerSample + 1; index += bytesPerSample, floatIndex++) { 
    double sample = 0; 
    for (int b = 0; b < bytesPerSample; b++) { 
     int v = audioData[index + b]; 
     if (b < bytesPerSample - 1 || bytesPerSample == 1) { 
      v &= 0xFF; 
     } 
     sample += v << (b * 8); 
    } 
    double sample32 = amplification * (sample/32768.0); 
    micBufferData[floatIndex] = sample32; 
} 

的代码然后继续如下:

//Create Complex array for use in FFT 
Complex[] fftTempArray = new Complex[bufferSizeInBytes]; 
for (int i=0; i<bufferSizeInBytes; i++) 
{ 
    fftTempArray[i] = new Complex(micBufferData[i], 0); 
} 

//Obtain array of FFT data 
final Complex[] fftArray = FFT.fft(fftTempArray); 
final Complex[] fftInverse = FFT.ifft(fftTempArray); 

//Create an array of magnitude of fftArray 
double[] magnitude = new double[fftArray.length]; 
for (int i=0; i<fftArray.length; i++){ 
    magnitude[i]= fftArray[i].abs(); 
} 


fft.setTextColor(Color.GREEN); 
fft.setText("fftArray is "+ fftArray[500] +" and fftTempArray is "+fftTempArray[500] + " and fftInverse is "+fftInverse[500]+" and audioData is "+audioData[500]+ " and magnitude is "+ magnitude[1] + ", "+magnitude[500]+", "+magnitude[1000]+" Good job!"); 
for(int i = 2; i < samples; i++){ 
    fft.append(" " + magnitude[i] + " Hz"); 
} 

最后一点就是要检查我得到什么值(和我保持理智!)。在上面提到的帖子中,它谈到了需要采样频率并给出这个代码:

private double ComputeFrequency(int arrayIndex) { 
    return ((1.0 * sampleRate)/(1.0 * fftOutWindowSize)) * arrayIndex; 
} 

如何实现这个代码?我不明白fftOutWindowSize和arrayIndex来自哪里?

任何帮助,非常感谢!

达斯汀

+0

这是什么音频数据?在行中:int v = audioData [index + b]; – Himanshu

+0

我相信这是音频的缓冲区(意思是它是代表音频信号的原始数值的数组)。 – dustinrwh

回答

2

你应该选择一个FFT窗口的大小取决于你的时间与频率分辨率的要求,并创建FFT临时数组时,不直接使用音频缓冲区大小。

数组索引是你的int i,用在你的幅度[i] print语句中。

音乐的基本音高频率通常不同于FFT峰值,因此您可能需要研究一些音高估计算法。

+0

好的。感谢您的回应。我仍然不完全明白该怎么做。 1)什么是FFT窗口大小,我怎么知道我有什么频率分辨率要求? 2)创建FFT temp数组时应该使用什么大小? 3)你是否建议我避免FFT并使用音调估计算法? – dustinrwh

3

最近我正在研究一个需要几乎相同的项目。可能你不需要任何帮助了,但是我会反过来说出我的想法。也许有人在将来需要这个。

  1. 我不确定short to double函数是否工作,我不明白这段代码也不明白。它被写入字节到双倍转换。
  2. 在代码:"double[] micBufferData = new double[bufferSizeInBytes];"我认为micBufferData的大小应该是“bufferSizeInBytes/2”,因为每个样本需要两个字节,并且micBufferData的大小应该是样本编号。
  3. FFT算法确实需要一个FFT窗口大小,它必须是一个2的幂的数。然而,很多算法可以接收任意数量的输入,其余部分将完成。在那些算法的文档中应该有输入的要求。在你的情况下,Complex数组的大小可以是FFT算法的输入。我并不真正了解FFT算法的细节,但我认为不需要相反的算法。
  4. 要使用您最后给出的代码,您应该首先在样本数组中找到峰值索引。我用双数组作为输入,而不是复杂的,因此,在我的情况是一样的东西:double maxVal = -1;int maxIndex = -1;

    for(int j=0; j < mFftSize/2; ++j) { 
        double v = fftResult[2*j] * fftResult[2*j] + fftResult[2*j+1] * fftResult[2*j+1]; 
        if(v > maxVal) { 
         maxVal = v; 
         maxIndex = j; 
        } 
    } 
    

    2 * j是的实部和2 * J + 1是虚部。 maxIndex是所需峰值的指数(更多详细信息here),并将其用作ComputeFrequency函数的输入。返回值是您想要的样本数组的频率。

希望它可以帮助某人。

2

我怀疑你得到的奇怪结果是因为你可能需要解开FFT。这是如何完成的,取决于你正在使用的库(例如here有关如何将它打包到GSL中的文档)。打包可能意味着实数和虚数分量不在阵列中您所期望的位置。

对于关于窗口大小和分辨率的其他问题,如果你正在创建一个调谐器,那么我会建议尝试一个约20ms的窗口大小(例如在44.1kHz的1024个样本)。对于调谐器,你需要相当高的分辨率,所以你可以尝试零填充8或16倍,这将给你一个3-6Hz的分辨率。