このビート検出コードがいくつかのビートを適切に登録できないのはなぜですか?


38

私はこのSoundAnalyzerクラスを作成して、曲のビートを検出しました。

class SoundAnalyzer
{
    public SoundBuffer soundData;
    public Sound sound;
    public List<double> beatMarkers = new List<double>();

    public SoundAnalyzer(string path)
    {
        soundData = new SoundBuffer(path);
        sound = new Sound(soundData);
    }

    // C = threshold, N = size of history buffer / 1024  B = bands
    public void PlaceBeatMarkers(float C, int N, int B)
    {
        List<double>[] instantEnergyList = new List<double>[B];
        GetEnergyList(B, ref instantEnergyList);
        for (int i = 0; i < B; i++)
        {
            PlaceMarkers(instantEnergyList[i], N, C);
        }
        beatMarkers.Sort();
    }

    private short[] getRange(int begin, int end, short[] array)
    {
        short[] result = new short[end - begin];
        for (int i = 0; i < end - begin; i++)
        {
            result[i] = array[begin + i];
        }
        return result;
    }

    // get a array of with a list of energy for each band
    private void GetEnergyList(int B, ref List<double>[] instantEnergyList)
    {
        for (int i = 0; i < B; i++)
        {
            instantEnergyList[i] = new List<double>();
        }
        short[] samples = soundData.Samples;

        float timePerSample = 1 / (float)soundData.SampleRate;
        int sampleIndex = 0;
        int nextSamples = 1024;
        int samplesPerBand = nextSamples / B;

        // for the whole song
        while (sampleIndex + nextSamples < samples.Length)
        {
            complex[] FFT = FastFourier.Calculate(getRange(sampleIndex, nextSamples + sampleIndex, samples));
            // foreach band
            for (int i = 0; i < B; i++)
            {
                double energy = 0;
                for (int j = 0; j < samplesPerBand; j++)
                    energy += FFT[i * samplesPerBand + j].GetMagnitude();

                energy /= samplesPerBand;
                instantEnergyList[i].Add(energy);

            }

            if (sampleIndex + nextSamples >= samples.Length)
                nextSamples = samples.Length - sampleIndex - 1;
            sampleIndex += nextSamples;
            samplesPerBand = nextSamples / B;
        }
    }

    // place the actual markers
    private void PlaceMarkers(List<double> instantEnergyList, int N, float C)
    {
        double timePerSample = 1 / (double)soundData.SampleRate;
        int index = N;
        int numInBuffer = index;
        double historyBuffer = 0;

        //Fill the history buffer with n * instant energy
        for (int i = 0; i < index; i++)
        {
            historyBuffer += instantEnergyList[i];
        }

        // If instantEnergy / samples in buffer < instantEnergy for the next sample then add beatmarker.
        while (index + 1 < instantEnergyList.Count)
        {
            if(instantEnergyList[index + 1] > (historyBuffer / numInBuffer) * C)
                beatMarkers.Add((index + 1) * 1024 * timePerSample); 
            historyBuffer -= instantEnergyList[index - numInBuffer];
            historyBuffer += instantEnergyList[index + 1];
            index++;
        }
    }
}

何らかの理由で、637秒から約641秒までのビートを検出しているだけで、その理由はわかりません。重複を見つけているので、複数のバンドからビートが挿入されていることはわかっていますが、それらの値の間の各瞬間エネルギー値にビートを割り当てているようです。

このモデル:http : //www.flipcode.com/misc/BeatDetectionAlgorithms.pdf

では、なぜビートが適切に登録されないのでしょうか?


2
1つのバンドについて、instanceEnergyList [index + 1]とhistoryBufferの経時的変化のプロットを投稿できますか?2つのグラフが重なり合っています。それは問題が何であるかについての手がかりを与えるでしょう。また、エネルギーは大きさの2乗でなければなりません。忘れないでください。
CeeJay

ああはい、それは、問題を発表、私は何とかいくつかのグラフを作ることができるなら、私は見てみましょうかもしれない
クインシー

2
しかし、このプロットは単なるhistoryBufferまたはhistoryBuffer / numInBuffer * Cですか?そこに大規模なCがあるように見えます。コードを見ると、historyBufferはinstantEnergyと同様の値を持っているはずです。このグラフは、Cが高すぎるか、numInBufferが低すぎる(1未満)場合にのみ表示されますが、そうではありません。
CeeJay

7
質問死なないでしょう ...
エンジニア

回答:


7

フーリエ変換や音楽理論に慣れていないので、私はそれを刺しました。だから、いくつかの研究の後、解決策はありませんが、いくつかの厄介なことがわかります:

  • サウンドとサウンドバッファのコードが欠落しており、簡単に犯人になる可能性があります
  • フーリエ変換
    • 名前空間とメソッド名をグーグルで検索しても同じフーリエ変換ライブラリが見つかりませんでした。つまり、コードがカスタムであり、問​​題の原因になる可能性があります
    • FastFourier.Calculateが短い配列をとるという事実は珍しい
  • メソッドGetEnergyListは参照リストを取得しますが、このリストは再び使用されませんか?
  • いくつかのスポットでは、SampleSizeが1024にハードコードされていますが、それが常に当てはまるかどうかは明らかではありません。
  • PlaceBeatMarkersのコメントで、Nを1024で除算する必要があると指摘されているのは厄介です。
  • 特にNが渡され、historyBufferを操作するために使用されるので、PlaceMarkersでhistoryBufferが操作される方法について非常に疑っています。
  • コメント*// Fill the history buffer with n * instant energy*とそれに続くコードは意味がありません。

しばらくして、コードが実際にはうまく整理されていないと感じたので、修正しようとすると時間の無駄になります。あなたがそれが価値があると思うなら、私が取る次のステップは次のとおりです。

  1. 最も単純な部分に分解する
  2. 最も冗長な方法でコードを書き直し、すべての隠し変数に名前を付けます
  3. 単体テストを作成して、コードのほとんどの部分が正しく機能しないことを確認します
  4. コードの別の小さなセクションを追加し、すべてが正常に機能するまで繰り返します

ヒント

  • ループロジックを簡素化するために、バンドの数を固定することができます。
  • N、C、Bなどの変数に明確で簡潔な名前を付けると、論理エラーを簡単に確認できます。
  • コードの大きなセクションをいくつかの呼び出されたメソッドに分割し、それぞれが大きなプロセスの小さな簡潔なステップを実行し、ユニットテストを作成して正しく動作することを確認できます。

私は、なぞが良い限り、コードのなぞなぞを解くのが好きです。したがって、報奨金。あなたがそれを受け入れてくれてうれしいですし、コードのエラーを見つけるためのあなたの答えは、コードのなぞなぞが得ることができる最高の答えです。
セスバティン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.