chef_jeff69
Published © GPL3+

ESP32 Stereo audio spectrum visualizer with amplifier

Make audio come to life. Get the mood on and "See the music" suitable for gaming and music.

IntermediateFull instructions provided4,513
ESP32 Stereo audio spectrum visualizer with amplifier

Things used in this project

Hardware components

ESP32S
Espressif ESP32S
×1
Texas Instruments UA741CP
×2
Audio / Video Cable Assembly, 3.5mm Slim Stereo Plug to 3.5mm Slim Stereo Jack
Audio / Video Cable Assembly, 3.5mm Slim Stereo Plug to 3.5mm Slim Stereo Jack
×1
Omron Electronic Components LLC Micro Switch
×1
Rotary Potentiometer, 10 kohm
Rotary Potentiometer, 10 kohm
×2
Capacitor 10 µF
Capacitor 10 µF
get a non polarized cap
×5
Capacitor 47 µF
Capacitor 47 µF
×2
WS2812 Addressable LED Strip
Digilent WS2812 Addressable LED Strip
I'm using a a total of 60 LEDS, buy the number of LEDS that you desire
×1
Through Hole Resistor, 500 ohm
Through Hole Resistor, 500 ohm
×2
Resistor 10k ohm
Resistor 10k ohm
×5
2.6k ohm resistor
×4
1.5k ohm resistor
×2
Vishay 11k ohm resistor
×2
XL6009 DC-DC Adjustable Step Up Voltage Booster Converter Module
×1

Software apps and online services

Texas Instruments Pspice

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Plier, Long Nose
Plier, Long Nose
Plier, Cutting
Plier, Cutting
Multitool, Screwdriver
Multitool, Screwdriver

Story

Read more

Schematics

20210107_011052_WYRU1N7R8U.jpg

Code

Stereo ESP32 audio visualizer

Arduino
#include <FastLED.h>
#include <arduinoFFT.h>
#include <EasyButton.h>
#include <EEPROM.h>

#define samples 512      
#define sampling_freq 40000
#define Noise_filter 1300       // change this value as needed
#define Left_AudioIn 36         // you can use different pins for the ADC but make sure that they do not conflict 
#define Right_AudioIn 35
#define max_brightness  200    // define the max brightness of all the leds   DOES NOT APPLY TO ALL MODES
#define basebrightness 15     // used to set the minimum brightness
#define NUM_LEDS 30    
#define LEFT_LED_PINOUT  22   
#define RIGHT_LED_PINOUT 23
#define peak 255              //define the peak of fft results
#define button_1 33           //choose pin 33 as the button to swtich modes. You can choose any pin you like but be aware of your circuit and which pins are useable
#define EEPROM_size 512         // define the number of bytes that will be used for eeprom storage

const float Lamp =1.7;        // extra amp for channel tuning may change these values depending on your situation to balance the audio channels
const float Ramp =1.3;

#define amp_0 200            // used to control the amplification to divide the value of the bands after fft
#define amp_1 200           
#define amp_2 200           // you can chnage these values to adjust the output of the FFT
#define amp_3 150
#define amp_4 150
#define amp_5 120
#define amp_6 120
#define amp_7 120
#define amp_8 120
#define amp_9 100
#define amp_10 80
#define amp_11 80
#define amp_12 50
#define amp_13 40
#define amp_14 40


unsigned int amp[] ={amp_0, amp_1, amp_2, amp_3, amp_4, amp_5, amp_6, amp_7,      // Create an array to store the amplitude adjustment values
                     amp_8, amp_9, amp_10, amp_11, amp_12, amp_13, amp_14,};

unsigned int sampling_period_us;    // create a 16bit unsigned integer value to store the sampling period

int LbandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //the bandvalues will store the data that has been processed from the arduino fft library
int RbandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
 
int LoldbandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //store the previous band values
int RoldbandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

double LvReal [samples];   // the arrays that will store the initial data created from fft
double LvImag [samples];
double RvReal [samples];
double RvImag [samples];

unsigned long newTime, oldTime;   // used to time the sampling for of signals

uint8_t hue =0;      //the hue integer number that will range from 0-255

int average[2];         // average will be used for the _amp modes
int old_average[2];
int peak_amp[2];


arduinoFFT FFTL =arduinoFFT(LvReal,LvImag, samples, sampling_freq);     // FFT magic starts here
arduinoFFT FFTR =arduinoFFT(RvReal,RvImag, samples, sampling_freq);

