Skip to content

Instantly share code, notes, and snippets.

@fuzzylogiq
Last active June 14, 2020 15:04
Show Gist options
  • Save fuzzylogiq/c58fe9d448d89e6f14d8169eb517c95f to your computer and use it in GitHub Desktop.
Save fuzzylogiq/c58fe9d448d89e6f14d8169eb517c95f to your computer and use it in GitHub Desktop.
Porting the SparkFun RGB LED Music Sound Visualizer to FastLED (testing sending audio amplitude from Processing via serial)
#include <FastLED.h>
#define DATA_PIN 6
#define COLOR_ORDER GRB
#define CHIPSET WS2812B
#define NUM_LEDS 60
#define HALF_LEDS NUM_LEDS/2
#define VISUALS 6
#define PALETTES 7
#define BRIGHTNESS 255
uint8_t palette = 0;
uint8_t visual = 0;
uint8_t volume = 0;
float maxVol = 15;
float avgVol = 0;
float avgBump = 0;
bool bump = false;
uint8_t last = 0;
float avgTime = 0;
float timeBump = 0;
int8_t dotPos = 15;
uint16_t gradient = 1;
int8_t pos[NUM_LEDS] = { -2};
bool left = false;
CRGB leds[NUM_LEDS];
CRGB rgb[NUM_LEDS] = {(0, 0, 0)};
int bumpCount = 0;
// Gradient palette "quagga_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/rc/tn/quagga.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
// Size: 24 bytes of program space.
DEFINE_GRADIENT_PALETTE( quagga_gp ) {
0, 1, 9, 84,
40, 42, 24, 72,
84, 6, 58, 2,
168, 88, 169, 24,
211, 42, 24, 72,
255, 1, 9, 84
};
// Gradient palette "bhw1_07_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_07.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
// Size: 8 bytes of program space.
DEFINE_GRADIENT_PALETTE( bhw1_07_gp ) {
0, 232, 65, 1,
255, 229, 227, 1
};
// Gradient palette "bhw1_06_gp", originally from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_06.png.index.html
// converted for FastLED with gammas (2.6, 2.2, 2.5)
// Size: 16 bytes of program space.
DEFINE_GRADIENT_PALETTE( bhw1_06_gp ) {
0, 184, 1, 128,
160, 1, 193, 182,
219, 153, 227, 190,
255, 255, 255, 255
};
CRGBPalette16 myPal = quagga_gp;
void setup() {
Serial.begin(115200);
FastLED.addLeds<CHIPSET, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.show();
}
void loop() {
if (Serial.available() > 0) {
volume = Serial.read();
}
if (volume < avgVol / 2.0 || volume < 15) volume = 0;
else avgVol = (avgVol + volume) / 2.0;
if (volume > maxVol) maxVol = volume;
ChangeMe();
if (gradient > 255) {
gradient %= 256;
maxVol = (maxVol + volume) / 2.0;
}
if (volume - last > 10) avgBump = (avgBump + (volume - last)) / 2.0;
bump = (volume - last > avgBump * 0.9);
if (bump) {
bumpCount++;
Serial.println("bump!: " + String(bumpCount));
avgTime = (((millis() / 1000.0) - timeBump) + avgTime) / 2.0;
timeBump = millis() / 1000.0;
Serial.println("avgtime: " + String(avgTime));
}
Visualize();
gradient++;
last = volume;
FastLED.delay(30);
}
void Visualize() {
switch (visual) {
case 0: return Pulse();
case 1: return PalettePulse();
case 2: return Traffic();
case 3: return Snake();
case 4: return PaletteDance();
case 5: return Glitter();
case 6: return Paintball();
default: return Pulse();
}
}
void ChangeMe() {
uint8_t secondHand = (millis() / 1000) % 60;
static uint8_t lastSecond = 99;
if (lastSecond != secondHand) {
lastSecond = secondHand;
switch (secondHand) {
case 59: CycleVisual(); break;
case 29: CyclePalette(); break;
}
}
}
void CycleVisual() {
visual++;
gradient = 0;
if (visual > VISUALS) visual = 0;
if (visual == 1) memset(pos, -2, sizeof(pos));
if (visual == 2 || visual == 3) {
randomSeed(analogRead(0));
dotPos = random(NUM_LEDS);
}
maxVol = avgVol;
}
void CyclePalette() {
palette++;
if (palette > PALETTES) palette = 0;
switch (palette) {
case 0: myPal = quagga_gp; break;
case 1: myPal = bhw1_07_gp; break;
case 2: myPal = bhw1_06_gp; break;
case 3: myPal = RainbowColors_p; break;
case 4: myPal = PartyColors_p; break;
case 5: myPal = ForestColors_p; break;
case 6: myPal = LavaColors_p; break;
case 7: myPal = OceanColors_p; break;
}
maxVol = avgVol;
}
void Pulse() {
fadeall(48);
if (bump) gradient += 7;
if (volume > 0) {
CRGB col = ColorFromPalette(myPal, gradient);
int start = HALF_LEDS - (HALF_LEDS * (volume / maxVol));
int finish = HALF_LEDS + (HALF_LEDS * (volume / maxVol)) + NUM_LEDS % 2;
for (int i = start; i < finish; i++) {
float damp = sin((i - start) * PI / float(finish - start));
damp = pow(damp, 2.0);
CRGB col2 = leds[i];
CRGB color;
color.r = col.r * damp * pow(volume / maxVol, 2);
color.g = col.g * damp * pow(volume / maxVol, 2);
color.b = col.b * damp * pow(volume / maxVol, 2);
float avgCol = (color.r + color.g + color.b) / 3.0;
float avgCol2 = (col2.r + col2.g + col2.b) / 3.0;
if (avgCol > avgCol2) leds[i] = color;
}
}
FastLED.show();
}
void PalettePulse() {
fadeall(48);
if (bump) gradient += 10;
if (volume > 0) {
int start = HALF_LEDS - (HALF_LEDS * (volume / maxVol));
int finish = HALF_LEDS + (HALF_LEDS * (volume / maxVol)) + NUM_LEDS % 2;
for (int i = start; i < finish; i++) {
float damp = sin((i - start) * PI / float(finish - start));
damp = pow(damp, 2.0);
int val = 255 * (i - start) / (finish - start);
val += gradient;
CRGB col = ColorFromPalette(myPal, val);
CRGB col2 = leds[i];
CRGB color;
color.r = col.r * damp * pow(volume / maxVol, 2);
color.g = col.g * damp * pow(volume / maxVol, 2);
color.b = col.b * damp * pow(volume / maxVol, 2);
float avgCol = (color.r + color.g + color.b) / 3.0;
float avgCol2 = (col2.r + col2.g + col2.b) / 3.0;
if (avgCol > avgCol2) leds[i] = color;
}
}
FastLED.show();
}
void Traffic() {
fadeall(64);
if (bump) {
int8_t slot = 0;
for (slot; slot < sizeof(pos); slot++) {
if (pos[slot] < -1) break;
else if (slot + 1 >= sizeof(pos)) {
slot = -3;
break;
}
}
if (slot != -3) {
pos[slot] = (slot % 2 == 0) ? -1 : NUM_LEDS;
rgb[slot] = ColorFromPalette(myPal, gradient);
gradient += 10;
}
}
if (volume > 0) {
for (int i = 0; i < sizeof(pos); i++) {
if (pos[i] < -1) continue;
pos[i] += (i % 2) ? -1 : 1;
if (pos[i] >= NUM_LEDS) pos[i] = -2;
int fadeAmount = map(pow(volume / maxVol, 2.0) * 100, 0, 100, 0, 255);
leds[pos[i]] = (rgb[i]);
leds[pos[i]].nscale8_video(fadeAmount);
}
}
FastLED.show();
}
void Snake() {
if (bump) {
gradient += 4;
left = !left;
}
fadeall(4);
CRGB col = ColorFromPalette(myPal, gradient);
if (volume > 0) {
int fadeAmount = map(pow(volume / maxVol, 1.5) * 100, 0, 100, 0, 255);
leds[dotPos] = col;
leds[dotPos].nscale8_video(fadeAmount);
if (avgTime < 0.15) dotPos += (left) ? -1 : 1;
else if (avgTime >= 0.15 && avgTime < 0.5 && gradient % 2 == 0) dotPos += (left) ? -1 : 1;
else if (avgTime >= 0.5 && avgTime < 1.0 && gradient % 3 == 0) dotPos += (left) ? -1 : 1;
else if (gradient % 4 == 0) dotPos += (left) ? -1 : 1;
}
FastLED.show();
if (dotPos < 0) dotPos = NUM_LEDS - 1;
else if (dotPos >= NUM_LEDS) dotPos = 0;
}
void PaletteDance() {
if (bump) left = !left;
if (volume > avgVol) {
for (int i = 0; i < NUM_LEDS; i++) {
float sinVal = abs(sin((i + dotPos) * (PI / float(NUM_LEDS / 1.25))));
sinVal *= sinVal;
sinVal *= volume / maxVol;
unsigned int val = 256
* (float(i + map(dotPos, -1 * (NUM_LEDS - 1), NUM_LEDS - 1, 0, NUM_LEDS - 1))
/ float(NUM_LEDS))
+ (gradient);
val %= 256;
CRGB col = ColorFromPalette(myPal, val);
leds[i].r = col.r * sinVal;
leds[i].g = col.g * sinVal;
leds[i].b = col.b * sinVal;
}
dotPos += (left) ? -1 : 1;
}
else fadeall(16);
FastLED.show();
if (dotPos < 0) dotPos = NUM_LEDS - NUM_LEDS / 6;
else if (dotPos >= NUM_LEDS - NUM_LEDS / 6) dotPos = 0;
}
void Glitter() {
gradient += 4;
for (int i = 0; i < NUM_LEDS; i++) {
unsigned int val = 256.0 *
(float(i) / float(NUM_LEDS))
+ (gradient);
val %= 255;
CRGB col = ColorFromPalette(myPal, val);
leds[i].r = col.r / 6.0,
leds[i].g = col.g / 6.0,
leds[i].b = col.b / 6.0;
}
if (bump) {
randomSeed(micros());
dotPos = random(NUM_LEDS - 1);
leds[dotPos].r = 255.0 * pow(volume / maxVol, 2.0),
leds[dotPos].g = 255.0 * pow(volume / maxVol, 2.0),
leds[dotPos].b = 255.0 * pow(volume / maxVol, 2.0);
}
bleed(dotPos);
FastLED.show(); //Show the lights.
}
void Paintball() {
if ((millis() / 1000.0) - timeBump > avgTime * 2.0) fadeall(4);
bleed(dotPos);
int fadeAmount = map(pow(volume / maxVol, 2.0) * 100, 0, 100, 0, 255);
if (bump) {
randomSeed(micros());
dotPos = random(0, NUM_LEDS - 1);
CRGB dotCol = ColorFromPalette(myPal, random(0, 255));
leds[dotPos] = dotCol;
leds[dotPos].nscale8_video(fadeAmount);
blur1d(leds, NUM_LEDS, 74);
}
FastLED.show();
}
void fadeall(int amount) {
for (int i = 0; i < NUM_LEDS; i++) {
leds[i].fadeToBlackBy(amount);
}
}
void bleed(uint8_t Point) {
for (int i = 1; i < NUM_LEDS; i++) {
int sides[] = {Point - i, Point + i};
for (int i = 0; i < 2; i++) {
int point = sides[i];
if (point < NUM_LEDS - 1 && point > 1) {
leds[point].r = float((leds[point - 1].r + leds[point].r + leds[point + 1].r ) / 3.0);
leds[point].g = float((leds[point - 1].g + leds[point].g + leds[point + 1].g ) / 3.0);
leds[point].b = float((leds[point - 1].b + leds[point].b + leds[point + 1].b ) / 3.0);
}
}
}
}
import processing.serial.*;
import processing.sound.*;
Amplitude amp;
AudioIn in;
float volume;
Serial myPort;
void setup() {
amp = new Amplitude(this);
in = new AudioIn(this, 0);
in.start();
amp.input(in);
myPort = new Serial(this, Serial.list()[2], 115200);
}
void draw() {
//if (frameCount % 5 == 0) {
volume = amp.analyze();
volume = map(volume, 0.0, 1.0, 0, 255);
myPort.write(int(volume));
//myPort.write('\n');
//println(int(volume));
delay(60);
//}
String val = myPort.readString();
if (val != null) {
println(val);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment