使用这个网站,我试图制作节拍检测引擎。 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是(短*)类型;
myDat是NSdata,用于保存格式化为的wav文件的实际音频数据 22050 khz和16位立体声。
这似乎无法正常工作。如果有人能帮助我,这将是惊人的。我已经坚持了3天。
期望的结果是在处理了10秒的数据之后我应该能够将其乘以6并且每分钟有一个估计的节拍。
我目前的成绩是每10秒389次,2334 BPM,我知道的歌曲大约是120 BPM。
答案 0 :(得分:7)
这个代码真的被丑陋的棍子打乱了。如果您要让其他人为您找到您的错误,那么首先让事物成为现实是个好主意。奇怪的是,这通常会帮助你自己找到它们。
所以,在我指出一些更基本的错误之前,我必须提出一些学术建议:
请勿使用magic numbers撒上代码。键入const ALuint SAMPLE_RATE = 22050
之类的几行真的很难吗?相信我,它使生活更容易。
使用您不会轻易混淆的变量名称。您的一个错误是将alDatal
替换为alDatar
。如果他们被称为left
和right
,那可能不会发生。同样地,如果你只是将它与无意义但又或多或少相同的energy
一起使用,那么拥有一个有意义的变量名称如aEnergy
的重点是什么?为什么不像average
那样提供信息?
声明变量靠近您要使用它们的位置并在适当的范围内。另一个缺点是,当您移动平均窗口时,您不会重置计算的能量总和,因此能量只会累加起来。但是你不需要那个循环之外的能量,如果你在里面声明它就不会发生问题。
我个人觉得还有其他一些令人讨厌的东西,比如随机支撑和缩进,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
,则不分配数据缓冲区,但假设它们是正确的长度 - 您只是刚刚计算过的。你可以在整个代码中坚持使用一些一致的用法来证明这一点,但从我们在这里看到的看来,它看起来是一个非常糟糕的主意。