EasyButton mode_button(button_1);           // set button to be used to change modes

bool mode_switch_call = false;
bool palette_flag =true;
bool band_flag =false;

uint8_t mode_num =0;

CRGB leds[2][NUM_LEDS];           // FastLED magic

int brightness[2][NUM_LEDS];

CRGBPalette16 TWICE_pal =CRGBPalette16(         //TWICE Colors (PS: its a Kpop girl group with a color theme dedicated to them)
    CHSV(32,205,200), CHSV(16,210,200), 
    CHSV(245,210,200), CHSV(237,255,200)
  );
CRGBPalette16 Rainbow_pal =CRGBPalette16(       //Rainbow colors
    CHSV(0,255,255), CHSV(85,255,255),
    CHSV(171,255,255), CHSV(255,255,255)
    );
CRGBPalette16 Ocean_pal =CRGBPalette16(         //Ocean colors
  CRGB(0,0,255), CRGB(0,128,255),
  CRGB(200,200,200)
  );
CRGBPalette16 Temp_pal =CRGBPalette16(         //Temp colors
  CRGB(0,0,255), CRGB(255,0,0), CRGB(255,255,0)
  );

CRGBPalette16 Poison_pal =CRGBPalette16(         //Poison colors
  CRGB(0,255,0), CRGB(0,0,255), CRGB(255,0,80)
  );

void mode_switch()                 // this function is called when a button press is detected
{
  mode_num = (mode_num + 1)%7;        // the mod numbers needs to be altered based on the number of modes currently available
  palette_flag = true;
  
}

void setup() {
  
  pinMode(Right_AudioIn, INPUT);
  pinMode(Left_AudioIn, INPUT);
  EEPROM.begin(EEPROM_size);
  delay(3000);
  mode_num =EEPROM.read(1);             // Reads the current mode saved in EEPROM address 1 to choose mode before running.
  
  mode_button.begin(); 
  FastLED.addLeds <WS2812B, LEFT_LED_PINOUT, GRB> (leds[0], NUM_LEDS);
  FastLED.addLeds <WS2812B, RIGHT_LED_PINOUT, GRB> (leds[1], NUM_LEDS);
  FastLED.setCorrection(TypicalSMD5050);
  
  sampling_period_us = round (1000000*(1/sampling_freq));
}

