我正在研究吉他调音器应用程序,方法是录制音频,获取音频的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");
是的,不是完全确定窗口功能是否起作用,功率谱是否包含谐波峰值,而且我不确定从哪里开始使用低通滤波器。
最佳答案
窗口功能可以帮助您增加一些结果。
窗口的目的是减小窗口两端的幅度分量,以避免出现杂散高频,这是必要的,因为傅里叶变换假定信号是无限的,因此在窗口的情况下,双方无数次重复,导致边界不连续!
如果应用一个窗口,此问题将被最小化,但仍会在某种程度上发生。
如果您正在与吉他一起工作,则构建一个低通滤波器以过滤期望的最高调谐频率,那么在应用“窗函数”之前,您需要低通!
您需要考虑麦克风的频率响应,我相信这些移动麦克风要捕捉调谐吉他的低频并不容易,我们正在谈论82.4Hz
找到FFT的峰值不是一个好的调谐器!