我有一个双精度数组,其中包含直接来自麦克风的音频样本,采样率为 44100。我想得到基频(样本包含振幅)。在维基百科的自相关页面,我找到了基于维纳-钦钦定理的解决方案的描述,我在互联网上进行了更多的研究,最终我编写了以下代码,但我不确定它是否正确:
private double determineFrequency(double[] signal) {
//Get a FastFourierTransformer instance (Apache library)
FastFourierTransformer fft = new FastFourierTransformer(DftNormalization.STANDARD);
//The size of the array used by the fft must be a power of two, wrapping
//the original array in a bigger one padded to zero
//NOTE: Here I assume that the input array is smaller than 8192
double[] paddedSignal = new double[8192];
System.arraycopy(signal, 0, paddedSignal, 0, signal.length);
//First fft (forward) to switch from amplitude domain to the frequency domain
Complex[] transformed = fft.transform(paddedSignal, TransformType.FORWARD);
// Calculate the conjugate of the complex array
for (int i=0; i<transformed.length; i++)
transformed[i] = transformed[i].conjugate();
//Second fft (inverse) to complete the autocorrelation
transformed = fft.transform(transformed, TransformType.INVERSE);
//Calculate the array of corresponding real values to switch
// from the frequency domain to the amplitude domain
double[] autocorrelationMatrix = new double[transformed.length];
for (int i=0; i<transformed.length; i++) {
if (Double.isNaN(transformed[i].abs()) || Double.isInfinite(transformed[i].abs()))
autocorrelationMatrix[i] = 0;
else
autocorrelationMatrix[i] = transformed[i].abs();
}
//Get the index of the max amplitude
Integer indexOfMax = Utils.indexOfMax(autocorrelationMatrix);
return transformed[indexOfMax].getReal()*audioFormat.getSampleRate()/transformed.length;
}
您在自相关域中找到最大值,然后使用该值读取频域。 这是行不通的,就像你可以使用时域中的索引来学习一些关于频域的知识一样。
相反
return autocorrelationMatrix[indexOfMax].getReal()*audioFormat.getSampleRate()/autocorrelationMatrix.length;
也就是说,您可能会发现避免额外的 IFFT 更容易。 相反,只需从频域中提取最大绝对值。 这将适用于采样率/变换长度的分辨率,并且可以在最大FFT箱的相位的帮助下进行改进。