所以我正在开发一个吉他调音器应用程序,我正在努力让我的脑袋完全使用FFT来查找基频。这是我的“尝试”从PCM声音数据数据[],大小4160(bufferSize),8000 Hz的采样率找到频率的代码
DoubleFFT_1D fft1d = new DoubleFFT_1D(bufferSize);
double[] fftBuffer = new double[bufferSize*2];
double[] magnitude = new double[bufferSize/2];
// copy real input data to complex FFT buffer
for(int i = 0; i < bufferSize-1; ++i){
fftBuffer[2*i] = data[i];
fftBuffer[2*i+1] = 0;
}
//perform FFT on fft[] buffer
fft1d.realForward(fftBuffer);
// calculate power spectrum (magnitude) values from fft[]
for(int i = 0; i < (bufferSize/2)-1; ++i) {
double real = (2 * fftBuffer[i]);
double imaginary = (2 * fftBuffer[i] + 1);
magnitude[i] = Math.sqrt( real*real + imaginary*imaginary );
}
// find largest peak in power spectrum
for(int i = 0; i < (bufferSize/2)-1; ++i) {
if(magnitude[i] > maxVal){
maxVal = (int) magnitude[i];
binNo = i;
}
}
// convert index of largest peak to frequency
freq = 8000 * binNo/bufferSize;
大部分内容都是基于本网站上类似问题的示例和答案,所以我对它的理解充其量只是一点粗略。
在使用音高发生器测试我的程序时,返回的频率值似乎变化很大。
我想知道我的代码中是否存在任何明显的缺陷,或者我对该过程的理解以及任何指向正确方向的指针
答案 0 :(得分:0)
首先要做的事情:
使用
double real = fftBuffer[2*i])
double imaginary = fftBuffer[2*i + 1];
因为这些是索引计算而不是值的转换。
你能给出一个关于realForward描述的链接吗?
第二:如果你使用realForward,那么缓冲区不会与零交错,但是你必须注意第一个频率。
如果使用在缓冲区中构造具有虚部0的复数的准备,则必须使用complexForward FFT方法。这对第一频率不需要特别注意。
第三,为避免频率检测中出现伪影,请对信号段应用窗口函数。即,将它淡入淡出。
realForward:
输入时:
buf[0]=x[0], buf[1]=x[1] etc.
buf[2n-1]=x[2n-1],
对于偶数N = 2n的样本。
输出时:
buf[0]=a[0], buf[1]=a[n], b[0]=0=b[n],
buf[2]=a[1], buf[3]=b[1],
buf[4]=a[2], buf[5]=b[2],
etc.,
buf[2n-2]=a[n-1], buf[2n-1]=b[n-1]
complexForward :(如果与上面相比,N = 2n)
输入时:
buf[0]=x[0], buf[1]=0,
buf[2]=x[1], buf[3]=0,
... ,
buf[2N-2]=x[N-1], buf[2N-1]=0
输出时:
buf[0]=a[0], buf[1]=b[0],
buf[2]=a[1], buf[3]=b[1],
buf[4]=a[2], buf[5]=b[2],
... ,
buf[2N-2]=a[N-1], buf[2N-1]=b[N-1],
对于这个实数输入,[N-k] = a [k]和b [N-k] = - b [k],因此大约一半的值是多余的。