// -------------------------------- // -- VU Meter - Scott's version -- // -------------------------------- #include #include #include # define LEFT_OUT_PIN 6 // Left channel data out pin to LEDs [6] # define RIGHT_OUT_PIN 5 // Right channel data out pin to LEDs [5] # define LEFT_IN_PIN A5 // Left aux in signal [A5] # define RIGHT_IN_PIN A4 // Right aux in signal [A4] # define BRIGHTNESS_PIN A2 // Brightness potentiometer in [A2] # define SENSITIVITY_PIN A1 // Sensitivity potentiometer in [A1] # define BTN_PIN 3 // Push button on this pin [3] # define DEBOUNCE_MS 20 // Number of ms to debounce the button [20] # define SWITCH_PIN 4 // Kippschalter zur Loopauswahl # define LONG_PRESS 500 // Number of ms to hold the button to count as long press [500] # define N_PIXELS 63 // Number of pixels in each string [18] # define MAX_MILLIAMPS 500 // Maximum current to draw [500] # define BRIGHTNESS 128 int AMBILIGHTBRIGHTNESS = 75; # define COLOR_ORDER GRB // Colour order of LED strip [GRB] # define LED_TYPE WS2812B // LED string type [WS2812B] # define DC_OFFSET 0 // DC offset in aux signal [0] # define NOISE 0 // Noise/hum/interference in aux signal [0] # define SAMPLES 64 // Length of buffer for dynamic level adjustment [64] # define TOP (N_PIXELS + 2) // Allow dot to go slightly off scale [(N_PIXELS + 2)] # define PEAK_FALL 20 // Rate of peak falling dot [20] # define N_PIXELS_HALF (N_PIXELS / 2) # define PATTERN_TIME 10 // Seconds to show eaach pattern on auto [10] # define STEREO false // If true, L&R channels are independent. If false, both L&R outputs display same data from L audio channel [true] uint8_t volCountLeft = 0; // Frame counter for storing past volume data uint16_t volLeft[SAMPLES]; // Collection of prior volume samples uint16_t lvlLeft = 0; // Current "dampened" audio level uint16_t minLvlAvgLeft = 0; // For dynamic adjustment of graph low & high uint16_t maxLvlAvgLeft = 512; uint8_t volCountRight = 0; // Frame counter for storing past volume data uint16_t volRight[SAMPLES]; // Collection of prior volume samples uint16_t lvlRight = 0; // Current "dampened" audio level uint16_t minLvlAvgRight = 0; // For dynamic adjustment of graph low & high uint16_t maxLvlAvgRight = 512; CRGB ledsLeft[N_PIXELS]; CRGB ledsRight[N_PIXELS]; uint8_t myhue = 0; void ambilight (); void vu4(bool is_centered, uint8_t channel); void vu5(bool is_centered, uint8_t channel); void vu6(bool is_centered, uint8_t channel); void vu7(bool show_background); void vu8(); void vu9(); void vu10(); void balls(); void fire(); void juggle(); void ripple(boolean show_background); void sinelon(); void rainbow(uint8_t rate); // -------------------- // --- Button Stuff --- // -------------------- uint8_t state = 0; int buttonPushCounter = 0; bool autoChangeVisuals = false; Button modeBtn(BTN_PIN, DEBOUNCE_MS); void incrementButtonPushCounter() { buttonPushCounter = (buttonPushCounter + 1) %16; EEPROM.write(1, buttonPushCounter); } int sensitivity = 0; void setup() { delay(1000); // power-up safety delay analogReference(DEFAULT); FastLED.addLeds < LED_TYPE, LEFT_OUT_PIN, COLOR_ORDER > (ledsLeft, N_PIXELS).setCorrection(TypicalLEDStrip); FastLED.addLeds < LED_TYPE, RIGHT_OUT_PIN, COLOR_ORDER > (ledsRight, N_PIXELS).setCorrection(TypicalLEDStrip); FastLED.setMaxPowerInVoltsAndMilliamps(5, MAX_MILLIAMPS); modeBtn.begin(); Serial.begin(57600); buttonPushCounter = 0; buttonPushCounter = (int)EEPROM.read(1); // load previous setting Serial.print("Starting pattern "); Serial.println(buttonPushCounter); pinMode(SWITCH_PIN,INPUT); } //---------------------------------------------------------------------------------------------- // loop0 just for ambilight, retrolight, vintage, ... void loop0 () { // Read button modeBtn.read(); switch (state) { case 0: if (modeBtn.wasReleased()) { Serial.print("Short press, pattern "); Serial.println(buttonPushCounter); incrementButtonPushCounter(); autoChangeVisuals = false; } else if (modeBtn.pressedFor(LONG_PRESS)) state = 1; break; case 1: if (modeBtn.wasReleased()) { state = 0; Serial.print("Long press, auto, pattern "); Serial.println(buttonPushCounter); autoChangeVisuals = true; } break; } // Switch pattern if on auto if(autoChangeVisuals){ EVERY_N_SECONDS(PATTERN_TIME) { incrementButtonPushCounter(); Serial.print("Auto, pattern "); Serial.println(buttonPushCounter); } } // Run selected pattern switch (buttonPushCounter) { case 0: red_dots(); break; case 1: ambilight (); break; case 2: red_dots (); break; case 3: break; case 4: break; case 5: break; case 6: break; case 7: break; case 8: break; case 9: break; case 10: break; case 11: break; case 12: break; case 13: break; case 14: break; case 15: break; } sensitivity = analogRead(SENSITIVITY_PIN); FastLED.setBrightness(map(analogRead(BRIGHTNESS_PIN),0,1023,0,255)); } //---------------------------------------------------------------------------------------------- // loop1: original vu meter void loop1() { // Read button modeBtn.read(); switch (state) { case 0: if (modeBtn.wasReleased()) { Serial.print("Short press, pattern "); Serial.println(buttonPushCounter); incrementButtonPushCounter(); autoChangeVisuals = false; } else if (modeBtn.pressedFor(LONG_PRESS)) state = 1; break; case 1: if (modeBtn.wasReleased()) { state = 0; Serial.print("Long press, auto, pattern "); Serial.println(buttonPushCounter); autoChangeVisuals = true; } break; } // Switch pattern if on auto if(autoChangeVisuals){ EVERY_N_SECONDS(PATTERN_TIME) { incrementButtonPushCounter(); Serial.print("Auto, pattern "); Serial.println(buttonPushCounter); } } // Run selected pattern switch (buttonPushCounter) { case 0: vu4(false, 0); if (STEREO) vu4(false, 1); else copyLeftToRight(); break; case 1: vu4(true, 0); if (STEREO) vu4(true, 1); else copyLeftToRight(); break; case 2: vu5(false, 0); if (STEREO) vu5(false, 1); else copyLeftToRight(); break; case 3: vu5(true, 0); if (STEREO) vu5(true, 1); else copyLeftToRight(); break; case 4: vu6(false, 0); if (STEREO) vu6(false, 1); else copyLeftToRight(); break; case 5: vu7(true); copyLeftToRight(); break; case 6: vu8(); copyLeftToRight(); break; case 7: vu9(); break; case 8: vu10(); break; case 9: vu7(false); copyLeftToRight(); break; case 10: sinelon(); break; case 11: balls(); break; case 12: juggle(); break; case 13: fire(); break; case 14: ripple(true); break; case 15: rainbow(10); break; } sensitivity = analogRead(SENSITIVITY_PIN); FastLED.setBrightness(map(analogRead(BRIGHTNESS_PIN),0,1023,0,255)); } //---------------------------------------------------------------------------------------------- //main loop void loop () { if (digitalRead(SWITCH_PIN)==0) loop0(); if (digitalRead(SWITCH_PIN)==1) loop1(); } // ------------------ // -- VU functions -- // ------------------ uint16_t auxReading(uint8_t channel) { uint16_t height = 0; if(channel == 0) { int n = analogRead(LEFT_IN_PIN); // Raw reading from left line in n = abs(n - 512 - DC_OFFSET); // Center on zero n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum lvlLeft = ((lvlLeft * 15) + n) >> 4; // "Dampened" reading else looks twitchy (>>3 is divide by 32) volLeft[volCountLeft] = n * ((float)(1023 - sensitivity) / 512); volCountLeft = (volCountLeft + 1) % SAMPLES; // Calculate bar height based on dynamic min/max levels (fixed point): height = TOP * (lvlLeft - minLvlAvgLeft) / (long)(maxLvlAvgLeft - minLvlAvgLeft); } else { int n = analogRead(RIGHT_IN_PIN); // Raw reading from mic n = abs(n - 512 - DC_OFFSET); // Center on zero n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum lvlRight = ((lvlRight * 15) + n) >> 4; // "Dampened" reading (else looks twitchy) volRight[volCountRight] = n * ((float)(1023 - sensitivity) / 512); // Save sample for dynamic leveling volCountRight = (volCountRight +1 ) % SAMPLES; // Calculate bar height based on dynamic min/max levels (fixed point): height = TOP * (lvlRight - minLvlAvgRight) / (long)(maxLvlAvgRight - minLvlAvgRight); } height = min(height, TOP); return height; } void copyLeftToRight() { for (uint8_t i = 0; i < N_PIXELS; i++) { ledsRight[i] = ledsLeft[i]; } } /* * Function for dropping the peak */ uint8_t peakLeft, peakRight; void dropPeak(uint8_t channel) { static uint8_t dotCountLeft, dotCountRight; if(channel == 0) { if(++dotCountLeft >= PEAK_FALL) { //fall rate if(peakLeft > 0) peakLeft--; dotCountLeft = 0; } } else { if(++dotCountRight >= PEAK_FALL) { //fall rate if(peakRight > 0) peakRight--; dotCountRight = 0; } } } /* * Function for averaging the sample readings */ void averageReadings(uint8_t channel) { uint16_t minLvl, maxLvl; // minLvl and maxLvl indicate the volume range over prior frames, used // for vertically scaling the output graph (so it looks interesting // regardless of volume level). If they're too close together though // (e.g. at very low volume levels) the graph becomes super coarse // and 'jumpy'...so keep some minimum distance between them (this // also lets the graph go to zero when no sound is playing): if(channel == 0) { minLvl = maxLvl = volLeft[0]; for (int i = 1; i < SAMPLES; i++) { if (volLeft[i] < minLvl) minLvl = volLeft[i]; else if (volLeft[i] > maxLvl) maxLvl = volLeft[i]; } if ((maxLvl - minLvl) < TOP) maxLvl = 10; minLvlAvgLeft = (minLvlAvgLeft * 63 + minLvl) >> 6; // Dampen min/max levels maxLvlAvgLeft = (maxLvlAvgLeft * 63 + maxLvl) >> 6; // (fake rolling average) } else { minLvl = maxLvl = volRight[0]; for (int i = 1; i < SAMPLES; i++) { if (volRight[i] < minLvl) minLvl = volRight[i]; else if (volRight[i] > maxLvl) maxLvl = volRight[i]; } if ((maxLvl - minLvl) < TOP) maxLvl = 10; minLvlAvgRight = (minLvlAvgRight * 63 + minLvl) >> 6; // Dampen min/max levels maxLvlAvgRight = (maxLvlAvgRight * 63 + maxLvl) >> 6; // (fake rolling average) } }