Spectrum analyzer; low/high fft.read() bins triggering at wrong frequencies

Status
Not open for further replies.

jwhendy

New member
I wasn't sure how to title this. I have an LED 16x16 matrix and I'm trying to create a simple spectrum analyzer/reactive music visualization. I thought it was working (lights more or less "bounce" with the music), but when really listening for high/low and watching where the action was, I got the sense that something wasn't right. I used audacity to send a min-max frequency (10-1000 Hz in this case) sine and square wave over 30sec and now see that the thing is pretty much haphazard.

I replicated the circuit that pops up for the adc input at the audio library design tool. I'm essentially replicating the AudioSpectrumAnalyzerBasic code, other than adding the mapping from a frequency level to some number of LEDs to light up. I'm using a teensy 3.2, and it's hard to capture, but pictures to examine my wiring can be found here.

To illustrate the behavior, here's a youtube video showing the LED strips lighting up during the test (bottom is level[0], top is level[15]). The also contains the output of the fft.read(min, max) plotted over the span of the audacity chirps; I was worried about embedding as some forums care about size... I see they're scaled anyway, so here you are:

sine:
S90lZM2.jpg

square:
gnK4zlV.jpg

This was collected via the serial monitor, and since I'm going to 1000 Hz and using AudioAnalyzeFFT1024, I did 1000/47 to see that this should only light up to ~fft.read(21), which is in my level[8]. There's activity all the way up to level[15], but plotting curves by color isn't great when using so many bins so I reduced to level[0] through level[9] for plotting.

It's hard to know how to hunt for this, but I found this post where the suggestion was made to short audio input to gnd. I did that and get all 0's.

I'm have essentially zero audio circuit knowledge, so I have no idea what to even look for! At one point I tried adding capacitors in parallel to create 20-40 uF values instead of the example circuit 10uF value, but that didn't create any noticeable differences.

Thanks for taking a look!

----------

Here's the code:

Code:
#define FASTLED_ALLOW_INTERRUPTS 0
#define ADC_INPUT_PIN   A2

// all these libraries are required for the Teensy Audio Library
#include <FastLED.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <Audio.h>

// fast led constants and initialize
#define DATA_PIN    22
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    256
CRGB leds[NUM_LEDS];

AudioInputAnalog         input(ADC_INPUT_PIN);
AudioAnalyzeFFT1024      fft;
AudioConnection          audioConnection(input, 0, fft, 0);

// An array to hold the 16 frequency bands
float level[16];

// this hardly does anything for debug;
// in real practice I create reactive scaling to watch for the
// highest frequency seen per level[i] and use this to various levels
// from always maxing out as it's hard to know per-song which
// bins will be hotter
float cur_max[16];

int hue = 0;
int delta = 3;
int bright = 255;
int sat = 255;
int fill = 0;

void setup()
{
    Serial.begin(9600);

    FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
    FastLED.setBrightness(60);

    // Audio requires memory to work.
    AudioMemory(12);

    // for debug, I set each bin to 400 and leave it
    // normally I'd replace with the max per bin seen as the audio streams
    for(int i = 0; i < 16; i++) { cur_max[i] = 400; }
}

void loop()
{
    
    if (fft.available())
    {
      
      level[0] = fft.read(2);
      level[1] = fft.read(3);
      level[2] = fft.read(4);
      level[3] = fft.read(5);
      level[4] = fft.read(6, 7);
      level[5] = fft.read(8, 9);
      level[6] = fft.read(10, 11);
      level[7] = fft.read(12, 14);
      level[8] = fft.read(15, 23);
      level[9] = fft.read(24, 38);
      level[10] = fft.read(39, 59);
      level[11] = fft.read(60, 75);
      level[12] = fft.read(75, 89);
      level[13] = fft.read(90, 111);
      level[14] = fft.read(112, 163);
      level[15] = fft.read(164, 375);
            

      // troubleshooting printing
      Serial.print(millis());

      for(int i = 0; i < 16; i ++)
      {

      	Serial.print(",");
        Serial.print(level[i]);
        
      }

      Serial.println();
                

      //// main eq section    
      for(int i = 0; i < 16; i++)
      {

        fill = constrain(map(level[i] * 1000, 30, int(cur_max[i]), 0, 16), 0, 16);
        fill_solid(leds + (i*16), fill, CHSV(i*220/16, 255, 255));

      }

      FastLED.show();
    
    } // if()
  
    EVERY_N_MILLISECONDS(10) { fadeToBlackBy(leds, 256, 25); }
        
  } // loop()
 
