Помогите с реализацией этого алгоритма обнаружения ударов?
Недавно я попытался реализовать найденный здесь код обнаружения ударов, а именно алгоритм Derivation and Combfilter # 1:: http://archive.gamedev.net/reference/programming/features/beatdetection/page2.asp
Я не слишком уверен, успешно ли я реализовал это, поскольку я не получаю хорошие результаты. Мне было интересно, если кто-то реализовал это успешно или просто к хорошим людям, которые хотят помочь в целом. Вот моя реализация:
//Cycle through Tempo's (60 to 200) incrementing each time by 10
for (int i = (int)mintempo; i <= maxtempo; i += 10)
{
//Clear variables to be used
curtempo = i;
fftPulse.Clear();
offset = 0;
energy = 0;
short[] prevBuffer = null;
//Calculate ti
ti = (60 / curtempo) * 44100;
ti = Math.Round(ti, 0);
//Generate pulse train
for (int j = 0; j < pulseTrain.Length; j++)
{
if ((j % ti) == 0)
pulseTrain[j] = short.MaxValue;
else
pulseTrain[j] = 0;
}
//Compute FFT of the pulseTrain array
while (offset < pulseTrain.Length)
{
//Generate block samples (1024 is my blocksize)
short[] fftPulseBuffer = new short[po.blocksize / 2];
//Store samples from pulseTrain in a 1024 block buffer for passing to the FFT algorithm
index = 0;
for (int j = offset; j < (offset + (po.blocksize / 2)) && j < pulseTrain.Length; j++)
{
fftPulseBuffer[index] = pulseTrain[j];
index++;
}
//Initialize prevBuffer, which contains samples from the previous block, used in conjunction with the current block for the FFT
if (prevBuffer == null)
prevBuffer = new short[po.blocksize / 2];
//Calculate the FFT using the current and previous blocks
fftPulse.Add(CalculateFFT(fftPulseBuffer,prevBuffer));
//Set prevBuffer and increment to next block start position
prevBuffer = fftPulseBuffer;
offset += (po.blocksize / 2);
}
//Calculate energy
for (int j = 0; j < intendomainarr.Count; j++)
{
double[] signalarr = intendomainarr[j];
double[] pulsearr = fftPulse[j];
for (int x = 0; x < signalarr.Length; x++)
{
energy += Math.Abs(signalarr[x] * pulsearr[x]);
}
}
//Get current best tempo match
if (energy > maxenergy)
{
chosentempo = curtempo;
maxenergy = energy;
}
}
Результаты, которые я получаю, всегда очень высоки, обычно около 190 и 200 ударов в минуту, что НЕ должно иметь место, поскольку мои файлы.wav имеют темпы только между 60-120 ударов в минуту.
Обратите внимание, что я использую файл.WAV (44,1 кГц, 16-бит, моно), так что некоторые формулы немного изменены (т. Е. Вычисляются значения энергии) для работы только с одним каналом. Я хотел бы подтвердить, были ли какие-либо расхождения в моей реализации? Я не беспокоюсь о части FFT, потому что я использую библиотеку для этого.
Большое спасибо!
2 ответа
Составьте график зависимости энергии от частоты.
Я думаю, вы обнаружите, что гармоники имеют почти такую же энергию, что и базовый сигнал, и если фактическая частота падает на полпути между частотными бинами, то пик второй гармоники дискретизируется и легко отбивает две выборки с любой стороны от истинной частоты.
Вам нужно будет немного оштрафовать более высокие частоты, чтобы преодолеть этот эффект.
Обратите внимание, что хотя C# не является необоснованным выбором для реализации такого алгоритма в режиме реального времени или для массовой пакетной обработки, он ужасен для разработки и настройки алгоритма. Я бы порекомендовал использовать MatLab (или бесплатный клон Octave), чтобы получить правильный алгоритм, и только после того, как он заработает на некоторых тестах, преобразуйте код в C# (или C++).
Я не совсем уверен, если это нужно, но в этом блоке комментарии не соответствуют коду:
//Generate block samples (1024 is my blocksize)
short[] fftPulseBuffer = new short[po.blocksize / 2];
//Store samples from pulseTrain in a 1024 block buffer for passing to the FFT algorithm
index = 0;
for (int j = offset; j < (offset + (po.blocksize / 2)) && j < pulseTrain.Length; j++)
{
fftPulseBuffer[index] = pulseTrain[j];
index++;
}
Согласно коду fftPulseBuffer из-за первого комментария имеет размер 512, а затем пишет 1024.