Got an Arduino kit recently and today I decided to make an audio visualizer using 8 LEDs! The computer plays the song in Processing, which talks to the Arduino using the Firmata firmware. The LEDs are controlled by the 74HC595 IC.

The cool thing about this implementation is the threshold for lighting an LED is based on an absolute amplitude as well as the amplitude compared to the maximum value seen per frequency band. In addition, the square root of the amplitudes are used instead of the log or the plain value, so as to normalize frequency bands with much more “energy” compared to the others. This way the threshold triggers at a similar rate as other bands that may have less amplitude on average.

Steps to recreate the project

  1. Using the Arduino IDE, open the Examples -> Firmata -> StandardFirmata sketch and upload it onto your Arduino
  2. Install Processing
  3. Install the Processing libraries Minim and Arduino (Firmata)
    Sketch -> Import Library… -> Add Library…

Finally, run the following Processing code:

import ddf.minim.*;
import ddf.minim.signals.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import processing.serial.*;
import cc.arduino.*;

Arduino ar; 
Minim minim;
AudioPlayer song;
FFT fft;

int latchPin = 11;
int clockPin = 9;
int dataPin = 12;

int bars;
int barWidth;

boolean[] leds = new boolean[8];
float[] maxVal = new float[8];

String song_file = "D:/Music/Music Stuff/Next Album/Fireworks.mp3";

void setup()
{
  size(600, 300);
  
  minim = new Minim(this);
  ar = new Arduino(this, Arduino.list()[1], 57600);
  
  ar.pinMode(latchPin, Arduino.OUTPUT);
  ar.pinMode(dataPin, Arduino.OUTPUT);  
  ar.pinMode(clockPin, Arduino.OUTPUT);
  
  song = minim.loadFile(song_file);
  song.play();
  fft = new FFT(song.bufferSize(), song.sampleRate());
  fft.logAverages(64, 1);
  //fft.linAverages(16);
  fft.window(FFT.HAMMING);
  println(fft.avgSize());
  bars = fft.avgSize();
  barWidth = width/bars;
  
  stroke(127, 255, 0);
  fill(127, 255, 0);
  
}

void shiftOut(boolean[] val)
{
  for (int i = 0; i < val.length; i++)
  {
    ar.digitalWrite(clockPin, Arduino.LOW);
    ar.digitalWrite(dataPin, val[i] ? Arduino.HIGH : Arduino.LOW);
    ar.digitalWrite(clockPin, Arduino.HIGH);
  }
}

void updateShiftRegister()
{
   ar.digitalWrite(latchPin, Arduino.LOW);
   shiftOut(leds);
   ar.digitalWrite(latchPin, Arduino.HIGH);
}

void draw()
{
  background(0);
  fft.forward(song.mix);
  
  for (int i = 0; i < bars; i++) {
    float val = sqrt(fft.getAvg(i))*20;
    
    int h = int(val);
    rect(i*barWidth, height-30-h, barWidth, h);
    text("Min Freq", 10, height-10);
    text("Max Freq", fft.specSize(), height-10);
    
    if (i < 8)
    {
      if (val > maxVal[i])
      {
        maxVal[i] = val;
      }
      
      leds[i] = (val > 80 || (val > 25 && val > maxVal[i] * 0.6)) ? true : false;
      
      updateShiftRegister();
    }
  }
}