void loop() {
  mode_button.read();
  if(mode_button.wasPressed()==true)
  {
    mode_switch();
    mode_switch_call =false;
  }
  
  for (int i=0; i<samples;i++)
  {
    newTime =micros();
    LvReal[i]=analogRead(Left_AudioIn);
    LvImag[i]= 0;
    RvReal[i] =analogRead(Right_AudioIn);
    RvImag[i]=0;
    while ((micros()-newTime) < sampling_period_us) // used to time the sampling rate
    {
    }
  }
   FFTL.DCRemoval();                                                  //removes the DC offset
   
   FFTL.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);                //FFT magic happening!! 
   FFTL.Compute(FFT_FORWARD);
   FFTL.ComplexToMagnitude();

   FFTR.DCRemoval();
   FFTR.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
   FFTR.Compute(FFT_FORWARD);
   FFTR.ComplexToMagnitude();

   //Analyse FFT results
   for(int i=1; i < (samples/2); i++)
   {
    if (RvReal[i]> Noise_filter ) //crude noise filter
    {
      if (i<=1) 
      {
        RbandValues[0] = (int)RvReal[i];
      }
      if (i>1   &&  i<=2     ) 
      {
        RbandValues[1] = (int)RvReal[i];
      }
      if (i>2   &&  i<=3     ) 
      {
        RbandValues[2] = (int)RvReal[i];
      }
      if (i>3   &&  i<=4     ) 
      {
        RbandValues[3] = (int)RvReal[i];
      }
      if (i>4   &&  i<=5     )
      {
        RbandValues[4] = (int)RvReal[i];
      }
      if (i>5   &&  i<=7     )
      {
        RbandValues[5] = (int)RvReal[i];
      }
      if (i>7   &&  i<=10    ) 
      {
        RbandValues[6] = (int)RvReal[i];
      }
      if (i>10  &&  i<=15    ) 
      {
        RbandValues[7] = (int)RvReal[i];
      }
      if (i>15  &&  i<=21    ) 
      {
        RbandValues[8] = (int)RvReal[i];
      }
      if (i>21  &&  i<=29    ) 
      {
        RbandValues[9] = (int)RvReal[i];
      }
      if (i>29  &&  i<=42    ) 
      {
        RbandValues[10] = (int)RvReal[i];
      }
      if (i>42  &&  i<=59    ) 
      {
        RbandValues[11] = (int)RvReal[i];
      }
      if (i>59  &&  i<=84    ) 
      {
        RbandValues[12] = (int)RvReal[i];
      }
      if (i>84  &&  i<=120    ) 
      {
        RbandValues[13] = (int)RvReal[i];
      }
      if (i>120  &&  i<=170    )
      {
        RbandValues[14] = (int)RvReal[i];
      }
    }
    if(LvReal[i] > Noise_filter)
    {
      if (i<=1) 
      {
        LbandValues[0] = (int)LvReal[i];
      }
      if (i>1   &&  i<=2     ) 
      {
        LbandValues[1] = (int)LvReal[i];
      }
      if (i>2   &&  i<=3     ) 
      {
        LbandValues[2] = (int)LvReal[i];
      }
      if (i>3   &&  i<=4     ) 
      {
        LbandValues[3] = (int)LvReal[i];
      }
      if (i>4   &&  i<=5     )
      {
        LbandValues[4] = (int)LvReal[i];
      }
      if (i>5   &&  i<=7     )
      {
        LbandValues[5] = (int)LvReal[i];
      }
      if (i>7   &&  i<=10    ) 
      {
        LbandValues[6] = (int)LvReal[i];
      }
      if (i>10  &&  i<=15    ) 
      {
        LbandValues[7] = (int)LvReal[i];
      }
      if (i>15  &&  i<=21    ) 
      {
        LbandValues[8] = (int)LvReal[i];
      }
      if (i>21  &&  i<=29    ) 
      {
        LbandValues[9] = (int)LvReal[i];
      }
      if (i>29  &&  i<=42    ) 
      {
        LbandValues[10] = (int)LvReal[i];
      }
      if (i>42  &&  i<=59    ) 
      {
        LbandValues[11] = (int)LvReal[i];
      }
      if (i>59  &&  i<=84    ) 
      {
        LbandValues[12] = (int)LvReal[i];
      }
      if (i>84  &&  i<=120    ) 
      {
        LbandValues[13] = (int)LvReal[i];
      }
      if (i>120  &&  i<=170    )
      {
        LbandValues[14] = (int)LvReal[i];
      }
    }
   }
  for( int i =0; i <=14; i++)   //adjust the values of the output.
  {
    
    LbandValues[i] =LbandValues[i]*Lamp/amp[i];
    RbandValues[i] =RbandValues[i]*Ramp/amp[i];

    
      
    if(LbandValues[i]>= peak)
    {
      LbandValues[i] =peak;
    }
    if(RbandValues[i]>= peak)
    {
      RbandValues[i] =peak;
    }
    
    LbandValues[i] =map(LbandValues[i],0, peak,0,max_brightness);   // adjusted band values 
    RbandValues[i] =map(RbandValues[i],0, peak,0,max_brightness);
    
    if (LoldbandValues[i] >= LbandValues[i])
    {
      LbandValues[i] =(LoldbandValues[i]*2+LbandValues[i])/3;        //weighted average to slow down decay
    }
    if (RoldbandValues[i] >= RbandValues[i])
    {
      RbandValues[i] =(RoldbandValues[i]*2+RbandValues[i])/3;
    }
    
    if(band_flag ==true)                                          // some modes will use these to make the lights move smoother
    {
     if (LoldbandValues[i] <= LbandValues[i])
      {
        LbandValues[i] =(LoldbandValues[i]*1+LbandValues[i])/2;        //weighted average to slow down increment
      }
      if (RoldbandValues[i] <= RbandValues[i])
     {
       RbandValues[i] =(RoldbandValues[i]*1+RbandValues[i])/2;
      }
    }
    LbandValues[i] += basebrightness;
    RbandValues[i] += basebrightness;
    
    LoldbandValues[i] =LbandValues[i];    //save the current band value to be used in future band averaging
    RoldbandValues[i] =RbandValues[i];
  }
  
  for(int i=0; i<NUM_LEDS; i++)
  {
    if (i<=1)                     // the brightness array values will determine the brightness of each leds.
      {
        brightness[0][i] =LbandValues[0];
        brightness[1][i] =RbandValues[0];
      }
      if (i>1 && i<=3)
      {
        brightness[0][i] =LbandValues[1];
        brightness[1][i] =RbandValues[1];
      }
      if(i>3 && i<=5)
      {
        brightness[0][i] =LbandValues[2];
        brightness[1][i] =RbandValues[2];
      }
      if(i>5 && i<=7)
      {
        brightness[0][i] =LbandValues[3];
        brightness[1][i] =RbandValues[3];
      }
      if(i>7 && i<=9)
      {
        brightness[0][i] =LbandValues[4];
        brightness[1][i] =RbandValues[4];
      }
      if(i>9 && i<=11)
      {
        brightness[0][i] =LbandValues[5];
        brightness[1][i] =RbandValues[5];
      }
      if(i>11 && i<=13)
      {
        brightness[0][i] =LbandValues[6];
        brightness[1][i] =RbandValues[6];
      }
      if(i>13 && i<=15)
      {
        brightness[0][i] =LbandValues[7];
        brightness[1][i] =RbandValues[7];
      }
      if(i>15 && i<=17)
      {
        brightness[0][i] =LbandValues[8];
        brightness[1][i] =RbandValues[8];
      }
      if(i>17 && i<=19)
      {
        brightness[0][i] =LbandValues[9];
        brightness[1][i] =RbandValues[9];
      }
      if(i>19 && i<=21)
      {
        brightness[0][i] =LbandValues[10];
        brightness[1][i] =RbandValues[10];
      }
      if(i>21 && i<=23)
      {
        brightness[0][i] =LbandValues[11];
        brightness[1][i] =RbandValues[11];
      }
      if(i>23 && i<=25)
      {
        brightness[0][i] =LbandValues[12];
        brightness[1][i] =RbandValues[12];
      }
      if(i>25 && i<=27)
      {
        brightness[0][i] =LbandValues[13];
        brightness[1][i] =RbandValues[13];
      }
      if(i>27 && i<=29)
      {
        brightness[0][i] =LbandValues[14];
        brightness[1][i] =RbandValues[14];
      }

  }
  
  switch (mode_num)                                       //choose modes
  {
    case 0:
    if(palette_flag ==true)
    {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =false;
    }
      palette_flag = false;
      default_rainbow();
      break;
    case 1:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =false;
      }
      palette_flag =false;
      TWICE();
      break;
     case 2:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =false;
      }
      palette_flag =false;
      Ocean();
      break;
     case 3:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =false;
      }
      palette_flag =false;
      Poison();
      break;
     case 4:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =true;
      }
      palette_flag =false;
      Temp();
      break;
     case 5:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =true;
      }
      palette_flag =false;
      Temp_amp();
      break;
     case 6:
      if(palette_flag==true)
      {
      EEPROM.put(1,mode_num);
      EEPROM.commit();
      band_flag =true;
      }
      palette_flag =false;
      Rainbow_amp();
      break;
      break;
  }
   
}

void default_rainbow ()                                                           // the functions that are used to generate the patterns 
{
  for(int i=0; i< NUM_LEDS; i++)
    {
      leds[1][i] =ColorFromPalette(Rainbow_pal, hue+i*8,brightness[1][i],LINEARBLEND);
      leds[0][i] =ColorFromPalette(Rainbow_pal, hue+i*8,brightness[0][i],LINEARBLEND);
      hue = inoise8(15*beatsin8(5,10,50),15*beatsin8(10,20,40),millis()/10);
    } 
    hue++; 
      FastLED.show();
}

void TWICE()
{
  for(int i=0; i<NUM_LEDS; i++)
  {
    leds[1][i] =ColorFromPalette(TWICE_pal, hue+i*8,brightness[1][i],LINEARBLEND);
    leds[0][i] =ColorFromPalette(TWICE_pal, hue+i*8,brightness[0][i],LINEARBLEND);
    hue = inoise8(i*beatsin8(5,10,50),i*beatsin8(10,20,40),millis()/7);
  }
  hue++;
  FastLED.show();
}

void Ocean()
{
   for(int i=0; i<NUM_LEDS; i++)
  {
    leds[1][i] =ColorFromPalette(Ocean_pal, hue,brightness[1][i],NOBLEND);
    leds[0][i] =ColorFromPalette(Ocean_pal, hue,brightness[0][i],NOBLEND);
    hue = inoise8(i*beatsin8(5,10,50),i*beatsin8(10,20,40),millis()/7);
  }
  hue++;
  FastLED.show();
}

