From 6ec1b05ce2a57199820e80dbd6ab55d7e5d3d65c Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:11:39 +0100 Subject: [PATCH 01/27] refactor: move color correction into separate method --- .../usermod_v2_animartrix.h | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 34e00ee07a..c780e25ae3 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -139,29 +139,37 @@ class ANIMartRIXMod:public ANIMartRIX { } use_gamma = SEGENV.check2; setSpeedFactor(speedFactor); + // gamma correction + static inline uint32_t applyGamma24(uint32_t colIn) { + #ifdef _MoonModules_WLED_ // upstream WLED does not need gamma-correction before setPixelColor + uint8_t colR = gamma8(R(colIn)); + uint8_t colG = gamma8(G(colIn)); + uint8_t colB = gamma8(B(colIn)); + return RGBW32(colR, colG, colB, 0U); + #else + return colIn; // do nothing + #endif + } + + inline uint32_t processColor(rgb pixel) const { + uint32_t colOut; + // color conversion; +0.5f for rounding + uint8_t colR = pixel.red+0.5f; + uint8_t colG = pixel.green+0.5f; + uint8_t colB = pixel.blue+0.5f; + colOut = RGBW32(colR, colG, colB,0U); + return use_gamma ? applyGamma24(colOut) : colOut; } void setPixelColor(int x, int y, rgb pixel) override { - uint8_t colR, colG, colB; - if (use_gamma) { - colR = gamma8(pixel.red); colG = gamma8(pixel.green); colB = gamma8(pixel.blue); - } else { - colR = pixel.red; colG = pixel.green; colB = pixel.blue; - } - SEGMENT.setPixelColorXY(x, y, RGBW32(colR,colG,colB,0)); + SEGMENT.setPixelColorXY(x, y, processColor(pixel)); } void setPixelColor(int index, rgb pixel) override { - uint8_t colR, colG, colB; - if (use_gamma) { - colR = gamma8(pixel.red); colG = gamma8(pixel.green); colB = gamma8(pixel.blue); - } else { - colR = pixel.red; colG = pixel.green; colB = pixel.blue; - } // get x and y, so we can us setPixelColorXY() - faster in WLEDMM int x = index % num_x; int y = index / num_x; - SEGMENT.setPixelColorXY(x,y, RGBW32(colR,colG,colB,0)); + SEGMENT.setPixelColorXY(x,y, processColor(pixel)); } // Add any extra custom effects not part of the ANIMartRIX libary here From d57a5e6c54839edcdb5ddccbf4e8963ada6e3ccb Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:17:39 +0100 Subject: [PATCH 02/27] add brightness / contrast boost functions --- .../usermod_v2_animartrix.h | 59 +++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index c780e25ae3..afe477e9a7 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -125,7 +125,11 @@ static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob class ANIMartRIXMod:public ANIMartRIX { private: + int hueshift = 0; // static HUE shift (16bit signed); default = neutral bool use_gamma = false; + bool cycle_hue = false; + bool boost_brightness = false; + bool boost_contrast = false; public: void initEffect() { if ((SEGENV.call == 0) || (SEGMENT.virtualWidth() != num_x) || (SEGMENT.virtualHeight() != num_y)) { @@ -139,6 +143,37 @@ class ANIMartRIXMod:public ANIMartRIX { } use_gamma = SEGENV.check2; setSpeedFactor(speedFactor); + // enhance middle ranges contrast (S-Function) + static inline float enhanceContrast(float color) { + if (color < 1.0f) return 0.0f; // shortcut for black + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4) + // floating point - faster when FPU is present + float x = color / 255.0f; // normalize to [0, 1] + float y = x * x * (3.0f - 2.0f * x); // smoothstep (S-curve) for contrast + float result = y * 255.0f; // scale back to [0, 255] + return min(max(result, 0.0f), 255.0f); // clamp to allowed range + #else + // fixed point with rounding - faster when no FPU + unsigned xcol = color; + unsigned ycol1 = ((xcol * xcol * 3) +127); // first part: y1 = 255 * (x * x * 3) + unsigned ycol2 = ((xcol * xcol * 2 * xcol) +32512) / 255; // second part: y2 = 255 * (x * x * 2 * x) + if (ycol2 > ycol1) return 0.0f; // catch underflow (overflow can't happen) + else return min(255.0f, float(ycol1 - ycol2) / 255.0f); // clamp to allowed range + #endif + } + + // enhance brightness (sqrt function) + static inline uint_fast8_t enhanceBrightness(float color) { + // square root - provides gentle and jump-free enhancement of lower brightness pixels + if (color < 0.125f) return 0; // shortcut for black + if (color > 255.0f) return 255; // shortcut for max color value + // floating point: slow due to sqrtf() + // return roundf(sqrtf(color/255.0f) * 255.0f); + // fixed point: faster + uint32_t col32 = 0.5f + (255.0f * color); // = ( color / 255 ) * 65535 + return sqrt32_bw(col32); // => equal to sqrt((color / 255) * 255 + } + // gamma correction static inline uint32_t applyGamma24(uint32_t colIn) { #ifdef _MoonModules_WLED_ // upstream WLED does not need gamma-correction before setPixelColor @@ -152,12 +187,26 @@ class ANIMartRIXMod:public ANIMartRIX { } inline uint32_t processColor(rgb pixel) const { + if (boost_contrast) { + // enhance contrast - keep "float" for better color accuracy + pixel.red = enhanceContrast(pixel.red); + pixel.green = enhanceContrast(pixel.green); + pixel.blue = enhanceContrast(pixel.blue); + } uint32_t colOut; - // color conversion; +0.5f for rounding - uint8_t colR = pixel.red+0.5f; - uint8_t colG = pixel.green+0.5f; - uint8_t colB = pixel.blue+0.5f; - colOut = RGBW32(colR, colG, colB,0U); + if (boost_brightness) { + // enhance brightness, convert colors from float to integer + uint8_t colR = enhanceBrightness(pixel.red); + uint8_t colG = enhanceBrightness(pixel.green); + uint8_t colB = enhanceBrightness(pixel.blue); + colOut = RGBW32(colR, colG, colB, 0U); + } else { + // color conversion only; +0.5f for rounding + uint8_t colR = pixel.red+0.5f; + uint8_t colG = pixel.green+0.5f; + uint8_t colB = pixel.blue+0.5f; + colOut = RGBW32(colR, colG, colB,0U); + } return use_gamma ? applyGamma24(colOut) : colOut; } From df759e2cbb1134a71638f6546dcddf98e7b70f3f Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:20:31 +0100 Subject: [PATCH 03/27] import CHSV32 (based on upstream WLED) thanks @dedehai, works perfectly --- .../usermod_v2_animartrix.h | 1 + wled00/colors.h | 174 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 wled00/colors.h diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index afe477e9a7..03839fe0ca 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -1,6 +1,7 @@ #pragma once #include "wled.h" +#include "colors.h" // include CHSV32 class by @dedehai #ifdef _MoonModules_WLED_ // WLEDMM: use faster math approximations - up to 40% faster diff --git a/wled00/colors.h b/wled00/colors.h new file mode 100644 index 0000000000..e9926c77e1 --- /dev/null +++ b/wled00/colors.h @@ -0,0 +1,174 @@ +#pragma once +#ifndef WLED_COLORS_H +#define WLED_COLORS_H + +/* + * Color structs and color utility functions + */ + +#include + +#if !defined(FASTLED_VERSION) // pull in FastLED if we don't have it yet (we need the CRGB type) + #define FASTLED_INTERNAL + #define USE_GET_MILLISECOND_TIMER + #include +#endif + + +#if 0 // WLEDMM not used yet +// CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color +// use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts +// it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB +struct CRGBW { + union { + uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB) + struct { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t w; + }; + uint8_t raw[4]; // Access as an array in the order B, G, R, W + }; + + // Default constructor + inline CRGBW() __attribute__((always_inline)) = default; + + // Constructor from a 32-bit color (0xWWRRGGBB) + constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {} + + // Constructor with r, g, b, w values + constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {} + + // Constructor from CRGB + constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {} + + // Access as an array + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } + + // Assignment from 32-bit color + inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; } + + // Assignment from r, g, b, w + inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; } + + // Conversion operator to uint32_t + inline operator uint32_t() const __attribute__((always_inline)) { + return color32; + } + /* + // Conversion operator to CRGB + inline operator CRGB() const __attribute__((always_inline)) { + return CRGB(r, g, b); + } + + CRGBW& scale32 (uint8_t scaledown) // 32bit math + { + if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit + uint32_t scale = scaledown + 1; + uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue + uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green + color32 = rb | wg; + return *this; + }*/ + +}; + +#endif + + +struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions - credits @dedehai + union { + struct { + uint16_t h; // hue + uint8_t s; // saturation + uint8_t v; // value + }; + uint32_t raw; // 32bit access + }; + inline CHSV32() __attribute__((always_inline)) = default; // default constructor + + /// Allow construction from hue, saturation, and value + /// @param ih input hue + /// @param is input saturation + /// @param iv input value + inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v + : h(ih), s(is), v(iv) {} + inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v + : h((uint16_t)ih << 8), s(is), v(iv) {} + inline CHSV32(const CHSV& chsv) __attribute__((always_inline)) // constructor from CHSV + : h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {} + inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV +}; + +static inline void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0) +{ + unsigned int remainder, region, p, q, t; + unsigned int h = hsv.h; + unsigned int s = hsv.s; + unsigned int v = hsv.v; + if (s == 0) { + rgb = v << 16 | v << 8 | v; + return; + } + region = h / 10923; // 65536 / 6 = 10923 + remainder = (h - (region * 10923)) * 6; + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 16))) >> 8; + t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8; + switch (region) { + case 0: + rgb = v << 16 | t << 8 | p; break; + case 1: + rgb = q << 16 | v << 8 | p; break; + case 2: + rgb = p << 16 | v << 8 | t; break; + case 3: + rgb = p << 16 | q << 8 | v; break; + case 4: + rgb = t << 16 | p << 8 | v; break; + default: + rgb = v << 16 | p << 8 | q; break; + } +} + +static inline void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version +{ + hsv.raw = 0; + int32_t r = (rgb>>16)&0xFF; + int32_t g = (rgb>>8)&0xFF; + int32_t b = rgb&0xFF; + int32_t minval, maxval, delta; + minval = min(r, g); + minval = min(minval, b); + maxval = max(r, g); + maxval = max(maxval, b); + if (maxval == 0) return; // black + hsv.v = maxval; + delta = maxval - minval; + hsv.s = (255 * delta) / maxval; + if (hsv.s == 0) return; // gray value + if (maxval == r) hsv.h = (10923 * (g - b)) / delta; + else if (maxval == g) hsv.h = 21845 + (10923 * (b - r)) / delta; + else hsv.h = 43690 + (10923 * (r - g)) / delta; +} + +#if 0 // WLEDMM not used yet +static inline void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb + uint32_t crgb; + hsv2rgb(CHSV32(hue, sat, 255), crgb); + rgb[0] = byte((crgb) >> 16); + rgb[1] = byte((crgb) >> 8); + rgb[2] = byte(crgb); +} + +// fast scaling function for colors, performs color*scale/256 for all four channels, speed over accuracy +// note: inlining uses less code than actual function calls +static inline uint32_t fast_color_scale(const uint32_t c, const uint8_t scale) { + uint32_t rb = (((c & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; + uint32_t wg = (((c>>8) & 0x00FF00FF) * scale) & ~0x00FF00FF; + return rb | wg; +} +#endif + +#endif // WLED_COLORS_H From f4aa8440b5121c669ab1c02ae53e29ef6b1bcfa9 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:27:34 +0100 Subject: [PATCH 04/27] add HUE shift feature * convert pixel color from RGBW32 to CHSV32, * shift HUE value, * convert back to RGBW32 HUE shift is either static (slider), or continuous based on elapsed time. --- .../usermod_v2_animartrix.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 03839fe0ca..8a1d90aa66 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -144,6 +144,16 @@ class ANIMartRIXMod:public ANIMartRIX { } use_gamma = SEGENV.check2; setSpeedFactor(speedFactor); + if (cycle_hue) { + unsigned tt = strip.now; + if (SEGMENT.intensity > 128) tt = (tt * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x + else if (SEGMENT.intensity < 127) tt = (tt * 22U) / (21U + 127 - SEGMENT.intensity); // slower down to 1/6 + hueshift = (tt << 4) | (tt & 0x0F); + } else { + hueshift = (128 - SEGMENT.intensity) * 256; + } + } + // enhance middle ranges contrast (S-Function) static inline float enhanceContrast(float color) { if (color < 1.0f) return 0.0f; // shortcut for black @@ -208,6 +218,15 @@ class ANIMartRIXMod:public ANIMartRIX { uint8_t colB = pixel.blue+0.5f; colOut = RGBW32(colR, colG, colB,0U); } + + // experimental: HUE shift + if (cycle_hue || (abs(hueshift) > 255)) { // cycle HUE selected, or manual HUE at lest 1 left/right from center of slider + CHSV32 cc; + rgb2hsv(colOut, cc); + cc.h = cc.h + unsigned(hueshift); // works due to 2's complement + hsv2rgb(cc, colOut); + } + return use_gamma ? applyGamma24(colOut) : colOut; } From 00b08812a2074e7ed1a508d54ff7a6cda48b55de Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:30:48 +0100 Subject: [PATCH 05/27] add new sliders to effects * slider for HUE shift (static shift, or dynamic speed of change) * checkbox for HUE cycling (dynamic) * checkboxes for brightness and contrast boost * remove checkbox for gamma correction, need to move this into usermod global settings. --- .../usermod_v2_animartrix.h | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 8a1d90aa66..2978301794 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -70,58 +70,58 @@ //======================================================================================================================== -static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; -static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@Speed,,,,,,Gamma Correction;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; class ANIMartRIXMod:public ANIMartRIX { @@ -142,8 +142,14 @@ class ANIMartRIXMod:public ANIMartRIX { } else { speedFactor = (float) map(SEGMENT.speed, 128, 255, 10, 100) / 10.0f; } - use_gamma = SEGENV.check2; setSpeedFactor(speedFactor); + + //use_gamma = SEGENV.check1; + use_gamma = false; // ToDO: move to usermod options + cycle_hue = SEGENV.check1; + boost_brightness = SEGENV.check2; + boost_contrast = SEGENV.check3; + if (cycle_hue) { unsigned tt = strip.now; if (SEGMENT.intensity > 128) tt = (tt * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x From 10ab6ffafda2633d1a718a4d8bacfee3b7be2390 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:32:26 +0100 Subject: [PATCH 06/27] indentation fix --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 2978301794..167b00da72 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -151,7 +151,7 @@ class ANIMartRIXMod:public ANIMartRIX { boost_contrast = SEGENV.check3; if (cycle_hue) { - unsigned tt = strip.now; + unsigned tt = strip.now; if (SEGMENT.intensity > 128) tt = (tt * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x else if (SEGMENT.intensity < 127) tt = (tt * 22U) / (21U + 127 - SEGMENT.intensity); // slower down to 1/6 hueshift = (tt << 4) | (tt & 0x0F); From c8a620a86536038cd312609b6bb2d6c9949853b9 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 00:02:35 +0100 Subject: [PATCH 07/27] fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI typo: “Brighness” → “Brightness”. --- .../usermod_v2_animartrix.h | 104 +++++++++--------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 167b00da72..f9b91e5637 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -70,58 +70,58 @@ //======================================================================================================================== -static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brighness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; class ANIMartRIXMod:public ANIMartRIX { From 8cd080ec78682f7beeaac8a9291acd15a7930722 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 00:14:43 +0100 Subject: [PATCH 08/27] 64-bit intermediate to prevent uptime-dependent hue discontinuities With strip.now in milliseconds, the fast-path overflows after ~7.5 hours and the slow-path after ~54.2 hours, causing visible hue jumps in continuous uptime scenarios. => Cast the multiplication operands to uint64_t before division. --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index f9b91e5637..6862d5f61b 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -152,8 +152,8 @@ class ANIMartRIXMod:public ANIMartRIX { if (cycle_hue) { unsigned tt = strip.now; - if (SEGMENT.intensity > 128) tt = (tt * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x - else if (SEGMENT.intensity < 127) tt = (tt * 22U) / (21U + 127 - SEGMENT.intensity); // slower down to 1/6 + if (SEGMENT.intensity > 128) tt = (uint64_t(tt) * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x + else if (SEGMENT.intensity < 127) tt = (uint64_t(tt) * 22U) / (21U + 127 - SEGMENT.intensity); // slower down to 1/6 hueshift = (tt << 4) | (tt & 0x0F); } else { hueshift = (128 - SEGMENT.intensity) * 256; From 7a9fcceb17dc69da34ffca0cad4f68b7e2fc7f03 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 11:33:18 +0100 Subject: [PATCH 09/27] hueshit accuracy improvement avoid losing accuracy in hue shift calculation --- .../usermod_v2_animartrix.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 6862d5f61b..751a32e5a8 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -151,12 +151,17 @@ class ANIMartRIXMod:public ANIMartRIX { boost_contrast = SEGENV.check3; if (cycle_hue) { - unsigned tt = strip.now; - if (SEGMENT.intensity > 128) tt = (uint64_t(tt) * (31U + SEGMENT.intensity - 127)) / 32U; // faster up to 4x - else if (SEGMENT.intensity < 127) tt = (uint64_t(tt) * 22U) / (21U + 127 - SEGMENT.intensity); // slower down to 1/6 - hueshift = (tt << 4) | (tt & 0x0F); - } else { - hueshift = (128 - SEGMENT.intensity) * 256; + unsigned tt = strip.now; // use strip time as timebase - change to millis() if you see jitter or stuttering + hueshift = (tt << 4) | (tt & 0x0F); // default shift based on time, without speedup or slowdown => one cycle in 4 seconds + if (SEGMENT.intensity > 128) { + // tt = (uint64_t(tt) * (31 + SEGMENT.intensity - 127)) / 32; // => faster up to 4x (128/32) + hueshift = (uint64_t(tt) * (31 + SEGMENT.intensity - 127)) / 2; // try to preserve accuracy: time/32 * 16 => time/2 + } else if (SEGMENT.intensity < 127) { + tt = (uint64_t(tt) * 22) / (21 + 127 - SEGMENT.intensity); // => slower down to 1/7 (22/148) + hueshift = (tt << 4) | (tt & 0x0F); + } + } else { // !cycle_hue + hueshift = (128 - SEGMENT.intensity) * 256; // static HUE shift } } From 2421825dbdf10610a7dc929a3fca11f2d9dde3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 11:58:59 +0100 Subject: [PATCH 10/27] comment clarification explains rationale behind enhanceBrightness fixed-point math using integer sqrt() --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 751a32e5a8..fe6c57d719 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -192,8 +192,8 @@ class ANIMartRIXMod:public ANIMartRIX { // floating point: slow due to sqrtf() // return roundf(sqrtf(color/255.0f) * 255.0f); // fixed point: faster - uint32_t col32 = 0.5f + (255.0f * color); // = ( color / 255 ) * 65535 - return sqrt32_bw(col32); // => equal to sqrt((color / 255) * 255 + uint32_t col32 = 0.5f + (255.0f * color); // = ( color / 255 ) * 65025 ; 16bit fixed-point representation of color/255 + return sqrt32_bw(col32); // => equal to sqrt((color / 255) * 255; produces [0-255] output } // gamma correction From 4f1986018e446ee97bd296ab7f038abe43eac353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 12:50:31 +0100 Subject: [PATCH 11/27] change default use_gamma to true AnimarTrix effects were designed with hardware-level gamma correction in mind => change default to apply gamma. Note: this may create an unexpected / different look for older presets. If old behaviour is needed, disable gamma correction in UM settings (option will be added soon) --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index fe6c57d719..51d119a663 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -145,7 +145,7 @@ class ANIMartRIXMod:public ANIMartRIX { setSpeedFactor(speedFactor); //use_gamma = SEGENV.check1; - use_gamma = false; // ToDO: move to usermod options + use_gamma = true; // ToDO: move to usermod options cycle_hue = SEGENV.check1; boost_brightness = SEGENV.check2; boost_contrast = SEGENV.check3; From 4ed4e0c2ffef0b7b97946cf3cbbafc2dc638fb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:28:28 +0100 Subject: [PATCH 12/27] accuracy improvement for hue shift slowdown calculation improves accuracy by embedding "<<4" into the main multiplication --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 51d119a663..b54099eec1 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -157,8 +157,8 @@ class ANIMartRIXMod:public ANIMartRIX { // tt = (uint64_t(tt) * (31 + SEGMENT.intensity - 127)) / 32; // => faster up to 4x (128/32) hueshift = (uint64_t(tt) * (31 + SEGMENT.intensity - 127)) / 2; // try to preserve accuracy: time/32 * 16 => time/2 } else if (SEGMENT.intensity < 127) { - tt = (uint64_t(tt) * 22) / (21 + 127 - SEGMENT.intensity); // => slower down to 1/7 (22/148) - hueshift = (tt << 4) | (tt & 0x0F); + //tt = (uint64_t(tt) * 22) / (21 + 127 - SEGMENT.intensity); // => slower down to 1/7 (22/148) + hueshift = (uint64_t(tt) * (22*16)) / (21 + 127 - SEGMENT.intensity); // try to preserve accuracy, by embedding "<<4" (*16) into the main multiplication } } else { // !cycle_hue hueshift = (128 - SEGMENT.intensity) * 256; // static HUE shift From 290c87a8d609310c74cb60ae2e8d2f476bacfe65 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 14:41:31 +0100 Subject: [PATCH 13/27] refactor: extract repeated UI sliders into a single macro --- .../usermod_v2_animartrix.h | 107 +++++++++--------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index b54099eec1..0e1d7cc17d 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -69,59 +69,60 @@ #warning WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! //======================================================================================================================== - -static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; -static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0"; +#define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0" + +static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment8[] PROGMEM = "Y💡Module_Experiment8 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment7[] PROGMEM = "Y💡Module_Experiment7 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment6[] PROGMEM = "Y💡Module_Experiment6 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment5[] PROGMEM = "Y💡Module_Experiment5 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment4[] PROGMEM = "Y💡Module_Experiment4 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Zoom2[] PROGMEM = "Y💡Zoom2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment3[] PROGMEM = "Y💡Module_Experiment3 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment2[] PROGMEM = "Y💡Module_Experiment2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Module_Experiment1[] PROGMEM = "Y💡Module_Experiment1 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Parametric_Water[] PROGMEM = "Y💡Parametric_Water ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Water[] PROGMEM = "Y💡Water ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido_6[] PROGMEM = "Y💡Complex_Kaleido_6 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido_5[] PROGMEM = "Y💡Complex_Kaleido_5 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido_4[] PROGMEM = "Y💡Complex_Kaleido_4 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido_3[] PROGMEM = "Y💡Complex_Kaleido_3 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido_2[] PROGMEM = "Y💡Complex_Kaleido_2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Complex_Kaleido[] PROGMEM = "Y💡Complex_Kaleido ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM10[] PROGMEM = "Y💡SM10 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM9[] PROGMEM = "Y💡SM9 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM8[] PROGMEM = "Y💡SM8 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM7[] PROGMEM = "Y💡SM7 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM6[] PROGMEM = "Y💡SM6 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM5[] PROGMEM = "Y💡SM5 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM4[] PROGMEM = "Y💡SM4 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM3[] PROGMEM = "Y💡SM3 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM2[] PROGMEM = "Y💡SM2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_SM1[] PROGMEM = "Y💡SM1 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Big_Caleido[] PROGMEM = "Y💡Big_Caleido ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_RGB_Blobs5[] PROGMEM = "Y💡RGB_Blobs5 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_RGB_Blobs4[] PROGMEM = "Y💡RGB_Blobs4 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_RGB_Blobs3[] PROGMEM = "Y💡RGB_Blobs3 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_RGB_Blobs2[] PROGMEM = "Y💡RGB_Blobs2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_RGB_Blobs[] PROGMEM = "Y💡RGB_Blobs ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Polar_Waves[] PROGMEM = "Y💡Polar_Waves ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Slow_Fade[] PROGMEM = "Y💡Slow_Fade ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Zoom[] PROGMEM = "Y💡Zoom ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Hot_Blob[] PROGMEM = "Y💡Hot_Blob ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Spiralus2[] PROGMEM = "Y💡Spiralus2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Spiralus[] PROGMEM = "Y💡Spiralus ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Yves[] PROGMEM = "Y💡Yves ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Scaledemo1[] PROGMEM = "Y💡Scaledemo1 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Lava1[] PROGMEM = "Y💡Lava1 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Caleido3[] PROGMEM = "Y💡Caleido3 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Caleido2[] PROGMEM = "Y💡Caleido2 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Caleido1[] PROGMEM = "Y💡Caleido1 ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Distance_Experiment[] PROGMEM = "Y💡Distance_Experiment ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Center_Field[] PROGMEM = "Y💡Center_Field ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@" ANIMARTRIX_UI_CONTROLS; +static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@" ANIMARTRIX_UI_CONTROLS; class ANIMartRIXMod:public ANIMartRIX { From c4fb00f9ebce928cc786de56339f3d1048aa6c30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:14:52 +0100 Subject: [PATCH 14/27] planning and ideas for audio reactive enhancements Ideas and ToDOs --- .../usermod_v2_animartrix.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 0e1d7cc17d..4be48fb451 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -132,6 +132,23 @@ class ANIMartRIXMod:public ANIMartRIX { bool cycle_hue = false; bool boost_brightness = false; bool boost_contrast = false; + + void handleAudio() { + // ToDo 1: sliders + // * for HUE change amount by audio (handling based on cycle_hue: static shift, or speedup/slowdown) + // * for AUDIO filtering: instant -> 1s decay -> what else? Maybe accumulate changes but use speed limit? + + // TODO 2 + // attach to audiosource + // filter raw audio input + // adjust HUE shift based on audio data + // (maybe) allow to adjust brightness + // (details) compare to audiorective palettes by @netmindz + + // ToDo 3: user option to configure audio input + // none, peak detection, zcr(major frequency), pressure, volumeSmth, High freqs (fftbin[7-10]), mid freqs (fftbin[4-8]), low freqs (fftbin[0-4]) + } + public: void initEffect() { if ((SEGENV.call == 0) || (SEGMENT.virtualWidth() != num_x) || (SEGMENT.virtualHeight() != num_y)) { @@ -164,6 +181,8 @@ class ANIMartRIXMod:public ANIMartRIX { } else { // !cycle_hue hueshift = (128 - SEGMENT.intensity) * 256; // static HUE shift } + + handleAudio(); // Adjust HUE shift (and intensity?) based on audio } // enhance middle ranges contrast (S-Function) From 1dc6efbbc8bb98243c5284dcdec99db2473fcb0b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:11:23 +0100 Subject: [PATCH 15/27] move gamma correction option into global usermod settings --- .../usermod_v2_animartrix/usermod_v2_animartrix.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 4be48fb451..19d097c0c8 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -124,6 +124,8 @@ static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@" ANIMARTRIX_U static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@" ANIMARTRIX_UI_CONTROLS; static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@" ANIMARTRIX_UI_CONTROLS; +// global settings - shared between ANIMartRIXMod and AnimartrixUsermod +static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disable to get the "legacy" gamma-free look class ANIMartRIXMod:public ANIMartRIX { private: @@ -162,9 +164,8 @@ class ANIMartRIXMod:public ANIMartRIX { } setSpeedFactor(speedFactor); - //use_gamma = SEGENV.check1; - use_gamma = true; // ToDO: move to usermod options - cycle_hue = SEGENV.check1; + use_gamma = animartrix_use_gamma > 0; // from global usermod options + cycle_hue = SEGENV.check1; // from segment checkboxes boost_brightness = SEGENV.check2; boost_contrast = SEGENV.check3; @@ -548,7 +549,7 @@ class AnimartrixUsermod : public Usermod { void setup() override { - if(!enabled) return; + if (initDone || !enabled) return; // WLEDMM don't register effects twice! strip.addEffect(203, &mode_Module_Experiment10, _data_FX_mode_Module_Experiment10); strip.addEffect(204, &mode_Module_Experiment9, _data_FX_mode_Module_Experiment9); @@ -633,6 +634,8 @@ class AnimartrixUsermod : public Usermod { infoArr.add(uiDomString); } + // add JSON entries that go to cfg.json + void addToConfig(JsonObject& obj) override { uint16_t getId() override { return USERMOD_ID_ANIMARTRIX; From 74643f8bd7eb39c12429942baf825ca830b115c7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:14:48 +0100 Subject: [PATCH 16/27] fix broken commit oops, forgot half of the code. --- .../usermod_v2_animartrix.h | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 19d097c0c8..0c1e69c162 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -636,6 +636,34 @@ class AnimartrixUsermod : public Usermod { // add JSON entries that go to cfg.json void addToConfig(JsonObject& obj) override { + JsonObject top = obj.createNestedObject(FPSTR(_name)); // WLEDMM: set enabled and _name + top[FPSTR("enabled")] = enabled; + top[FPSTR("gamma_correction")] = animartrix_use_gamma; + } + + bool readFromConfig(JsonObject& root) override { + JsonObject top = root[FPSTR(_name)]; //WLEDMM + bool configComplete = !top.isNull(); + // remember previous values + auto oldEnabled = enabled; + // read config + configComplete &= getJsonValue(top[FPSTR("enabled")], enabled); + configComplete &= getJsonValue(top[FPSTR("gamma_correction")], animartrix_use_gamma); + if (oldEnabled != enabled) setup(); // re-run setup if enabled status changed + return configComplete; + } + + /* + * appendConfigData() is called when user enters usermod settings page + * it may add additional metadata for certain entry fields (adding drop down is possible) + */ + void appendConfigData() override { + // create dropdown for "gamma_correction" + oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','gamma_correction');")); + oappend(SET_F("addOption(dd,'On  (⎌)',1);")); + oappend(SET_F("addOption(dd,'Off',0);")); + } + uint16_t getId() override { return USERMOD_ID_ANIMARTRIX; From e4dbc49f9859c68d0e96b43752e6e9781d430688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20M=C3=B6hle?= <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:42:55 +0100 Subject: [PATCH 17/27] Fix typos in comments --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 0c1e69c162..4eb1fe5132 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -125,7 +125,7 @@ static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spiral static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@" ANIMARTRIX_UI_CONTROLS; // global settings - shared between ANIMartRIXMod and AnimartrixUsermod -static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disable to get the "legacy" gamma-free look +static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disabled to get the "legacy" gamma-free look class ANIMartRIXMod:public ANIMartRIX { private: @@ -252,7 +252,7 @@ class ANIMartRIXMod:public ANIMartRIX { } // experimental: HUE shift - if (cycle_hue || (abs(hueshift) > 255)) { // cycle HUE selected, or manual HUE at lest 1 left/right from center of slider + if (cycle_hue || (abs(hueshift) > 255)) { // cycle HUE selected, or manual HUE if at least 1 left/right from center of slider CHSV32 cc; rgb2hsv(colOut, cc); cc.h = cc.h + unsigned(hueshift); // works due to 2's complement From 9a343d6b057646e8c630fb4535e82ac4ec1882cf Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:26:19 +0100 Subject: [PATCH 18/27] utility functions and minor stuff --- .../usermod_v2_animartrix.h | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 4eb1fe5132..100d9c57bd 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -2,6 +2,7 @@ #include "wled.h" #include "colors.h" // include CHSV32 class by @dedehai +#include "fcn_declare.h" // utilities #ifdef _MoonModules_WLED_ // WLEDMM: use faster math approximations - up to 40% faster @@ -69,7 +70,7 @@ #warning WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! //======================================================================================================================== -#define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,,,,cycle HUE,boost Brightness,boost Contrast;;1;2;o2=0" +#define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,Audio Strength,Audio Decay,,cycle HUE,boost Brightness,boost Contrast;;1;2;c1=0,c2=64,o2=0" static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@" ANIMARTRIX_UI_CONTROLS; static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@" ANIMARTRIX_UI_CONTROLS; @@ -124,6 +125,34 @@ static const char _data_FX_mode_Waves[] PROGMEM = "Y💡Waves ☾@" ANIMARTRIX_U static const char _data_FX_mode_Chasing_Spirals[] PROGMEM = "Y💡Chasing_Spirals ☾@" ANIMARTRIX_UI_CONTROLS; static const char _data_FX_mode_Rotating_Blob[] PROGMEM = "Y💡Rotating_Blob ☾@" ANIMARTRIX_UI_CONTROLS; +// local utility functions +// + +// Attach to audiosource, or fall back to simulateSound +static um_data_t* getAudioDataOrSim() { + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + return um_data; +} + +// better "map" that can be used when in_min = out_min = 0. +static inline uint32_t map0(uint32_t val, uint32_t in_max, uint32_t out_max) { + // Fast and accurate (error always below 0.5) + if (in_max == 0) return 0; // avoid division by zero + return ( (val*out_max) + (in_max/2) ) / in_max; // +(in_max/2) for rounding +} +#if 0 // not used yet +// a variant of map0() that handles output ranges not starting at 0 - but still requires val in [0 ... in_max] +static inline int32_t map0(uint32_t val, uint32_t in_max, int32_t out_min, int32_t out_max) { + if (out_min > out_max) std::swap(out_min, out_max); // a hack, just to treat inverted ranges without producing overflows + int range = out_max - out_min; + return int(map0(val, in_max, unsigned(range))) + out_min; +} +#endif + // global settings - shared between ANIMartRIXMod and AnimartrixUsermod static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disabled to get the "legacy" gamma-free look @@ -145,7 +174,7 @@ class ANIMartRIXMod:public ANIMartRIX { // filter raw audio input // adjust HUE shift based on audio data // (maybe) allow to adjust brightness - // (details) compare to audiorective palettes by @netmindz + // (details) compare to audioreactive palettes by @netmindz // ToDo 3: user option to configure audio input // none, peak detection, zcr(major frequency), pressure, volumeSmth, High freqs (fftbin[7-10]), mid freqs (fftbin[4-8]), low freqs (fftbin[0-4]) From a8618107707f7c65e87adcf020989daf3b724ba8 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:30:40 +0100 Subject: [PATCH 19/27] basic audio core (experimental) audio extraction and filtering --- .../usermod_v2_animartrix.h | 89 ++++++++++++++++++- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 100d9c57bd..51bac22b4b 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -154,7 +154,16 @@ static inline int32_t map0(uint32_t val, uint32_t in_max, int32_t out_min, int32 #endif // global settings - shared between ANIMartRIXMod and AnimartrixUsermod +// static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disabled to get the "legacy" gamma-free look +#ifdef USERMOD_AUDIOREACTIVE +static uint8_t animartrix_detectorID = 5; // default = bass detector +#else +static uint8_t animartrix_detectorID = 0; // not AR usermod -> default = no audio +#endif + +// ANIMartRIXMod class +// class ANIMartRIXMod:public ANIMartRIX { private: @@ -164,7 +173,8 @@ class ANIMartRIXMod:public ANIMartRIX { bool boost_brightness = false; bool boost_contrast = false; - void handleAudio() { + // --- audio core ------------------------------------------------------------------------------ + // ToDo 1: sliders // * for HUE change amount by audio (handling based on cycle_hue: static shift, or speedup/slowdown) // * for AUDIO filtering: instant -> 1s decay -> what else? Maybe accumulate changes but use speed limit? @@ -178,9 +188,81 @@ class ANIMartRIXMod:public ANIMartRIX { // ToDo 3: user option to configure audio input // none, peak detection, zcr(major frequency), pressure, volumeSmth, High freqs (fftbin[7-10]), mid freqs (fftbin[4-8]), low freqs (fftbin[0-4]) - } + + // detection engine + #define NUM_DETECTORS 8 // total, including "none" + #define DET_NONE 0 // no audio + #define DET_VOLUME 1 // AR volumeSmth (relative) + #define DET_PRESSURE 2 // AR soundPressure (absolute) + #define DET_ZCR 3 // AR ZeroCrossingCount (music "density") + #define DET_BASS 5 // AR bass (FFT channel 1+2) + #define DET_MID 6 // AR voices & melody (FFT channel 5,7,8,10) + #define DET_HIGH 7 // AR high frequencies - high-hats, pipes, high pitch (FFT channel 12,13,15) + + // get audio - either soundSim or audioReactive UM + static float getAudio(unsigned detectorID) { + constexpr float M_LOG_256 = 5.54517744f; // log(256) = max value + constexpr float M_LOG_3 = 1.098612289f; // log(3) = minimal value + um_data_t *um_data = getAudioDataOrSim(); + float volumeSmth = *(float*) um_data->u_data[0]; + // int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; + uint8_t *fftResult = (uint8_t*) um_data->u_data[2]; + float soundPressure = *(float*) um_data->u_data[9]; + //float agcSensitivity=*(float*) um_data->u_data[10]; + uint16_t zCr = *(uint16_t*) um_data->u_data[11]; + + bool isSilence = volumeSmth < 1.0f; + if (isSilence) return 0.0f; + + float audioSample = 0.0f; + return (audioSample > 0.8f) ? audioSample : 0.0f; // clamp silence and underflows + } + + + // filter audio + float lastAudioData[NUM_DETECTORS] = {0.0f}; + unsigned lastAudioTime[NUM_DETECTORS] = {0}; + + float processAudio(unsigned detectorID, unsigned decayMS) { + constexpr float audioSmooth = 0.88f; // audio smoothing factor - to avoid instant flashes and very hard jumps + if (detectorID >= NUM_DETECTORS) detectorID = 0; + if (detectorID == 0) return 0.0f; + + unsigned timestamp = millis(); + long delta_time = timestamp - lastAudioTime[detectorID]; + delta_time = min(max(delta_time , 1L), 1000L); // clamp to meaningful values + if (delta_time < 3) return min(max(0.0f, lastAudioData[detectorID]), 255.0f); // too early, value has not changed since last time + + float newAudio = getAudio(detectorID); + float deltaSample = newAudio - lastAudioData[detectorID]; // positive attack, negative during decay + + if ((deltaSample > 0.0f) || (decayMS < 1)) { + // fast attack/decay with minimal filtering + lastAudioData[detectorID] += 0.9f * audioSmooth * deltaSample; // 0.9 for damping of jumps + } else { + // slow decay: time-based linear decay; similar to AR limitSampleDynamics() function + constexpr float bigChange = 184; // a large, expected sample value that decays to 0 in decayMS millis + float maxDecay = - bigChange * float(delta_time) / float(decayMS); // allowed decay for elapsed time (must be negative!) + if (deltaSample < maxDecay) deltaSample = maxDecay; // limit delta if new value is too low + lastAudioData[detectorID] += audioSmooth * deltaSample; + } + + lastAudioTime[detectorID] = timestamp; + return min(max(0.0f, lastAudioData[detectorID]), 255.0f); // clamp result to 0..255, but keep exact value internally + } + + // --- audio core end -------------------------------------------------------------------------- public: + // audio processing, called once per frame per segment + void handleAudioHUE(unsigned detectorID) { + unsigned audioDecay = map0(SEGMENT.custom2, 255, 3500); // 0 (instant) up to 4 seconds + float audioShift = 255.0f * processAudio(detectorID, audioDecay); // 0.0 ... 65025 = full "HUE turn" + float audioStrength = float(SEGMENT.custom1) / 255.0f; // multiplier [0...1] + audioShift = audioShift * audioStrength; + if (SEGMENT.custom1 > 1) hueshift = hueshift + unsigned(audioShift); + } + void initEffect() { if ((SEGENV.call == 0) || (SEGMENT.virtualWidth() != num_x) || (SEGMENT.virtualHeight() != num_y)) { init(SEGMENT.virtualWidth(), SEGMENT.virtualHeight(), false); @@ -212,7 +294,8 @@ class ANIMartRIXMod:public ANIMartRIX { hueshift = (128 - SEGMENT.intensity) * 256; // static HUE shift } - handleAudio(); // Adjust HUE shift (and intensity?) based on audio + handleAudioHUE(animartrix_detectorID); + } } // enhance middle ranges contrast (S-Function) From 77931c6f070397201f529a72875d0ae1cfd552cc Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:31:37 +0100 Subject: [PATCH 20/27] simple audio detectors (experimental) --- .../usermod_v2_animartrix.h | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 51bac22b4b..6e31e5c93a 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -214,7 +214,40 @@ class ANIMartRIXMod:public ANIMartRIX { bool isSilence = volumeSmth < 1.0f; if (isSilence) return 0.0f; + // extract audio based on selected detection method float audioSample = 0.0f; + switch (detectorID) { + case DET_VOLUME: audioSample = volumeSmth; break; + case DET_PRESSURE: audioSample = soundPressure; break; + case DET_ZCR: + // zero crossings count + audioSample = float(zCr); + if (audioSample > 3.0f) + audioSample = 255.0f * (logf(audioSample) - M_LOG_3) / M_LOG_256; // log scaling - amplifies lower frequencies, reduces noise + else audioSample = 0.0f; + break; + case DET_BASS: + // low freqs RMS + audioSample = 0.6f * sqrtf(float(fftResult[1])*float(fftResult[1]) // 86 - 129 hz + + float(fftResult[2])*float(fftResult[2])); // 129 - 216 hz + break; + case DET_MID: + // mid freqs RMS with some boost (voices, chorus, some instruments) + audioSample = sqrtf(float(fftResult[5])*float(fftResult[5]) // 430 - 560 hz + + float(fftResult[7])*float(fftResult[7]) // 818 - 1120 hz + + float(fftResult[8])*float(fftResult[8]) // 1120 - 1421 hz + + float(fftResult[10])*float(fftResult[10])) /2.5f; // 1895 - 2412 hz + break; + case DET_HIGH: + // high freqs RMS (hats, pipes, high rattle stuff) + audioSample = sqrtf(float(fftResult[12])*float(fftResult[12]) // 3015 - 3704 hz + + float(fftResult[13])*float(fftResult[13]) // 3704 - 4479 hz + + float(fftResult[15])*float(fftResult[15])) / 2.2f; // 7106 - 9259 hz + break; + case DET_NONE: + // falls through + default: break; + } return (audioSample > 0.8f) ? audioSample : 0.0f; // clamp silence and underflows } @@ -751,6 +784,7 @@ class AnimartrixUsermod : public Usermod { JsonObject top = obj.createNestedObject(FPSTR(_name)); // WLEDMM: set enabled and _name top[FPSTR("enabled")] = enabled; top[FPSTR("gamma_correction")] = animartrix_use_gamma; + top[FPSTR("audio_detector")] = animartrix_detectorID; } bool readFromConfig(JsonObject& root) override { @@ -761,6 +795,7 @@ class AnimartrixUsermod : public Usermod { // read config configComplete &= getJsonValue(top[FPSTR("enabled")], enabled); configComplete &= getJsonValue(top[FPSTR("gamma_correction")], animartrix_use_gamma); + configComplete &= getJsonValue(top[FPSTR("audio_detector")], animartrix_detectorID); if (oldEnabled != enabled) setup(); // re-run setup if enabled status changed return configComplete; } @@ -774,6 +809,15 @@ class AnimartrixUsermod : public Usermod { oappend(SET_F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','gamma_correction');")); oappend(SET_F("addOption(dd,'On  (⎌)',1);")); oappend(SET_F("addOption(dd,'Off',0);")); + + oappend(SET_F("dd1=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F("','audio_detector');")); + oappend(SET_F("addOption(dd1,'Sound Level (relative)',1);")); + oappend(SET_F("addOption(dd1,'Sound Pressure (absolute)',2);")); + oappend(SET_F("addOption(dd1,'ZeroCrossings (density)',3);")); + oappend(SET_F("addOption(dd1,'Bass Frequencies (⎌)',5);")); + oappend(SET_F("addOption(dd1,'Mid Frequencies (voices)',6);")); + oappend(SET_F("addOption(dd1,'Very High Frequencies',7);")); + oappend(SET_F("addOption(dd1,'No Audio',0);")); } uint16_t getId() override From 21e72c490182b05474e2354defab871364f4e107 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 00:33:13 +0100 Subject: [PATCH 21/27] =?UTF-8?q?adding=20"Y=F0=9F=92=A1AudioMonitor=201D"?= =?UTF-8?q?=20effect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gravimeter-like 1D effect for audio debugging --- .../usermod_v2_animartrix.h | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 6e31e5c93a..38ab2cb1df 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -70,6 +70,9 @@ #warning WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! //======================================================================================================================== +#define ANIMARTRIX_UI_MONITOR "Speed(nothing),HUE Change(nothing),Audio Strength,Audio Decay,Audio Detector ID,Amplify,Amplify,;!;1v;c1=255,c2=64,c3=0,o1=0,o2=0,m12=7,si=0" +static const char _data_FX_mode_AudioMon1D[] PROGMEM = "Y💡AudioMonitor 1D ☾@" ANIMARTRIX_UI_MONITOR; + #define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,Audio Strength,Audio Decay,,cycle HUE,boost Brightness,boost Contrast;;1;2;c1=0,c2=64,o2=0" static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@" ANIMARTRIX_UI_CONTROLS; @@ -329,6 +332,19 @@ class ANIMartRIXMod:public ANIMartRIX { handleAudioHUE(animartrix_detectorID); } + + // AUDIO DEBUG: simplified variant of initEffect() + inline int getAudioHUE() const { return hueshift; } + void initMonitor() { + #if !defined(WLEDMM_NO_GAMMA) + use_gamma = animartrix_use_gamma > 0; // from global usermod options + #else + use_gamma = false; // ToDO: move to usermod options + #endif + cycle_hue = false; + boost_brightness = false; + boost_contrast = false; + hueshift = 0; } // enhance middle ranges contrast (S-Function) @@ -422,6 +438,32 @@ class ANIMartRIXMod:public ANIMartRIX { }; ANIMartRIXMod anim; +uint16_t mode_AudioMon() { + // debug 1D audio monitor (gravimeter syle) + anim.initMonitor(); + SEGMENT.custom3 = min(SEGMENT.custom3, uint8_t(NUM_DETECTORS-1)); + unsigned detectorID = SEGMENT.custom3 == 0 ? animartrix_detectorID : SEGMENT.custom3; + + anim.handleAudioHUE(detectorID); + float volumeSmth = float(uint16_t(anim.getAudioHUE())) / 255.0f; + + if (SEGMENT.check1) volumeSmth *= 1.5f; // amplify by 1.5 for better visibility + if (SEGMENT.check2) volumeSmth *= 1.5f; // amplify again by 1.5 + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + SEGMENT.fade_out(253); + + float mySampleAvg = volumeSmth/255.0f * float(SEGLEN-1); // map to pixels available in current segment + unsigned segmentSampleAvg = volumeSmth / 4; + int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. + for (int i=0; i Date: Thu, 5 Mar 2026 13:10:28 +0100 Subject: [PATCH 22/27] remove extra filtering coefficient not needed, as AR core already does some filtering --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 38ab2cb1df..3a1820c2b2 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -274,7 +274,8 @@ class ANIMartRIXMod:public ANIMartRIX { if ((deltaSample > 0.0f) || (decayMS < 1)) { // fast attack/decay with minimal filtering - lastAudioData[detectorID] += 0.9f * audioSmooth * deltaSample; // 0.9 for damping of jumps + // lastAudioData[detectorID] += 0.9f * audioSmooth * deltaSample; // experimental - 0.9 for damping of jumps + lastAudioData[detectorID] += audioSmooth * deltaSample; } else { // slow decay: time-based linear decay; similar to AR limitSampleDynamics() function constexpr float bigChange = 184; // a large, expected sample value that decays to 0 in decayMS millis From d542af986578d7f970a1b5a82c74839cf3b9ba4d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:11:31 +0100 Subject: [PATCH 23/27] bugfix: missing filter 4 correct missing filter 4 add input validation --- .../usermod_v2_animartrix.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 3a1820c2b2..c866be027e 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -160,7 +160,7 @@ static inline int32_t map0(uint32_t val, uint32_t in_max, int32_t out_min, int32 // static uint8_t animartrix_use_gamma = 1; // default = enabled. Can be disabled to get the "legacy" gamma-free look #ifdef USERMOD_AUDIOREACTIVE -static uint8_t animartrix_detectorID = 5; // default = bass detector +static uint8_t animartrix_detectorID = 4; // default = bass detector #else static uint8_t animartrix_detectorID = 0; // not AR usermod -> default = no audio #endif @@ -193,14 +193,14 @@ class ANIMartRIXMod:public ANIMartRIX { // none, peak detection, zcr(major frequency), pressure, volumeSmth, High freqs (fftbin[7-10]), mid freqs (fftbin[4-8]), low freqs (fftbin[0-4]) // detection engine - #define NUM_DETECTORS 8 // total, including "none" + #define NUM_DETECTORS 7 // total, including "none" #define DET_NONE 0 // no audio #define DET_VOLUME 1 // AR volumeSmth (relative) #define DET_PRESSURE 2 // AR soundPressure (absolute) #define DET_ZCR 3 // AR ZeroCrossingCount (music "density") - #define DET_BASS 5 // AR bass (FFT channel 1+2) - #define DET_MID 6 // AR voices & melody (FFT channel 5,7,8,10) - #define DET_HIGH 7 // AR high frequencies - high-hats, pipes, high pitch (FFT channel 12,13,15) + #define DET_BASS 4 // AR bass (FFT channel 1+2) + #define DET_MID 5 // AR voices & melody (FFT channel 5,7,8,10) + #define DET_HIGH 6 // AR high frequencies - high-hats, pipes, high pitch (FFT channel 12,13,15) // get audio - either soundSim or audioReactive UM static float getAudio(unsigned detectorID) { @@ -828,6 +828,7 @@ class AnimartrixUsermod : public Usermod { JsonObject top = obj.createNestedObject(FPSTR(_name)); // WLEDMM: set enabled and _name top[FPSTR("enabled")] = enabled; top[FPSTR("gamma_correction")] = animartrix_use_gamma; + animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS); top[FPSTR("audio_detector")] = animartrix_detectorID; } @@ -840,6 +841,7 @@ class AnimartrixUsermod : public Usermod { configComplete &= getJsonValue(top[FPSTR("enabled")], enabled); configComplete &= getJsonValue(top[FPSTR("gamma_correction")], animartrix_use_gamma); configComplete &= getJsonValue(top[FPSTR("audio_detector")], animartrix_detectorID); + animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS); if (oldEnabled != enabled) setup(); // re-run setup if enabled status changed return configComplete; } @@ -858,9 +860,9 @@ class AnimartrixUsermod : public Usermod { oappend(SET_F("addOption(dd1,'Sound Level (relative)',1);")); oappend(SET_F("addOption(dd1,'Sound Pressure (absolute)',2);")); oappend(SET_F("addOption(dd1,'ZeroCrossings (density)',3);")); - oappend(SET_F("addOption(dd1,'Bass Frequencies (⎌)',5);")); - oappend(SET_F("addOption(dd1,'Mid Frequencies (voices)',6);")); - oappend(SET_F("addOption(dd1,'Very High Frequencies',7);")); + oappend(SET_F("addOption(dd1,'Bass Frequencies (⎌)',4);")); + oappend(SET_F("addOption(dd1,'Mid Frequencies (voices)',5);")); + oappend(SET_F("addOption(dd1,'Very High Frequencies',6);")); oappend(SET_F("addOption(dd1,'No Audio',0);")); } From d8cb5504ab50d572002ed9a3c3c37927c2b99c8a Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:12:52 +0100 Subject: [PATCH 24/27] UI metadata correction correct metadata errors, change default for c2 from 64 to 32 --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index c866be027e..31a36444a7 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -70,10 +70,10 @@ #warning WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! //======================================================================================================================== -#define ANIMARTRIX_UI_MONITOR "Speed(nothing),HUE Change(nothing),Audio Strength,Audio Decay,Audio Detector ID,Amplify,Amplify,;!;1v;c1=255,c2=64,c3=0,o1=0,o2=0,m12=7,si=0" +#define ANIMARTRIX_UI_MONITOR "Speed(nothing),HUE Change(nothing),Audio Strength,Audio Decay,Audio Detector ID,Amplify,Amplify,;!,!;!;1f;c1=255,c2=32,c3=0,o1=0,o2=0" static const char _data_FX_mode_AudioMon1D[] PROGMEM = "Y💡AudioMonitor 1D ☾@" ANIMARTRIX_UI_MONITOR; -#define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,Audio Strength,Audio Decay,,cycle HUE,boost Brightness,boost Contrast;;1;2;c1=0,c2=64,o2=0" +#define ANIMARTRIX_UI_CONTROLS "Speed,HUE Change,Audio Strength,Audio Decay,,cycle HUE,boost Brightness,boost Contrast;;1;2f;c1=0,c2=32,o2=0" static const char _data_FX_mode_Module_Experiment10[] PROGMEM = "Y💡Module_Experiment10 ☾@" ANIMARTRIX_UI_CONTROLS; static const char _data_FX_mode_Module_Experiment9[] PROGMEM = "Y💡Module_Experiment9 ☾@" ANIMARTRIX_UI_CONTROLS; From 60675352b0b8fc4d66b3197576b9735e0a2ef15b Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:14:24 +0100 Subject: [PATCH 25/27] separate ANIMartRIXMod instance for AudioMonitor effect "anim" is one global object - create separate object for Monitor, to prevent interferences between effects wtih different filter options --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 31a36444a7..866c4a814c 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -439,14 +439,15 @@ class ANIMartRIXMod:public ANIMartRIX { }; ANIMartRIXMod anim; +ANIMartRIXMod animAudioMon; // second object instance fir monitor, to avoid overlaping of audio processing uint16_t mode_AudioMon() { // debug 1D audio monitor (gravimeter syle) - anim.initMonitor(); + animAudioMon.initMonitor(); SEGMENT.custom3 = min(SEGMENT.custom3, uint8_t(NUM_DETECTORS-1)); unsigned detectorID = SEGMENT.custom3 == 0 ? animartrix_detectorID : SEGMENT.custom3; - anim.handleAudioHUE(detectorID); - float volumeSmth = float(uint16_t(anim.getAudioHUE())) / 255.0f; + animAudioMon.handleAudioHUE(detectorID); + float volumeSmth = float(uint16_t(animAudioMon.getAudioHUE())) / 255.0f; if (SEGMENT.check1) volumeSmth *= 1.5f; // amplify by 1.5 for better visibility if (SEGMENT.check2) volumeSmth *= 1.5f; // amplify again by 1.5 From 82a8cab37d02ffcc91d87f7cfa8d008797d663e9 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:32:13 +0100 Subject: [PATCH 26/27] Audio Monitor improvements * use different state object when running monitor with detectorID == 0 * monitor takes over main effect parameters when detectorID == 0 * minor tinkering * adding rounding in monitor effect drawing routine --- .../usermod_v2_animartrix.h | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index 866c4a814c..e032a54013 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -272,7 +272,7 @@ class ANIMartRIXMod:public ANIMartRIX { float newAudio = getAudio(detectorID); float deltaSample = newAudio - lastAudioData[detectorID]; // positive attack, negative during decay - if ((deltaSample > 0.0f) || (decayMS < 1)) { + if ((deltaSample >= 0.0f) || (decayMS < 1)) { // fast attack/decay with minimal filtering // lastAudioData[detectorID] += 0.9f * audioSmooth * deltaSample; // experimental - 0.9 for damping of jumps lastAudioData[detectorID] += audioSmooth * deltaSample; @@ -300,6 +300,10 @@ class ANIMartRIXMod:public ANIMartRIX { if (SEGMENT.custom1 > 1) hueshift = hueshift + unsigned(audioShift); } + // copy of UI slider values + uint8_t myAudioDecay = 32; // default slider value + uint8_t myAudioStrength = 127; + void initEffect() { if ((SEGENV.call == 0) || (SEGMENT.virtualWidth() != num_x) || (SEGMENT.virtualHeight() != num_y)) { init(SEGMENT.virtualWidth(), SEGMENT.virtualHeight(), false); @@ -330,7 +334,8 @@ class ANIMartRIXMod:public ANIMartRIX { } else { // !cycle_hue hueshift = (128 - SEGMENT.intensity) * 256; // static HUE shift } - + myAudioStrength = SEGMENT.custom1; + myAudioDecay = SEGMENT.custom2; handleAudioHUE(animartrix_detectorID); } @@ -346,6 +351,8 @@ class ANIMartRIXMod:public ANIMartRIX { boost_brightness = false; boost_contrast = false; hueshift = 0; + myAudioStrength = SEGMENT.custom1; + myAudioDecay = SEGMENT.custom2; } // enhance middle ranges contrast (S-Function) @@ -439,16 +446,25 @@ class ANIMartRIXMod:public ANIMartRIX { }; ANIMartRIXMod anim; -ANIMartRIXMod animAudioMon; // second object instance fir monitor, to avoid overlaping of audio processing +ANIMartRIXMod animAudioMon; // second object instance for monitor, to avoid overlaping of audio processing +ANIMartRIXMod animAudioMon0; // used if custom3 (detector) == 0 (use global) uint16_t mode_AudioMon() { // debug 1D audio monitor (gravimeter syle) - animAudioMon.initMonitor(); SEGMENT.custom3 = min(SEGMENT.custom3, uint8_t(NUM_DETECTORS-1)); unsigned detectorID = SEGMENT.custom3 == 0 ? animartrix_detectorID : SEGMENT.custom3; - - animAudioMon.handleAudioHUE(detectorID); - float volumeSmth = float(uint16_t(animAudioMon.getAudioHUE())) / 255.0f; - + float volumeSmth = 0.0f; + + if (SEGMENT.custom3 == 0) { + animAudioMon0.initMonitor(); + SEGMENT.custom2 = anim.myAudioDecay; // steal AudioDecay from main effect + SEGMENT.custom1 = anim.myAudioStrength; // steal from main effect + animAudioMon0.handleAudioHUE(detectorID); + volumeSmth = float(uint16_t(animAudioMon0.getAudioHUE())) / 255.0f; + } else { + animAudioMon.initMonitor(); + animAudioMon.handleAudioHUE(detectorID); + volumeSmth = float(uint16_t(animAudioMon.getAudioHUE())) / 255.0f; + } if (SEGMENT.check1) volumeSmth *= 1.5f; // amplify by 1.5 for better visibility if (SEGMENT.check2) volumeSmth *= 1.5f; // amplify again by 1.5 if (SEGENV.call == 0) { @@ -456,9 +472,9 @@ uint16_t mode_AudioMon() { } SEGMENT.fade_out(253); - float mySampleAvg = volumeSmth/255.0f * float(SEGLEN-1); // map to pixels available in current segment - unsigned segmentSampleAvg = volumeSmth / 4; - int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. + float mySampleAvg = volumeSmth * float(SEGLEN-1) /255.0f; // map to pixels available in current segment + unsigned segmentSampleAvg = (volumeSmth+2) / 4; + int tempsamp = constrain(roundf(mySampleAvg),0,SEGLEN-1); // Keep the sample from overflowing. for (int i=0; i Date: Thu, 5 Mar 2026 18:11:36 +0100 Subject: [PATCH 27/27] fix off-by-one in detector ID constraint --- usermods/usermod_v2_animartrix/usermod_v2_animartrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index e032a54013..dbec00ab20 100644 --- a/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -845,7 +845,7 @@ class AnimartrixUsermod : public Usermod { JsonObject top = obj.createNestedObject(FPSTR(_name)); // WLEDMM: set enabled and _name top[FPSTR("enabled")] = enabled; top[FPSTR("gamma_correction")] = animartrix_use_gamma; - animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS); + animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS-1); top[FPSTR("audio_detector")] = animartrix_detectorID; } @@ -858,7 +858,7 @@ class AnimartrixUsermod : public Usermod { configComplete &= getJsonValue(top[FPSTR("enabled")], enabled); configComplete &= getJsonValue(top[FPSTR("gamma_correction")], animartrix_use_gamma); configComplete &= getJsonValue(top[FPSTR("audio_detector")], animartrix_detectorID); - animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS); + animartrix_detectorID = constrain(animartrix_detectorID, 0, NUM_DETECTORS-1); if (oldEnabled != enabled) setup(); // re-run setup if enabled status changed return configComplete; }