使用AudioRecord实现Android音频FFT以检测特定频率的幅度值

39

我目前正在尝试使用Android来检测通过手机麦克风播放的特定音频频率范围。我已经使用AudioRecord类设置了该类:

int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int format = AudioFormat.ENCODING_PCM_16BIT;
int sampleSize = 8000;
int bufferSize = AudioRecord.getMinBufferSize(sampleSize, channel_config, format);
AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleSize, channel_config, format, bufferSize);

随后,音频被读取:

short[] audioBuffer = new short[bufferSize];
audioInput.startRecording();
audioInput.read(audioBuffer, 0, bufferSize);

由于我在这个领域几乎没有经验,所以我在执行FFT时遇到了问题。我一直在尝试使用这个类:

Java中的FFT相应的复数类

然后我发送以下值:

Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
    fftTempArray[i] = new Complex(audio[i], 0);
}
Complex[] fftArray = fft(fftTempArray);

我可能误解了这个类的工作方式,但返回的值跳来跳去,即使在静音状态下也不代表一致的频率。有人知道如何执行此任务吗?或者我是不是过于复杂化了问题,试图仅获取少量的频率范围而不是将其绘制为图形表示?


11
嘿,如果你想通了,能否请你发布最终版本的代码?谢谢。 - Stefan Alexandru
1个回答

33

首先,您需要确保所得到的结果已正确转换为float/double类型。我不确定short[]版本如何工作,但byte[]版本仅返回原始字节版本。然后,该字节数组需要被正确地转换为浮点数。用于转换的代码应该类似于以下内容:

    double[] micBufferData = new double[<insert-proper-size>];
    final int bytesPerSample = 2; // As it is 16bit PCM
    final double amplification = 100.0; // choose a number as you like
    for (int index = 0, floatIndex = 0; index < bytesRecorded - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
        double sample = 0;
        for (int b = 0; b < bytesPerSample; b++) {
            int v = bufferData[index + b];
            if (b < bytesPerSample - 1 || bytesPerSample == 1) {
                v &= 0xFF;
            }
            sample += v << (b * 8);
        }
        double sample32 = amplification * (sample / 32768.0);
        micBufferData[floatIndex] = sample32;
    }

使用 micBufferData[] 函数创建输入的复数数组。

获得结果后,使用结果中复数的幅值。大多数幅值应该接近于零,除了具有实际值的频率。

你需要采样频率将数组索引转换为这些幅值对应的频率:

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

2
非常感谢您的回复,但我仍然有几个问题。在运行“ComputeFrequency”方法之前,我是否仍然能够从返回的复杂数组中提取值?不幸的是,在房间保持安静的情况下,仍然存在相同的问题,偶尔会出现从10到约3000的数字。 - user723060
是的,你仍然可以从复杂数组中提取值,你需要使用复数的幅度(即sqrt(rere + imim))。即使房间里完全寂静,麦克风可能引入背景噪声,这些噪声会显示在FFT上。将数组索引转换为频率,以查看确切的频率是什么。这些频率的值可能有助于了解它们是否是背景噪声。 - shams
我很好奇我是否正确调用了复杂数组,特别是虚数部分。我现在实现的方式与我的原始示例非常相似,但现在我正在遍历新的micBufferData数组,并将每个值分配给一个复杂数组,实数部分为常数,虚数部分为0。这可能是我出错的地方,但我之前阅读的示例似乎表明这是正确的方法。有没有什么其他东西需要加进去?再次感谢! - user723060
1
我有类似的问题,请查看我的问题,任何帮助都将不胜感激。http://stackoverflow.com/questions/10908582/android-audiorecord-listening-sound-with-frequency-filter - d-man
嗨,如果你已经让它跑起来了,你能告诉我一下这个改变后,代码执行速度是否更快吗? 因为我正在使用相同的FFT和复数类来分析音频信号,但对5秒小音频进行FFT需要大约10秒钟在我的HTC Sensation上。 非常感谢任何帮助。 - Ahsan Zaheer
显示剩余4条评论

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