java - 如何在每次FFT计算中检索不同的原始频率并且在java中没有任何频率泄漏

标签 java audio fft frequency javasound

我正在为从麦克风录制的音频数据中恢复原始频率这样的问题而奋斗。

抱歉我的英语...

让我更清楚地解释这个问题。我使用以下代码生成了一些特定频率:

void genTone() {
    numSamples = (int)(0.2 * sampleRate);   //duration * sampleRate;
    sample = new double[numSamples];
    generatedSnd = new byte[2 * numSamples];

    // fill out the array
    for (int i = 0; i < numSamples; ++i) {
        sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone));
    }

    // convert to 16 bit pcm sound array
    // assumes the sample buffer is normalised.
    int idx = 0;
    for (final double dVal : sample) {
        // scale to maximum amplitude
        final short val = (short) ((dVal * 32767));
        // in 16 bit wav PCM, first byte is the low order byte
        generatedSnd[idx++] = (byte) (val & 0x00ff);
        generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
    }
}

我使用以下代码II录制了声音:

private void recordInBackground() {
    int read = 0;
    while (isRecording) {
        short data[] = new short[bufferSize];   // bufferSize = 4096

        read = audioRecorder.read(data, 0, bufferSize);
        if (read != AudioRecord.ERROR_INVALID_OPERATION) {
            try {
                float tempHammingRes[] = null;
                hamming(bufferSize);

                Complex[] complexs = new Complex[bufferSize];
                Complex[] results = new Complex[bufferSize];
                for (int i = 0; i < bufferSize; ++i) {
                    data[i] /= 32767; 
                    tempHammingRes[i] = tempHammingRes[i] * data[i];
                    complexs[i]= new Complex(tempHammingRes[i], 0);
                }

                results = FFT.fft(complexs);

                double highScore = 0.0;
                int freq = 1;

                for (int line = 1; line < bufferSize; ++line) {
                    double magnitude = Math.log(results[line].abs() + 1) / Math.log(10.0)*20.0;
                    if (magnitude > highScore) {
                        highScore = magnitude;
                        freq = line;
                    }
                }

                double currentFrequence = ComputeFrequency(freq, bufferSize);

                Log.d(TAG, "highScore = " + highScore + " freq = " + currentFrequence);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}

现在我有一个问题,在代码块II中,在连续的FFT计算间隔中会得到相同的频率。例如代码块II输出一些日志:

highScore = 151.77662972416104 freq = 7999.5849609375//前 8000

highScore = 146.33073029829455 freq = 7999.5849609375//第二个8000

高分 = 146.44411729898255 频率 = 9000.87890625

高分 = 144.43481176938155 频率 = 9000.87890625

高分 = 142.78046692784702 频率 = 10002.1728515625

高分 = 141.91874938214298 频率 = 10002.1728515625

高分 = 136.47269911015098 频率 = 11003.466796875

高分 = 136.6873278405228 频率 = 11003.466796875

我只生成了一个8khz,但我得到了两个声音频率。我还减少了输出音调的持续时间或增加了录音机的输入缓冲区大小。不幸的是,这对我想做的事没有帮助..

有谁知道我是否错了或者 fft 的输出本质上是这样的?

非常感谢您的回答!

最佳答案

我在这里发现了一些潜在的问题。我可能会误读您的代码,但我会提到这些事情,因为它们看起来像问题:

  1. 尽管有加窗,FFT 始终具有“旁瓣”。您选择的汉明窗可能非常适合此目的,但您可能会看到旁瓣。您不应该这样做,但如果 genTone 和 recordInBackground 之间发生了某些情况(例如,您通过扬声器播放声音并重新录制),可能会产生足够的噪声和失真,有时会使旁瓣数据与主要数据一样突出。

  2. 看起来您正在从头到尾阅读 FFT 结果。只有 FFT 的前半部分会包含相关结果,后半部分是前半部分的镜像。由于轻微的数值误差,您可能会发现后半部分的结果大于前半部分。这个问题还表明您可能计算出错误的频率。我在这里介绍这一点(以及更多!):Frequency detection using fft aka pitch

  3. 您对传出的数据进行了位反转,但没有传入。也许这很好,具体取决于您在做什么,但仅从这么多代码来看,这是错误的。 FFT 可以“看穿”这一点,但实际上您已经产生了巨大的噪声。

我还注意到您正在尝试计算 FFT 结果的绝对值的对数。这只会让你的计算时间更长。出于您的目的,magnitude = results[line].abs() 就可以了。

关于java - 如何在每次FFT计算中检索不同的原始频率并且在java中没有任何频率泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12329880/

相关文章:

c - FFT 和卷积

opencv - 使用 CUFFT 的实数到复数 FFT,使用 OpenCV 作为数据源

java - 如何查看 JAR 文件中的源代码?

java - Java 中类型层次结构的效率

java - 一种类型的死亡(但不是另一种)的故障并在 Java 小程序游戏中获胜

c++ - 带有波形文件的 QAudioFormat

c# - C#中的文件比较

java - 如何在Java中高效存储滚动播放项属性?

Java:如何在开关盒中初始化 int 数组?

c++ - 使用 vDSP 函数的自相关