From 06326b0cfab1fbb9c5756322d25b3bc412259ffa Mon Sep 17 00:00:00 2001 From: Will Tatam Date: Sat, 12 Sep 2026 09:47:36 +0100 Subject: [PATCH] Add audioreactive color palettes (Ratio, Hue, Spectrum) Port the getCRGBForBand palette generation logic from the upstream WLED usermods/audioreactive fork, adding an opt-in 'add-palettes' setting that registers three audio-reactive color palettes with the core palette system and refreshes them each loop. --- audio_reactive.h | 125 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/audio_reactive.h b/audio_reactive.h index 040c3ca6..672cbdf0 100644 --- a/audio_reactive.h +++ b/audio_reactive.h @@ -145,6 +145,7 @@ static bool audioSyncBroadcast = false; // if true, use subnet static bool udpSyncConnected = false; // UDP connection status -> true if connected to UDP sync #define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !! +#define MAX_PALETTES 3 // number of audioreactive palettes provided by this usermod // audioreactive variables #ifdef ARDUINO_ARCH_ESP32 @@ -1205,6 +1206,8 @@ class AudioReactive : public Usermod { bool enabled = false; #endif bool initDone = false; + bool addPalettes = false; + int8_t palettes = 0; // variables for UDP sound sync WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!) @@ -1254,8 +1257,16 @@ class AudioReactive : public Usermod { static const char _digitalmic[]; static const char UDP_SYNC_HEADER[]; static const char UDP_SYNC_HEADER_v1[]; + static const char _addPalettes[]; + static const char _palName0[]; + static const char _palName1[]; + static const char _palName2[]; // private methods + void removeAudioPalettes(void); + void createAudioPalettes(void); + CRGB getCRGBForBand(int x, int pal); + void fillAudioPalettes(void); //////////////////// // Debug support // @@ -2150,6 +2161,7 @@ class AudioReactive : public Usermod { receivedFormat = 0; delay(100); if (enabled) connectUDPSoundSync(); + if (enabled && addPalettes) createAudioPalettes(); initDone = true; DEBUGSR_PRINT(F("AR: init done, enabled = ")); DEBUGSR_PRINTLN(enabled ? F("true.") : F("false.")); @@ -2456,6 +2468,8 @@ class AudioReactive : public Usermod { lastTime = millis(); } #endif + + fillAudioPalettes(); } #if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH) @@ -2800,6 +2814,11 @@ class AudioReactive : public Usermod { if (usermod[FPSTR(_enabled)].is()) { enabled = usermod[FPSTR(_enabled)].as(); if (prevEnabled != enabled) onUpdateBegin(!enabled); + if (addPalettes) { + // add/remove custom/audioreactive palettes + if (prevEnabled && !enabled) removeAudioPalettes(); + if (!prevEnabled && enabled) createAudioPalettes(); + } } #ifdef ARDUINO_ARCH_ESP32 if (usermod[FPSTR(_inputLvl)].is()) { @@ -2809,6 +2828,13 @@ class AudioReactive : public Usermod { } } + void onStateChange(uint8_t callMode) { + if (initDone && enabled && addPalettes && palettes==0) { + // if palettes were removed during JSON call re-add them + createAudioPalettes(); + } + } + /* * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. @@ -2848,6 +2874,7 @@ class AudioReactive : public Usermod { void addToConfig(JsonObject& root) override { JsonObject top = root.createNestedObject(FPSTR(_name)); top[FPSTR(_enabled)] = enabled; + top[FPSTR(_addPalettes)] = addPalettes; #ifdef ARDUINO_ARCH_ESP32 #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); @@ -2920,6 +2947,7 @@ class AudioReactive : public Usermod { bool readFromConfig(JsonObject& root) override { JsonObject top = root[FPSTR(_name)]; bool configComplete = !top.isNull(); + bool oldAddPalettes = addPalettes; #ifdef ARDUINO_ARCH_ESP32 // remember previous values @@ -2932,6 +2960,7 @@ class AudioReactive : public Usermod { #endif configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); + configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes); #ifdef ARDUINO_ARCH_ESP32 #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); @@ -2985,6 +3014,14 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top["sync"][F("check_sequence")], audioSyncSequence); configComplete &= getJsonValue(top["sync"][F("broadcast")], audioSyncBroadcast); +#ifdef ARDUINO_ARCH_ESP32 + if (initDone) { + // add/remove custom/audioreactive palettes + if ((oldAddPalettes && !addPalettes) || (oldAddPalettes && !enabled)) removeAudioPalettes(); + if ((addPalettes && !oldAddPalettes && enabled) || (addPalettes && !oldEnabled && enabled)) createAudioPalettes(); + } +#endif + // WLEDMM notify user when a reboot is necessary #ifdef ARDUINO_ARCH_ESP32 if (initDone) { @@ -3289,6 +3326,90 @@ class AudioReactive : public Usermod { } }; +void AudioReactive::removeAudioPalettes(void) { + DEBUG_PRINTLN(F("Removing audio palettes.")); + palettes -= (int8_t)removeUsermodPalettes(_name); + if (palettes < 0) palettes = 0; // safeguard +} + +void AudioReactive::createAudioPalettes(void) { + if (palettes) return; + DEBUG_PRINTLN(F("Adding audio palettes.")); + static const char *const palNames[MAX_PALETTES] PROGMEM = {_palName0, _palName1, _palName2}; + for (int i=0; i