java - 如何从 java 中的 PCM 数据中获取频率 - fft

标签 java signal-processing fft frequency

由于某种原因,频率发生了偏移

 391 hz => 1162
 440 hz => 2196
 493 hz => 2454

我正在使用这个值

 final int audioFrames= 1024;
 final float sampleRate= 44100.0f;
 final int bitsPerRecord= 16;
 final int channels= 1;
 final boolean bigEndian = true;
 final boolean signed= true;

 byteData= new byte[audioFrames * 2];  //two bytes per audio frame, 16 bits
 dData= new double[audioFrames * 2];  // real & imaginary

这就是我准备数据并将其转换为 double 据的方式:

format = new AudioFormat(sampleRate, bitsPerRecord, channels, signed, bigEndian);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); 
microphone = (TargetDataLine) AudioSystem.getLine(info);
microphone.open(format);
microphone.start();
int numBytesRead =  microphone.read(byteData, 0, byteData.length);

一旦数据被读取,从 16 位转换,大端,签名为 double

 public void byteToDouble(){
    ByteBuffer buf= ByteBuffer.wrap(byteData);
    buf.order(ByteOrder.BIG_ENDIAN);
    int i=0;
    while(buf.remaining()>1){
        short s = buf.getShort();
        dData[ 2 * i ] = (double) s / 32768.0; //real 
        dData[ 2 * i + 1] = 0.0;    // imag
        ++i;
    }
}

最后,运行 FFT 并找到频率:

 public void findFrequency(){

    double frequency;

            DoubleFFT_1D fft= new DoubleFFT_1D(audioFrames); 
/* edu/emory/mathcs/jtransforms/fft/DoubleFFT_1D.java */

    fft.complexForward(dData); // do the magic so we can find peak      
    for(int i = 0; i < audioFrames; i++){
        re[i] = dData[i*2];
        im[i] = dData[(i*2)+1];
        mag[i] = Math.sqrt((re[i] * re[i]) + (im[i]*im[i]));
    }

    double peak = -1.0;
    int peakIn=-1;
    for(int i = 0; i < audioFrames; i++){
        if(peak < mag[i]){
            peakIn=i;
            peak= mag[i];
        }
    }
    frequency = (sampleRate * (double)peakIn) / (double)audioFrames;
    System.out.print("Peak: "+peakIn+", Frequency: "+frequency+"\n");
}

最佳答案

您可以在 FFT 结果 bin 之间进行插值(抛物线或 Sinc 插值)以获得更准确的频率估计。但是您可能有一个更大的问题:您的频率源可能正在产生(或被削波产生)一些非常强的奇次谐波或泛音,这些谐波或泛音掩盖了 FFT 结果幅度中的任何基波正弦波。因此,您应该尝试使用音调检测/估计算法,而不是仅仅尝试寻找(可能丢失的)FFT 峰值。

关于java - 如何从 java 中的 PCM 数据中获取频率 - fft,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18375656/

相关文章:

java - 从自定义 IStorage 实现获取 IProject

matlab - 识别信号之间的相移

python - 在 Python 2.7 中分解来自 Gstreamer 的音频流

python - 如何让 numpy 数组的 FFT 工作?

java - 傅里叶变换在java中获取幅度值

java - Hibernate JPA hasOne 映射?

java - 如何使用 Oauth 2 从 Gmail 帐户导入 Gmail 联系人

java - 尝试在 Selenium 中读取跨度之间的文本时出现数字格式异常

matlab - 回响干净的声音

linux - P3DFFT 编译 - ifort 编译器错误 - "multiple definiton of ' main' "