Help required: Spectrum Analyser on single LED strip

Status
Not open for further replies.

wurls80

Member
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(" ");

      }
    }

    
}
 
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) 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 :)
 
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.
 
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-microphone-amplifier-breakout/measuring-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.
 
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();
  }
}
 
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();
  }
}
 
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(" ");

      }
    }

    
}
 
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.
 
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();
  }
}
 
So a few more things I've worked out, that have helped me get it working properly:
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:
Getting Error after uploading

Hi, I am getting an error after uploading on serial monitor.
It says :
ADC2_CFG=00000637
resetting

Can you help with this error




So a few more things I've worked out, that have helped me get it working properly:


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();
  }
}
 
Rairo

The restting message is normal, looking at the code

Code:
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);
  
}

Not clear where that ADC message is coming from - that would be a register setting line that would not normally hit the serial terminal.

So more information may be required about what is/is not working. For the reseting message to show up the Teensy must be getting code and starting to run at least as far as that Serial.println

As a basic step have a look at
Code:
  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];
      }
  }

And remove the double backslashes commenting out the serial.print statements and see if they show up - if they do then the code is running fine and your problem is else where. If they do not show up than things are halting and confirming you can use other audo and LED examples may be useful.
 
Hi Gremlin,

I check the code after commenting out serial.print lines. It is not showing anything.
I checked program is not entering if(fft.avaialble), however if I write anything outside if(fft.avaialble) it is getting printed on serial monitor.
Can you please suggest. I checked similar problem is occurring in other codes as well. If I write anything outside the if statement, program does not enter if statement. ADC2_CFG is by default text which appear before setup funtion, it appears in codes which are running well. The problem here is program may or may not enter if statenment.


QUOTE=GremlinWrangler;255643]Rairo

The restting message is normal, looking at the code

Code:
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);
  
}

Not clear where that ADC message is coming from - that would be a register setting line that would not normally hit the serial terminal.

So more information may be required about what is/is not working. For the reseting message to show up the Teensy must be getting code and starting to run at least as far as that Serial.println

As a basic step have a look at
Code:
  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];
      }
  }

And remove the double backslashes commenting out the serial.print statements and see if they show up - if they do then the code is running fine and your problem is else where. If they do not show up than things are halting and confirming you can use other audo and LED examples may be useful.[/QUOTE]
 
So glad to see @GremlinWrangler on here, I'm afraid I've never come across that issue before so can't help!

ADC_CONFIG isn't a statement from my code but is called up in one of audio modules it uses - as above it looks like it's setting a register and that's not something I tried to Do!

@rairo in your last comment there is a typo in the text - it states if(fft.avaialble) when it should be if(fft.available) - only mention it because it is repeated. So if that is copied from your code that might explain why it never goes inside the if loop.

Good luck and sorry I can't be of more help!
 
Status
Not open for further replies.
Back
Top