Last edited:
Feeding the Audio signal to the Teensy's ADC using a breadboard circuit is going to add bunch of noise I bet? Do you have a scope to look at the actual input signal to the Teensy's adc (i did see you grounded the input and got a zeros so thats good) but still I would look at the actual signal going to your adc with a scope if you have one. Also what is driving this input signal to the adc? Is there DC bias to it before it gets to your circuit? What are its min and max voltage levels? Also FastLed non spi disables interrupts for period of time to update the LEDS and Audio Library works in interrupts so that can cause a problem.
 
Feeding the Audio signal to the Teensy's ADC using a breadboard circuit is going to add bunch of noise I bet? Do you have a scope to look at the actual input signal to the Teensy's adc (i did see you grounded the input and got a zeros so thats good) but still I would look at the actual signal going to your adc with a scope if you have one. Also what is driving this input signal to the adc? Is there DC bias to it before it gets to your circuit? What are its min and max voltage levels? Also FastLed non spi disables interrupts for period of time to update the LEDS and Audio Library works in interrupts so that can cause a problem.

Hi duff! Thanks for the suggestions. To your various inquiries:

- breadboard noise: I hadn't heard of that. So using the wiring I have is prone to significant noise vs. say, if I soldered on a perf board? Unfortunately I don't own a scope...

- audio source: this is straight out of my headphone jack. Is this an issue? There's a post here that suggests straight out of a jack could be done. I know it's not ideal, but figured it could work for a non-precise visualization like this? Some others also suggest (using a raw mic feed) it's possible. Then again, like I said I have no audio circuit knowledge, so maybe the raw 3.5mm output was doomed to begin!

- Not sure on DC bias, and also not sure on min/max voltage. If I can measure this with a multimeter, is there a frequency that would help me know this best (thinking some frequencies might be more measurable)? I'm not getting anything that makes any sense holding my multimeter probes to various combinations on the headphone jack cable when I play the same wave as I tested with.

- FastLED interrupts: I figured this is what the first line of my code turns off. That said, I re-ran the test with all of the FastLED stuff turned off (just reading the teensy). It does look different! Here's the with and without FastLED code with a 10-1000 Hz square wave results:

with FastLED code (also in post above):
gnK4zlV.jpg

without FastLED code:
WiX3aoA.jpg

It's a little hard to see, but if I follow bin_8, it's the furthest right huge peak... it also goes wild from 5-12 sec, has a mini dip and then pleateaus from maybe 17-25sec, then a huge dip at 25, and the big peak (which is probably where it should trigger at almost 30sec.

That said, it's still cleaner, so it's probably also worth bringing to the FastLED folks unless there's suggestions on what to do about it.
 
Switched to fft256, and maybe that's better. While technically I can't split my audio into as many "buckets", the overall output appears a little better/sane. Plots added to original imgur album as above. Here's an example, which uses the same 10-1000 Hz square wave. My bins (level) are looking at fft.read(i) for 1 through 7 in this example. This should more than cover the 1000 Hz / 172 Hz per bin resolution.

95pG3Mc.jpg

At this point, I might just call it good for this project. It moves to music and that's about all I need to make something look cool even if I know analytically it's not "correct." I found this issue reported at the FastLED github page for teensy 3.0/3.1 and ws2811 strips; I will assume it translates to the teensy 3.2 and ws2812b LEDs. For others who find this, looks like using the APA102 LEDs is recommended by the FastLED devs as they are not interrupt sensitive. Thanks again duff for pointing me in the right direction!
 
At this point, I might just call it good for this project. It moves to music and that's about all I need to make something look cool even if I know analytically it's not "correct." I found this issue reported at the FastLED github page for teensy 3.0/3.1 and ws2811 strips; I will assume it translates to the teensy 3.2 and ws2812b LEDs. For others who find this, looks like using the APA102 LEDs is recommended by the FastLED devs as they are not interrupt sensitive.
Yes I the WS281B's are still an issue with FastLED and Audio library, I did hack pauls octo library for single pin use while freeing up the other pins used for the other strips, this will work with Audio Library. I could post that if you wanted but sounds like your ready to move to next phase, I don't blame you!
 
Status
Not open for further replies.
Back
Top