音高检测/谐波产品频谱 - 怎么了?

信息处理 fft 沥青
2022-02-17 05:53:13

我整理了一个小例子,尝试使用谐波乘积频谱算法来提取简单正弦波的音高。我不确定我的实现,如果我完全理解了一切,这就是为什么我希望在这里得到一些帮助。

代码在这里:https : //gist.github.com/akuehntopf/4da9bced2cb88cfa2d19(不过,您需要 JTransform 和 JavaFX 来编译)。

请使用以下图表查看在处理过程中数据会发生什么情况。注意:我使用的是 160Hz 的正弦波,采样率为 8000。声音数据的持续时间为 1 秒。

在此处输入图像描述

首先显示正弦波(见第一张图片)。

其次,我正在对数据应用汉明窗(第二张图片)

然后我根据以下计算数据的功率谱

private float[] powerSpectrum(float[] window) {
    float[] powerSpectrum = new float[window.length];
    float[] fftBuffer = new float[window.length * 2 + 1];
    System.arraycopy(window, 0, fftBuffer, 0, window.length);
    FloatFFT_1D fft = new FloatFFT_1D(window.length);
    fft.realForward(fftBuffer);

    for (int i=0; i < fftBuffer.length / 2 - 1; i++) {
        float real = fftBuffer[2*i];
        float imag = fftBuffer[2*i+1];

        powerSpectrum[i] = (float)Math.sqrt(real*real + imag * imag); 
    }

    return powerSpectrum;
}

这给了我第三张图中的频谱。

观察到峰值已经大约在正确的位置,但我们选择 HPS,所以继续(我不太确定从这里开始的所有内容):

接下来,我将信号与其压缩形式相乘数次(随着压缩的增加)......

// 4. Compress
float[] spectrumCopy = new float[spectrum.length];
System.arraycopy(spectrum, 0, spectrumCopy, 0, spectrum.length);        
for (int compression = 2; compression < 4; compression++) {
    for (int i = 1; i < spectrum.length; i++) {
        spectrum[i] = spectrum[i] * getCompressedSample(spectrumCopy, 1, compression, i);
    }
}

它使用函数

private float getCompressedSample(float[] buffer, int offset, int compression, int loc) {
    if (offset + loc * compression < buffer.length) {
        return buffer[offset + loc * compression];
    }
    return 0;
}

我在互联网上的某个地方找到了它。但我的理解是,我们压缩功率谱 n 次,同时仅每 2 次,然后仅每 3 次,以此类推功率谱中的样本。然后将原始光谱与这些压缩光谱中的每一个相乘。

我得到的结果是第四张图

观察峰是否发生了变化。

根据我的理解,下一步将是找到具有最高峰值的 bin,进行插值(我使用二次插值)并使用以下公式重新计算音调频率:

private float getFrequencyForIndex(int index, int size, int rate) {
    float freq = (float)index * (float)rate / (float)size;
    return freq;
}

然而,这给了我错误的结果。我几乎被困住了。对此主题的任何帮助将不胜感激!提前致谢!

对于下一步,我希望能够从乐器(吉他/尤克里里)中提取音高频率数据,但当然首先要做的是:-)

2个回答

使用纯乘法加权的谐波积谱基音估计方法仅适用于具有足够幅度的全套谐波的信号。在使用 HPS 之前,应该验证所讨论的信号是否符合该标准,或者为所有相乘的谐波幅度添加一个适当大的非零底。否则,具有零谐波的纯正弦波在乘以零幅度后将简单地从结果中消失。

吉他和尤克里里的声音通常具有比测试正弦波更丰富的谐波。

抱歉,您可能没有注意到振动“频率”和音乐“音高”之间的重要区别。

“音高”不是单一的振动,例如正弦波,而是以不同的数学相关频率发生的多个声音振动的复合这种不同频率的振动复合的元素被称为谐波或分音。例如,如果我们按下钢琴上的中间 C 键,复合谐波的各个频率将从 261.6 Hz 作为基频开始,523 Hz 将是 2 次谐波,785 Hz 将是 3 次谐波,1046 Hz 将是 4 次谐波,等等。后面的谐波是基频 261.6 Hz 的整数倍(例如:2 x 261.6 = 523、3 x 261.6 = 785、4 x 261.6 = 1046)。

下图是 3 秒吉他独奏在复音 mp3 录音中的对数 DFT 图像。它显示了演奏独奏时吉他上各个音符的谐波如何出现。对于这个对数 DFT 上的每个音符,我们可以看到它的多个谐波垂直延伸,因为每个谐波将具有相同的时间宽度。 在此处输入图像描述

这篇 Wikipedia 文章为与音乐有关的“音高”概念提供了很好的背景知识。 https://en.wikipedia.org/wiki/Transcription_(music)#Pitch_detection