Last active
June 14, 2020 15:04
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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