5

I recently tried to implement a beat detection code found here, namely the Derivation and Combfilter algorithm #1:: http://archive.gamedev.net/reference/programming/features/beatdetection/page2.asp

Im not too sure if I implemented it successfully as I am not getting good results. I was wondering if anyone have implemented this successfully or just towards the good people who want to help in general. Here is my implementation:

//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;
    }
}

The results that I am getting are always very high, usually around 190 and 200BPM, which should NOT be the case, as my .wav files have tempos only between 60-120BPM.

Note that I am using a .WAV file (44.1Khz, 16-bit, Mono), so that some of the formulas are a bit modified (i.e. calculating the energy) to work with only one channel. I would like to confirm if there have been any discrepancies in my implementation? I am not worrying about the FFT part because I am using a library for that.

Thank you very much!

hammar
  • 138,522
  • 17
  • 304
  • 385
dspboy
  • 91
  • 2

2 Answers2

3

Make a plot of energy vs frequency.

I think you'll find that the harmonics have nearly identical energy to the base signal, and if the actual frequency falls halfway between frequency bins, then the second harmonic's peak is sampled and easily beats the two samples to either side of the true frequency.

You'll need to penalize higher frequencies a little bit to overcome this effect.


Please note, while C# is not an unreasonable choice for implementation of such an algorithm in real time or for bulk batch processing, it's horrible for algorithm development and tweaking. I'd recommend using MatLab (or the free clone, Octave) to get the algorithm right, and only once it's working on some test cases, convert the code to C# (or C++).

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

I'm not quite sure if this is wanted but in this block the comments do not fit the code:

    //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++;
    }

According to code fftPulseBuffer because of first comment has size 512, and then it says 1024.

Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • Oops, yeah sorry that was just error in the comment. The size is really 512. But I will try experimenting on that part. – dspboy Jul 03 '11 at 16:43