Android 低通安卓PCM音频数据

Android 低通安卓PCM音频数据,android,audio,fft,lowpass-filter,Android,Audio,Fft,Lowpass Filter,我正在开发一个吉他调谐器应用程序,通过录制音频,获得音频的FFT,并找到峰值大小以找到基频。到目前为止,结果表明我的代码工作正常,当播放纯音时,将返回准确的频率,特别是在500+hz时,但是吉他的低频率和高次谐波,结果有点混乱 我相信我需要引入一个窗口功能,以及一个低通滤波器来优化我的结果,并帮助我的应用程序检测正确的峰值,而不是谐波,但我不太确定 我已经实现了一个窗口函数,尽管我不确定它是否会影响最终结果,我完全被困在如何实现低通滤波器上 byte data[] = new b

我正在开发一个吉他调谐器应用程序,通过录制音频,获得音频的FFT,并找到峰值大小以找到基频。到目前为止,结果表明我的代码工作正常,当播放纯音时,将返回准确的频率,特别是在500+hz时,但是吉他的低频率和高次谐波,结果有点混乱

我相信我需要引入一个窗口功能,以及一个低通滤波器来优化我的结果,并帮助我的应用程序检测正确的峰值,而不是谐波,但我不太确定

我已经实现了一个窗口函数,尽管我不确定它是否会影响最终结果,我完全被困在如何实现低通滤波器上

        byte data[] = new byte[bufferSize]; //the audio data read in
        ...

        double[] window = new double[bufferSize]; //window array

           //my window function, not sure if correct
           for(int i = 0; i< bufferSize-1; ++i){   
               window[i] = ((1 - Math.cos(i*2*Math.PI/bufferSize-1))/2);
               data[i] = (byte) (data[i] * window[i]);
           }  


            DoubleFFT_1D fft1d = new DoubleFFT_1D(bufferSize); 
            double[] fftBuffer = new double[bufferSize*2]; 

            for(int i = 0; i < bufferSize-1; ++i){
                fftBuffer[2*i] = data[i];
                fftBuffer[2*i+1] = 0;
            }

            fft1d.complexForward(fftBuffer);


            //create/populate power spectrum
            double[] magnitude = new double[bufferSize/2];  
            maxVal = 0;
            for(int i = 0; i < (bufferSize/2)-1; ++i) {

                double real =  fftBuffer[2*i];
                double imaginary =  fftBuffer[2*i + 1];

                magnitude[i] = Math.sqrt( real*real + imaginary*imaginary ); 
                                Log.i("mag",String.valueOf(magnitude[i]) + " " + i);

            //find peak magnitude
            for(int i = 0; i < (bufferSize/2)-1; ++i) { 
            if(magnitude[i] > maxVal){
                maxVal = (int) magnitude[i];           
                binNo = i;                  
                }   
            }

            //results
            freq = 8000 * binNo/(bufferSize/2);  
            Log.i("freq","Bin "+String.valueOf(binNo));
            Log.i("freq",String.valueOf(freq) + "Hz");
byte data[]=新字节[bufferSize]//读入的音频数据
...
double[]窗口=新的double[bufferSize]//窗口数组
//我的窗口功能,不确定是否正确
对于(inti=0;imaxVal){
maxVal=(int)震级[i];
binNo=i;
}   
}
//结果
freq=8000*binNo/(缓冲区大小/2);
Log.i(“freq”、“Bin”+String.valueOf(binNo));
Log.i(“freq”,String.valueOf(freq)+“Hz”);

所以,是的,不完全确定窗口函数是否发挥了很大作用,功率谱包含谐波峰值,我也不确定从哪里开始使用低通滤波器。

