3

自己相関を使用して、録音された音声 (44.1 kHz) からピッチ クラスを取得しようとしています。私がやっていることは基本的にここで説明されています: http://cnx.org/content/m11714/latest/また、ここで実装されています: http://code.google.com/p/yaalp/source/browse/trunk/csaudio /WaveAudio/WaveAudio/PitchDetection.cs (PitchDetectAlgorithm.Amdfを使っている部分)

したがって、ピッチ クラスを検出するために、C2 から B3 (2 オクターブ) の周波数の正規化された相関を持つ配列を構築し、最も高い値を持つものを選択します (「1 - 相関」変換を最初に実行するため、最小だが最大)

生成されたオーディオ(単純な洞)でテストしました:

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

ただし、B4 よりも低い入力周波数でのみ機能します。生成された配列を調査すると、G3 から始まる別のピークが進化し、最終的に正しいものよりも大きくなることがわかりました。私の B4 は E として検出されます。分析される周波数の数を変更しても、まったく役に立ちませんでした。

私のバッファ サイズは 4000 サンプルで、B4 の周波数は ~493Hz なので、これが失敗する理由が思いつきません。頻度またはバッファ サイズに関するその他の制約はありますか? そこで何がうまくいかないのですか?

Performousが使用しているようにFFTを使用できることは承知していますが、この方法を使用すると簡単に見え、視覚化を表示するために使用できる重み付けされた周波数も得られます。私はそれを簡単に捨てたくないし、少なくともこれが失敗する理由を理解しています。

更新:使用されるコア関数:

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);

しかし、それは E としての B4 に加えて D としての A3 の取得に失敗します

注: Bufferlength で除算するのではなく、実際に比較したサンプル数で除算します。これが正しいかどうかはわかりませんが、論理のようです。

4

3 に答える 3

1

私は c# を知りませんが、提供された少量のコードが正しく、他のほとんどの C ライクな言語と同様に正しい場合、モジュール間歪みと呼ばれるものが大量に導入されています。

ほとんどの C ライクな言語 (および Java など、私が知っている他のほとんどの言語) では、Math.sin() などの出力は [-1,1] の範囲になります。int、short または long にキャストすると、これは [-1,0] に変更されます。基本的に、正弦波を、多くの倍音を含む非常に歪んだ方形波に変更します。これは、これらのライブラリが拾っている可能性があります。

これを試して:

data[i] = (short)(32,767 * Math.Sin(2 * Math.PI * i/fs * freq));
于 2014-03-22T01:58:16.573 に答える
0

@Bjorn と @Hotpaw によって話されたすべてのことに加えて、過去に @hotpaw2 によって説明された問題を見つけました。

1つのサンプルの差で計算している場合、コードから明確ではありませんでした(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;
    }
于 2014-03-22T20:05:35.810 に答える