如何使用自相关获得信号的基频?

信息处理 fft 声音的 自相关 信号分析
2022-02-06 21:52:29

我试图获得只有一个音高的信号的基频。我使用 FFT 编码了自相关函数,并且已经得到了自相关结果。不幸的是,我不知道如何从自相关结果中获得基频。有人能帮我吗?我使用自相关获取基频的 JAVA 代码如下:

public double getPitch(double[] buffer, int firstSample, int lastSample, double sampleRate)
{
    int lengthOfFFTWindow=lastSample-firstSample;
    double[] input_buffer=new double[lengthOfFFTWindow];
    DoubleFFT_1D fft = new DoubleFFT_1D(lengthOfFFTWindow);
    double[] autocorrelation_values=new double[lengthOfFFTWindow];
    double[] fftData = new double[lengthOfFFTWindow * 2];
    double max=-1;
    double max_i=-1;
    //FFT on each sample in each window
    for (int i = 0; i < lengthOfFFTWindow; i++) {
        // copying audio data to the fft data buffer, imaginary part is 0
        fftData[2 * i] = buffer[i+firstSample];
        fftData[2 * i + 1] = 0;
    }
    fft.complexForward(fftData);
    for (int i = 0,j=0; i < fftData.length; i += 2,j++) {
        // complex numbers -> vectors, so we compute the length of the vector, which is sqrt(realpart^2+imaginarypart^2)
        autocorrelation_values[j] = Math.sqrt((fftData[i] * fftData[i]) + (fftData[i + 1] * fftData[i + 1]));
    }
    fft.complexInverse(fftData, false);
    for(int i=0;i<autocorrelation_values.length;i++)
    {
        if(max<autocorrelation_values[i])
        {
            max=autocorrelation_values[i];
            max_i=i;
        }
    }
    return max/2;
}

返回最大自相关值除以 2 作为基频是否正确?当我这样做时,我总是得到错误的答案。

3个回答

自相关函数的峰值(最大值)并不重要——尽管它可以用作浊音/清音分类的基本特征,或更一般地用于“音高”。

重要的是函数取其最大值(arg-maximum)的索引。这很直观的理解:如果信号有一个周期τ样本,原始信号和信号移位τ样本应该非常相似,因此自相关函数在评估时会有一个峰值τ.

因此,有趣的变量max_i在您的代码中。我注意到的一件事是您搜索整个autocorrelation_values向量。但是自相关函数的最大值为 0,因此您的最大值将始终为 0!我建议您为要检测的基频考虑一个有意义的范围,并将搜索限制在此范围内以获得有意义的结果。

我没有详细检查您的其余代码,我认为本网站的目的不是深入研究编码/实现问题。

还有两个问题需要担心,以便您选择与准周期波形中的局部周期相对应的正确峰值。

首先,如果您使用 FFT -> 幅度平方 -> 反 FFT 来获得自相关,则您的 FFT 长度需要是等长补零的信号段长度的两倍。这对于防止由于使用此技术时发生的循环卷积而导致时间混叠是必要的。

现在这是一个矩形窗口的窗口,其结果是自相关将由一个三角形函数的包络控制。因此,即使存在完美的相关性,该滞后处的明显自相关也不会上升到滞后 0 的水平。但如果你小心的话,这种影响可以得到补偿。

第二个问题与第一个问题有点相反,如果一个周期的滞后导致高自相关,那么两个或三个周期的滞后也是如此。实际上,两个周期可以看作是周期波形的一个周期(只是所有奇次谐波都为零)。和数值问题可能会使第二个峰值略高于第一个峰值,您将选择错误的峰值。所以你可能需要做一些稍微强调第一个峰值的事情

返回最大自相关值除以 2 作为基频是否正确?当我这样做时,我总是得到错误的答案。

不,自相关峰值的位置 ( argmax(autocorrelation_values)) 是波形的周期(通常写成T),以样本为单位,因此要转换为以 Hz 为单位的频率,例如,您可以:

f=fsT

fs是样本/秒,T是样本/周期,因此除以周期/秒 = Hz。