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