Question about the spectrum analyzer example, best practises and making it smooth

Status
Not open for further replies.

harald25

Well-known member
Hello!
Yesterday I go the spectrum analyzer working with the LEDs I've installed in my roof at home. It was very cool to watch the music visually unfold above me :)

Now I have to questions:
1. How can I make the spectrum analyzer effect more smooth? I'm thinking about making a delay in how fast a collumn of pixels are 'allowed' to fall back down.
The way it is out of the box is a little too responsive. The LEDs react instantaneously to every change in the music, and if a lot of things are happening the lights are almost flickering.
In many spectrum analyzer visualisers, that come with audio players, the peak is often marked while the rest of the bar falls back down.
Is there some project you can point me to maybe where this has already been done, so I can take a peek at the code?

2. What are the best practises for wiring a mini jack connector to a Teensy? At the moment I have the the left channel (tip of the mini jack) connected to the teensy + ground.
Should I be using some resistors or condensors between the teensy and the minijack in order to get a better signal?
I'd be happy if someone could point me to an article, or similar, about this. I'd love to learn some more :)
 
Hi Harald,

it is easy to smooth the spectrum with an exponential averager, which uses two lines of code. But it´s very hard to help you if you do not post your code . . .

Have fun,

Frank
 
Hi Harald,

it is easy to smooth the spectrum with an exponential averager, which uses two lines of code. But it´s very hard to help you if you do not post your code . . .

Have fun,

Frank

Oh, sorry. I didn't post it because it's basically just the Spectrum Analyzer example that comes with FastLED or Teensyduino (I don't remember which)
But here it is:

Code:
// LED Audio Spectrum Analyzer Display
//
// Creates an impressive LED light show to music input
//   using Teensy 3.1 with the OctoWS2811 adaptor board
//   http://www.pjrc.com/store/teensy31.html
//   http://www.pjrc.com/store/octo28_adaptor.html
//
// Line Level Audio Input connects to analog pin A3
//   Recommended input circuit:
//   http://www.pjrc.com/teensy/gui/?info=AudioInputAnalog
//
// This example code is in the public domain.

#define USE_OCTOWS2811
#include <OctoWS2811.h>
#include <FastLED.h>
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

// The display size and color to use
const unsigned int matrix_width = 60;
const unsigned int matrix_height = 32;
const unsigned int myColor = 0x400020;

// These parameters adjust the vertical thresholds
const float maxLevel = 0.5;      // 1.0 = max, lower is more "sensitive"
const float dynamicRange = 40.0; // total range to display, in decibels
const float linearBlend = 0.3;   // useful range is 0 to 0.7

CRGB leds[matrix_width * matrix_height];

// Audio library objects
AudioInputAnalog         adc1(A3);       //xy=99,55
AudioAnalyzeFFT1024      fft;            //xy=265,75
AudioConnection          patchCord1(adc1, fft);


// This array holds the volume level (0 to 1.0) for each
// vertical pixel to turn on.  Computed in setup() using
// the 3 parameters above.
float thresholdVertical[matrix_height];

// This array specifies how many of the FFT frequency bin
// to use for each horizontal pixel.  Because humans hear
// in octaves and FFT bins are linear, the low frequencies
// use a small number of bins, higher frequencies use more.
int frequencyBinsHorizontal[matrix_width] = {
   1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
   2,  2,  2,  2,  2,  2,  2,  2,  2,  3,
   3,  3,  3,  3,  4,  4,  4,  4,  4,  5,
   5,  5,  6,  6,  6,  7,  7,  7,  8,  8,
   9,  9, 10, 10, 11, 12, 12, 13, 14, 15,
  15, 16, 17, 18, 19, 20, 22, 23, 24, 25
};



// Run setup once
void setup() {
  // the audio library needs to be given memory to start working
  AudioMemory(12);

  // compute the vertical thresholds before starting
  computeVerticalLevels();

  // turn on the display
  FastLED.addLeds<OCTOWS2811>(leds,(matrix_width * matrix_height) / 8);
}

// A simple xy() function to turn display matrix coordinates
// into the index numbers OctoWS2811 requires.  If your LEDs
// are arranged differently, edit this code...
unsigned int xy(unsigned int x, unsigned int y) {
  if ((y & 1) == 0) {
    // even numbered rows (0, 2, 4...) are left to right
    return y * matrix_width + x;
  } else {
    // odd numbered rows (1, 3, 5...) are right to left
    return y * matrix_width + matrix_width - 1 - x;
  }
}

// Run repetitively
void loop() {
  unsigned int x, y, freqBin;
  float level;

  if (fft.available()) {
    // freqBin counts which FFT frequency data has been used,
    // starting at low frequency
    freqBin = 0;

    for (x=0; x < matrix_width; x++) {
      // get the volume for each horizontal pixel position
      level = fft.read(freqBin, freqBin + frequencyBinsHorizontal[x] - 1);

      // uncomment to see the spectrum in Arduino's Serial Monitor
      // Serial.print(level);
      // Serial.print("  ");

      for (y=0; y < matrix_height; y++) {
        // for each vertical pixel, check if above the threshold
        // and turn the LED on or off
        if (level >= thresholdVertical[y]) {
          leds[xy(x,y)] = CRGB(myColor);
        } else {
          leds[xy(x,y)] = CRGB::Black;
        }
      }
      // increment the frequency bin count, so we display
      // low to higher frequency from left to right
      freqBin = freqBin + frequencyBinsHorizontal[x];
    }
    // after all pixels set, show them all at the same instant
    FastLED.show();
    // Serial.println();
  }
}


// Run once from setup, the compute the vertical levels
void computeVerticalLevels() {
  unsigned int y;
  float n, logLevel, linearLevel;

  for (y=0; y < matrix_height; y++) {
    n = (float)y / (float)(matrix_height - 1);
    logLevel = pow10f(n * -1.0 * (dynamicRange / 20.0));
    linearLevel = 1.0 - n;
    linearLevel = linearLevel * linearBlend;
    logLevel = logLevel * (1.0 - linearBlend);
    thresholdVertical[y] = (logLevel + linearLevel) * maxLevel;
  }
}
 
Hi Harald,

use something like this:

You need an array to hold your previous level values for each frequency bin and a smoothing coefficient:
Code:
float level_old[60];
float smoothing_coeff = 0.1; // experiment with different coefficients; --> 0.0 < smoothing_coeff < 1.0

then try to put this into your loop
Code:
for (x=0; x < matrix_width; x++) {
      // get the volume for each horizontal pixel position
      level = fft.read(freqBin, freqBin + frequencyBinsHorizontal[x] - 1);
      level = smoothing_coeff * level + (1.0 – smoothing_coeff) * level_old[x];   

// at the end of the for loop (be careful to put this at the right place ;-)):
level_old[x] = level;

Have not tested the code (maybe I am missing some other things), but it should be able to do what you intend: a smoothing of the display depending on the smoothing coefficient.
This is basically a first order IIR filter, also called exponential averager, fancy names for a simple method to take into account previous values of your FFT bin amplitudes.

Have fun with the Teensy,

Frank
 
You might also try using two coefficients, one for positive changes and another for negative change.

Code:
      float diff = level - level_old[x];
      if (diff > 0.0) {
        level = level + diff * smoothing_coeff_positive;
      } else {
        level = level + diff * smoothing_coeff_negative;
      }

You'd probably want to make smoothing_coeff_positive a larger number, close to 1.0, and smoothing_coeff_negative a small number. Then you'd get rapid change for increase, but slow change to decrease.
 
Status
Not open for further replies.
Back
Top