From 62e4b915e7bbfcbf2528a70c373d41c7fa4f32fb Mon Sep 17 00:00:00 2001 From: Andrew Tuline Date: Mon, 23 Mar 2020 14:42:07 -0700 Subject: [PATCH] First commit of sound reactive. --- wled00/FX.cpp | 355 ++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 63 ++++++- wled00/wled06_usermod.ino | 192 ++++++++++++++++++++- 3 files changed, 604 insertions(+), 6 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 022b4b2325..b3c7eae53d 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3167,3 +3167,358 @@ uint16_t WS2812FX::mode_heartbeat(void) { return FRAMETIME; } + + +////////////////////////////////////////////////////////////////////////////////////// +// ASOUND routines by Andrew Tuline // +////////////////////////////////////////////////////////////////////////////////////// + +uint16_t WS2812FX::mode_asound1(void) { // Pixels + + int segLoc; + CRGB color; + + fade_out(4); + + for (int i = 0; i < SEGMENT.intensity/16; i++) { + segLoc = random(SEGLEN); + color = ColorFromPalette(currentPalette, myVals[i%32]+i*4, sampleAgc, LINEARBLEND); // myVals only has 32 elements defined + setPixelColor(segLoc, color.red, color.green, color.blue); + } + +// EVERY_N_MILLIS(1000) { + +// Serial.print("SEGENV.call "); Serial.println(SEGENV.call); + Serial.print("SEGENV.next_time "); Serial.print(SEGENV.next_time); + Serial.print(" "); + Serial.print("now"); Serial.println(now); + +// Serial.print("SEGENV.step "); Serial.println(SEGENV.step); +// Serial.print("SEGENV.aux0 "); Serial.println(SEGENV.aux0); +// Serial.print("SEGENV.aux1 "); Serial.println(SEGENV.aux1); + +// Serial.print("SEGLEN "); Serial.println(SEGLEN); +// Serial.print("SEGMENT.start "); Serial.println(SEGMENT.start); +// Serial.print("SEGMENT.stop "); Serial.println(SEGMENT.stop); +// Serial.print("SEGMENT.intensity "); Serial.println(SEGMENT.intensity); +// Serial.print("SEGMENT.speed "); Serial.println(SEGMENT.speed); +// Serial.print("SEGMENT.options "); Serial.println(SEGMENT.options); +// setPixelColor(0, random8(), random8(), random8()); +// setPixelColor(29, random8(), random8(), random8()); +// } + + + return FRAMETIME; + +} // mode_asound1() + + + + +uint16_t WS2812FX::mode_asound2(void) { // Pixelwave + + EVERY_N_MILLISECONDS_I(pixTimer, SEGMENT.speed) { // Using FastLED's timer. You want to change speed? You need to . . + + pixTimer.setPeriod((256 - SEGMENT.speed) >> 2); // change it down here!!! By Andrew Tuline. + + int pixVal = sample * SEGMENT.intensity / 256; + + if (pixVal > 20) {pixVal = 255; } else {pixVal = 0;} + + CRGB color = ColorFromPalette(currentPalette, millis(), pixVal, LINEARBLEND); + setPixelColor(SEGLEN/2, color.red, color.green, color.blue); + setPixelColor(SEGLEN/2-1, color.red, color.green, color.blue); + + for (int i = SEGLEN - 1; i > SEGLEN/2; i--) { // Move to the right. + setPixelColor(i,getPixelColor(i-1)); + } + + for (int i = 0; i < SEGLEN/2; i++) { // Move to the left. + setPixelColor(i,getPixelColor(i+1)); + } + + } + + return FRAMETIME; + +} // mode_asound2() + + + + +uint16_t WS2812FX::mode_asound3(void) { // Puddle + + uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 255); + fade_out(fadeVal); + + int pos = random(SEGLEN); // Set a random starting position. + + int size = 0; + + if (sample > 0 ) { + size = sample * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume. + if (pos + size >= SEGLEN) size = SEGLEN - pos - 1; + } + + for(int i=0; i> 2); // change it down here!!! By Andrew Tuline. + + int matVal; + if (sample*3 > (255 - SEGMENT.intensity)) {matVal = 255;} else {matVal = 0;} + + CRGB color = ColorFromPalette(currentPalette, millis(), matVal, LINEARBLEND); +// CRGB color = ColorFromPalette(currentPalette, millis(), sampleAgc*2, LINEARBLEND); + setPixelColor(SEGLEN-1, color.red, color.green, color.blue); + for (int i=0; i= topLED) + topLED = tempsamp; + else if (gravityCounter % gravity == 0) + topLED--; + + if (topLED > 0) { + CRGB color = ColorFromPalette(currentPalette, millis(), 255, LINEARBLEND); + setPixelColor(topLED, color.red, color.green, color.blue); + } + + gravityCounter = (gravityCounter + 1) % gravity; + return FRAMETIME; + +} // mode_asound5() + + + + +uint16_t WS2812FX::mode_asound6(void) { // Plasma + + static int16_t thisphase = 0; // Phase of a cubicwave8. + static int16_t thatphase = 0; // Phase of the cos8. + + uint16_t thisbright; + uint16_t colorIndex; + +// fade_out(32); + + thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. + thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. + + for (int k=0; k thisbright) {thisbright = 255;} else {thisbright = 0;} + + CRGB color = ColorFromPalette(currentPalette, colorIndex, thisbright, LINEARBLEND); + setPixelColor(k, color.red, color.green, color.blue); + } + + return FRAMETIME; + +} // mode_asound6() + + + + +uint16_t WS2812FX::mode_asound7(void) { // Jugglep + + static int thistime = 20; + CRGB color; + + EVERY_N_MILLISECONDS_I(pixTimer, SEGMENT.speed) { // Using FastLED's timer. You want to change speed? You need to + + pixTimer.setPeriod((256 - SEGMENT.speed) >> 2); // change it down here!!! By Andrew Tuline. + + fade_out(224); + + for (int i= 0; i < SEGMENT.intensity/32; i++) { + color = ColorFromPalette(currentPalette, millis()/4+i*2, sampleAgc, LINEARBLEND); + setPixelColor(beatsin16(thistime+i*2,0,SEGLEN-1), color.red, color.green, color.blue); + } + } + + return FRAMETIME; + +} // mode_asound7() + + + + + +uint16_t WS2812FX::mode_asound8(void) { // FillnoiseMid + + static int xdist; + static int ydist; + + fade_out(224); + + int maxLen = sampleAvg * SEGMENT.intensity / 256; // Too sensitive. + maxLen = maxLen * SEGMENT.intensity / 256; // Reduce sensitity/length. + + if (maxLen >SEGLEN/2) maxLen = SEGLEN/2; + +// for (int i = (SEGLEN-maxLen)/2; i <(SEGLEN+maxLen+1)/2; i++) { // The louder the sound, the wider the soundbar. + for (int i = (SEGLEN/2 - maxLen); i < (SEGLEN/2+maxLen); i++) { + uint8_t index = inoise8(i*sampleAvg+xdist, ydist+i*sampleAvg); // Get a value from the noise function. I'm using both x and y axis. + CRGB color = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); + setPixelColor(i, color.red, color.green, color.blue); + } + + xdist=xdist+beatsin8(5,0,10); + ydist=ydist+beatsin8(4,0,10); + + return FRAMETIME; + +} // mode_asound8() + + + + + +uint16_t WS2812FX::mode_asound9(void) { // Fillnoise + + static int xdist; + static int ydist; + + fade_out(240); + + int maxLen = sampleAvg; + if (sample > sampleAvg) maxLen = sample-sampleAvg; + maxLen = maxLen * SEGMENT.intensity / 256; // Still a bit too sensitive. + maxLen = maxLen * SEGMENT.intensity / 256; // Reduce sensitity/length. + if (maxLen >SEGLEN) maxLen = SEGLEN; + + for (int i = 0; i < maxLen; i++) { // The louder the sound, the wider the soundbar. By Andrew Tuline. + uint8_t index = inoise8(i*sampleAvg+xdist, ydist+i*sampleAvg); // Get a value from the noise function. I'm using both x and y axis. + CRGB color = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); + setPixelColor(i, color.red, color.green, color.blue); + } + + xdist=xdist+beatsin8(5,0,10); + ydist=ydist+beatsin8(4,0,10); + + return FRAMETIME; + +} // mode_asound9() + + + + +uint16_t WS2812FX::mode_asound10(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + + return FRAMETIME; +} // mode_asound10() + + + +uint16_t WS2812FX::mode_asound11(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + return FRAMETIME; +} // mode_asound11() + + + +uint16_t WS2812FX::mode_asound12(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + return FRAMETIME; +} // mode_asound12() + + + +uint16_t WS2812FX::mode_asound13(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + return FRAMETIME; +} // mode_asound13() + + + +uint16_t WS2812FX::mode_asound14(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + return FRAMETIME; +} // mode_asound14() + + + +uint16_t WS2812FX::mode_asound15(void) { + delay(1); + CRGB color = ColorFromPalette(currentPalette, 0, 255, LINEARBLEND); + setPixelColor(0, color.red, color.green, color.blue); + return FRAMETIME; +} // mode_asound15() + + + + + +/* void lineit() { + for (int i=0; i SEGLEN/2; i--) { // Move to the right. + setPixelColor(i,getPixelColor(i-1)); + } + + for (int i = 0; i < SEGLEN/2; i++) { // Move to the left. + setPixelColor(i,getPixelColor(i+1)); + } + +} // waveit() +*/ diff --git a/wled00/FX.h b/wled00/FX.h index d24250f9bd..82be374636 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -95,7 +95,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) -#define MODE_COUNT 101 +#define MODE_COUNT 116 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -198,6 +198,31 @@ #define FX_MODE_PERCENT 98 #define FX_MODE_RIPPLE_RAINBOW 99 #define FX_MODE_HEARTBEAT 100 +#define FX_MODE_ASound1 101 +#define FX_MODE_ASound2 102 +#define FX_MODE_ASound3 103 +#define FX_MODE_ASound4 104 +#define FX_MODE_ASound5 105 +#define FX_MODE_ASound6 106 +#define FX_MODE_ASound7 107 +#define FX_MODE_ASound8 108 +#define FX_MODE_ASound9 109 +#define FX_MODE_ASound10 110 +#define FX_MODE_ASound11 111 +#define FX_MODE_ASound12 112 +#define FX_MODE_ASound13 113 +#define FX_MODE_ASound14 114 +#define FX_MODE_ASound15 115 + + +// Sound reactive external variables +extern int sample; +extern float sampleAvg; +extern bool samplePeak; +extern uint8_t myVals[32]; +extern int sampleAgc; +extern uint8_t squelch; + class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -387,6 +412,22 @@ class WS2812FX { _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; + _mode[FX_MODE_ASound1] = &WS2812FX::mode_asound1; + _mode[FX_MODE_ASound2] = &WS2812FX::mode_asound2; + _mode[FX_MODE_ASound3] = &WS2812FX::mode_asound3; + _mode[FX_MODE_ASound4] = &WS2812FX::mode_asound4; + _mode[FX_MODE_ASound5] = &WS2812FX::mode_asound5; + _mode[FX_MODE_ASound6] = &WS2812FX::mode_asound6; + _mode[FX_MODE_ASound7] = &WS2812FX::mode_asound7; + _mode[FX_MODE_ASound8] = &WS2812FX::mode_asound8; + _mode[FX_MODE_ASound9] = &WS2812FX::mode_asound9; + _mode[FX_MODE_ASound10] = &WS2812FX::mode_asound10; + _mode[FX_MODE_ASound11] = &WS2812FX::mode_asound11; + _mode[FX_MODE_ASound12] = &WS2812FX::mode_asound12; + _mode[FX_MODE_ASound13] = &WS2812FX::mode_asound13; + _mode[FX_MODE_ASound14] = &WS2812FX::mode_asound14; + _mode[FX_MODE_ASound15] = &WS2812FX::mode_asound15; + _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -571,7 +612,22 @@ class WS2812FX { mode_plasma(void), mode_percent(void), mode_ripple_rainbow(void), - mode_heartbeat(void); + mode_heartbeat(void), + mode_asound1(void), + mode_asound2(void), + mode_asound3(void), + mode_asound4(void), + mode_asound5(void), + mode_asound6(void), + mode_asound7(void), + mode_asound8(void), + mode_asound9(void), + mode_asound10(void), + mode_asound11(void), + mode_asound12(void), + mode_asound13(void), + mode_asound14(void), + mode_asound15(void); private: @@ -656,7 +712,8 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", -"Heartbeat" +"Heartbeat","ASound1","ASound2","ASound3","ASound4","ASound5","ASound6","ASound7","ASound8","ASound9", +"ASound10","ASound11","ASound12","ASound13","ASound14","ASound15" ])====="; diff --git a/wled00/wled06_usermod.ino b/wled00/wled06_usermod.ino index 010d0f3860..513d2e1a4d 100644 --- a/wled00/wled06_usermod.ino +++ b/wled00/wled06_usermod.ino @@ -5,12 +5,74 @@ * bytes 2400+ are currently ununsed, but might be used for future wled features */ +#ifndef ESP8266 +TaskHandle_t FFT_Task; +#endif + //Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) +#ifdef ESP8266 +#define MIC_PIN A0 +#else +#define MIC_PIN 36 // Changed to directly naming pin since ESP32 has multiple ADCs 8266: A0 ESP32: 36(ADC1_0) Analog port for microphone +#endif + +#define SQUELCH 10 // Our fixed squelch value. +uint8_t squelch = 10; // Anything below this is background noise, so we'll make it '0'. Can be adjusted. +int sample; // Current sample. +float sampleAvg = 0; // Smoothed Average. +float micLev = 0; // Used to convert returned value to have '0' as minimum. A leveller. +uint8_t maxVol = 11; // Reasonable value for constant volume for 'peak detector', as it won't always trigger. +bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag. + +int sampleAgc; // Our AGC sample. +float multAgc; // sample * multAgc = sampleAgc. Our multiplier. +uint8_t targetAgc = 60; // This is our setPoint at 20% of max for the adjusted output. + +long lastTime = 0; +int delayMs = 10; + +uint8_t myVals[32]; + + +#ifndef ESP8266 +#include "arduinoFFT.h" + +arduinoFFT FFT = arduinoFFT(); /* Create FFT object */ + +const uint16_t samples = 1024; //This value MUST ALWAYS be a power of 2 +const double samplingFrequency = 20480; + +unsigned int sampling_period_us; +unsigned long microseconds; + +/* +These are the input and output vectors +Input vectors receive computed results from FFT +*/ +double vReal[samples]; +double vImag[samples]; +#endif + + + //gets called once at boot. Do all initialization that doesn't depend on network here void userSetup() { - +#ifndef ESP8266 + + sampling_period_us = round(1000000*(1.0/samplingFrequency)); + +// Define the FFT Task and lock it to core 0 +xTaskCreatePinnedToCore( + FFTcode, /* Function to implement the task */ + "FFT", /* Name of the task */ + 10000, /* Stack size in words */ + NULL, /* Task input parameter */ + 1, /* Priority of the task */ + &FFT_Task, /* Task handle. */ + 0); /* Core where the task should run */ +#endif } //gets called every time WiFi is (re-)connected. Initialize own network interfaces here @@ -19,8 +81,132 @@ void userConnected() } + + + //loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ +void userLoop() { + if (millis()-lastTime > delayMs) { + lastTime = millis(); + getSample(); // Sample the microphone. + agcAvg(); // Calculated the PI adjusted value as sampleAvg. + } + myVals[millis()%32] = sampleAgc; + +} + + +void getSample() { + int16_t micIn; // Current sample starts with negative values and large values, which is why it's 16 bit signed. + static long peakTime; + + micIn = analogRead(MIC_PIN); // Poor man's analog Read. + micLev = ((micLev * 31) + micIn) / 32; // Smooth it out over the last 32 samples for automatic centering. + micIn -= micLev; // Let's center it to 0 now. + micIn = abs(micIn); // And get the absolute value of each sample. + + sample = (micIn <= squelch) ? 0 : (sample*3 + micIn) / 4; // Using a ternary operator, the resultant sample is either 0 or it's a bit smoothed out with the last sample. + sampleAvg = ((sampleAvg * 15) + sample) / 16; // Smooth it out over the last 32 samples. + + if (userVar1 == 0) samplePeak = 0; + if (sample > (sampleAvg+maxVol) && millis() > (peakTime + 100)) { // Poor man's beat detection by seeing if sample > Average + some value. + samplePeak = 1; // Then we got a peak, else we don't. Display routines need to reset the samplepeak value in case they miss the trigger. + userVar1 = samplePeak; + peakTime=millis(); + } + +} // getSample() + + + +void agcAvg() { // A simple averaging multiplier to automatically adjust sound sensitivity. + + multAgc = (sampleAvg < 1) ? targetAgc : targetAgc / sampleAvg; // Make the multiplier so that sampleAvg * multiplier = setpoint + sampleAgc = sample * multAgc; + if (sampleAgc > 255) sampleAgc = 0; + + userVar0 = sampleAvg * 4; + if (userVar0 > 255) userVar0 = 255; + +//------------ Oscilloscope output --------------------------- +// Serial.print(targetAgc); Serial.print(" "); +// Serial.print(multAgc); Serial.print(" "); +// Serial.print(sampleAgc); Serial.print(" "); + +// Serial.print(sample); Serial.print(" "); +// Serial.print(sampleAvg); Serial.print(" "); +// Serial.print(micLev); Serial.print(" "); +// Serial.print(samplePeak); Serial.print(" "); //samplePeak = 0; +// Serial.print(100); Serial.print(" "); +// Serial.print(0); Serial.print(" "); +// Serial.println(" "); +#ifndef ESP8266 // if we are on a ESP32 +// Serial.print("running on core "); // identify core +// Serial.println(xPortGetCoreID()); +#endif + +} // agcAvg() + + +#ifndef ESP8266 + +// #include "esp_task_wdt.h" + +double FFT_MajorPeak = 0; + +// FFT main code +void FFTcode( void * parameter) { + double sum, mean = 0; + + for(;;) { + delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy. + microseconds = micros(); + for(int i=0; i