2

このウェブサイトを使用して、ビート検出エンジンを作成しようとしました。http://www.gamedev.net/reference/articles/article1952.asp

{


ALfloat energy = 0;
ALfloat aEnergy = 0;
ALint beats = 0;
bool init = false;
ALfloat Ei[42];
ALfloat V = 0;
ALfloat C = 0;


ALshort *hold;
hold = new ALshort[[myDat length]/2];

[myDat getBytes:hold length:[myDat length]];

ALuint uiNumSamples;
uiNumSamples = [myDat length]/4;

if(alDatal == NULL)
    alDatal = (ALshort *) malloc(uiNumSamples*2);
if(alDatar == NULL)
    alDatar = (ALshort *) malloc(uiNumSamples*2);
for (int i = 0; i < uiNumSamples; i++)
{
    alDatal[i] = hold[i*2];
    alDatar[i] = hold[i*2+1];
}
energy = 0;
for(int start = 0; start<(22050*10); start+=512){
for(int i = start; i<(start+512); i++){
    energy+= ((alDatal[i]*alDatal[i]) + (alDatal[i]*alDatar[i]));

}
    aEnergy = 0;
for(int i = 41; i>=0; i--){

    if(i ==0){
        Ei[0] = energy;
    }
    else {
    Ei[i] = Ei[i-1];
    }
    if(start >= 21504){
    aEnergy+=Ei[i];
    }
}
    aEnergy = aEnergy/43.f;
    if (start >= 21504) {
        for(int i = 0; i<42; i++){
            V += (Ei[i]-aEnergy);
        }
        V = V/43.f;
        C = (-0.0025714*V)+1.5142857;
        init = true;
        if(energy >(C*aEnergy)) beats++;
    }

}

}

alDatal と alDatar は (short*) 型です。

myDat は、22050 khz および 16 ビット ステレオにフォーマットされた wav ファイルの実際のオーディオ データを保持する NSdata です。

これは正しく動作していないようです。誰かが私を助けることができれば、それは素晴らしいことです. 私はこれに3日間立ち往生しています。

望ましい結果は、10 秒相当のデータが処理された後、それを 6 倍して 1 分あたりの心拍数を推定できるはずです。

私の現在の結果は、10 秒ごとに 389 ビート、2334 BPM、私が知っている曲は 120 BPM 前後です。

4

1 に答える 1

7

そのコードは本当に醜い棒で叩かれています。他の人に自分のバグを見つけてもらうように頼む場合は、最初に見栄えをよくすることをお勧めします。奇妙なことに、これは自分でそれらを見つけるのにも役立つことがよくあります。

したがって、より根本的な誤りを指摘する前に、いくつかの学校教育的な提案をしなければなりません。

  1. コードにマジック ナンバーを散りばめないでください。のような数行を入力するのは本当に難しいconst ALuint SAMPLE_RATE = 22050ですか?私を信じてください、それは人生をずっと楽にします。

  2. 簡単に混同しない変数名を使用してください。あなたのバグの 1 つは for の置換alDatalですalDatarleftと呼ばれていれば、おそらくそうはならなかったでしょうright。同様にenergy、無意味だが多かれ少なかれ同一のaEnergy. のような有益なものではないのはなぜaverageですか?

  3. 変数は、使用する場所の近くで適切なスコープで宣言します。もう 1 つのバグは、平均化ウィンドウを移動するときに計算されたエネルギーの合計をリセットしないため、エネルギーがどんどん加算されることです。しかし、そのループの外側にエネルギーは必要ありません。また、ループの内側でエネルギーを宣言した場合、問題は発生しません。

ランダムなブレースとインデント、C と C++ の割り当ての混合、ハンガリー語のプレフィックスの奇妙で一貫性のないスクラップなど、私が個人的に少し厄介だと思うものは他にもいくつかありますが、少なくともそれらのいくつかは好みの問題かもしれないので、続きません。

とにかく、コードが機能しない理由は次のとおりです。

まず、この行の右側を見てください。

energy+= ((alDatal[i]*alDatal[i]) + (alDatal[i]*alDatar[i]));

各チャネル値の 2 乗が必要なので、実際には次のように言う必要があります。

energy+= ((alDatal[i]*alDatal[i]) + (alDatar[i]*alDatar[i]));

違いを見つけますか?それらの名前では簡単ではありませんね。

次に、サンプルの各ウィンドウで総エネルギーを計算する必要がありますが、設定energy = 0は外側のループの外側にあるだけです。したがって、合計が累積され、その結果、現在のウィンドウ エネルギーは、これまでに遭遇した中で常に最大になります。

第三に、分散の計算が間違っています。あなたが持っている:

V += (Ei[i]-aEnergy);

ただし、平均からの差の二乗和でなければなりません。

V += (Ei[i] - aEnergy) * (Ei[i] - aEnergy);

他のエラーもあるかもしれません。たとえば、そうでない場合はデータ バッファーを割り当てませんNULLが、ちょうど計算したばかりの正しい長さであると想定します。コード全体で固執している一貫した使用法という点でそれを正当化するかもしれませんが、ここで見ることができる観点からは、それはかなり悪い考えのように見えます.

于 2010-06-22T14:39:25.810 に答える