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 是 NSdata,它保存格式为 22050 khz 和 16 位立体声的 wav 文件的实际音频数据。

这似乎无法正常工作。如果有人能帮助我,那就太棒了。我已经坚持了3天。

所需的结果是在处理完 10 秒的数据后,我应该能够将其乘以 6 并估算出每分钟的节拍数。

我目前的结果是每 10 秒 389 次节拍,2334 BPM 我知道的歌曲大约是 120 BPM。

4

1 回答 1

7

那个代码真的被丑陋的棍子砸了。如果您要让其他人为您找到错误,最好先让事情看起来像样。奇怪的是,这通常也会帮助您自己找到它们。

所以,在我指出一些更根本的错误之前,我必须提出一些学术建议:

  1. 不要在你的代码中撒上幻数。写几行真的有那么难const ALuint SAMPLE_RATE = 22050吗?相信我,它让生活轻松。

  2. 使用不会轻易混淆的变量名称。您的一个错误是替换alDatalfor alDatarleft如果他们被称为and ,这可能不会发生right。同样,energy如果您只是要将它与无意义但或多或少相同的变量名放在一起,那么拥有一个有意义的变量名有什么意义aEnergy呢?为什么不提供信息之类的东西average

  3. 将变量声明在您将要使用它们的位置附近并在适当的范围内。您的另一个错误是,当您移动平均窗口时,您没有重置计算的能量总和,因此能量只会累加起来。但是你不需要那个循环之外的能量,如果你在里面声明它,问题就不会发生。

还有一些我个人觉得有点讨厌的东西,比如随机支撑和缩进,C 和 C++ 分配的混合,以及匈牙利前缀的奇怪不一致的碎片,但至少其中一些可能更像是一个口味问题,所以我不会继续。

无论如何,以下是您的代码不起作用的一些原因:

首先,看看这条线的右手边:

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

你想要每个通道值的平方,所以它应该说:

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 回答