5

出于某种原因,频率偏移

 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

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

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 位、大端、有符号转换为双精度

 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");
}
4

2 回答 2

0

首先,如果您录制的音频很长,您需要分块进行 FFT,最好在执行 FFT 之前对每个块进行窗口化。FFT只计算一个基频,所以如果频率变化很多次就需要多处进行FFT。

滑动窗口也可以提高准确性。这意味着您将取一个块,然后轻轻滑过并取另一个块,以便块重叠。你滑过多少是可变的,每个块的大小也是可变的。

然后,单独的 FFT 可能会产生错误的结果。您可以对 FFT 产生的功率谱进行更多分析,如倒谱分析或谐波乘积谱分析,以尝试更准确地估计音高。

于 2013-09-06T19:56:11.510 回答
0

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

于 2013-08-28T20:51:04.463 回答