简体   繁体   English

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

[英]How to get the frequency from PCM data in java - fft

For some reason the frequencies as displaced 由于某种原因,频率被取代

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

I am using this values 我正在使用这个值

 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

This is how I ready the data and transform it to doubles: 这就是我准备数据并将其转换为双精度数的方法:

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);

Once the data is read, cast from 16 bit, big endian, signed to double 一旦数据被读取,从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;
    }
}

And at last, run the FFT and find the frequency: 最后,运行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");
}

You can interpolate between FFT result bins (parabolic or Sinc interpolation) to get a more accurate estimate of frequency. 您可以在FFT结果区间(抛物线或Sinc插值)之间进行插值,以获得更准确的频率估计。 But you may have a bigger problem: your frequency source may be producing (or be being clipped to produce) some very strong odd harmonics or overtones that mask any fundamental sinusoid in the FFT result magnitudes. 但是你可能会遇到一个更大的问题:你的频率源可能会产生(或被剪切产生)一些非常强的奇次谐波或泛音,它们会掩盖FFT结果幅度中的任何基波正弦曲线。 Thus you should try using a pitch detection/estimation algorithm instead of just trying to look for a (possibly missing) FFT peak. 因此,您应该尝试使用音调检测/估计算法,而不是仅仅尝试寻找(可能缺失的)FFT峰值。

Firstly, if the audio you're recording is long, you'll need to do FFT in chunks, preferably with windowing each chunk before performing FFT. 首先,如果你录制的音频很长,你需要在块中进行FFT,最好在执行FFT之前对每个块进行加窗。 FFT only computes one fundamental frequency, so you need to take FFT at many places if the frequency changes many times. FFT仅计算一个基频,因此如果频率改变很多次,您需要在许多地方进行FFT。

Accuracy can also be improved from sliding windows. 滑动窗户也可以提高精度。 This means that you would take a chunk, then slide over slightly and take another chunk, so that the chunks overlap. 这意味着您将获取一个块,然后稍微滑动并取出另一个块,以便块重叠。 How much you slide over is variable, and the size of each chunk is also variable. 您滑过多少是可变的,每个块的大小也是可变的。

Then, FFT alone might produce false results. 然后,单独的FFT可能会产生错误的结果。 You can do more analysis like Cepstrum analysis or Harmonic Product Spectrum analysis on the power spectrum produces by the FFT to try and estimate the pitch more accurately. 您可以对FFT产生的功率谱进行更多分析,如倒谱分析或谐波产品频谱分析,以更准确地估算音调。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM