Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: Help required: Spectrum Analyser on single LED strip

  1. #1
    Junior Member
    Join Date
    Oct 2018
    Posts
    9

    Help required: Spectrum Analyser on single LED strip

    Hi All,

    Complete novice here. Have bought a Teensy 3.5 and strip of 150 WS2812b LEDs, along with adafruit Electret microphone with amplifier and adaptive gain. The thought was to produce a spectrum analyser where:
    - the LED strip is split (within the code) into sections to represent different frequency bands
    - the colour of the LEDs would be set based on the amplitude of the signal sampled within that frequency band
    My thought was to have the middle of the strip representing low frequency, with the ends of the strip indicating higher frequency. So there would be symmetry of colours around the centre of the strip.

    Using the SpectrumAnalyserBasic example I've hacked some code together to try and do what I require, however I'm not seeing any seperation of the frequency bands in my light strip. They respond to music, but all of the lights change colour together, rather than in sections.

    They seem to be responding to higher frequency sounds in the music (hi-hats or snares) also - at the very least to some feature of the music.

    The code I've put together is below. At the moment I can't work out whether the issue I'm seeing is due to the code, or the hardware setup I have.
    Note: I think that I can probably use an array/pointer construct or similar to simplify my code greatly however I'm not quite there yet in my abilities so have stuck with what I have below to try and get it working.

    Questions:
    - Am I using the fft libraries correctly to get the seperation of bands? The frequency bands covered are the same as the example - do they cover a wide enough band to provide separate bands within music?
    - Is my mistake using a microphone with adaptive gain? Could this be "levelling out" amplitudes at different frequencies?
    - Is the issue with how I'm trying to map hue values to the FFT results? I'm not convinced with this aspect of the code. Looking at the serial monitor the values are different in the different bins but I'm not seeing the different colours in the different sections of the strip, which makes me think I've got something fundamentally wrong.

    All pointers willingly received, and thanks in advance.

    Code:
    // Let's see what I can cobble together
    
    #include <Audio.h>
    #include <Wire.h>
    #include <math.h>
    #include <WS2812Serial.h>
    #define USE_WS2812SERIAL
    #include <FastLED.h>
    
    // How many leds in the strip
    #define NUM_LEDS 150
    
    // Sort out number of leds per band. 16 bands, display low frequencies in the middle of LED strip then higher at either end
    // 15 segments | middle segment | 15 segments
    // 4 x 30 = 120, makes middle segment 30 LEDs, each other segment 4 LEDs
    // LED 0-3: HF1_1 segment
    // LED 4-7: HF2_1 segment
    // LED 8-11: HF3_1 segment
    // LED 12-15: HF4_1 segment
    // LED 16-19: HF5_1 segment
    // LED 20-23: MF1_1 segment
    // LED 24-27: MF2_1 segment
    // LED 28-31: MF3_1 segment
    // LED 32-35: MF4_1 segment
    // LED 36-39: MF5_1 segment
    // LED 40-43: LF1_1 segment
    // LED 44-47: LF2_1 segment
    // LED 48-51: LF3_1 segment
    // LED 52-55: LF4_1 segment
    // LED 56-59: LF5_1 segment
    // LED 60-89: LF segment
    // LED 90-93: LF5_2 segment
    // LED 94-97: LF4_2 segment
    // LED 98-101: LF3_2 segment
    // LED 102-105: LF2_2 segment
    // LED 106-109: LF1_2 segment
    // LED 110-113: MF5_2 segment
    // LED 114-117: MF4_2 segment
    // LED 118-121: MF3_2 segment
    // LED 122-125: MF2_2 segment
    // LED 126-129: MF1_2 segment
    // LED 130-133: HF5_2 segment
    // LED 134-137: HF4_2 segment
    // LED 138-141: HF3_2 segment
    // LED 142-145: HF2_2 segment
    // LED 146-149: HF1_2 segment
    
    // LED data pin
    #define DATA_PIN 1
    
    // Define the array of leds
    CRGB leds[NUM_LEDS];
    
    // Create audio components
    // Microphone analog input to Teensy
    AudioInputAnalog ADC1(A9);
    // FFT component
    AudioAnalyzeFFT1024 myFFT;
    // Connect live input - is this needed?
    AudioConnection patchCord1(ADC1, 0, myFFT, 0);
    
    // The scale sets how much sound is needed in each frequency range to
    // show change colours. 161 colour scale used (hue values red to blue).  Higher numbers are more sensitive.
    float scale = 10000.0;
    
    // An array to hold the 16 frequency bands
    float level[16];
    
    // This array holds the on-screen levels.  When the signal drops quickly,
    // these are used to lower the on-screen level 1 bar per update, which
    // looks more pleasing to corresponds to human sound perception.
    int   shown[16];
    
    void setup() {
      // Setup serial for WS2812Serial
      Serial.begin(57600);
      Serial.println("resetting");
      LEDS.addLeds<WS2812SERIAL,DATA_PIN,RGB>(leds,NUM_LEDS);
      LEDS.setBrightness(40);
      
      // Setup audio memory
      AudioMemory(12);
      // Configure FFT window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      
    }
    
    void loop() {
    //  static uint8_t hue =0;
      if (myFFT.available()) {
        // read the 512 FFT frequencies into 16 levels
        // music is heard in octaves, but the FFT data
        // is linear, so for the higher octaves, read
        // many FFT bins together.
     //   level[0] =  myFFT.read(0); Reads the average power across spectrum so ignore
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
      
    for (int i=1; i<16; i++) {
     //     Serial.println(level[i]);
    
          // TODO: conversion from FFT data to display colours should be
          // exponentially scaled.  But how keep it a simple example?
          int val = level[i] * scale;
          if (val > 255) val = 255;
    
          if (val >= shown[i]) {
            shown[i] = val;
          } else {
            if (shown[i] > 0) shown[i] = shown[i] - 1;
            val = shown[i];
          }
          val = val-160;
          Serial.println(val);
          // CHSV: Hue values 0-255 blue back to (near) blue again (should be red but isn't?; Saturation 0-255 how rich vs pale; Value 0-255 how bright vs dim
          // So if map hue 0-160 (red to blue) to val then can set colour based on frequency content
          // set saturation and value to 255 both for maximum colour/brightness      
    
          if (i=1) {
            for (int k=56; k<94; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=2) {
            for (int k=52; k<56; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=94; k<98; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=3) {
            for (int k=48; k<52; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=98; k<102; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=4) {
            for (int k=44; k<48; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=102; k<106; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=5) {
            for (int k=40; k<44; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=106; k<110; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=6) {
            for (int k=36; k<40; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=110; k<114; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=7) {
            for (int k=32; k<36; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=114; k<118; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=8) {
            for (int k=28; k<32; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=118; k<122; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=9) {
            for (int k=24; k<28; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=122; k<126; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=10) {
            for (int k=20; k<24; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=126; k<130; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=11) {
            for (int k=16; k<20; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=130; k<134; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=12) {
            for (int k=12; k<16; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=134; k<138; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=13) {
            for (int k=8; k<12; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=138; k<142; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=14) {
            for (int k=4; k<8; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=142; k<146; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          if (i=15) {
            for (int k=0; k<4; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
            for (int k=146; k<150; k++) {
              leds[k] = CHSV(val, 255, 255);
            }
          }
          // show the leds
          FastLED.show();
    
          //Serial.print(shown[i]);
    //      Serial.print(" ");
    
          }
        }
    
        
    }

  2. #2
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    Having slept on it, a bit more detail. Microphone is powered by 3.3v from teensy, with 60dB gain, and output connected directly to A9 on teensy. The values it displays (level[i]) are all very low (x.xx), hence the big scaling factor being used.

    I think something is wrong there - I'd expect higher values but don't know if I should, before I try and do any scaling to colour values. Is it worth powering it from 5v instead? What else can I try?

    I might play the the basic spectrum analyser example properly to see what spread of values I get, then work out scaling from that. But would appreciate a sanity check that I'm not doing something daft in the first place :-)

  3. #3
    Senior Member
    Join Date
    Apr 2013
    Posts
    1,808
    Really boring test of the microphone is to use a scope if you have one, or a looped analog read if not to confirm that it does actually produce a steady signal of half/Vin when in silence and look for max and min values when playing sound into it.

    The level meter example using the RMS function may also be informative for confirming your mic is sensibly capturing audio, and is not clipping massivly when subject to your target audio.

  4. #4
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    Thanks GremlinWrangler.

    Looped analog read shows a stream of values (380 seems to be no sound) that increases with sound.

    Using the adafruit sound level example (https://learn.adafruit.com/adafruit-...g-sound-levels) gives a voltage reading that increases with sound volume, and is at about 0.02 in "silence" (or as silent as being next to a laptop is).

    So I think the microphone is sensibly capturing audio and not clipping massively.

    One difference in these tests compared to my code above is I'm just using a straight analog pin input, rather than the audio library. But I'm hoping that it isn't in how I have implemented that that is causing a problem.

  5. #5
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    Taking the FFT example and modifying it to provide the same 16 bands as trying using in my spectrum analyser code allowed me to capture values returned by the fft function by displaying them on the serial monitor. I then added a scaling factor so that I could return values that were nearer a spread of 0-255, so I could use these as hue values.

    This now means I have code where the values variable val returned for each frequency bin is spread across the different hue values, and I've satisfied myself that the values in different bins for a given piece of music are sufficiently different that different colours should be shown.

    However, updating my spectrum analyser code with this scaling factor I'm still seeing the whole strip change colour together, rather than sections. So looks like the issue is in the code where I'm trying to address sections of the LEDs.

    Has anyone spotted something I haven't yet? It looks like I must have a fundamental flaw somewhere.

    In case it helps anyone else, my editted fft example code to display the values from each of the 16 frequency bins in the serial monitor is given here:
    Code:
    // FFT Test
    //
    // Compute a 1024 point Fast Fourier Transform (spectrum analysis)
    // on audio connected to the Left Line-In pin.  By changing code,
    // a synthetic sine wave can be input instead.
    //
    // modified from the FFT example to determine value
    // within 16 frequency bands returned by FFT
    // and to test scaling approaches
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // Create the Audio components.  These should be created in the
    // order data flows, inputs/sources -> processing -> outputs
    //
    AudioInputAnalog  ADC1(A8);                //analog mic in
    AudioSynthWaveformSine sinewave;
    AudioAnalyzeFFT1024    myFFT;
    
    // Connect either the live input or synthesized sine wave
    AudioConnection patchCord1(ADC1, myFFT);
    //AudioConnection patchCord1(sinewave, myFFT);
    
    // An array to hold the 16 frequency bands
    float level[16];
    
    // Scaling factor to get different values
    float scale = 10*255/0.5;
    
    void setup() {
      // Audio connections require memory to work.  For more
      // detailed information, see the MemoryAndCpuUsage example
      AudioMemory(12);
    
      // Configure the window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      //myFFT.windowFunction(NULL);
    
      // Create a synthetic sine wave, for testing
      // To use this, edit the connections above
      sinewave.amplitude(0.8);
      sinewave.frequency(1034.007);
    }
    
    void loop() {
      float n;
      int i;
    
      if (myFFT.available()) {
        // each time new FFT data is available
        // put it into the 16 frequency bands
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
        // print the outputs of the bands to the Arduino Serial Monitor
        Serial.print("FFT: ");
        for (i=1; i<16; i++) {
          n = level[i];
          n = myFFT.read(i);
          int val = n * scale;
          if (n >= 0.01) {
            Serial.print(val);
            // Serial.print(n);
            Serial.print(" ");
          } else {
            Serial.print("  -  "); // don't print "0.00"
          }
        }
        Serial.println();
      }
    }

  6. #6
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    Taking the FFT example and modifying it to provide the same 16 bands as trying using in my spectrum analyser code allowed me to capture values returned by the fft function by displaying them on the serial monitor. I then added a scaling factor so that I could return values that were nearer a spread of 0-255, so I could use these as hue values.

    This now means I have code where the values variable val returned for each frequency bin is spread across the different hue values, and I've satisfied myself that the values in different bins for a given piece of music are sufficiently different that different colours should be shown.

    However, updating my spectrum analyser code with this scaling factor I'm still seeing the whole strip change colour together, rather than sections. So looks like the issue is in the code where I'm trying to address sections of the LEDs.

    Has anyone spotted something I haven't yet? It looks like I must have a fundamental flaw somewhere.

    In case it helps anyone else, my editted fft example code to display the values from each of the 16 frequency bins in the serial monitor is given here:
    Code:
    // FFT Test
    //
    // Compute a 1024 point Fast Fourier Transform (spectrum analysis)
    // on audio connected to the Left Line-In pin.  By changing code,
    // a synthetic sine wave can be input instead.
    //
    // modified from the FFT example to determine value
    // within 16 frequency bands returned by FFT
    // and to test scaling approaches
    
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    
    // Create the Audio components.  These should be created in the
    // order data flows, inputs/sources -> processing -> outputs
    //
    AudioInputAnalog  ADC1(A8);                //analog mic in
    AudioSynthWaveformSine sinewave;
    AudioAnalyzeFFT1024    myFFT;
    
    // Connect either the live input or synthesized sine wave
    AudioConnection patchCord1(ADC1, myFFT);
    //AudioConnection patchCord1(sinewave, myFFT);
    
    // An array to hold the 16 frequency bands
    float level[16];
    
    // Scaling factor to get different values
    float scale = 10*255/0.5;
    
    void setup() {
      // Audio connections require memory to work.  For more
      // detailed information, see the MemoryAndCpuUsage example
      AudioMemory(12);
    
      // Configure the window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      //myFFT.windowFunction(NULL);
    
      // Create a synthetic sine wave, for testing
      // To use this, edit the connections above
      sinewave.amplitude(0.8);
      sinewave.frequency(1034.007);
    }
    
    void loop() {
      float n;
      int i;
    
      if (myFFT.available()) {
        // each time new FFT data is available
        // put it into the 16 frequency bands
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
        // print the outputs of the bands to the Arduino Serial Monitor
        Serial.print("FFT: ");
        for (i=1; i<16; i++) {
          n = level[i];
          n = myFFT.read(i);
          int val = n * scale;
          if (n >= 0.01) {
            Serial.print(val);
            // Serial.print(n);
            Serial.print(" ");
          } else {
            Serial.print("  -  "); // don't print "0.00"
          }
        }
        Serial.println();
      }
    }

  7. #7
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    As a simple test I modified my code so that it just addresses the first 16 LEDs, so one for each frequency bin.

    This works as I was hoping - each LED being assigned a colour based on frequency content in the matching frequency bin!

    So the problem is with the part of the code where I'm trying to address specific LEDs along the strip. But I can't see what it is. Could it be a timing issue? Am I just trying to do something that can't be done?!

    Code for just 16 LEDs below if it helps comparisons:
    Code:
    // Let's see what I can cobble together
    
    #include <Audio.h>
    #include <Wire.h>
    #include <math.h>
    #include <WS2812Serial.h>
    #define USE_WS2812SERIAL
    #include <FastLED.h>
    
    // How many leds in the strip
    #define NUM_LEDS 16
    
    // Just have 1 LED for each frequency bin
    
    // LED data pin
    #define DATA_PIN 1
    
    // Define the array of leds
    CRGB leds[NUM_LEDS];
    
    // Create audio components
    // Microphone analog input to Teensy
    AudioInputAnalog ADC1(A8);
    // FFT component
    AudioAnalyzeFFT1024 myFFT;
    // Connect live input - is this needed?
    AudioConnection patchCord1(ADC1, myFFT);
    
    // The scale sets how much sound is needed in each frequency range to
    // show change colours. 161 colour scale used (hue values red to blue).  Higher numbers are more sensitive.
    float scale = 10*255/0.5;
    
    // An array to hold the 16 frequency bands
    float level[16];
    
    // This array holds the on-screen levels.  When the signal drops quickly,
    // these are used to lower the on-screen level 1 bar per update, which
    // looks more pleasing to corresponds to human sound perception.
    int   shown[16];
    
    void setup() {
      // Setup serial for WS2812Serial
      Serial.begin(57600);
      Serial.println("resetting");
      LEDS.addLeds<WS2812SERIAL,DATA_PIN,RGB>(leds,NUM_LEDS);
      LEDS.setBrightness(40);
      
      // Setup audio memory
      AudioMemory(12);
      // Configure FFT window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      
    }
    
    void loop() {
    //  static uint8_t hue =0;
      if (myFFT.available()) {
        // read the 512 FFT frequencies into 16 levels
        // music is heard in octaves, but the FFT data
        // is linear, so for the higher octaves, read
        // many FFT bins together.
     //   level[0] =  myFFT.read(0); Reads the average power across spectrum so ignore
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
      
    for (int i=1; i<16; i++) {
          Serial.println(level[i]);
    
          // TODO: conversion from FFT data to display colours should be
          // exponentially scaled.  But how keep it a simple example?
          int val = level[i] * scale;
          Serial.println(val);
          val = val-160;
          if (val > 255) val = 255;
    
          if (val >= shown[i]) {
            shown[i] = val;
          } else {
            if (shown[i] > 0) shown[i] = shown[i] - 1;
            val = shown[i];
          }
     //     Serial.println(val);
          // CHSV: Hue values 0-255 blue back to (near) blue again (should be red but isn't?; Saturation 0-255 how rich vs pale; Value 0-255 how bright vs dim
          // So if map hue 0-160 (red to blue) to val then can set colour based on frequency content
          // set saturation and value to 255 both for maximum colour/brightness      
          
           leds[i-1] = CHSV(val, 255, 255);
          // show the leds
          FastLED.show();
    
          //Serial.print(shown[i]);
    //      Serial.print(" ");
    
          }
        }
    
        
    }

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,511
    When happen if "val" becomes negative?

    Code:
          val = val - 160;

  9. #9
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    I believe CHSV will treat negative Val as 0. However there is potential for it to mess up the shown logic so will assign negative Val to 0 before that. Good point.

    However it does seem to work in the 16 leds example.

    Having played around with the 16 led example I could assign values to multiple leds using some logic on the i values when I called CHSV, however if I tried to do this by calling CHSV within a for loop (like in my other code) I got odd behaviour.

    So I think I will look at re-structuring my code to put the CHSV call in a simple loop running from 0 to num_leds.

  10. #10
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    Wahoo! Success. It looks like the issue was having the assignment to the LEDs in a nested loop. No idea why but sorted my code out better and it works.

    Probably still need to play around with the scaling for sensitivity (perhaps using a potentiometer) but it works! Thanks for the help. Code below if it helps anyone else.
    Code:
    // Let's see what I can cobble together
    
    #include <Audio.h>
    #include <Wire.h>
    #include <math.h>
    #include <WS2812Serial.h>
    #define USE_WS2812SERIAL
    #include <FastLED.h>
    
    // How many leds in the strip
    #define NUM_LEDS 150
    
    // LED data pin
    #define DATA_PIN 1
    
    // Define the array of leds
    CRGB leds[NUM_LEDS];
    
    // Create audio components
    // Microphone analog input to Teensy
    AudioInputAnalog ADC1(A8);
    // FFT component
    AudioAnalyzeFFT1024 myFFT;
    // Connect live input - is this needed?
    AudioConnection patchCord1(ADC1, myFFT);
    
    // The scale sets how much sound is needed in each frequency range to
    // show change colours. 161 colour scale used (hue values red to blue).  Higher numbers are more sensitive.
    float scale = 10*255/0.5;
    
    // An array to hold the 16 frequency band fft outputs
    float level[16];
    
    //An array to hold the 16 frequency band fft outputs converted into hue values
    float value[16];
    
    // Define array to map frequency bins to LED position on strip
    // 10 in middle for lowest frequency (1), then go up in groups of five to the ends for highest frequency (15)
    int FREQ_LED[150]={15,15,15,15,15,14,14,14,14,14,13,13,13,13,13,12,12,12,12,12,11,11,11,11,11,
                       10,10,10,10,10,9,9,9,9,9,8,8,8,8,8,7,7,7,7,7,6,6,6,6,6,5,5,5,5,5,4,4,4,4,4,
                       3,3,3,3,3,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,
                       5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,
                       12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15};
    
    // Define array to hold hue values for each LED on strip
    float LED_VALUES[NUM_LEDS];
    
    // This array holds the on-screen levels.  When the signal drops quickly,
    // these are used to lower the on-screen level 1 bar per update, which
    // looks more pleasing to corresponds to human sound perception.
    int   shown[16];
    
    void setup() {
      // Setup serial for WS2812Serial
      Serial.begin(57600);
      Serial.println("resetting");
      LEDS.addLeds<WS2812SERIAL,DATA_PIN,RGB>(leds,NUM_LEDS);
      LEDS.setBrightness(40);
      
      // Setup audio memory
      AudioMemory(12);
      // Configure FFT window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      
    }
    
    void loop() {
    //  static uint8_t hue =0;
      if (myFFT.available()) {
        // read the 512 FFT frequencies into 16 levels
        // music is heard in octaves, but the FFT data
        // is linear, so for the higher octaves, read
        // many FFT bins together.
     //   level[0] =  myFFT.read(0); Reads the average power across spectrum so ignore
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
      
      for (int i=1; i<16; i++) {
        //  Serial.println(level[i]);
    
          // TODO: conversion from FFT data to display colours should be
          // exponentially scaled.  But how keep it a simple example?
          value[i] = level[i] * scale;
        //  Serial.println(value[i]);
          value[i] = value[i]-160;
          if (value[i] < 0) value[i] = 0; 
          
          if (value[i] > 255) value[i] = 255;
    
          if (value[i] >= shown[i]) {
            shown[i] = value[i];
          } else {
            if (shown[i] > 0) shown[i] = shown[i] - 1;
            value[i] = shown[i];
          }
      }
      for (int i=0; i<NUM_LEDS; i++) {
        LED_VALUES[i] = value[FREQ_LED[i]];
      }
      for (int i=0; i<NUM_LEDS; i++) {
        leds[i] = CHSV(LED_VALUES[i], 255, 255);
      }
      // Show the LEDs
      FastLED.show();
      }
    }

  11. #11
    Junior Member
    Join Date
    Oct 2018
    Posts
    9
    So a few more things I've worked out, that have helped me get it working properly:
    Quote Originally Posted by PaulStoffregen View Post
    When happen if "val" becomes negative?

    Code:
          val = val - 160;
    The hue values are on a "wheel" from 0-255, where 0 and 255 are essentially the same colour. So if val = -160 then it treats it as if it was 255-160, and you get the corresponding colour. So it cycles through. So I had that completely wrong, which I suspect was the reason for your question ;-)

    However what I have found is that my LEDs do not respond to the hue values as I was expecting from https://github.com/FastLED/FastLED/wiki/Pixel-reference - namely 0 isn't red, but is blue. In addition, the yellow is very weak and thin in the wheel, which makes me think it's using the spectrum wheel rather than the rainbow one; though that's not apparently what the code should do! So I've either got something configured wrong somewhere or the individual chips on the strip are making it do something funny.

    As it's a wheel realised mapping fft outputs across the full extent (255) of the wheel wasn't sensible, as then both minimum and maximum would be the same colour. Having sorted this so that it maps from 0-192 (blue to aqua having gone through the other colours on the way) it looks ace.

    The mapping of fft values to hue values I still don't really understand, and I'm not sure how consistent it is. From what I can work out the fft for multiple frequency bins is an average of each frequency, not a sum? I'm suspicious that the adaptive gain on the microphone wasn't a wise choice as this might cause issues over time, but we shall see.

    But, for a first project, I'm pretty happy with how quickly I've got it working and have learnt a lot, so all good. Not sure the wife agrees mind, but hopefully the daughter will appreciate it at her party tomorrow.

    Code:
    // Let's see what I can cobble together
    
    #include <Audio.h>
    #include <Wire.h>
    #include <math.h>
    #include <WS2812Serial.h>
    #define USE_WS2812SERIAL
    #include <FastLED.h>
    
    // How many leds in the strip
    #define NUM_LEDS 150
    
    // LED data pin
    #define DATA_PIN 1
    
    // Define the array of leds
    CRGB leds[NUM_LEDS];
    
    // Create audio components
    // Microphone analog input to Teensy
    AudioInputAnalog ADC1(A8);
    // FFT component
    AudioAnalyzeFFT1024 myFFT;
    // Connect live input - is this needed?
    AudioConnection patchCord1(ADC1, myFFT);
    
    // The scale sets how much sound is needed in each frequency range to
    // show change colours. Trying to use a 192 colour scale (hue values blue to aqua for these LEDs).  Higher numbers are more sensitive.
    float scale = 192/0.1;
    
    // An array to hold the 16 frequency band fft outputs
    float level[16];
    
    //An array to hold the 16 frequency band fft outputs converted into hue values
    float value[16];
    
    // Define array to map frequency bins to LED position on strip
    // 10 in middle for lowest frequency (1), then go up in groups of five to the ends for highest frequency (15)
    int FREQ_LED[150]={15,15,15,15,15,14,14,14,14,14,13,13,13,13,13,12,12,12,12,12,11,11,11,11,11,
                       10,10,10,10,10,9,9,9,9,9,8,8,8,8,8,7,7,7,7,7,6,6,6,6,6,5,5,5,5,5,4,4,4,4,4,
                       3,3,3,3,3,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,
                       5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11,
                       12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,15,15,15,15,15};
    
    // Define array to hold hue values for each LED on strip
    float LED_VALUES[NUM_LEDS];
    
    // This array holds the on-screen levels.  When the signal drops quickly,
    // these are used to lower the on-screen level 1 bar per update, which
    // looks more pleasing to corresponds to human sound perception.
    int   shown[16];
    
    void setup() {
      // Setup serial for WS2812Serial
      Serial.begin(57600);
      Serial.println("resetting");
      LEDS.addLeds<WS2812SERIAL,DATA_PIN,RGB>(leds,NUM_LEDS);
      LEDS.setBrightness(50);
      
      // Setup audio memory
      AudioMemory(12);
      // Configure FFT window algorithm to use
      myFFT.windowFunction(AudioWindowHanning1024);
      
    }
    
    void loop() {
    //  static uint8_t hue =0;
      if (myFFT.available()) {
        // read the 512 FFT frequencies into 16 levels
        // music is heard in octaves, but the FFT data
        // is linear, so for the higher octaves, read
        // many FFT bins together.
     //   level[0] =  myFFT.read(0); Reads the average power across spectrum so ignore
        level[1] =  myFFT.read(1);
        level[2] =  myFFT.read(2, 3);
        level[3] =  myFFT.read(4, 6);
        level[4] =  myFFT.read(7, 10);
        level[5] =  myFFT.read(11, 15);
        level[6] =  myFFT.read(16, 22);
        level[7] =  myFFT.read(23, 32);
        level[8] =  myFFT.read(33, 46);
        level[9] =  myFFT.read(47, 66);
        level[10] = myFFT.read(67, 93);
        level[11] = myFFT.read(94, 131);
        level[12] = myFFT.read(132, 184);
        level[13] = myFFT.read(185, 257);
        level[14] = myFFT.read(258, 359);
        level[15] = myFFT.read(360, 511);
      
      for (int i=1; i<16; i++) {
        //  Serial.println(level[i]);
    
          // TODO: conversion from FFT data to display colours should be
          // exponentially scaled.  But how keep it a simple example?
          value[i] = level[i] * scale;
        //  Serial.println(value[i]);
    
          if (value[i] >= shown[i]) {
            shown[i] = value[i];
          } else {
            if (shown[i] > 0) shown[i] = shown[i] - 2;
            value[i] = shown[i];
          }
      }
      for (int i=0; i<NUM_LEDS; i++) {
        LED_VALUES[i] = value[FREQ_LED[i]];
        leds[i] = CHSV(LED_VALUES[i], 255, 255); 
      }
      
      // Show the LEDs
      FastLED.show();
      }
    }
    Last edited by wurls80; 11-03-2018 at 12:08 AM. Reason: typos & added code

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •