FFT Beat Detection

I’m working on a VU spectrum analyzer which uses the FFT.h library and Arduino MEGA, and I wanted to implement beat detection. My problem is, using this library, I’m unable to detect the “bass kick” beat due to the size of the bins - they’re approximately 150Hz in size, and I also had to ignore the 0-150Hz bin due to weird noise values I couldn’t figure out how to get rid of.

From my limited knowledge, I’m fairly sure it’s impossible to have varying bin sizes in the same FFT, so my immediate solution would be lowering the sampling rate to decrease the size of the bins (but if I had 128 bins that were 10Hz each, I’d only be able to sample up to 1280Hz which is barely enough for an acceptable music spectrum)

I read somewhere that the beat is most dominant in the 45-55Hz range, so I was wondering what options I have with my current setup.

Relevant FFT code:

#define LIN_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#define DC_OFFSET 512

#include <FFT.h>  //FFT library

void setup() {
  delay(1000);  //soft startup to ease flow of electrons

  LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS);

  pinMode(BUTTON_DT, INPUT);

  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0

  Pal = HeatColors_p; //assigning palette

  attachInterrupt (digitalPinToInterrupt (BUTTON_DT), changeEffect, CHANGE); // pressed
}

void loop() {
    while (1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      while (!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= DC_OFFSET;
      adc_val = k<<6;
      fft_input[i] = adc_val; // put real data into even bins
      fft_input[i + 1] = 0; // set odd bins to 0
    }
    // window data, then reorder, then run, then take output
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_lin(); // take the output of the fft
    sei(); // turn interrupts back on

    fft_lin_out[0] = 0;
    fft_lin_out[1] -= 20; //tested minimum noise in this bin, so subtract it
    if(fft_lin_out[1]<0)
      fft_lin_out[1] = 0; //adjust for values in bin when silent (unknown cause)

also had to ignore the 0-150Hz bin due to weird noise values I couldn't figure out how to get rid of.

The lowest bin contains the D.C. component of the waveform which is fixed.
If yo make there be fewer bins then this problem will be even greater. You need more bins not less. After the FFT you then need to combine bins by adding them to gather to get the frequency range you want to look at.

Also I think you’re expectation of the FFT is a bit too idealised, beat detection is not as simple as this.

Grumpy_Mike:
The lowest bin contains the D.C. component of the waveform which is fixed.
If yo make there be fewer bins then this problem will be even greater. You need more bins not less. After the FFT you then need to combine bins by adding them to gather to get the frequency range you want to look at.

Also I think you’re expectation of the FFT is a bit too idealised, beat detection is not as simple as this.

Thanks for the explanation, I'm aware of these issues, perhaps I wasn't too clear with my post :stuck_out_tongue:

I was wondering if there are any options/algorithms I can use with my setup in order to achieve an acceptable beat detection. I've tried lowering the sampling rate by using analogRead (around 9000Hz sampling rate IIRC), which would mean with 256 bins, I would get a bin size of approximately 9000/256=35Hz - but according to Nyquist's theorem, I'd only be able to analyze an audio range of up to 4500Hz.

I realize that it's improbable I'll be able to achieve beat detection without starting my project over, but I'm going to try.

matledoublev:

  • but according to Nyquist's theorem, I'd only be able to analyze an audio range of up to 4500Hz.

Which raises another interesting issue - what about aliasing? If you don't have a good low pass filter before the ADC, the ample audio content between 4500 and 20000Hz is going to splatter all over your sampled values.

aarg:
Which raises another interesting issue - what about aliasing? If you don’t have a good low pass filter before the ADC, the ample audio content between 4500 and 20000Hz is going to splatter all over your sampled values.

True, currently I’m sampling at ~34.6kHz so I don’t really have the need for a low pass filter, but if I decide to lower the sampling frequency to obtain smaller bins, I will have to implement one.