musical beat detection with microphone sensor

Hey all! I am building an led light with RGB leds, that I want to control via music. I have a microphone sensor like this (https://www.amazon.com/gp/product/B00XT0PH10/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1) that outputs high on sound detection.

Once a sound is sensed, the light, composed of 6 RGB leds does something: changes color, turns on/off, etc.

The theory is that I could build a bunch of “scenes” that he light would rotate through in sequence, all synced to the beat of the music.

Everything is sort of working, but I am having trouble fine tuning the beat detection. I’ve been playing with the value for debounceDelay… It either doesn’t seem to catch the beat all of the time, or it proceeds through the different scenes so quickly that it is skipping some of them. And frankly, I don’t think that playing with this value is really what I need to do to to actually solve the problem, but it does have an effect, so that’s what I’ve been doing :slight_smile:

I suspect that the issue is that the output of the microphone sensor is so momentary that if my code doesn’t read the input pin at the exact right moment, it misses the pin at its high state, and doesn’t proceed through the code.

If I replace the microphone sensor with a push button, everything works like a charm.

I think that I need to build some kind of hardware solution that takes the momentary pulse from the microphone sensor and lengthens it so that the arduino has time to read it as high even if it’s off by a few milliseconds. I am quite new to electronics, so I’m not exactly sure how to build such a thing, though I suspect it could be done easily with a capacitor?

If you have any suggestions, please let me know!

My code is here:

const int led1[3] = {1, 2, 3};
const int led2[3] = {4, 5, 6};
const int led3[3] = {7, 8, 9};
const int led4[3] = {10, 11, 12};
const int led5[3] = {13, A0, A1};
const int led6[3] = {A2, A3, A4};

const int beatpin = A5;
int beat = LOW;
int lastbeat = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 5;

//{255, 50, 0}        //pwm orange analogWrite (0-255) for off to 100% on
//{50, 0, 255}        //pwm purple

int scene = 0;

void setup() {
  // put your setup code here, to run once:
for(int i=0; i<3; i++)
   {
    pinMode(led1[i], OUTPUT);
    pinMode(led2[i], OUTPUT);
    pinMode(led3[i], OUTPUT);
    pinMode(led4[i], OUTPUT);
    pinMode(led5[i], OUTPUT);
    pinMode(led6[i], OUTPUT);
   }

pinMode(beatpin, INPUT);

}

int readBeatFunction(){
  int reading = digitalRead(beatpin);
  int result = 0;
  
if (reading != lastbeat){
  lastDebounceTime = millis();
}

if ((millis() - lastDebounceTime) > debounceDelay){

  if (reading != beat){
    beat = reading;

    if (beat == HIGH){
      result = 1;
    }
    
  }
}

lastbeat = reading;
return result;
}

void red(int led[]){ //funtction to make red color from array led
 digitalWrite (led[0], HIGH);
 digitalWrite (led[1], LOW);
 digitalWrite (led[2], LOW);
  }

void yellow(int led[]){ //funtction to make yellow color from array led
 digitalWrite (led[0], HIGH);
 digitalWrite (led[1], HIGH);
 digitalWrite (led[2], LOW);
  }

void green(int led[]){ //funtction to make green color from array led
 digitalWrite (led[0], LOW);
 digitalWrite (led[1], HIGH);
 digitalWrite (led[2], LOW);
 }

void teal(int led[]){ //funtction to make teal color from array led
 digitalWrite (led[0], LOW);
 digitalWrite (led[1], HIGH);
 digitalWrite (led[2], HIGH);
 }

 void blue(int led[]){ //funtction to make blue color from array led
 digitalWrite (led[0], LOW);
 digitalWrite (led[1], LOW);
 digitalWrite (led[2], HIGH);
 }

void pink(int led[]){ //funtction to make pink color from array led
 digitalWrite (led[0], HIGH);
 digitalWrite (led[1], LOW);
 digitalWrite (led[2], HIGH);
 }

void white(int led[]){ //funtction to make white color from array led
 digitalWrite (led[0], HIGH);
 digitalWrite (led[1], HIGH);
 digitalWrite (led[2], HIGH);
 }

void off(int led[]){  //funtction to turn off array led
 digitalWrite (led[0], LOW);
 digitalWrite (led[1], LOW);
 digitalWrite (led[2], LOW);
}

void loop() {
  // put your main code here, to run repeatedly:

//check for beat
scene = scene + readBeatFunction();

if (scene == 1){ //rotate all on/off colors around 6 led sections

for(int i=0; i<6; i++){
  if(i==0){
   red(led1);
   yellow(led2);
   green(led3);
   teal(led4);
   blue(led5);
   pink(led6);
  }
  
  if(i==1){
   red(led6);
   yellow(led1);
   green(led2);
   teal(led3);
   blue(led4);
   pink(led5);
  }

  if(i==2){
   red(led5);
   yellow(led6);
   green(led1);
   teal(led2);
   blue(led3);
   pink(led4);
  }

  if(i==3){
   red(led4);
   yellow(led5);
   green(led6);
   teal(led1);
   blue(led2);
   pink(led3);
  }

  if(i==4){
   red(led3);
   yellow(led4);
   green(led5);
   teal(led6);
   blue(led1);
   pink(led2);
  }

  if(i==5){
   red(led2);
   yellow(led3);
   green(led4);
   teal(led5);
   blue(led6);
   pink(led1);
  }
  
i = i-1+readBeatFunction(); //only proceed on beat detection
  
}
scene = 2;
}//end scene 1

if (scene==2){    //strobe 50ms on/off for 5 beats
  
  int j = 0;
  
  white(led1);
  white(led2);
  white(led3);
  white(led4);
  white(led5);
  white(led6);

 
  long strobeoff = millis() + 50;
  long strobeon = strobeoff + 50;
  
  while(j<6){
    
  if ((millis() > strobeoff)&&(millis() < strobeon)){
  off(led1);
  off(led2);
  off(led3);
  off(led4);
  off(led5);
  off(led6);
  }

  if (millis()> strobeon){
  white(led1);
  white(led2);
  white(led3);
  white(led4);
  white(led5);
  white(led6);

  strobeoff = strobeon + 50;
  strobeon = strobeoff + 50;
  }

  j = j + readBeatFunction(); //count the beats

}
scene = 1;

}//end scene 2


}

Your ear performs literal miracles of sound processing before sending signals to your brain. The brain then does even more massively parallel processing to make sense of the sound.

A microphone like that is good to detect quiet/not-quiet. It cannot detect beats.

The “loudest” part of the music is not the beats. It is actually the high frequencies. The low frequencies carry much less energy. To detect beats, you need to filter out the high frequencies. Buy a different module to do that.

beats aside, if I use the following test code I can get the led to blink very accurately on my snapping fingers:

int soundSensor = 2;
int LED = 4;

void setup() 
{
  pinMode (soundSensor, INPUT);
  pinMode (LED, OUTPUT);

}

void loop() {
  int state=digitalRead(soundSensor); 
  if(state==1)       //when detect some sound
  {
    digitalWrite(LED, HIGH);
  }
  else
  {
    digitalWrite(LED, LOW);
  }
}

But I can't get the code written in my first post to proceed smoothly and accurately on my finger snapping

You have 5ms of debouncing. But your code attempts to read the function 3 times in just a few microseconds. So only one of those will succeed.

Read all your inputs at the top of loop(). Use those values for all calculation without reading again.

The two additional readings of the function occur within for and while loops respectively, they are necessary in those locations so that the program proceeds on each sound detection. If anything, I could eliminate the first "scene" reading, which really only serves to start things off with the initial sound detection.

How fast does the arduino make it through one loop of code? How do I determine this number?

It seems like regardless of how fast it is, it isn't fast enough to catch every sound detection. That is why I thought that if I could elongate the pulse of HIGH being read on the input pin, I wouldn't drop as many sounds. Does that make sense? The fastest rate of sounds that need to be detected is only something like 4x/second, so I think that I should be able to debounce for much longer and still catch each pulse. In practice, when I elongate the debounce, I don't pick up anything at all. When I make the debounce shorter (1 or 2ms) it picks up the input more reliably, but sometimes it will proceed through several iterations all at once which tells me that in these cases it is counting each sound detection event as several events, in other words the debounce is too short.

Is there a simple way to elongate the pulse via a capacitor? It seems like this should be possible, but I haven't worked much with capacitors.

I have a microphone sensor like this (Amazon.com: DAOKI 5PCS High Sensitivity Sound Microphone Sensor Detection Module for Arduino AVR PIC: Industrial & Scientific) that outputs high on sound detection.

With "real music" you need an analog signal. If you have the sensor on just one drum (or finger-snaps or hand-claps), that mic board may work if the loudnes is consistant and you get teh pot adjusted just-right.

BTW - If it will work in your application, it's better if you can use a have a line-level audio signal or a headphone signal instead of picking-up sound with a microphone.

If you just connect an LED (with the normal current-limiting resistor) to the output of that microphone board, it should be obvious that it's no good with music.

But your "debouncing" should be MUCH slower. Your beats are going to be at least a couple-hundred milliseconds apart and you don't want false triggering between beats.

I've done some crude beat detection for a lighting effect and personally I think it's a little more "interesting" if it's not a perfect 1234, 1234... Just my opinion with my effects...

I think the trick to good beat detection is to have some "fuzzy logic" that adapts, gets in-sync and anticipates the beat... When you tap your foot to the music (or dance) you don't wait for the beat before you tap your foot, you anticipate and get in-time with the music.


I didn't do anything like that with my code. I don't have the code because the computer crashed and it wasn't backed-up, plus I was using a peak detector circuit (envelope follower).

Here's an outline of how my beat detector worked - I find the peaks, assuming the peaks are the beats.

After getting a peak and triggering, I hold-off for maybe 200ms because the beats shouldn't be much faster than that. Then, I start looking-for the next peak/beat.

But here's the trick... I start looking for a peak that's equal to the previous one, but I start ramping-down the trigger threshold. So the more time-goes by, the more sensitive it gets and it should find a beat in the next 1/4 second (or so) even if the next beat is not as strong as the previous one. After 1 second or so, it's getting very-sensitive and sooner or later it's going to re-trigger.

P.S
I don't recommend buying this kind of thing from 3rd-party Amazon sellers or eBay, etc. I recommend buying from reputable suppliers such as SparkFun or Adafruit or other specialty electronics suppliers that give you complete specs and application notes, and sometimes a schematic.

P.P.S
This is NOT beat detection, but take a look at my [u]World Simplest Lighting Effect[/u]. There is an attached schematic for a line-level or headphone-level connection but it will also work with [u]this analog microphone board[/u] (if the sound is loud enough). With the microphone board you'll probably have to increase the noise threshold.