diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ca36c44049..39b9eac4b6 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1211,7 +1211,7 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" /* * Fireworks function. */ -uint16_t mode_fireworks() { +static uint16_t mode_fireworks_core(bool useaudio) { if (SEGLEN == 1) return mode_static(); const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); const uint16_t height = SEGMENT.virtualHeight(); @@ -1227,17 +1227,44 @@ uint16_t mode_fireworks() { bool valid1 = (SEGENV.aux0 < width*height); bool valid2 = (SEGENV.aux1 < width*height); uint32_t sv1 = 0, sv2 = 0; + + // WLEDMM begin + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + useaudio = false; // no audio - fallback to standard behaviour (don't use soundSim) + } + bool addPixels = true; // false -> inhibit new pixels in silence + unsigned myIntensity = 129 - (SEGMENT.intensity >> 1); // make parameter explicit, so we can work with it + int soundColor = -1; // -1 = random color; 0..255 = use as palette index + + if (useaudio) { + float volumeSmth = *(float*) um_data->u_data[0]; + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + if ((volumeSmth > 1.0f) && (FFT_MajorPeak > 60.0f)) { // we have sound - select color based on major frequency + float musicIndex = logf(FFT_MajorPeak); // log scaling of peak freq + soundColor = mapf(musicIndex, 4.6f, 9.06f, 0, 255); // pick color from frequency (4.6 = ln(100), 9.06 = ln(8600)) + soundColor = constrain(soundColor, 0, 255); // remove over-shoot + if (samplePeak > 0) myIntensity -= myIntensity / 4; // increase effect intensity at peaks + } else { // silence -> fade away + valid1 = valid2 = false; // do not copy last pixels + addPixels = false; // don't add new pixels + } + } + // WLEDMM end + if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width) : SEGMENT.getPixelColor(SEGENV.aux1); if (!SEGENV.step) SEGMENT.blur(16); if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux0%width, SEGENV.aux0/width, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(SEGENV.aux1%width, SEGENV.aux1/width, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur + if (addPixels) // WLEDMM for (int i=0; i> 1)) == 0) { + if (random8(myIntensity) == 0) { // WLEDMM uint16_t index = random16(width*height); uint16_t j = index % width, k = index / width; - uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); + uint32_t col = SEGMENT.color_from_palette((soundColor > 0) ? soundColor + random8(24) : random8(), false, false, 0); // WLEDMM if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(j, k, col); else SEGMENT.setPixelColor(index, col); SEGENV.aux1 = SEGENV.aux0; // old spark @@ -1246,8 +1273,12 @@ uint16_t mode_fireworks() { } return FRAMETIME; } + +uint16_t mode_fireworks(void) { return mode_fireworks_core(false); } static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;!;12;ix=192,pal=11"; +uint16_t mode_fireworks_audio(void) { return mode_fireworks_core(true); } +static const char _data_FX_MODE_FIREWORKS_AR[] PROGMEM = "🎉 audio Fireworks@,Frequency;!,!;!;12;ix=192,pal=11"; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h uint16_t mode_rain() { @@ -3157,7 +3188,7 @@ typedef struct Spark { * POPCORN * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h */ -uint16_t mode_popcorn(void) { +static uint16_t mode_popcorn_core(bool useaudio) { if (SEGLEN == 1) return mode_static(); //allocate segment data uint16_t strips = SEGMENT.nrOfVStrips(); @@ -3169,20 +3200,44 @@ uint16_t mode_popcorn(void) { bool hasCol2 = SEGCOLOR(2); if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); + // WLEDMM init um_data + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // no audio - fallback to standard behaviour + useaudio = false; + um_data = simulateSound(SEGMENT.soundSim); // dummy + } + struct virtualStrip { - static void runStrip(uint16_t stripNr, Spark* popcorn) { + static void runStrip(uint16_t stripNr, Spark* popcorn, bool useaudio, um_data_t *um_data) { // WLEDMM added useaudio and um_data float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s gravity *= SEGLEN; uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; if (numPopcorn == 0) numPopcorn = 1; + // WLEDMM audioreactive vars + float volumeSmth = *(float*) um_data->u_data[0]; + int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; + uint8_t samplePeak = *(uint8_t*) um_data->u_data[3]; for(int i = 0; i < numPopcorn; i++) { if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position popcorn[i].pos += popcorn[i].vel; popcorn[i].vel += gravity; } else { // if kernel is inactive, randomly pop it - if (random8() < 2) { // POP!!! + bool doPopCorn = false; // WLEDMM allows to inhibit new pops + // WLEDMM begin + if (useaudio) { + if ( (volumeSmth > 1.0f) // no pops in silence + &&((samplePeak > 0) || (volumeRaw > 128)) // try to pop at onsets (our peek detector still sucks) + &&(random8() < 4) ) // stay somewhat random + doPopCorn = true; + } else { + if (random8() < 2) doPopCorn = true; // default POP!!! + } + // WLEDMM end + + if (doPopCorn) { // POP!!! popcorn[i].pos = 0.01f; uint16_t peakHeight = 128 + random8(128); //0-255 @@ -3210,12 +3265,16 @@ uint16_t mode_popcorn(void) { }; for (int stripNr=0; stripNru_data[0]; + int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; + uint8_t samplePeak = *(uint8_t*) um_data->u_data[3]; + for (int j = 0; j < numStars; j++) { // speed to adjust chance of a burst, max is nearly always. - if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) + bool doNewStar = random8((144-(SEGMENT.speed >> 1))) == 0; // WLEDMM original spawning trigger + // WLEDMM begin + if (useaudio) { + doNewStar = false; + int burstplus = (volumeSmth > 159)? 128:0; // high volume -> more stars + if (volumeRaw <= 56) burstplus = -64; // low volume -> fewer stars + int birthrate = (144-(SEGMENT.speed >> 1)) - burstplus; + birthrate = constrain(birthrate, 0, 144); + if ( (volumeSmth > 1.0f) // no bursts in silence + && ((samplePeak > 0) || (volumeRaw > 48)) // try to burst with sound + && (random8(birthrate) == 0) ) // original random rate + doNewStar = true; + } + // WLEDMM end + + if (doNewStar && stars[j].birth == 0) // WLEDMM { // Pick a random color and location. uint16_t startPos = (SEGLEN > 1) ? random16(SEGLEN-1) : 0; @@ -3364,7 +3449,7 @@ uint16_t mode_starburst(void) { stars[j].color = CRGB(SEGMENT.color_wheel(random8())); stars[j].pos = startPos; - stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; + stars[j].vel = maxSpeed * (float)(random8())/255.0f * multiplier; stars[j].birth = it; stars[j].last = it; // more fragments means larger burst effect @@ -3442,8 +3527,12 @@ uint16_t mode_starburst(void) { return FRAMETIME; } #undef STARBURST_MAX_FRAG + +uint16_t mode_starburst(void) { return mode_starburst_core(false); } static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments,,,,,Overlay;,!;!;;pal=11,m12=0"; +uint16_t mode_starburst_audio(void) { return mode_starburst_core(true); } +static const char _data_FX_MODE_STARBURST_AR[] PROGMEM = "🔨 audio Fw Starburst@Chance,Fragments,,,,,Overlay;,!;!;;pal=11,m12=0"; /* * Exploding fireworks effect @@ -8336,6 +8425,11 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_WAVESINS, &mode_wavesins, _data_FX_MODE_WAVESINS); addEffect(FX_MODE_ROCKTAVES, &mode_rocktaves, _data_FX_MODE_ROCKTAVES); + // --- WLEDSR experimental 1D audio enhanced + addEffect(FX_MODE_POPCORN_AR, &mode_popcorn_audio, _data_FX_MODE_POPCORN_AR); + addEffect(FX_MODE_STARBURST_AR, &mode_starburst_audio, _data_FX_MODE_STARBURST_AR); + addEffect(FX_MODE_FIREWORKS_AR, &mode_fireworks_audio, _data_FX_MODE_FIREWORKS_AR); + // --- 2D effects --- #ifndef WLED_DISABLE_2D addEffect(FX_MODE_2DSPACESHIPS, &mode_2Dspaceships, _data_FX_MODE_2DSPACESHIPS); diff --git a/wled00/FX.h b/wled00/FX.h index 3b41238793..e9043e60b5 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -336,7 +336,15 @@ void strip_wait_until_idle(String whoCalledMe); // WLEDMM implemented in FX_fcn. #define FX_MODE_ARTIFX 187 //WLEDMM ARTIFX #define FX_MODE_PARTYJERK 188 -#define MODE_COUNT 189 +// Experimental Audioresponsive modes from WLED-SR +// #define FX_MODE_3DSphereMove 189 // experimental WLED-SR "cube" mode +#define FX_MODE_POPCORN_AR 190 // WLED-SR audioreactive popcorn +// #define FX_MODE_MULTI_COMET_AR 191 // WLED-SR audioreactive multi-comet +#define FX_MODE_STARBURST_AR 192 // WLED-SR audioreactive fireworks starburst +// #define FX_MODE_PALETTE_AR 193 // WLED-SR audioreactive palette +#define FX_MODE_FIREWORKS_AR 194 // WLED-SR audioreactive fireworks 1D + +#define MODE_COUNT 195 typedef enum mapping1D2D { M12_Pixels = 0, diff --git a/wled00/wled.h b/wled00/wled.h index 1b14c82249..5c74d605bd 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2312290 +#define VERSION 2312310 // WLEDMM - you can check for this define in usermods, to only enabled WLEDMM specific code in the "right" fork. Its not defined in AC WLED. #define _MoonModules_WLED_