void Poison()
{
   for(int i=0; i<NUM_LEDS; i++)
  {
    leds[1][i] =ColorFromPalette(Poison_pal, hue,brightness[1][i],NOBLEND);
    leds[0][i] =ColorFromPalette(Poison_pal, hue,brightness[0][i],NOBLEND);
    hue = inoise8(i*beatsin8(5,10,50),i*beatsin8(10,20,40),millis()/7);
  }
  hue++;
  FastLED.show();
}

void Temp ()
{
   for(int i=0; i<NUM_LEDS; i++)
  {
    leds[1][i] =ColorFromPalette(Temp_pal, 0+brightness[1][i]-basebrightness,brightness[1][i]/4,LINEARBLEND);
    leds[0][i] =ColorFromPalette(Temp_pal, 0+brightness[0][i]-basebrightness,brightness[0][i]/4,LINEARBLEND);
  }
  FastLED.show();
}

void Temp_amp ()
{
  for(int i=0; i<11; i++)
  {
    average[0] += (LbandValues[i]-basebrightness);
    average[1] += (RbandValues[i]-basebrightness);
  }
  average[0] =average[0]/50-6;
  average[1] =average[1]/50-6;

  average[0] =(2*average[0]+old_average[0])/3;
  average[1] =(2*average[1]+old_average[1])/3;

  if (average[0] >=29)
  {
    average[0] =29;
  }
  if(average[1] >=29)
  {
    average[1] =29;
  }
  fill_palette(leds[0],NUM_LEDS, hue,8,Temp_pal,10,LINEARBLEND);
  fill_palette(leds[1],NUM_LEDS, hue,8,Temp_pal,10,LINEARBLEND);
  fill_palette(leds[0],average[0], hue,8,Temp_pal,50,LINEARBLEND);
  fill_palette(leds[1],average[1], hue,8,Temp_pal,50,LINEARBLEND);
  
  old_average[0] =average[0];
  old_average[1] =average[1];

  if(peak_amp[0]<= average[0])
  {
  peak_amp[0] =average[0];
  }
  if(peak_amp[1]<= average[1])
  {
  peak_amp[1] =average[1];
  }
  if(peak_amp[0]>=0)
    {
  leds[0][peak_amp[0]] =ColorFromPalette(Temp_pal, hue+peak_amp[0]*8,50,LINEARBLEND);
    }
   if(peak_amp[1]>=0)
   {
     leds[1][peak_amp[1]] =ColorFromPalette(Temp_pal, hue+peak_amp[0]*8,50,LINEARBLEND);
   }
   hue++;
  EVERY_N_MILLISECONDS(100)
  {
    peak_amp[0]-=1;
    peak_amp[1]-=1; 
  }
  FastLED.show();
}

void Rainbow_amp ()
{
  for(int i=0; i<11; i++)
  {
    average[0] += (LbandValues[i]-basebrightness);
    average[1] += (RbandValues[i]-basebrightness);
  }
  average[0] =average[0]/50-6;
  average[1] =average[1]/50-6;

  average[0] =(2*average[0]+old_average[0])/3;
  average[1] =(2*average[1]+old_average[1])/3;
  
  if (average[0] >=29)
  {
    average[0] =29;
  }
  if(average[1] >=29)
  {
    average[1] =29;
  }
  fill_palette(leds[0],NUM_LEDS, hue,8,Rainbow_pal,10,LINEARBLEND);
  fill_palette(leds[1],NUM_LEDS, hue,8,Rainbow_pal,10,LINEARBLEND);
  fill_palette(leds[0],average[0], hue,8,Rainbow_pal,50,LINEARBLEND);
  fill_palette(leds[1],average[1], hue,8,Rainbow_pal,50,LINEARBLEND);

  
  
  old_average[0] =average[0];
  old_average[1] =average[1];

  if(peak_amp[0]<= average[0])
  {
  peak_amp[0] =average[0];
  }
  if(peak_amp[1]<= average[1])
  {
  peak_amp[1] =average[1];
  }
  if(peak_amp[0]>=0)
    {
  leds[0][peak_amp[0]] =ColorFromPalette(Rainbow_pal, hue +peak_amp[0]*8,50,LINEARBLEND);
    }
   if(peak_amp[1]>=0)
   {
     leds[1][peak_amp[1]] =ColorFromPalette(Rainbow_pal, hue +peak_amp[1]*8,50,LINEARBLEND);
   }
   hue++;
  EVERY_N_MILLISECONDS(100)
  {
    peak_amp[0]-=1;
    peak_amp[1]-=1; 
  }
  FastLED.show();
}

Credits

chef_jeff69

chef_jeff69

1 project • 0 followers

Comments