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

Posted

技术标签:

【中文标题】如何在每个FFT计算中检索不同的原始频率并且在java中没有任何频率泄漏【英文标题】:how to retrieve the different original frequency in each FFT caculating and without any frequency leakage in java 【发布时间】:2012-09-08 10:21:10 【问题描述】:

我正在为诸如从麦克风记录的音频数据中恢复原始频率之类的问题而战。

对不起我的英语......

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

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 计算间隔中会获得相同的频率。比如代码块二输出了一些日志:

highScore = 151.77662972416104 freq = 7999.5849609375 // 第一个 8000

highScore = 146.33073029829455 freq = 7999.5849609375 // 秒 8000

highScore = 146.44411729898255 频率 = 9000.87890625

highScore = 144.43481176938155 频率 = 9000.87890625

highScore = 142.78046692784702 频率 = 10002.1728515625

highScore = 141.91874938214298 频率 = 10002.1728515625

highScore = 136.47269911015098 频率 = 11003.466796875

highScore = 136.6873278405228 频率 = 11003.466796875

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

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

非常感谢您的任何回答!

【问题讨论】:

“FFT 计算.. 没有任何频率泄漏。” 我相信 FFT 本质上是“泄漏的”。我认为您可以通过增加分析的波段数量来降低影响。 您使用的音频信号的性质是什么?吵吗?是否存在快速改变幅度/频率的组件? 我很难听懂你的文章,所以我不确定你的问题到底是什么,但一般的“如何使用 FFT 跟踪频率”已经被问过很多次了。 感谢您的回答,对不起我的英语和描述,我会尽量清楚地描述这个问题。 @Paul R (Andrew Thompson, Bjorn Roche) 你能帮我回答这个问题吗,我已经再次描述了这个问题。如果我还没有解决问题,请告诉我。非常感谢。 【参考方案1】:

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

    尽管有窗口化,FFT 总是有“旁瓣”。您已经选择了可能是理想的汉明窗,但您可能会看到旁瓣。你不应该这样,但是如果 genTone 和 recordInBackground 之间发生了一些事情(例如,你正在通过扬声器播放声音并重新录制它),可能会产生足够的噪音和失真,偶尔会导致旁瓣数据与主要数据一样突出。

    您似乎一直在阅读 FFT 结果。只有 FFT 的前半部分会包含相关结果,后半部分是前半部分的镜像。由于轻微的数值错误,您可能会发现后半部分的结果大于前半部分。此问题还表明您可能计算错误的频率。我在这里介绍这个(还有更多!):Frequency detection using fft aka pitch

    您对输出的数据进行位反转,但没有输入。根据您的操作,这可能很好,但仅从这么多代码来看,它是错误的。 FFT 可以“看穿”这一点,但您实际上已经产生了巨大数量的噪音。

我还注意到您正在尝试计算 FFT 结果绝对值的对数。这只会使您的计算花费更长的时间。出于您的目的,magnitude = results[line].abs() 很好。

【讨论】:

感谢您的回答,@Bjorn Roche。我想做的是发出特定的频率来指示某些字符。使用 fft 录制语音并从原始数据流中检索频率。然后从特定频率解码字符。就像 ios 中的啁啾应用程序。我已经完成了检索具体频率,就像你在博客中写的一样。但我不知道如何确定将用于 fft 计算的语音帧大小。你能帮我指出我哪里错了吗?谢谢! fft 分辨率与窗口大小有关。我也在博文中介绍了这一点。 嗨,@Bjorn Roche。我知道窗口大小和 fft 分辨率是多少。嗯,谢谢你的回答,我会自己考虑的。 那么我不确定我是否理解你的新问题——这听起来与你原来的问题不同,你和你可能想发布一个新问题。【参考方案2】:

AudioRecorder 与您的输入信号不同步。因此,您从中获得的数据块很可能会将您的音调分成两部分。这就是为什么您会获得两个频率相同的连续日志。

【讨论】:

以上是关于如何在每个FFT计算中检索不同的原始频率并且在java中没有任何频率泄漏的主要内容,如果未能解决你的问题,请参考以下文章

np.fft.fft()结果的物理意义

如何获得 FFT 中每个值的频率?

MATLAB中fft的频率轴怎么计算

如何使用示波器FFT功能查找感兴趣的FFT频率点?

如何识别 FFT 数据的局部最大值

Android音频FFT使用audiorecord检索特定频率幅度