窗口函数可以帮助提高一点结果

        byte data[] = new byte[bufferSize]; //the audio data read in
        ...

        double[] window = new double[bufferSize]; //window array

           //my window function, not sure if correct
           for(int i = 0; i< bufferSize-1; ++i){   
               window[i] = ((1 - Math.cos(i*2*Math.PI/bufferSize-1))/2);
               data[i] = (byte) (data[i] * window[i]);
           }  


            DoubleFFT_1D fft1d = new DoubleFFT_1D(bufferSize); 
            double[] fftBuffer = new double[bufferSize*2]; 

            for(int i = 0; i < bufferSize-1; ++i){
                fftBuffer[2*i] = data[i];
                fftBuffer[2*i+1] = 0;
            }

            fft1d.complexForward(fftBuffer);


            //create/populate power spectrum
            double[] magnitude = new double[bufferSize/2];  
            maxVal = 0;
            for(int i = 0; i < (bufferSize/2)-1; ++i) {

                double real =  fftBuffer[2*i];
                double imaginary =  fftBuffer[2*i + 1];

                magnitude[i] = Math.sqrt( real*real + imaginary*imaginary ); 
                                Log.i("mag",String.valueOf(magnitude[i]) + " " + i);

            //find peak magnitude
            for(int i = 0; i < (bufferSize/2)-1; ++i) { 
            if(magnitude[i] > maxVal){
                maxVal = (int) magnitude[i];           
                binNo = i;                  
                }   
            }

            //results
            freq = 8000 * binNo/(bufferSize/2);  
            Log.i("freq","Bin "+String.valueOf(binNo));
            Log.i("freq",String.valueOf(freq) + "Hz");
窗口的目的是减小窗口两端的振幅分量,以避免出现杂散高频,这是必要的,因为傅里叶变换假定信号是无限的,因此在窗口的情况下,它会在两侧重复无数次,导致边界处不连续!

如果应用一个窗口,此问题将最小化,但在某种程度上仍然会发生

如果您正在使用吉他构建一个低通滤波器,以过滤预期的最高调谐频率,那么在应用窗口功能之前,您需要低通

你需要考虑来自麦克风的频率响应,我相信这些移动麦克风捕捉低频调谐吉他是不容易的,我们谈论的是82.4HZ


找到FFT的峰值对于调谐器来说不是一个好主意!

FFT可以被认为是一系列带通滤波器,每个单元的大小都是窗口上的平均功率。FFT的LPF上游不会给你带来太多好处-你可以放弃更高阶的FFT单元,除非你需要反应特别强烈

用FFT实现吉他调谐器的方法是有问题的(尽管 通过这种方式实现成功的调谐器,它们并非不可逾越)

找到峰值箱是一种幼稚的方法,不会给你精确的结果。永远。每个箱都是带通滤波器,所以你假设测量结果是箱的中心频率。这里是错误的:

  • 相同音律的半音频率是几何级数(比率为~1.06),但FFT音箱的间距是线性的。如果我们假设
    Fs
    为44.1k,并使用1024点FFT,音箱间距
    44.1Hz
    。为E2(吉他的底弦为~82Hz@A440),很明显,使用这种方法的调谐器在很大程度上是无用的。即使用极大的窗口大小换取实时响应(以及大量的处理),它仍然不是非常准确。你在尝试调整电低音(底部字符串:E1,~41Hz)时完全失败了
  • 跨音箱的频率会发生什么情况?所有八度音阶中的C频率都离2的幂不远。B-吉他调谐器需要演奏的音符也很接近。对于这些音符,基频的能量几乎在两个波段之间平均分配。它可能不再是最大的
  • 基本频率是峰值频率吗?(提示:通常不是)
  • 箱子重叠。重叠的程度取决于使用的窗口功能
如果您想坚持使用FFT解决方案,您可能希望使用。关于如何使用FFT解决方案,有一个很好的描述。该页中缺少的一条信息是频率的定义是相位变化率:

F=dPhi/dt

因此,可以估计
F
,知道两个连续结果窗口之间的相位差


请注意,加窗是采样,因此采样理论和奈奎斯特速率适用于使用它可以实现的频率分辨率。吉他调谐器至少需要2048点FFT。

FFT峰值幅度频率检测通常无法确定吉他音高,因为峰值频率是