安卓音频FFT显示基频

4
我已经在Android项目上工作了一段时间,该项目显示输入信号的基础频率(用作调音器)。我已成功实现了AudioRecord类,并从中获取数据。然而,我在对这些数据执行FFT以获取输入信号的基础频率方面遇到了困难。我一直在查看这里的帖子,并使用Java中的FFT复数类
我已成功使用Java中FFT中找到的FFT函数,但我不确定是否获得了正确的结果。对于FFT的幅度(sqrt[rere+imim]),我得到的值开始很高,约为15000 Hz,然后慢慢减少到约300 Hz。这似乎不对。
另外,就麦克风的原始数据而言,数据似乎很好,除了前50个左右的值始终是数字3,除非我在应用程序中仍然按下调音钮,那么我只会得到大约15个。这正常吗?
以下是我的部分代码。
首先,我使用来自我一直在查看的帖子的以下代码将从麦克风获取的短数据转换为double。我不完全理解这段代码,但我认为它有效。
//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是从哪里来的?

非常感谢您的帮助!

Dustin


这里的audioData是什么?在这一行中:int v = audioData[index + b]; - Himanshu
我相信这是音频缓冲区(意思是它是表示音频信号的原始值数组)。 - dustinrwh
3个回答

3

我最近正在做一个几乎相同的项目。可能你已经不需要任何帮助了,但我还是想分享一下我的想法,也许将来会有人需要。

  1. I'm not sure whether the short to double function works, I don't understand that snippet of code neither. It is wrote for byte to double conversion.
  2. In the code: "double[] micBufferData = new double[bufferSizeInBytes];" I think the size of micBufferData should be "bufferSizeInBytes / 2", since every sample takes two bytes and the size of micBufferData should be the sample number.
  3. FFT algorithms do require a FFT window size, and it has to be a number which is the power of 2. However many algorithms can receive an arbitrary of number as input and it will do the rest. In the document of those algorithms should have the requirements of input. In your case, the size of the Complex array can be the input of FFT algorithms. And I don't really know the detail of the FFT algorithm but I think the inverse one is not needed.
  4. To use the code you gave at last, you should firstly find the peak index in the sample array. I used double array as input instead of Complex, so in my case it is something like: 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 is the real part and 2*j+1 is the imaginary part. maxIndex is the index of the peak magnitude you want (More detail here), and use it as input to the ComputeFrequency function. The return value is the frequency of the sample array you want.

希望这能帮助到某些人。


2
我怀疑你得到的奇怪结果是因为你可能需要解包FFT。如何解包取决于你使用的库(例如,在GSL中打包方式的文档请参见这里)。打包可能意味着实部和虚部不在你期望的数组位置上。
对于你关于窗口大小和分辨率的其他问题,如果你正在创建调音器,我建议尝试使用约20毫秒的窗口大小(例如,以44.1kHz的1024个样本为例)。对于调音器,你需要相当高的分辨率,因此可以尝试通过8或16倍的零填充来获得3-6Hz的分辨率。

2

根据您对时间和频率分辨率的要求选择FFT窗口大小,而不仅仅是在创建FFT临时数组时使用音频缓冲区大小。

数组索引是您的int i,在您的magnitude[i]打印语句中使用。

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


好的。感谢您的回复。我仍然不完全明白该怎么做。1)FFT窗口大小是多少,我如何知道自己需要什么频率分辨率要求?2)创建FFT临时数组时应使用什么大小?3)您是否建议我避免使用FFT并使用音高估计算法? - dustinrwh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接