通过自相关的音调检测在更高的音调上失败



我试图使用自相关从录制的声音(44.1 kHz)中获得音调类。我所做的基本上是在这里描述:http://cnx.org/content/m11714/latest/,也实现在这里:http://code.google.com/p/yaalp/source/browse/trunk/csaudio/WaveAudio/WaveAudio/PitchDetection.cs(部分使用pitchdetectalgalgorithm . amdf)

因此,为了检测音高类,我建立了一个数组,其中包含C2到B3(2个八度)频率的归一化相关性,并选择具有最高值的一个(首先进行"1 -相关性"转换,因此不搜索最小值而是最大值)

我用生成的音频(简单的鼻窦)测试它:

data[i] = (short)(Math.Sin(2 * Math.PI * i/fs * freq) * short.MaxValue);

但它只适用于低于B4的输入频率。在研究生成的数组时,我发现从G3开始,另一个peek进化了,最终比正确的更大。我的B4被检测为E。改变分析频率的数量根本没有帮助。

我的缓冲大小是4000个样本,B4的频率是~493Hz,所以我想不出这失败的原因。对频率或缓冲区大小有更多的限制吗?哪里出了问题?

我知道我可以像Performous一样使用FFT,但是使用这种方法看起来很简单,并且还给出了可用于显示可视化的加权频率。我不想那么轻易地把它扔掉,至少要明白为什么它失败了。

Update: Core function used:

private double _GetAmdf(int tone)
    {
        int samplesPerPeriod = _SamplesPerPeriodPerTone[tone]; // samples in one period
        int accumDist = 0; // accumulated distances
        int sampleIndex = 0; // index of sample to analyze
        // Start value= index of sample one period ahead
        for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex < _AnalysisBufLen; correlatingSampleIndex++, sampleIndex++)
        {
            // calc distance (correlation: 1-dist/IntMax*2) to corresponding sample in next period (0=equal .. IntMax*2=totally different)
            int dist = Math.Abs(_AnalysisBuffer[sampleIndex] - _AnalysisBuffer[correlatingSampleIndex]);
            accumDist += dist;
        }
        return 1.0 - (double)accumDist / Int16.MaxValue / sampleIndex;
    }

使用该函数,音高/音调为(伪代码)

tone = Max(_GetAmdf(tone)) <- for tone = C2..

我还尝试使用实际的自相关:

double accumDist=0;
//...
double dist = _AnalysisBuffer[sampleIndex] * _AnalysisBuffer[correlatingSampleIndex];
//...
const double scaleValue = (double)Int16.MaxValue * (double)Int16.MaxValue;
return accumDist / (scaleValue * sampleIndex);

但这不能得到A3和D除了B4作为E

注意:我没有除以Bufferlength,而是除以实际比较的样本数。

这是使用自相关和类似的基音滞后估计(AMDF, ASDF等)时常见的八度问题

低一个倍频(或任何其他整数倍)的频率也会在移位的波形相似性中提供良好的匹配(例如,移位2pi的正弦波看起来与移位4pi的正弦波相同,这表示低一个倍频。根据噪声和连续峰值与采样峰值的接近程度,一个或另一个估计峰值可能略高,但基音没有变化。

因此,需要使用一些其他测试来去除波形相关或滞后匹配中的低倍频(或其他次倍频)峰值(例如,峰值是否看起来足够接近一个或多个其他峰值,一个或多个倍频或其他频率倍频,等等)

我不懂c#,但如果你提供的一小部分代码是正确的,就像大多数其他类c语言一样,它引入了大量所谓的模块化扭曲。

在大多数类c语言(以及我知道的大多数其他语言,如java)中,类似Math.sin()的输出将在[-1,1]的范围内。在转换为int类型时,无论是短类型还是长类型,都将变为[-1,0]。从本质上讲,您将把正弦波更改为带有许多泛音的非常扭曲的方波,这可能就是这些库所拾取的。

试试这个:

data[i] = (short)(32,767 * Math.Sin(2 * Math.PI * i/fs * freq));

除了@Bjorn和@Hotpaw所说的所有问题外,过去我发现@hotpaw2描述的问题。

从您的代码中不清楚,如果您正在计算一个样本的差异(正如我在计算AMDF的方程中所见)!

我在java中做的,你可以在Tarsos中找到完整的源代码!

下面是您在java中发布的等效步骤:

    int maxShift = audioBuffer.length;

    for (int i = 0; i < maxShift; i++) {
        frames1 = new double[maxShift - i + 1];
        frames2 = new double[maxShift - i + 1];
        t = 0;
        for (int aux1 = 0; aux1 < maxShift - i; aux1++) {
            t = t + 1;
            frames1[t] = audioBuffer[aux1];
        }
        t = 0;
        for (int aux2 = i; aux2 < maxShift; aux2++) {
            t = t + 1;
            frames2[t] = audioBuffer[aux2];
        }
        int frameLength = frames1.length;
        calcSub = new double[frameLength];
        for (int u = 0; u < frameLength; u++) {
            calcSub[u] = frames1[u] - frames2[u];
        }
        double summation = 0;
        for (int l = 0; l < frameLength; l++) {
            summation +=  Math.abs(calcSub[l]);
        }
        amd[i] = summation;
    }

相关内容

  • 没有找到相关文章

最新更新