低通android PCM音频数据

时间:2014-02-15 15:34:14

标签: android audio fft lowpass-filter

我正在制作吉他调音器应用程序,通过录制音频,获取音频的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");

所以是的,不完全确定窗函数是否做得多,功率谱包含谐波峰值,我不知道从哪里开始使用低通滤波器。

3 个答案:

答案 0 :(得分:1)

窗口功能可以帮助您增加结果。

窗口的目的是减小窗口两端的振幅分量,以避免出现虚假的高频,这是必要的,因为傅立叶变换假设信号是无穷大的,所以在一个窗口,双方重复无数次,导致边界不连续!

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

如果您正在使用吉他构建低通以过滤预期的最高调谐频率,则在应用窗函数之前需要低通!

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

找到FFT的峰值不是做调谐器的好主意!

答案 1 :(得分:0)

FFT可以被认为是一系列带通滤波器,每个bin的大小是窗口上的平均功率。 FFT上游的LPF不会让您受到太多影响 - 您可以放弃更高阶的FFT分档 - 除非您需要特别陡峭的响应。

使用FFT实现吉他调谐器的方法存在问题(尽管如此) 以这种方式实现成功的调谐器,它们并非不可克服)。

找到峰值箱是一种天真的方法,不会给你精确的结果。永远。每个bin都是一个带通滤波器,因此您可以假设测量结果是bin中心频率。这是错的:

  • 相等气质的半音频率是几何级数(比率为~1.06),但FFT频段的间距是线性的。如果我们假设Fs为44.1k且使用了1024点FFT,则bin间距为44.1Hz。作为E2(吉他的底部琴弦是@ 82Hz @ A440),很明显,使用这种方法的调音器将大部分无用。即使交易一个非常大的窗口大小来进行实时响应(以及大量处理),它仍然不是很准确。你试图调整电贝司(底部字符串:E1,~41Hz)
  • 完全搞砸了
  • 横跨垃圾箱的频率会怎样?正如所发生的那样,所有八度音阶中C的频率与2的幂相差不远.B - 吉他调音器确实需要表现良好的音符也很接近。对于这些音符,基波的能量几乎均匀地分布在两个波段之间。它可能不再是最大的了。
  • 基本甚至是峰值频率? (提示:通常不是)。
  • 箱子重叠。究竟有多少取决于所使用的窗口功能。

如果您想坚持使用FFT解决方案,您可能希望使用STFT。从DSPDimension.com可以很好地描述如何做到这一点。该页面缺少的一条信息是频率的定义与阶段的变化率相同:

F = dPhi/dt

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

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

答案 2 :(得分:0)

FFT峰值幅度频率检测通常不适用于确定吉他音高,因为峰值频率通常不是音符音高的频率。请尝试使用音高检测或估算算法。有关其他选择,请参阅More accurate estimating guitar pitch frequency based on FFT(already) result