diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bc0de4d1..b962bb58 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -24,516 +24,562 @@ Modified heavily for WLED */ -#include "FX.h" #include "wled.h" - -// WLEDSR Custom Effects -#include "src/dependencies/arti/arti_wled.h" +#include "FX.h" +#include "fcn_declare.h" #define IBN 5100 -#define PALETTE_SOLID_WRAP (paletteBlend == 1 || paletteBlend == 3) + +// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) +#define PALETTE_SOLID_WRAP (strip.paletteBlend == 1 || strip.paletteBlend == 3) +#define PALETTE_MOVING_WRAP !(strip.paletteBlend == 2 || (strip.paletteBlend == 0 && SEGMENT.speed == 0)) + +#define indexToVStrip(index, stripNr) ((index) | (int((stripNr)+1)<<16)) + +// effect utility functions +uint8_t sin_gap(uint16_t in) { + if (in & 0x100) return 0; + return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0 +} + +uint16_t triwave16(uint16_t in) { + if (in < 0x8000) return in *2; + return 0xFFFF - (in - 0x8000)*2; +} + +/* + * Generates a tristate square wave w/ attac & decay + * @param x input value 0-255 + * @param pulsewidth 0-127 + * @param attdec attac & decay, max. pulsewidth / 2 + * @returns signed waveform value + */ +int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) { + int8_t a = 127; + if (x > 127) { + a = -127; + x -= 127; + } + + if (x < attdec) { //inc to max + return (int16_t) x * a / attdec; + } + else if (x < pulsewidth - attdec) { //max + return a; + } + else if (x < pulsewidth) { //dec to 0 + return (int16_t) (pulsewidth - x) * a / attdec; + } + return 0; +} + +// effect functions /* * No blinking. Just plain old static light. */ -uint16_t WS2812FX::mode_static(void) -{ - fill(SEGCOLOR(0)); - return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 350; // update faster if in transition +uint16_t mode_static(void) { + SEGMENT.fill(SEGCOLOR(0)); + return 350; } +static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; + /* * Blink/strobe function * Alternate between color1 and color2 * if(strobe == true) then create a strobe effect */ -uint16_t WS2812FX::blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) -{ - uint32_t cycleTime = (255 - SEGMENT.speed) * 20; +uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { + uint32_t cycleTime = (255 - SEGMENT.speed)*20; uint32_t onTime = FRAMETIME; - if (!strobe) - onTime += ((cycleTime * SEGMENT.intensity) >> 8); - cycleTime += FRAMETIME * 2; - uint32_t it = now / cycleTime; - uint32_t rem = now % cycleTime; + if (!strobe) onTime += ((cycleTime * SEGMENT.intensity) >> 8); + cycleTime += FRAMETIME*2; + uint32_t it = strip.now / cycleTime; + uint32_t rem = strip.now % cycleTime; bool on = false; - if (it != SEGENV.step // new iteration, force on state for one frame, even if set time is too brief - || rem <= onTime) - { + if (it != SEGENV.step //new iteration, force on state for one frame, even if set time is too brief + || rem <= onTime) { on = true; } - SEGENV.step = it; // save previous iteration + SEGENV.step = it; //save previous iteration uint32_t color = on ? color1 : color2; if (color == color1 && do_palette) { - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - } - else - fill(color); + } else SEGMENT.fill(color); return FRAMETIME; } + /* - * Normal blinking. 50% on/off time. + * Normal blinking. Intensity sets duty cycle. */ -uint16_t WS2812FX::mode_blink(void) -{ +uint16_t mode_blink(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), false, true); } +static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;01"; + /* * Classic Blink effect. Cycling through the rainbow. */ -uint16_t WS2812FX::mode_blink_rainbow(void) -{ - return blink(color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); +uint16_t mode_blink_rainbow(void) { + return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); } +static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!;01"; + /* * Classic Strobe effect. */ -uint16_t WS2812FX::mode_strobe(void) -{ +uint16_t mode_strobe(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), true, true); } +static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;01"; + /* * Classic Strobe effect. Cycling through the rainbow. */ -uint16_t WS2812FX::mode_strobe_rainbow(void) -{ - return blink(color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); +uint16_t mode_strobe_rainbow(void) { + return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); } +static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!;01"; + /* * Color wipe function * LEDs are turned on (color1) in sequence, then turned off (color2) in sequence. * if (bool rev == true) then LEDs are turned off in reverse order */ -uint16_t WS2812FX::color_wipe(bool rev, bool useRandomColors) -{ - uint32_t cycleTime = 750 + (255 - SEGMENT.speed) * 150; - uint32_t perc = now % cycleTime; +uint16_t color_wipe(bool rev, bool useRandomColors) { + uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; bool back = (prog > 32767); - if (back) - { + if (back) { prog -= 32767; - if (SEGENV.step == 0) - SEGENV.step = 1; - } - else - { - if (SEGENV.step == 2) - SEGENV.step = 3; // trigger color change + if (SEGENV.step == 0) SEGENV.step = 1; + } else { + if (SEGENV.step == 2) SEGENV.step = 3; //trigger color change } - if (useRandomColors) - { - if (SEGENV.call == 0) - { + if (useRandomColors) { + if (SEGENV.call == 0) { SEGENV.aux0 = random8(); SEGENV.step = 3; } - if (SEGENV.step == 1) - { // if flag set, change to new random color - SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0); + if (SEGENV.step == 1) { //if flag set, change to new random color + SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.step = 2; } - if (SEGENV.step == 3) - { - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1); + if (SEGENV.step == 3) { + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1); SEGENV.step = 0; } } uint16_t ledIndex = (prog * SEGLEN) >> 15; uint16_t rem = 0; - rem = (prog * SEGLEN) * 2; // mod 0xFFFF - rem /= (SEGMENT.intensity + 1); - if (rem > 255) - rem = 255; + rem = (prog * SEGLEN) * 2; //mod 0xFFFF + rem /= (SEGMENT.intensity +1); + if (rem > 255) rem = 255; - uint32_t col1 = useRandomColors ? color_wheel(SEGENV.aux1) : SEGCOLOR(1); - for (uint16_t i = 0; i < SEGLEN; i++) + uint32_t col1 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux1) : SEGCOLOR(1); + for (int i = 0; i < SEGLEN; i++) { - uint16_t index = (rev && back) ? SEGLEN - 1 - i : i; - uint32_t col0 = useRandomColors ? color_wheel(SEGENV.aux0) : color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); + uint16_t index = (rev && back)? SEGLEN -1 -i : i; + uint32_t col0 = useRandomColors? SEGMENT.color_wheel(SEGENV.aux0) : SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); if (i < ledIndex) { - setPixelColor(index, back ? col1 : col0); - } - else + SEGMENT.setPixelColor(index, back? col1 : col0); + } else { - setPixelColor(index, back ? col0 : col1); - if (i == ledIndex) - setPixelColor(index, color_blend(back ? col0 : col1, back ? col1 : col0, rem)); + SEGMENT.setPixelColor(index, back? col0 : col1); + if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem)); } } return FRAMETIME; } + /* * Lights all LEDs one after another. */ -uint16_t WS2812FX::mode_color_wipe(void) -{ +uint16_t mode_color_wipe(void) { return color_wipe(false, false); } +static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!;!"; + /* * Lights all LEDs one after another. Turns off opposite */ -uint16_t WS2812FX::mode_color_sweep(void) -{ +uint16_t mode_color_sweep(void) { return color_wipe(true, false); } +static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!;!"; + /* * Turns all LEDs after each other to a random color. * Then starts over with another color. */ -uint16_t WS2812FX::mode_color_wipe_random(void) -{ +uint16_t mode_color_wipe_random(void) { return color_wipe(false, true); } +static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!;;!"; + /* * Random color introduced alternating from start and end of strip. */ -uint16_t WS2812FX::mode_color_sweep_random(void) -{ +uint16_t mode_color_sweep_random(void) { return color_wipe(true, true); } +static const char _data_FX_MODE_COLOR_SWEEP_RANDOM[] PROGMEM = "Sweep Random@!;;!"; + /* - * Lights all LEDs in one random color up. Then switches them + * Lights all LEDs up in one random color. Then switches them * to the next random color. */ -uint16_t WS2812FX::mode_random_color(void) -{ - uint32_t cycleTime = 200 + (255 - SEGMENT.speed) * 50; - uint32_t it = now / cycleTime; - uint32_t rem = now % cycleTime; +uint16_t mode_random_color(void) { + uint32_t cycleTime = 200 + (255 - SEGMENT.speed)*50; + uint32_t it = strip.now / cycleTime; + uint32_t rem = strip.now % cycleTime; uint16_t fadedur = (cycleTime * SEGMENT.intensity) >> 8; uint32_t fade = 255; - if (fadedur) - { + if (fadedur) { fade = (rem * 255) / fadedur; - if (fade > 255) - fade = 255; + if (fade > 255) fade = 255; } - if (SEGENV.call == 0) - { + if (SEGENV.call == 0) { SEGENV.aux0 = random8(); SEGENV.step = 2; } - if (it != SEGENV.step) // new color + if (it != SEGENV.step) //new color { SEGENV.aux1 = SEGENV.aux0; - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); // aux0 will store our random color wheel index + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index SEGENV.step = it; } - fill(color_blend(color_wheel(SEGENV.aux1), color_wheel(SEGENV.aux0), fade)); + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); return FRAMETIME; } +static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01"; + /* * Lights every LED in a random color. Changes all LED at the same time * to new random colors. */ -uint16_t WS2812FX::dynamic(boolean smooth = false) -{ - if (!SEGENV.allocateData(SEGLEN)) - return mode_static(); // allocation failed +uint16_t mode_dynamic(void) { + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed - if (SEGENV.call == 0) - { - for (uint16_t i = 0; i < SEGLEN; i++) - SEGENV.data[i] = random8(); + if(SEGENV.call == 0) { + //SEGMENT.fill(BLACK); + for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); } - uint32_t cycleTime = 50 + (255 - SEGMENT.speed) * 15; - uint32_t it = now / cycleTime; - if (it != SEGENV.step && SEGMENT.speed != 0) // new color + uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; + uint32_t it = strip.now / cycleTime; + if (it != SEGENV.step && SEGMENT.speed != 0) //new color { - for (uint16_t i = 0; i < SEGLEN; i++) - { - if (random8() <= SEGMENT.intensity) - SEGENV.data[i] = random8(); + for (int i = 0; i < SEGLEN; i++) { + if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); // random color index } SEGENV.step = it; } - if (smooth) - { - for (uint16_t i = 0; i < SEGLEN; i++) - { - blendPixelColor(i, color_wheel(SEGENV.data[i]), 16); + if (SEGMENT.check1) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]), 16); } - } - else - { - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_wheel(SEGENV.data[i])); + } else { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i])); } } return FRAMETIME; } +static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!,,,,Smooth;;!"; + /* - * Original effect "Dynamic" + * effect "Dynamic" with smooth color-fading */ -uint16_t WS2812FX::mode_dynamic(void) -{ - return dynamic(false); -} +uint16_t mode_dynamic_smooth(void) { + bool old = SEGMENT.check1; + SEGMENT.check1 = true; + mode_dynamic(); + SEGMENT.check1 = old; + return FRAMETIME; + } +static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;;!"; -/* - * effect "Dynamic" with smoth color-fading - */ -uint16_t WS2812FX::mode_dynamic_smooth(void) -{ - return dynamic(true); -} /* * Does the "standby-breathing" of well known i-Devices. */ -uint16_t WS2812FX::mode_breath(void) -{ +uint16_t mode_breath(void) { uint16_t var = 0; - uint16_t counter = (now * ((SEGMENT.speed >> 3) + 10)); - counter = (counter >> 2) + (counter >> 4); // 0-16384 + 0-2048 - if (counter < 16384) - { - if (counter > 8192) - counter = 8192 - (counter - 8192); - var = sin16(counter) / 103; // close to parabolic in range 0-8192, max val. 23170 + uint16_t counter = (strip.now * ((SEGMENT.speed >> 3) +10)); + counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048 + if (counter < 16384) { + if (counter > 8192) counter = 8192 - (counter - 8192); + var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170 } uint8_t lum = 30 + var; - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } return FRAMETIME; } +static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01"; + /* * Fades the LEDs between two colors */ -uint16_t WS2812FX::mode_fade(void) -{ - uint16_t counter = (now * ((SEGMENT.speed >> 3) + 10)); +uint16_t mode_fade(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 3) +10)); uint8_t lum = triwave16(counter) >> 8; - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } return FRAMETIME; } +static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;01"; + /* * Scan mode parent function */ -uint16_t WS2812FX::scan(bool dual) +uint16_t scan(bool dual) { - uint32_t cycleTime = 750 + (255 - SEGMENT.speed) * 150; - uint32_t perc = now % cycleTime; + uint32_t cycleTime = 750 + (255 - SEGMENT.speed)*150; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; uint16_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 9); - uint16_t ledIndex = (prog * ((SEGLEN * 2) - size * 2)) >> 16; + uint16_t ledIndex = (prog * ((SEGLEN *2) - size *2)) >> 16; - fill(SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); int led_offset = ledIndex - (SEGLEN - size); led_offset = abs(led_offset); - if (dual) - { - for (uint16_t j = led_offset; j < led_offset + size; j++) - { - uint16_t i2 = SEGLEN - 1 - j; - setPixelColor(i2, color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2)) ? 2 : 0)); + if (dual) { + for (int j = led_offset; j < led_offset + size; j++) { + uint16_t i2 = SEGLEN -1 -j; + SEGMENT.setPixelColor(i2, SEGMENT.color_from_palette(i2, true, PALETTE_SOLID_WRAP, (SEGCOLOR(2))? 2:0)); } } - for (uint16_t j = led_offset; j < led_offset + size; j++) - { - setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + for (int j = led_offset; j < led_offset + size; j++) { + SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } + /* * Runs a single pixel back and forth. */ -uint16_t WS2812FX::mode_scan(void) -{ +uint16_t mode_scan(void) { return scan(false); } +static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots,,,,,Overlay;!,!,!;!"; + /* * Runs two pixel back and forth in opposite directions. */ -uint16_t WS2812FX::mode_dual_scan(void) -{ +uint16_t mode_dual_scan(void) { return scan(true); } +static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots,,,,,Overlay;!,!,!;!"; + /* * Cycles all LEDs at once through a rainbow. */ -uint16_t WS2812FX::mode_rainbow(void) -{ - uint16_t counter = (now * ((SEGMENT.speed >> 2) + 2)) & 0xFFFF; +uint16_t mode_rainbow(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - if (SEGMENT.intensity < 128) - { - fill(color_blend(color_wheel(counter), WHITE, 128 - SEGMENT.intensity)); - } - else - { - fill(color_wheel(counter)); + if (SEGMENT.intensity < 128){ + SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,128-SEGMENT.intensity)); + } else { + SEGMENT.fill(SEGMENT.color_wheel(counter)); } return FRAMETIME; } +static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!;01"; + /* * Cycles a rainbow over the entire string of LEDs. */ -uint16_t WS2812FX::mode_rainbow_cycle(void) -{ - uint16_t counter = (now * ((SEGMENT.speed >> 2) + 2)) & 0xFFFF; +uint16_t mode_rainbow_cycle(void) { + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - for (uint16_t i = 0; i < SEGLEN; i++) - { - // intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) - uint8_t index = (i * (16 << (SEGMENT.intensity / 29)) / SEGLEN) + counter; - setPixelColor(i, color_wheel(index)); + for (int i = 0; i < SEGLEN; i++) { + //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) + uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter; + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(index)); } return FRAMETIME; } +static const char _data_FX_MODE_RAINBOW_CYCLE[] PROGMEM = "Rainbow@!,Size;;!"; + + +/* + * Alternating pixels running function. + */ +uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) { + uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window + uint32_t cycleTime = 50 + (255 - SEGMENT.speed); + uint32_t it = strip.now / cycleTime; + bool usePalette = color1 == SEGCOLOR(0); + + for (int i = 0; i < SEGLEN; i++) { + uint32_t col = color2; + if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); + if (theatre) { + if ((i % width) == SEGENV.aux0) col = color1; + } else { + int8_t pos = (i % (width<<1)); + if ((pos < SEGENV.aux0-width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0+width))) col = color1; + } + SEGMENT.setPixelColor(i,col); + } + + if (it != SEGENV.step) { + SEGENV.aux0 = (SEGENV.aux0 +1) % (theatre ? width : (width<<1)); + SEGENV.step = it; + } + return FRAMETIME; +} + /* * Theatre-style crawling lights. * Inspired by the Adafruit examples. */ -uint16_t WS2812FX::mode_theater_chase(void) -{ +uint16_t mode_theater_chase(void) { return running(SEGCOLOR(0), SEGCOLOR(1), true); } +static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!,!;!"; + /* * Theatre-style crawling lights with rainbow effect. * Inspired by the Adafruit examples. */ -uint16_t WS2812FX::mode_theater_chase_rainbow(void) -{ - return running(color_wheel(SEGENV.step), SEGCOLOR(1), true); +uint16_t mode_theater_chase_rainbow(void) { + return running(SEGMENT.color_wheel(SEGENV.step), SEGCOLOR(1), true); } +static const char _data_FX_MODE_THEATER_CHASE_RAINBOW[] PROGMEM = "Theater Rainbow@!,Gap size;,!;!"; + /* * Running lights effect with smooth sine transition base. */ -uint16_t WS2812FX::running_base(bool saw, bool dual = false) -{ +uint16_t running_base(bool saw, bool dual=false) { uint8_t x_scale = SEGMENT.intensity >> 2; - uint32_t counter = (now * SEGMENT.speed) >> 9; + uint32_t counter = (strip.now * SEGMENT.speed) >> 9; - for (uint16_t i = 0; i < SEGLEN; i++) - { - uint16_t a = i * x_scale - counter; - if (saw) - { + for (int i = 0; i < SEGLEN; i++) { + uint16_t a = i*x_scale - counter; + if (saw) { a &= 0xFF; if (a < 16) { - a = 192 + a * 8; - } - else - { - a = map(a, 16, 255, 64, 192); + a = 192 + a*8; + } else { + a = map(a,16,255,64,192); } a = 255 - a; } uint8_t s = dual ? sin_gap(a) : sin8(a); - uint32_t ca = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s); - if (dual) - { - uint16_t b = (SEGLEN - 1 - i) * x_scale - counter; + uint32_t ca = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s); + if (dual) { + uint16_t b = (SEGLEN-1-i)*x_scale - counter; uint8_t t = sin_gap(b); - uint32_t cb = color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t); + uint32_t cb = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t); ca = color_blend(ca, cb, 127); } - setPixelColor(i, ca); + SEGMENT.setPixelColor(i, ca); } + return FRAMETIME; } + /* * Running lights in opposite directions. * Idea: Make the gap width controllable with a third slider in the future */ -uint16_t WS2812FX::mode_running_dual(void) -{ +uint16_t mode_running_dual(void) { return running_base(false, true); } +static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual@!,Wave width;L,!,R;!"; + /* * Running lights effect with smooth sine transition. */ -uint16_t WS2812FX::mode_running_lights(void) -{ +uint16_t mode_running_lights(void) { return running_base(false); } +static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width;!,!;!"; + /* * Running lights effect with sawtooth transition. */ -uint16_t WS2812FX::mode_saw(void) -{ +uint16_t mode_saw(void) { return running_base(true); } +static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!"; + /* * Blink several LEDs in random colors on, reset, repeat. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_twinkle(void) -{ - fill(SEGCOLOR(1)); +uint16_t mode_twinkle(void) { + SEGMENT.fade_out(224); - uint32_t cycleTime = 20 + (255 - SEGMENT.speed) * 5; - uint32_t it = now / cycleTime; + uint32_t cycleTime = 20 + (255 - SEGMENT.speed)*5; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { uint16_t maxOn = map(SEGMENT.intensity, 0, 255, 1, SEGLEN); // make sure at least one LED is on if (SEGENV.aux0 >= maxOn) { SEGENV.aux0 = 0; - SEGENV.aux1 = random16(); // new seed for our PRNG + SEGENV.aux1 = random16(); //new seed for our PRNG } SEGENV.aux0++; SEGENV.step = it; @@ -546,272 +592,238 @@ uint16_t WS2812FX::mode_twinkle(void) PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16; uint16_t j = p >> 16; - // setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); - setPixelColor(j, color_blend(SEGCOLOR(1), color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), 255)); // This supports RGBW. + SEGMENT.setPixelColor(j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } +static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; //pixels + /* * Dissolve function */ -uint16_t WS2812FX::dissolve(uint32_t color) -{ - bool wa = (SEGCOLOR(1) != 0 && _brightness < 255); // workaround, can't compare getPixel to color if not full brightness +uint16_t dissolve(uint32_t color) { + //bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness + if (SEGENV.call == 0) { + SEGMENT.fill(SEGCOLOR(1)); + } - for (uint16_t j = 0; j <= SEGLEN / 15; j++) - { - if (random8() <= SEGMENT.intensity) - { - for (uint8_t times = 0; times < 10; times++) // attempt to spawn a new pixel 5 times + for (int j = 0; j <= SEGLEN / 15; j++) { + if (random8() <= SEGMENT.intensity) { + for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times { uint16_t i = random16(SEGLEN); - if (SEGENV.aux0) - { // dissolve to primary/palette - if (getPixelColor(i) == SEGCOLOR(1) || wa) - { - if (color == SEGCOLOR(0)) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + if (SEGENV.aux0) { //dissolve to primary/palette + if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) /*|| wa*/) { + if (color == SEGCOLOR(0)) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } else { + SEGMENT.setPixelColor(i, color); } - else - { - setPixelColor(i, color); - } - break; // only spawn 1 new pixel per frame per 50 LEDs - } - } - else - { // dissolve to secondary - if (getPixelColor(i) != SEGCOLOR(1)) - { - setPixelColor(i, SEGCOLOR(1)); - break; + break; //only spawn 1 new pixel per frame per 50 LEDs } + } else { //dissolve to secondary + if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; } } } } } - if (SEGENV.call > (255 - SEGMENT.speed) + 15U) - { + if (SEGENV.step > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; - SEGENV.call = 0; + SEGENV.step = 0; + } else { + SEGENV.step++; } return FRAMETIME; } + /* * Blink several LEDs on and then off */ -uint16_t WS2812FX::mode_dissolve(void) -{ - return dissolve(SEGCOLOR(0)); +uint16_t mode_dissolve(void) { + return dissolve(SEGMENT.check1 ? SEGMENT.color_wheel(random8()) : SEGCOLOR(0)); } +static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed,,,,Random;!,!;!"; + /* * Blink several LEDs on and then off in random colors */ -uint16_t WS2812FX::mode_dissolve_random(void) -{ - return dissolve(color_wheel(random8())); +uint16_t mode_dissolve_random(void) { + return dissolve(SEGMENT.color_wheel(random8())); } +static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!;!"; + /* * Blinks one LED at a time. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_sparkle(void) -{ - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_sparkle(void) { + if (!SEGMENT.check2) for(int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - uint32_t cycleTime = 10 + (255 - SEGMENT.speed) * 2; - uint32_t it = now / cycleTime; + uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { SEGENV.aux0 = random16(SEGLEN); // aux0 stores the random led index SEGENV.step = it; } - setPixelColor(SEGENV.aux0, SEGCOLOR(0)); + SEGMENT.setPixelColor(SEGENV.aux0, SEGCOLOR(0)); return FRAMETIME; } +static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!;!;;m12=0"; + /* * Lights all LEDs in the color. Flashes single col 1 pixels randomly. (List name: Sparkle Dark) * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_flash_sparkle(void) -{ - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); +uint16_t mode_flash_sparkle(void) { + if (!SEGMENT.check2) for(uint16_t i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - if (now - SEGENV.aux0 > SEGENV.step) - { - if (random8((255 - SEGMENT.intensity) >> 4) == 0) - { - setPixelColor(random16(SEGLEN), SEGCOLOR(1)); // flash + if (strip.now - SEGENV.aux0 > SEGENV.step) { + if(random8((255-SEGMENT.intensity) >> 4) == 0) { + SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); //flash } - SEGENV.step = now; - SEGENV.aux0 = 255 - SEGMENT.speed; + SEGENV.step = strip.now; + SEGENV.aux0 = 255-SEGMENT.speed; } return FRAMETIME; } +static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!,,,,,Overlay;Bg,Fx;!;;m12=0"; + /* * Like flash sparkle. With more flash. * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ -uint16_t WS2812FX::mode_hyper_sparkle(void) -{ - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); +uint16_t mode_hyper_sparkle(void) { + if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - if (now - SEGENV.aux0 > SEGENV.step) - { - if (random8((255 - SEGMENT.intensity) >> 4) == 0) - { - for (uint16_t i = 0; i < MAX(1, SEGLEN / 3); i++) - { - setPixelColor(random16(SEGLEN), SEGCOLOR(1)); + if (strip.now - SEGENV.aux0 > SEGENV.step) { + if (random8((255-SEGMENT.intensity) >> 4) == 0) { + for (int i = 0; i < max(1, SEGLEN/3); i++) { + SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } } - SEGENV.step = now; - SEGENV.aux0 = 255 - SEGMENT.speed; + SEGENV.step = strip.now; + SEGENV.aux0 = 255-SEGMENT.speed; } return FRAMETIME; } +static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!,,,,,Overlay;Bg,Fx;!;;m12=0"; + /* * Strobe effect with different strobe count and pause, controlled by speed. */ -uint16_t WS2812FX::mode_multi_strobe(void) -{ - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_multi_strobe(void) { + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - SEGENV.aux0 = 50 + 20 * (uint16_t)(255 - SEGMENT.speed); + SEGENV.aux0 = 50 + 20*(uint16_t)(255-SEGMENT.speed); uint16_t count = 2 * ((SEGMENT.intensity / 10) + 1); - if (SEGENV.aux1 < count) - { - if ((SEGENV.aux1 & 1) == 0) - { - fill(SEGCOLOR(0)); + if(SEGENV.aux1 < count) { + if((SEGENV.aux1 & 1) == 0) { + SEGMENT.fill(SEGCOLOR(0)); SEGENV.aux0 = 15; - } - else - { + } else { SEGENV.aux0 = 50; } } - if (now - SEGENV.aux0 > SEGENV.step) - { + if (strip.now - SEGENV.aux0 > SEGENV.step) { SEGENV.aux1++; - if (SEGENV.aux1 > count) - SEGENV.aux1 = 0; - SEGENV.step = now; + if (SEGENV.aux1 > count) SEGENV.aux1 = 0; + SEGENV.step = strip.now; } return FRAMETIME; } +static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!;01"; + /* * Android loading circle */ -uint16_t WS2812FX::mode_android(void) -{ +uint16_t mode_android(void) { - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - if (SEGENV.aux1 > ((float)SEGMENT.intensity / 255.0) * (float)SEGLEN) + if (SEGENV.aux1 > ((float)SEGMENT.intensity/255.0)*(float)SEGLEN) { SEGENV.aux0 = 1; - } - else + } else { - if (SEGENV.aux1 < 2) - SEGENV.aux0 = 0; + if (SEGENV.aux1 < 2) SEGENV.aux0 = 0; } uint16_t a = SEGENV.step; if (SEGENV.aux0 == 0) { - if (SEGENV.call % 3 == 1) - { - a++; - } - else - { - SEGENV.aux1++; - } - } - else + if (SEGENV.call %3 == 1) {a++;} + else {SEGENV.aux1++;} + } else { a++; - if (SEGENV.call % 3 != 1) - SEGENV.aux1--; + if (SEGENV.call %3 != 1) SEGENV.aux1--; } - if (a >= SEGLEN) - a = 0; + if (a >= SEGLEN) a = 0; if (a + SEGENV.aux1 < SEGLEN) { - for (int i = a; i < a + SEGENV.aux1; i++) - { - setPixelColor(i, SEGCOLOR(0)); + for (int i = a; i < a+SEGENV.aux1; i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } - } - else + } else { - for (int i = a; i < SEGLEN; i++) - { - setPixelColor(i, SEGCOLOR(0)); + for (int i = a; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } - for (int i = 0; i < SEGENV.aux1 - (SEGLEN - a); i++) - { - setPixelColor(i, SEGCOLOR(0)); + for (int i = 0; i < SEGENV.aux1 - (SEGLEN -a); i++) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } } SEGENV.step = a; return 3 + ((8 * (uint32_t)(255 - SEGMENT.speed)) / SEGLEN); } +static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=1"; //vertical + /* * color chase function. * color1 = background color * color2 and color3 = colors of two adjacent leds */ -uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) -{ - uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); - uint16_t a = counter * SEGLEN >> 16; +uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); + uint16_t a = counter * SEGLEN >> 16; bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); - if (chase_random) - { - if (a < SEGENV.step) // we hit the start again, choose new color for Chase random + if (chase_random) { + if (a < SEGENV.step) //we hit the start again, choose new color for Chase random { - SEGENV.aux1 = SEGENV.aux0; // store previous random color - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); + SEGENV.aux1 = SEGENV.aux0; //store previous random color + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); } - color1 = color_wheel(SEGENV.aux0); + color1 = SEGMENT.color_wheel(SEGENV.aux0); } SEGENV.step = a; @@ -819,430 +831,315 @@ uint16_t WS2812FX::chase(uint32_t color1, uint32_t color2, uint32_t color3, bool uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); uint16_t b = a + size; //"trail" of chase, filled with color1 - if (b > SEGLEN) - b -= SEGLEN; + if (b > SEGLEN) b -= SEGLEN; uint16_t c = b + size; - if (c > SEGLEN) - c -= SEGLEN; + if (c > SEGLEN) c -= SEGLEN; - // background + //background if (do_palette) { - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } - } - else - fill(color1); + } else SEGMENT.fill(color1); - // if random, fill old background between a and end + //if random, fill old background between a and end if (chase_random) { - color1 = color_wheel(SEGENV.aux1); - for (uint16_t i = a; i < SEGLEN; i++) - setPixelColor(i, color1); + color1 = SEGMENT.color_wheel(SEGENV.aux1); + for (int i = a; i < SEGLEN; i++) + SEGMENT.setPixelColor(i, color1); } - // fill between points a and b with color2 + //fill between points a and b with color2 if (a < b) { - for (uint16_t i = a; i < b; i++) - setPixelColor(i, color2); - } - else - { - for (uint16_t i = a; i < SEGLEN; i++) // fill until end - setPixelColor(i, color2); - for (uint16_t i = 0; i < b; i++) // fill from start until b - setPixelColor(i, color2); + for (int i = a; i < b; i++) + SEGMENT.setPixelColor(i, color2); + } else { + for (int i = a; i < SEGLEN; i++) //fill until end + SEGMENT.setPixelColor(i, color2); + for (int i = 0; i < b; i++) //fill from start until b + SEGMENT.setPixelColor(i, color2); } - // fill between points b and c with color2 + //fill between points b and c with color2 if (b < c) { - for (uint16_t i = b; i < c; i++) - setPixelColor(i, color3); - } - else - { - for (uint16_t i = b; i < SEGLEN; i++) // fill until end - setPixelColor(i, color3); - for (uint16_t i = 0; i < c; i++) // fill from start until c - setPixelColor(i, color3); + for (int i = b; i < c; i++) + SEGMENT.setPixelColor(i, color3); + } else { + for (int i = b; i < SEGLEN; i++) //fill until end + SEGMENT.setPixelColor(i, color3); + for (int i = 0; i < c; i++) //fill from start until c + SEGMENT.setPixelColor(i, color3); } return FRAMETIME; } + /* * Bicolor chase, more primary color. */ -uint16_t WS2812FX::mode_chase_color(void) -{ +uint16_t mode_chase_color(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true); } +static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!"; + /* * Primary running followed by random color. */ -uint16_t WS2812FX::mode_chase_random(void) -{ +uint16_t mode_chase_random(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false); } +static const char _data_FX_MODE_CHASE_RANDOM[] PROGMEM = "Chase Random@!,Width;!,,!;!"; + /* * Primary, secondary running on rainbow. */ -uint16_t WS2812FX::mode_chase_rainbow(void) -{ +uint16_t mode_chase_rainbow(void) { uint8_t color_sep = 256 / SEGLEN; - if (color_sep == 0) - color_sep = 1; // correction for segments longer than 256 LEDs + if (color_sep == 0) color_sep = 1; // correction for segments longer than 256 LEDs uint8_t color_index = SEGENV.call & 0xFF; - uint32_t color = color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); + uint32_t color = SEGMENT.color_wheel(((SEGENV.step * color_sep) + color_index) & 0xFF); return chase(color, SEGCOLOR(0), SEGCOLOR(1), false); } +static const char _data_FX_MODE_CHASE_RAINBOW[] PROGMEM = "Chase Rainbow@!,Width;!,!;!"; + /* * Primary running on rainbow. */ -uint16_t WS2812FX::mode_chase_rainbow_white(void) -{ +uint16_t mode_chase_rainbow_white(void) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; - uint32_t color2 = color_wheel(((n * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); - uint32_t color3 = color_wheel(((m * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); + uint32_t color2 = SEGMENT.color_wheel(((n * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); + uint32_t color3 = SEGMENT.color_wheel(((m * 256 / SEGLEN) + (SEGENV.call & 0xFF)) & 0xFF); return chase(SEGCOLOR(0), color2, color3, false); } +static const char _data_FX_MODE_CHASE_RAINBOW_WHITE[] PROGMEM = "Rainbow Runner@!,Size;Bg;!"; + /* * Red - Amber - Green - Blue lights running */ -uint16_t WS2812FX::mode_colorful(void) -{ - uint8_t numColors = 4; // 3, 4, or 5 - uint32_t cols[9]{0x00FF0000, 0x00EEBB00, 0x0000EE00, 0x000077CC}; - if (SEGMENT.intensity > 160 || SEGMENT.palette) - { // palette or color - if (!SEGMENT.palette) - { +uint16_t mode_colorful(void) { + uint8_t numColors = 4; //3, 4, or 5 + uint32_t cols[9]{0x00FF0000,0x00EEBB00,0x0000EE00,0x000077CC}; + if (SEGMENT.intensity > 160 || SEGMENT.palette) { //palette or color + if (!SEGMENT.palette) { numColors = 3; - for (uint8_t i = 0; i < 3; i++) - cols[i] = SEGCOLOR(i); - } - else - { + for (size_t i = 0; i < 3; i++) cols[i] = SEGCOLOR(i); + } else { uint16_t fac = 80; - if (SEGMENT.palette == 52) - { - numColors = 5; - fac = 61; - } // C9 2 has 5 colors - for (uint8_t i = 0; i < numColors; i++) - { - cols[i] = color_from_palette(i * fac, false, true, 255); + if (SEGMENT.palette == 52) {numColors = 5; fac = 61;} //C9 2 has 5 colors + for (size_t i = 0; i < numColors; i++) { + cols[i] = SEGMENT.color_from_palette(i*fac, false, true, 255); } } - } - else if (SEGMENT.intensity < 80) // pastel (easter) colors + } else if (SEGMENT.intensity < 80) //pastel (easter) colors { cols[0] = 0x00FF8040; cols[1] = 0x00E5D241; cols[2] = 0x0077FF77; cols[3] = 0x0077F0F0; } - for (uint8_t i = numColors; i < numColors * 2 - 1; i++) - cols[i] = cols[i - numColors]; + for (size_t i = numColors; i < numColors*2 -1U; i++) cols[i] = cols[i-numColors]; uint32_t cycleTime = 50 + (8 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; if (it != SEGENV.step) { - if (SEGMENT.speed > 0) - SEGENV.aux0++; - if (SEGENV.aux0 >= numColors) - SEGENV.aux0 = 0; + if (SEGMENT.speed > 0) SEGENV.aux0++; + if (SEGENV.aux0 >= numColors) SEGENV.aux0 = 0; SEGENV.step = it; } - for (uint16_t i = 0; i < SEGLEN; i += numColors) + for (int i = 0; i < SEGLEN; i+= numColors) { - for (uint16_t j = 0; j < numColors; j++) - setPixelColor(i + j, cols[SEGENV.aux0 + j]); + for (int j = 0; j < numColors; j++) SEGMENT.setPixelColor(i + j, cols[SEGENV.aux0 + j]); } return FRAMETIME; } +static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!"; + /* * Emulates a traffic light. */ -uint16_t WS2812FX::mode_traffic_light(void) -{ - for (uint16_t i = 0; i < SEGLEN; i++) - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); +uint16_t mode_traffic_light(void) { + if (SEGLEN == 1) return mode_static(); + for (int i=0; i < SEGLEN; i++) + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; - for (int i = 0; i < SEGLEN - 2; i += 3) + for (int i = 0; i < SEGLEN-2 ; i+=3) { switch (SEGENV.aux0) { - case 0: - setPixelColor(i, 0x00FF0000); - mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed)); - break; - case 1: - setPixelColor(i, 0x00FF0000); - mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); - setPixelColor(i + 1, 0x00EECC00); - break; - case 2: - setPixelColor(i + 2, 0x0000FF00); - mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed)); - break; - case 3: - setPixelColor(i + 1, 0x00EECC00); - mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); - break; + case 0: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 1: SEGMENT.setPixelColor(i, 0x00FF0000); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed)); SEGMENT.setPixelColor(i+1, 0x00EECC00); break; + case 2: SEGMENT.setPixelColor(i+2, 0x0000FF00); mdelay = 150 + (100 * (uint32_t)(255 - SEGMENT.speed));break; + case 3: SEGMENT.setPixelColor(i+1, 0x00EECC00); mdelay = 150 + (20 * (uint32_t)(255 - SEGMENT.speed));break; } } - if (now - SEGENV.step > mdelay) + if (strip.now - SEGENV.step > mdelay) { SEGENV.aux0++; - if (SEGENV.aux0 == 1 && SEGMENT.intensity > 140) - SEGENV.aux0 = 2; // skip Red + Amber, to get US-style sequence - if (SEGENV.aux0 > 3) - SEGENV.aux0 = 0; - SEGENV.step = now; + if (SEGENV.aux0 == 1 && SEGMENT.intensity > 140) SEGENV.aux0 = 2; //skip Red + Amber, to get US-style sequence + if (SEGENV.aux0 > 3) SEGENV.aux0 = 0; + SEGENV.step = strip.now; } return FRAMETIME; } +static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US style;,!;!"; + /* * Sec flashes running on prim. */ #define FLASH_COUNT 4 -uint16_t WS2812FX::mode_chase_flash(void) -{ +uint16_t mode_chase_flash(void) { + if (SEGLEN == 1) return mode_static(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } uint16_t delay = 10 + ((30 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN); - if (flash_step < (FLASH_COUNT * 2)) - { - if (flash_step % 2 == 0) - { + if(flash_step < (FLASH_COUNT * 2)) { + if(flash_step % 2 == 0) { uint16_t n = SEGENV.step; uint16_t m = (SEGENV.step + 1) % SEGLEN; - setPixelColor(n, SEGCOLOR(1)); - setPixelColor(m, SEGCOLOR(1)); + SEGMENT.setPixelColor( n, SEGCOLOR(1)); + SEGMENT.setPixelColor( m, SEGCOLOR(1)); delay = 20; - } - else - { + } else { delay = 30; } - } - else - { + } else { SEGENV.step = (SEGENV.step + 1) % SEGLEN; } return delay; } +static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!"; + /* * Prim flashes running, followed by random color. */ -uint16_t WS2812FX::mode_chase_flash_random(void) -{ +uint16_t mode_chase_flash_random(void) { + if (SEGLEN == 1) return mode_static(); uint8_t flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for (uint16_t i = 0; i < SEGENV.step; i++) - { - setPixelColor(i, color_wheel(SEGENV.aux0)); + for (int i = 0; i < SEGENV.aux1; i++) { + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.aux0)); } uint16_t delay = 1 + ((10 * (uint16_t)(255 - SEGMENT.speed)) / SEGLEN); - if (flash_step < (FLASH_COUNT * 2)) - { - uint16_t n = SEGENV.step; - uint16_t m = (SEGENV.step + 1) % SEGLEN; - if (flash_step % 2 == 0) - { - setPixelColor(n, SEGCOLOR(0)); - setPixelColor(m, SEGCOLOR(0)); + if(flash_step < (FLASH_COUNT * 2)) { + uint16_t n = SEGENV.aux1; + uint16_t m = (SEGENV.aux1 + 1) % SEGLEN; + if(flash_step % 2 == 0) { + SEGMENT.setPixelColor( n, SEGCOLOR(0)); + SEGMENT.setPixelColor( m, SEGCOLOR(0)); delay = 20; - } - else - { - setPixelColor(n, color_wheel(SEGENV.aux0)); - setPixelColor(m, SEGCOLOR(1)); + } else { + SEGMENT.setPixelColor( n, SEGMENT.color_wheel(SEGENV.aux0)); + SEGMENT.setPixelColor( m, SEGCOLOR(1)); delay = 30; } - } - else - { - SEGENV.step = (SEGENV.step + 1) % SEGLEN; + } else { + SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; - if (SEGENV.step == 0) - { - SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); + if (SEGENV.aux1 == 0) { + SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); } } return delay; } +static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@!;!,!;!"; -/* - * Alternating pixels running function. - */ -uint16_t WS2812FX::running(uint32_t color1, uint32_t color2, bool theatre) -{ - uint8_t width = (theatre ? 3 : 1) + (SEGMENT.intensity >> 4); // window - uint32_t cycleTime = 50 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - bool usePalette = color1 == SEGCOLOR(0); - - for (uint16_t i = 0; i < SEGLEN; i++) - { - uint32_t col = color2; - if (usePalette) - color1 = color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); - if (theatre) - { - if ((i % width) == SEGENV.aux0) - col = color1; - } - else - { - int8_t pos = (i % (width << 1)); - if ((pos < SEGENV.aux0 - width) || ((pos >= SEGENV.aux0) && (pos < SEGENV.aux0 + width))) - col = color1; - } - setPixelColor(i, col); - } - - if (it != SEGENV.step) - { - SEGENV.aux0 = (SEGENV.aux0 + 1) % (theatre ? width : (width << 1)); - SEGENV.step = it; - } - return FRAMETIME; -} /* * Alternating color/sec pixels running. */ -uint16_t WS2812FX::mode_running_color(void) -{ +uint16_t mode_running_color(void) { return running(SEGCOLOR(0), SEGCOLOR(1)); } +static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!;!"; -/* - * Alternating red/white pixels running. - */ -uint16_t WS2812FX::mode_candy_cane(void) -{ - return running(RED, WHITE); -} - -/* - * Alternating orange/purple pixels running. - */ -uint16_t WS2812FX::mode_halloween(void) -{ - return running(PURPLE, ORANGE); -} /* * Random colored pixels running. ("Stream") */ -uint16_t WS2812FX::mode_running_random(void) -{ +uint16_t mode_running_random(void) { uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; - if (SEGENV.call == 0) - SEGENV.aux0 = random16(); // random seed for PRNG on start + uint32_t it = strip.now / cycleTime; + if (SEGENV.call == 0) SEGENV.aux0 = random16(); // random seed for PRNG on start - uint8_t zoneSize = ((255 - SEGMENT.intensity) >> 4) + 1; + uint8_t zoneSize = ((255-SEGMENT.intensity) >> 4) +1; uint16_t PRNG16 = SEGENV.aux0; uint8_t z = it % zoneSize; bool nzone = (!z && it != SEGENV.aux1); - for (int i = SEGLEN - 1; i >= 0; i--) - { // WLEDSR bugfix - if (nzone || z >= zoneSize) - { + for (int i=SEGLEN-1; i > 0; i--) { + if (nzone || z >= zoneSize) { uint8_t lastrand = PRNG16 >> 8; int16_t diff = 0; - while (abs(diff) < 42) - { // make sure the difference between adjacent colors is big enough + while (abs(diff) < 42) { // make sure the difference between adjacent colors is big enough PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next zone, next 'random' number diff = (PRNG16 >> 8) - lastrand; } - if (nzone) - { + if (nzone) { SEGENV.aux0 = PRNG16; // save next starting seed nzone = false; } z = 0; } - setPixelColor(i, color_wheel(PRNG16 >> 8)); + SEGMENT.setPixelColor(i, SEGMENT.color_wheel(PRNG16 >> 8)); z++; } SEGENV.aux1 = it; return FRAMETIME; } +static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;!"; -/* - * K.I.T.T. - */ -uint16_t WS2812FX::mode_larson_scanner(void) -{ - return larson_scanner(false); -} -uint16_t WS2812FX::larson_scanner(bool dual) -{ - uint16_t counter = now * ((SEGMENT.speed >> 2) + 8); - uint16_t index = counter * SEGLEN >> 16; +uint16_t larson_scanner(bool dual) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); + uint16_t index = counter * SEGLEN >> 16; - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); - if (SEGENV.step > index && SEGENV.step - index > SEGLEN / 2) - { + if (SEGENV.step > index && SEGENV.step - index > SEGLEN/2) { SEGENV.aux0 = !SEGENV.aux0; } - for (uint16_t i = SEGENV.step; i < index; i++) - { - uint16_t j = (SEGENV.aux0) ? i : SEGLEN - 1 - i; - setPixelColor(j, color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); + for (int i = SEGENV.step; i < index; i++) { + uint16_t j = (SEGENV.aux0)?i:SEGLEN-1-i; + SEGMENT.setPixelColor( j, SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0)); } - if (dual) - { + if (dual) { uint32_t c; - if (SEGCOLOR(2) != 0) - { + if (SEGCOLOR(2) != 0) { c = SEGCOLOR(2); - } - else - { - c = color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); + } else { + c = SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0); } - for (uint16_t i = SEGENV.step; i < index; i++) - { - uint16_t j = (SEGENV.aux0) ? SEGLEN - 1 - i : i; - setPixelColor(j, c); + for (int i = SEGENV.step; i < index; i++) { + uint16_t j = (SEGENV.aux0)?SEGLEN-1-i:i; + SEGMENT.setPixelColor(j, c); } } @@ -1250,227 +1147,250 @@ uint16_t WS2812FX::larson_scanner(bool dual) return FRAMETIME; } + +/* + * K.I.T.T. + */ +uint16_t mode_larson_scanner(void){ + return larson_scanner(false); +} +static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Fade rate;!,!;!;;m12=0"; + + +/* + * Creates two Larson scanners moving in opposite directions + * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h + */ +uint16_t mode_dual_larson_scanner(void){ + return larson_scanner(true); +} +static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,Fade rate;!,!,!;!;;m12=0"; + + /* * Firing comets from one end. "Lighthouse" */ -uint16_t WS2812FX::mode_comet(void) -{ - uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); - uint16_t index = counter * SEGLEN >> 16; - if (SEGENV.call == 0) - SEGENV.aux0 = index; +uint16_t mode_comet(void) { + if (SEGLEN == 1) return mode_static(); + uint16_t counter = strip.now * ((SEGMENT.speed >>2) +1); + uint16_t index = (counter * SEGLEN) >> 16; + if (SEGENV.call == 0) SEGENV.aux0 = index; - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); - setPixelColor(index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); - if (index > SEGENV.aux0) - { - for (uint16_t i = SEGENV.aux0; i < index; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor( index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + if (index > SEGENV.aux0) { + for (int i = SEGENV.aux0; i < index ; i++) { + SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } - } - else if (index < SEGENV.aux0 && index < 10) - { - for (uint16_t i = 0; i < index; i++) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } else if (index < SEGENV.aux0 && index < 10) { + for (int i = 0; i < index ; i++) { + SEGMENT.setPixelColor( i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } SEGENV.aux0 = index++; return FRAMETIME; } +static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!"; + /* * Fireworks function. */ -uint16_t WS2812FX::mode_fireworks() -{ - fade_out(0); - if (SEGENV.call == 0) - { +uint16_t mode_fireworks() { + if (SEGLEN == 1) return mode_static(); + const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); + const uint16_t height = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.fill(SEGCOLOR(1)); SEGENV.aux0 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX; } - bool valid1 = (SEGENV.aux0 < SEGLEN); - bool valid2 = (SEGENV.aux1 < SEGLEN); - uint32_t sv1 = 0, sv2 = 0; - if (valid1) - sv1 = getPixelColor(SEGENV.aux0); - if (valid2) - sv2 = getPixelColor(SEGENV.aux1); - blur(255 - SEGMENT.speed); - if (valid1) - setPixelColor(SEGENV.aux0, sv1); - if (valid2) - setPixelColor(SEGENV.aux1, sv2); + SEGMENT.fade_out(128); - for (uint16_t i = 0; i < MAX(1, SEGLEN / 20); i++) - { - if (random8(129 - (SEGMENT.intensity >> 1)) == 0) - { - uint16_t index = random(SEGLEN); - setPixelColor(index, color_from_palette(random8(), false, false, 0)); - SEGENV.aux1 = SEGENV.aux0; - SEGENV.aux0 = index; + bool valid1 = (SEGENV.aux0 < width*height); + bool valid2 = (SEGENV.aux1 < width*height); + uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte + uint32_t sv1 = 0, sv2 = 0; + if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color + if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1); + if (!SEGENV.step) SEGMENT.blur(16); + if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur + if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur + + for (int i=0; i> 1)) == 0) { + uint16_t index = random16(width*height); + x = index % width; + y = index / width; + uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, col); + else SEGMENT.setPixelColor(index, col); + SEGENV.aux1 = SEGENV.aux0; // old spark + SEGENV.aux0 = index; // remember where spark occured } } return FRAMETIME; } +static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;!;12;ix=192,pal=11"; -// Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h -uint16_t WS2812FX::mode_rain() -{ + +//Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h +uint16_t mode_rain() { + if (SEGLEN == 1) return mode_static(); + const uint16_t width = SEGMENT.virtualWidth(); + const uint16_t height = SEGMENT.virtualHeight(); SEGENV.step += FRAMETIME; - if (SEGENV.step > SPEED_FORMULA_L) - { - SEGENV.step = 0; - // shift all leds left - uint32_t ctemp = getPixelColor(0); - for (uint16_t i = 0; i < SEGLEN - 1; i++) - { - setPixelColor(i, getPixelColor(i + 1)); + if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) { + SEGENV.step = 1; + if (strip.isMatrix) { + //uint32_t ctemp[width]; + //for (int i = 0; i= width*height) SEGENV.aux0 = 0; // ignore + if (SEGENV.aux1 >= width*height) SEGENV.aux1 = 0; } return mode_fireworks(); } +static const char _data_FX_MODE_RAIN[] PROGMEM = "Rain@!,Spawning rate;!,!;!;12;ix=128,pal=0"; + /* * Fire flicker function */ -uint16_t WS2812FX::mode_fire_flicker(void) -{ +uint16_t mode_fire_flicker(void) { uint32_t cycleTime = 40 + (255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) - return FRAMETIME; + uint32_t it = strip.now / cycleTime; + if (SEGENV.step == it) return FRAMETIME; byte w = (SEGCOLOR(0) >> 24); byte r = (SEGCOLOR(0) >> 16); - byte g = (SEGCOLOR(0) >> 8); - byte b = (SEGCOLOR(0)); + byte g = (SEGCOLOR(0) >> 8); + byte b = (SEGCOLOR(0) ); byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; - lum /= (((256 - SEGMENT.intensity) / 16) + 1); - for (uint16_t i = 0; i < SEGLEN; i++) - { + lum /= (((256-SEGMENT.intensity)/16)+1); + for (int i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); - if (SEGMENT.palette == 0) - { - setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); - } - else - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); + if (SEGMENT.palette == 0) { + SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); + } else { + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, 255 - flicker)); } } SEGENV.step = it; return FRAMETIME; } +static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;01"; + /* * Gradient run base function */ -uint16_t WS2812FX::gradient_base(bool loading) -{ - uint16_t counter = now * ((SEGMENT.speed >> 2) + 1); +uint16_t gradient_base(bool loading) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); uint16_t pp = counter * SEGLEN >> 16; - if (SEGENV.call == 0) - pp = 0; - float val; // 0.0 = sec 1.0 = pri - float brd = loading ? SEGMENT.intensity : SEGMENT.intensity / 2; - if (brd < 1.0) - brd = 1.0; - int p1 = pp - SEGLEN; - int p2 = pp + SEGLEN; + if (SEGENV.call == 0) pp = 0; + float val; //0.0 = sec 1.0 = pri + float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2; + if (brd <1.0) brd = 1.0; + int p1 = pp-SEGLEN; + int p2 = pp+SEGLEN; - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { if (loading) { - val = abs(((i > pp) ? p2 : pp) - i); + val = abs(((i>pp) ? p2:pp) -i); + } else { + val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); } - else - { - val = MIN(abs(pp - i), MIN(abs(p1 - i), abs(p2 - i))); - } - val = (brd > val) ? val / brd * 255 : 255; - setPixelColor(i, color_blend(SEGCOLOR(0), color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); + val = (brd > val) ? val/brd * 255 : 255; + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); } return FRAMETIME; } + /* * Gradient run */ -uint16_t WS2812FX::mode_gradient(void) -{ +uint16_t mode_gradient(void) { return gradient_base(false); } +static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!;!;;ix=16"; + /* * Gradient run with hard transition */ -uint16_t WS2812FX::mode_loading(void) -{ +uint16_t mode_loading(void) { return gradient_base(true); } +static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16"; -// American Police Light with all LEDs Red and Blue -uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2) -{ - uint16_t delay = 1 + (FRAMETIME << 3) / SEGLEN; // longer segments should change faster - uint32_t it = now / map(SEGMENT.speed, 0, 255, delay << 4, delay); + +//American Police Light with all LEDs Red and Blue +uint16_t police_base(uint32_t color1, uint32_t color2) { + if (SEGLEN == 1) return mode_static(); + uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster + uint32_t it = strip.now / map(SEGMENT.speed, 0, 255, delay<<4, delay); uint16_t offset = it % SEGLEN; - uint16_t width = ((SEGLEN * (SEGMENT.intensity + 1)) >> 9); // max width is half the strip - if (!width) - width = 1; - for (uint16_t i = 0; i < width; i++) - { + uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip + if (!width) width = 1; + for (int i = 0; i < width; i++) { uint16_t indexR = (offset + i) % SEGLEN; - uint16_t indexB = (offset + i + (SEGLEN >> 1)) % SEGLEN; - setPixelColor(indexR, color1); - setPixelColor(indexB, color2); + uint16_t indexB = (offset + i + (SEGLEN>>1)) % SEGLEN; + SEGMENT.setPixelColor(indexR, color1); + SEGMENT.setPixelColor(indexB, color2); } return FRAMETIME; } -// Police Lights Red and Blue -uint16_t WS2812FX::mode_police() -{ - fill(SEGCOLOR(1)); - return police_base(RED, BLUE); -} -// Police Lights with custom colors -uint16_t WS2812FX::mode_two_dots() +//Police Lights Red and Blue +//uint16_t mode_police() +//{ +// SEGMENT.fill(SEGCOLOR(1)); +// return police_base(RED, BLUE); +//} +//static const char _data_FX_MODE_POLICE[] PROGMEM = "Police@!,Width;,Bg;0"; + + +//Police Lights with custom colors +uint16_t mode_two_dots() { - fill(SEGCOLOR(2)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2)); uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1); - return police_base(SEGCOLOR(0), color2); } +static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size,,,,,Overlay;1,2,Bg;!"; + /* * Fairy, inspired by https://www.youtube.com/watch?v=zeOw5MZWq24 */ -// 4 bytes -typedef struct Flasher -{ +//4 bytes +typedef struct Flasher { uint16_t stateStart; uint8_t stateDur; bool stateOn; @@ -1479,367 +1399,313 @@ typedef struct Flasher #define FLASHERS_PER_ZONE 6 #define MAX_SHIMMER 92 -uint16_t WS2812FX::mode_fairy() -{ - // set every pixel to a 'random' color from palette (using seed so it doesn't change between frames) - uint16_t PRNG16 = 5100 + _segment_index; - for (uint16_t i = 0; i < SEGLEN; i++) - { - PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number - setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0)); +uint16_t mode_fairy() { + //set every pixel to a 'random' color from palette (using seed so it doesn't change between frames) + uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); + for (int i = 0; i < SEGLEN; i++) { + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0)); } - // amount of flasher pixels depending on intensity (0: none, 255: every LED) - if (SEGMENT.intensity == 0) - return FRAMETIME; - uint8_t flasherDistance = ((255 - SEGMENT.intensity) / 28) + 1; // 1-10 - uint16_t numFlashers = (SEGLEN / flasherDistance) + 1; + //amount of flasher pixels depending on intensity (0: none, 255: every LED) + if (SEGMENT.intensity == 0) return FRAMETIME; + uint8_t flasherDistance = ((255 - SEGMENT.intensity) / 28) +1; //1-10 + uint16_t numFlashers = (SEGLEN / flasherDistance) +1; uint16_t dataSize = sizeof(flasher) * numFlashers; - if (!SEGENV.allocateData(dataSize)) - return FRAMETIME; // allocation failed - Flasher *flashers = reinterpret_cast(SEGENV.data); - uint16_t now16 = now & 0xFFFF; + if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed + Flasher* flashers = reinterpret_cast(SEGENV.data); + uint16_t now16 = strip.now & 0xFFFF; - // Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers - uint16_t zones = numFlashers / FLASHERS_PER_ZONE; - if (!zones) - zones = 1; - uint8_t flashersInZone = numFlashers / zones; - uint8_t flasherBri[FLASHERS_PER_ZONE * 2 - 1]; + //Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers + uint16_t zones = numFlashers/FLASHERS_PER_ZONE; + if (!zones) zones = 1; + uint8_t flashersInZone = numFlashers/zones; + uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1]; - for (uint16_t z = 0; z < zones; z++) - { + for (int z = 0; z < zones; z++) { uint16_t flasherBriSum = 0; - uint16_t firstFlasher = z * flashersInZone; - if (z == zones - 1) - flashersInZone = numFlashers - (flashersInZone * (zones - 1)); + uint16_t firstFlasher = z*flashersInZone; + if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1)); - for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) - { + for (int f = firstFlasher; f < firstFlasher + flashersInZone; f++) { uint16_t stateTime = now16 - flashers[f].stateStart; - // random on/off time reached, switch state - if (stateTime > flashers[f].stateDur * 10) - { + //random on/off time reached, switch state + if (stateTime > flashers[f].stateDur * 10) { flashers[f].stateOn = !flashers[f].stateOn; - if (flashers[f].stateOn) - { + if (flashers[f].stateOn) { flashers[f].stateDur = 12 + random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms - } - else - { + } else { flashers[f].stateDur = 20 + random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms } - // flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1)); + //flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1)); flashers[f].stateStart = now16; - if (stateTime < 255) - { - flashers[f].stateStart -= 255 - stateTime; // start early to get correct bri - flashers[f].stateDur += 26 - stateTime / 10; + if (stateTime < 255) { + flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri + flashers[f].stateDur += 26 - stateTime/10; stateTime = 255 - stateTime; - } - else - { + } else { stateTime = 0; } } - if (stateTime > 255) - stateTime = 255; // for flasher brightness calculation, fades in first 255 ms of state - // flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-gamma8((510 - stateTime) >> 1) : gamma8((510 - stateTime) >> 1); + if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state + //flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-SEGMENT.gamma8((510 - stateTime) >> 1) : SEGMENT.gamma8((510 - stateTime) >> 1); flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0); flasherBriSum += flasherBri[f - firstFlasher]; } - // dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on + //dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on uint8_t avgFlasherBri = flasherBriSum / flashersInZone; - uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); // 183-255, suitable for 1/5th of LEDs flashers + uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers - for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) - { + for (int f = firstFlasher; f < firstFlasher + flashersInZone; f++) { uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255; - PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number - uint16_t flasherPos = f * flasherDistance; - setPixelColor(flasherPos, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), bri)); - for (uint16_t i = flasherPos + 1; i < flasherPos + flasherDistance && i < SEGLEN; i++) - { - PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number - setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri)); + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number + uint16_t flasherPos = f*flasherDistance; + SEGMENT.setPixelColor(flasherPos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), bri)); + for (int i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) { + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri)); } } } return FRAMETIME; } +static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy@!,# of flashers;!,!;!"; + /* - * Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on getPixelColor + * Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on strip.getPixelColor * Warning: Uses 4 bytes of segment data per pixel */ -uint16_t WS2812FX::mode_fairytwinkle() -{ +uint16_t mode_fairytwinkle() { uint16_t dataSize = sizeof(flasher) * SEGLEN; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Flasher *flashers = reinterpret_cast(SEGENV.data); - uint16_t now16 = now & 0xFFFF; - uint16_t PRNG16 = 5100 + _segment_index; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Flasher* flashers = reinterpret_cast(SEGENV.data); + uint16_t now16 = strip.now & 0xFFFF; + uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); - uint16_t riseFallTime = 400 + (255 - SEGMENT.speed) * 3; - uint16_t maxDur = riseFallTime / 100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); + uint16_t riseFallTime = 400 + (255-SEGMENT.speed)*3; + uint16_t maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); - for (uint16_t f = 0; f < SEGLEN; f++) - { + for (int f = 0; f < SEGLEN; f++) { uint16_t stateTime = now16 - flashers[f].stateStart; - // random on/off time reached, switch state - if (stateTime > flashers[f].stateDur * 100) - { + //random on/off time reached, switch state + if (stateTime > flashers[f].stateDur * 100) { flashers[f].stateOn = !flashers[f].stateOn; bool init = !flashers[f].stateDur; - if (flashers[f].stateOn) - { - flashers[f].stateDur = riseFallTime / 100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) + 1; - } - else - { - flashers[f].stateDur = riseFallTime / 100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) + 1; + if (flashers[f].stateOn) { + flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1; + } else { + flashers[f].stateDur = riseFallTime/100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) +1; } flashers[f].stateStart = now16; stateTime = 0; - if (init) - { - flashers[f].stateStart -= riseFallTime; // start lit - flashers[f].stateDur = riseFallTime / 100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) + 5; // fire up a little quicker + if (init) { + flashers[f].stateStart -= riseFallTime; //start lit + flashers[f].stateDur = riseFallTime/100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker stateTime = riseFallTime; } } - if (flashers[f].stateOn && flashers[f].stateDur > maxDur) - flashers[f].stateDur = maxDur; // react more quickly on intensity change - if (stateTime > riseFallTime) - stateTime = riseFallTime; // for flasher brightness calculation, fades in first 255 ms of state + if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change + if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state uint8_t fadeprog = 255 - ((stateTime * 255) / riseFallTime); - uint8_t flasherBri = (flashers[f].stateOn) ? 255 - gamma8(fadeprog) : gamma8(fadeprog); + uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog); uint16_t lastR = PRNG16; uint16_t diff = 0; - while (diff < 0x4000) - { // make sure colors of two adjacent LEDs differ enough - PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number + while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough + PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16; } - setPixelColor(f, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri)); + SEGMENT.setPixelColor(f, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri)); } return FRAMETIME; } +static const char _data_FX_MODE_FAIRYTWINKLE[] PROGMEM = "Fairytwinkle@!,!;!,!;!;;m12=0"; //pixels + /* * Tricolor chase function */ -uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2) -{ - uint32_t cycleTime = 50 + ((255 - SEGMENT.speed) << 1); - uint32_t it = now / cycleTime; // iterator - uint8_t width = (1 + (SEGMENT.intensity >> 4)); // value of 1-16 for each colour - uint8_t index = it % (width * 3); +uint16_t tricolor_chase(uint32_t color1, uint32_t color2) { + uint32_t cycleTime = 50 + ((255 - SEGMENT.speed)<<1); + uint32_t it = strip.now / cycleTime; // iterator + uint8_t width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour + uint8_t index = it % (width*3); - for (uint16_t i = 0; i < SEGLEN; i++, index++) - { - if (index > (width * 3) - 1) - index = 0; + for (int i = 0; i < SEGLEN; i++, index++) { + if (index > (width*3)-1) index = 0; uint32_t color = color1; - if (index > (width << 1) - 1) - color = color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); - else if (index > width - 1) - color = color2; + if (index > (width<<1)-1) color = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1); + else if (index > width-1) color = color2; - setPixelColor(SEGLEN - i - 1, color); + SEGMENT.setPixelColor(SEGLEN - i -1, color); } return FRAMETIME; } + /* * Tricolor chase mode */ -uint16_t WS2812FX::mode_tricolor_chase(void) -{ +uint16_t mode_tricolor_chase(void) { return tricolor_chase(SEGCOLOR(2), SEGCOLOR(0)); } +static const char _data_FX_MODE_TRICOLOR_CHASE[] PROGMEM = "Chase 3@!,Size;1,2,3;!"; + /* * ICU mode */ -uint16_t WS2812FX::mode_icu(void) -{ +uint16_t mode_icu(void) { uint16_t dest = SEGENV.step & 0xFFFF; - uint8_t space = (SEGMENT.intensity >> 3) + 2; + uint8_t space = (SEGMENT.intensity >> 3) +2; - fill(SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); - byte pindex = map(dest, 0, SEGLEN - SEGLEN / space, 0, 255); - uint32_t col = color_from_palette(pindex, false, false, 0); + byte pindex = map(dest, 0, SEGLEN-SEGLEN/space, 0, 255); + uint32_t col = SEGMENT.color_from_palette(pindex, false, false, 0); - setPixelColor(dest, col); - setPixelColor(dest + SEGLEN / space, col); + SEGMENT.setPixelColor(dest, col); + SEGMENT.setPixelColor(dest + SEGLEN/space, col); - if (SEGENV.aux0 == dest) - { // pause between eye movements - if (random8(6) == 0) - { // blink once in a while - setPixelColor(dest, SEGCOLOR(1)); - setPixelColor(dest + SEGLEN / space, SEGCOLOR(1)); + if(SEGENV.aux0 == dest) { // pause between eye movements + if(random8(6) == 0) { // blink once in a while + SEGMENT.setPixelColor(dest, SEGCOLOR(1)); + SEGMENT.setPixelColor(dest + SEGLEN/space, SEGCOLOR(1)); return 200; } - SEGENV.aux0 = random16(SEGLEN - SEGLEN / space); + SEGENV.aux0 = random16(SEGLEN-SEGLEN/space); return 1000 + random16(2000); } - if (SEGENV.aux0 > SEGENV.step) - { + if(SEGENV.aux0 > SEGENV.step) { SEGENV.step++; dest++; - } - else if (SEGENV.aux0 < SEGENV.step) - { + } else if (SEGENV.aux0 < SEGENV.step) { SEGENV.step--; dest--; } - setPixelColor(dest, col); - setPixelColor(dest + SEGLEN / space, col); + SEGMENT.setPixelColor(dest, col); + SEGMENT.setPixelColor(dest + SEGLEN/space, col); return SPEED_FORMULA_L; } +static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!,,,,,Overlay;!,!;!"; + /* * Custom mode by Aircoookie. Color Wipe, but with 3 colors */ -uint16_t WS2812FX::mode_tricolor_wipe(void) -{ - uint32_t cycleTime = 1000 + (255 - SEGMENT.speed) * 200; - uint32_t perc = now % cycleTime; +uint16_t mode_tricolor_wipe(void) { + uint32_t cycleTime = 1000 + (255 - SEGMENT.speed)*200; + uint32_t perc = strip.now % cycleTime; uint16_t prog = (perc * 65535) / cycleTime; uint16_t ledIndex = (prog * SEGLEN * 3) >> 16; uint16_t ledOffset = ledIndex; - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); } - if (ledIndex < SEGLEN) - { // wipe from 0 to 1 - for (uint16_t i = 0; i < SEGLEN; i++) + if(ledIndex < SEGLEN) { //wipe from 0 to 1 + for (int i = 0; i < SEGLEN; i++) { - setPixelColor(i, (i > ledOffset) ? SEGCOLOR(0) : SEGCOLOR(1)); + SEGMENT.setPixelColor(i, (i > ledOffset)? SEGCOLOR(0) : SEGCOLOR(1)); } - } - else if (ledIndex < SEGLEN * 2) - { // wipe from 1 to 2 + } else if (ledIndex < SEGLEN*2) { //wipe from 1 to 2 ledOffset = ledIndex - SEGLEN; - for (uint16_t i = ledOffset + 1; i < SEGLEN; i++) + for (int i = ledOffset +1; i < SEGLEN; i++) { - setPixelColor(i, SEGCOLOR(1)); + SEGMENT.setPixelColor(i, SEGCOLOR(1)); } - } - else // wipe from 2 to 0 + } else //wipe from 2 to 0 { - ledOffset = ledIndex - SEGLEN * 2; - for (uint16_t i = 0; i <= ledOffset; i++) + ledOffset = ledIndex - SEGLEN*2; + for (int i = 0; i <= ledOffset; i++) { - setPixelColor(i, SEGCOLOR(0)); + SEGMENT.setPixelColor(i, SEGCOLOR(0)); } } return FRAMETIME; } +static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!"; + /* * Fades between 3 colors * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/TriFade.h * Modified by Aircoookie */ -uint16_t WS2812FX::mode_tricolor_fade(void) -{ - uint16_t counter = now * ((SEGMENT.speed >> 3) + 1); +uint16_t mode_tricolor_fade(void) { + uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1); uint32_t prog = (counter * 768) >> 16; uint32_t color1 = 0, color2 = 0; byte stage = 0; - if (prog < 256) - { + if(prog < 256) { color1 = SEGCOLOR(0); color2 = SEGCOLOR(1); stage = 0; - } - else if (prog < 512) - { + } else if(prog < 512) { color1 = SEGCOLOR(1); color2 = SEGCOLOR(2); stage = 1; - } - else - { + } else { color1 = SEGCOLOR(2); color2 = SEGCOLOR(0); stage = 2; } byte stp = prog; // % 256 - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { uint32_t color; - if (stage == 2) - { - color = color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); - } - else if (stage == 1) - { - color = color_blend(color1, color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), stp); - } - else - { + if (stage == 2) { + color = color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), color2, stp); + } else if (stage == 1) { + color = color_blend(color1, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), stp); + } else { color = color_blend(color1, color2, stp); } - setPixelColor(i, color); + SEGMENT.setPixelColor(i, color); } return FRAMETIME; } +static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade@!;1,2,3;!"; + /* * Creates random comets * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/MultiComet.h */ -uint16_t WS2812FX::mode_multi_comet(void) -{ +uint16_t mode_multi_comet(void) { uint32_t cycleTime = 10 + (uint32_t)(255 - SEGMENT.speed); - uint32_t it = now / cycleTime; - if (SEGENV.step == it) - return FRAMETIME; - if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) - return mode_static(); // allocation failed + uint32_t it = strip.now / cycleTime; + if (SEGENV.step == it) return FRAMETIME; + if (!SEGENV.allocateData(sizeof(uint16_t) * 8)) return mode_static(); //allocation failed - fade_out(SEGMENT.intensity); + SEGMENT.fade_out(SEGMENT.intensity); - uint16_t *comets = reinterpret_cast(SEGENV.data); + uint16_t* comets = reinterpret_cast(SEGENV.data); - for (uint8_t i = 0; i < 8; i++) - { - if (comets[i] < SEGLEN) - { + for (int i=0; i < 8; i++) { + if(comets[i] < SEGLEN) { uint16_t index = comets[i]; if (SEGCOLOR(2) != 0) { - setPixelColor(index, i % 2 ? color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2)); - } - else + SEGMENT.setPixelColor(index, i % 2 ? SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(2)); + } else { - setPixelColor(index, color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0)); } comets[i]++; - } - else - { - if (!random(SEGLEN)) - { + } else { + if(!random(SEGLEN)) { comets[i] = 0; } } @@ -1848,42 +1714,31 @@ uint16_t WS2812FX::mode_multi_comet(void) SEGENV.step = it; return FRAMETIME; } +static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet"; -/* - * Creates two Larson scanners moving in opposite directions - * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/DualLarson.h - */ -uint16_t WS2812FX::mode_dual_larson_scanner(void) -{ - return larson_scanner(true); -} /* * Running random pixels ("Stream 2") * Custom mode by Keith Lord: https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/RandomChase.h */ -uint16_t WS2812FX::mode_random_chase(void) -{ - if (SEGENV.call == 0) - { +uint16_t mode_random_chase(void) { + if (SEGENV.call == 0) { SEGENV.step = RGBW32(random8(), random8(), random8(), 0); SEGENV.aux0 = random16(); } uint16_t prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; uint32_t color = SEGENV.step; random16_set_seed(SEGENV.aux0); - for (int i = SEGLEN - 1; i >= 0; i--) - { // WLEDSR bugfix + for (int i = SEGLEN -1; i > 0; i--) { uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8(); - uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); - uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); + uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); + uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); color = RGBW32(r, g, b, 0); - setPixelColor(i, r, g, b); - if (i == SEGLEN - 1 && SEGENV.aux1 != (it & 0xFFFF)) - { // new first color in next frame + SEGMENT.setPixelColor(i, r, g, b); + if (i == SEGLEN -1 && SEGENV.aux1 != (it & 0xFFFF)) { //new first color in next frame SEGENV.step = color; SEGENV.aux0 = random16_get_seed(); } @@ -1894,116 +1749,105 @@ uint16_t WS2812FX::mode_random_chase(void) random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } +static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2@!;;"; -// 7 bytes -typedef struct Oscillator -{ + +//7 bytes +typedef struct Oscillator { int16_t pos; - int8_t size; - int8_t dir; - int8_t speed; + int8_t size; + int8_t dir; + int8_t speed; } oscillator; /* / Oscillating bars of color, updated with standard framerate */ -uint16_t WS2812FX::mode_oscillate(void) -{ +uint16_t mode_oscillate(void) { uint8_t numOscillators = 3; uint16_t dataSize = sizeof(oscillator) * numOscillators; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Oscillator *oscillators = reinterpret_cast(SEGENV.data); + Oscillator* oscillators = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - oscillators[0] = {(int16_t)(SEGLEN / 4), (int8_t)(SEGLEN / 8), 1, 1}; - oscillators[1] = {(int16_t)(SEGLEN / 4 * 3), (int8_t)(SEGLEN / 8), 1, 2}; - oscillators[2] = {(int16_t)(SEGLEN / 4 * 2), (int8_t)(SEGLEN / 8), -1, 1}; + oscillators[0] = {(int16_t)(SEGLEN/4), (int8_t)(SEGLEN/8), 1, 1}; + oscillators[1] = {(int16_t)(SEGLEN/4*3), (int8_t)(SEGLEN/8), 1, 2}; + oscillators[2] = {(int16_t)(SEGLEN/4*2), (int8_t)(SEGLEN/8), -1, 1}; } uint32_t cycleTime = 20 + (2 * (uint32_t)(255 - SEGMENT.speed)); - uint32_t it = now / cycleTime; + uint32_t it = strip.now / cycleTime; - for (uint8_t i = 0; i < numOscillators; i++) - { + for (int i = 0; i < numOscillators; i++) { // if the counter has increased, move the oscillator by the random step - if (it != SEGENV.step) - oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; - oscillators[i].size = SEGLEN / (3 + SEGMENT.intensity / 8); - if ((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) - { + if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed; + oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8); + if((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) { oscillators[i].pos = 0; oscillators[i].dir = 1; // make bigger steps for faster speeds - oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4) : random8(1, 3); + oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4):random8(1, 3); } - if ((oscillators[i].dir == 1) && (oscillators[i].pos >= (SEGLEN - 1))) - { + if((oscillators[i].dir == 1) && (oscillators[i].pos >= (SEGLEN - 1))) { oscillators[i].pos = SEGLEN - 1; oscillators[i].dir = -1; - oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4) : random8(1, 3); + oscillators[i].speed = SEGMENT.speed > 100 ? random8(2, 4):random8(1, 3); } } - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { uint32_t color = BLACK; - for (uint8_t j = 0; j < numOscillators; j++) - { - if (i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) - { + for (int j = 0; j < numOscillators; j++) { + if(i >= oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) { color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128); } } - setPixelColor(i, color); + SEGMENT.setPixelColor(i, color); } SEGENV.step = it; return FRAMETIME; } +static const char _data_FX_MODE_OSCILLATE[] PROGMEM = "Oscillate"; -uint16_t WS2812FX::mode_lightning(void) -{ - uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash - uint16_t ledlen = 1 + random16(SEGLEN - ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) - uint8_t bri = 255 / random8(1, 3); - if (SEGENV.aux1 == 0) // init, leader flash +//TODO +uint16_t mode_lightning(void) { + if (SEGLEN == 1) return mode_static(); + uint16_t ledstart = random16(SEGLEN); // Determine starting location of flash + uint16_t ledlen = 1 + random16(SEGLEN -ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) + uint8_t bri = 255/random8(1, 3); + + if (SEGENV.aux1 == 0) //init, leader flash { - SEGENV.aux1 = random8(4, 4 + SEGMENT.intensity / 20); // number of flashes + SEGENV.aux1 = random8(4, 4 + SEGMENT.intensity/20); //number of flashes SEGENV.aux1 *= 2; - bri = 52; // leader has lower brightness - SEGENV.aux0 = 200; // 200ms delay after leader + bri = 52; //leader has lower brightness + SEGENV.aux0 = 200; //200ms delay after leader } - fill(SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); - if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) - { // flash on even number >2 + if (SEGENV.aux1 > 3 && !(SEGENV.aux1 & 0x01)) { //flash on even number >2 for (int i = ledstart; i < ledstart + ledlen; i++) { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri)); + SEGMENT.setPixelColor(i,SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0, bri)); } SEGENV.aux1--; SEGENV.step = millis(); - // return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds - } - else - { - if (millis() - SEGENV.step > SEGENV.aux0) - { + //return random8(4, 10); // each flash only lasts one frame/every 24ms... originally 4-10 milliseconds + } else { + if (millis() - SEGENV.step > SEGENV.aux0) { SEGENV.aux1--; - if (SEGENV.aux1 < 2) - SEGENV.aux1 = 0; + if (SEGENV.aux1 < 2) SEGENV.aux1 = 0; - SEGENV.aux0 = (50 + random8(100)); // delay between flashes - if (SEGENV.aux1 == 2) - { + SEGENV.aux0 = (50 + random8(100)); //delay between flashes + if (SEGENV.aux1 == 2) { SEGENV.aux0 = (random8(255 - SEGMENT.speed) * 100); // delay between strikes } SEGENV.step = millis(); @@ -2011,90 +1855,92 @@ uint16_t WS2812FX::mode_lightning(void) } return FRAMETIME; } +static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!,,,,,Overlay;!,!;!"; + // Pride2015 // Animated, ever-changing rainbows. // by Mark Kriegsman: https://gist.github.com/kriegsman/964de772d64c502760e5 -uint16_t WS2812FX::mode_pride_2015(void) -{ +uint16_t mode_pride_2015(void) { uint16_t duration = 10 + SEGMENT.speed; uint16_t sPseudotime = SEGENV.step; uint16_t sHue16 = SEGENV.aux0; - uint8_t sat8 = beatsin88(87, 220, 250); - uint8_t brightdepth = beatsin88(341, 96, 224); - uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256)); + uint8_t sat8 = beatsin88( 87, 220, 250); + uint8_t brightdepth = beatsin88( 341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); uint8_t msmultiplier = beatsin88(147, 23, 60); - uint16_t hue16 = sHue16; // gHue * 256; + uint16_t hue16 = sHue16;//gHue * 256; uint16_t hueinc16 = beatsin88(113, 1, 3000); sPseudotime += duration * msmultiplier; - sHue16 += duration * beatsin88(400, 5, 9); + sHue16 += duration * beatsin88( 400, 5,9); uint16_t brightnesstheta16 = sPseudotime; - CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; - brightnesstheta16 += brightnessthetainc16; - uint16_t b16 = sin16(brightnesstheta16) + 32768; + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16( brightnesstheta16 ) + 32768; uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; bri8 += (255 - brightdepth); CRGB newcolor = CHSV(hue8, sat8, bri8); - fastled_col = col_to_crgb(getPixelColor(i)); - - nblend(fastled_col, newcolor, 64); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.blendPixelColor(i, newcolor, 64); } SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; + return FRAMETIME; } +static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; + + +//eight colored dots, weaving in and out of sync with each other +uint16_t mode_juggle(void) { + if (SEGLEN == 1) return mode_static(); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4)); -// eight colored dots, weaving in and out of sync with each other -uint16_t WS2812FX::mode_juggle(void) -{ - fade_out(SEGMENT.intensity); CRGB fastled_col; byte dothue = 0; - for (byte i = 0; i < 8; i++) - { - uint16_t index = 0 + beatsin88((128 + SEGMENT.speed) * (i + 7), 0, SEGLEN - 1); - fastled_col = col_to_crgb(getPixelColor(index)); - fastled_col |= (SEGMENT.palette == 0) ? CHSV(dothue, 220, 255) : ColorFromPalette(currentPalette, dothue, 255); - setPixelColor(index, fastled_col.red, fastled_col.green, fastled_col.blue); + for (int i = 0; i < 8; i++) { + uint16_t index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); + fastled_col = CRGB(SEGMENT.getPixelColor(index)); + fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255); + SEGMENT.setPixelColor(index, fastled_col); dothue += 32; } return FRAMETIME; } +static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=64,ix=128"; -uint16_t WS2812FX::mode_palette() -{ + +uint16_t mode_palette() { uint16_t counter = 0; if (SEGMENT.speed != 0) { - counter = (now * ((SEGMENT.speed >> 3) + 1)) & 0xFFFF; + counter = (strip.now * ((SEGMENT.speed >> 3) +1)) & 0xFFFF; counter = counter >> 8; } - bool noWrap = (paletteBlend == 2 || (paletteBlend == 0 && SEGMENT.speed == 0)); - for (uint16_t i = 0; i < SEGLEN; i++) + for (int i = 0; i < SEGLEN; i++) { uint8_t colorIndex = (i * 255 / SEGLEN) - counter; - - if (noWrap) - colorIndex = map(colorIndex, 0, 255, 0, 240); // cut off blend at palette "end" - - setPixelColor(i, color_from_palette(colorIndex, false, true, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_MOVING_WRAP, 255)); } + return FRAMETIME; } +static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!;;c3=0,o2=0"; + // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active // Fire2012 by Mark Kriegsman, July 2012 @@ -2112,7 +1958,7 @@ uint16_t WS2812FX::mode_palette() // // Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). // -// This simulation scales it self a bit depending on NUM_LEDS; it should look +// This simulation scales it self a bit depending on SEGLEN; it should look // "OK" on anywhere from 20 to 100 LEDs without too much tweaking. // // I recommend running this simulation at anywhere from 30-100 frames per second, @@ -2124,408 +1970,386 @@ uint16_t WS2812FX::mode_palette() // There are two main parameters you can play with to control the look and // feel of your fire: COOLING (used in step 1 above) (Speed = COOLING), and SPARKING (used // in step 3 above) (Effect Intensity = Sparking). +uint16_t mode_fire_2012() { + if (SEGLEN == 1) return mode_static(); + const uint16_t strips = SEGMENT.nrOfVStrips(); + if (!SEGENV.allocateData(strips * SEGLEN)) return mode_static(); //allocation failed + byte* heat = SEGENV.data; -uint16_t WS2812FX::mode_fire_2012() -{ - uint32_t it = now >> 5; // div 32 + const uint32_t it = strip.now >> 5; //div 32 - if (!SEGENV.allocateData(SEGLEN)) - return mode_static(); // allocation failed + struct virtualStrip { + static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { - byte *heat = SEGENV.data; + const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels + + // Step 1. Cool down every cell a little + for (int i = 0; i < SEGLEN; i++) { + uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(4); + uint8_t minTemp = (i 1; k--) { + heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 + } + + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if (random8() <= SEGMENT.intensity) { + uint8_t y = random8(ignition); + uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math! + heat[y] = qadd8(heat[y], random8(96+2*boost,207+boost)); + } + } + + // Step 4. Map from heat cells to LED colors + for (int j = 0; j < SEGLEN; j++) { + SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, NOBLEND)); + } + } + }; + + for (int stripNr=0; stripNr 1; k--) - { - heat[k] = (heat[k - 1] + (heat[k - 2] << 1)) / 3; // heat[k-2] multiplied by 2 - } - - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if (random8() <= SEGMENT.intensity) - { - uint8_t y = random8(ignition); - if (y < SEGLEN) - heat[y] = qadd8(heat[y], random8(160, 255)); - } SEGENV.step = it; - } - // Step 4. Map from heat cells to LED colors - for (uint16_t j = 0; j < SEGLEN; j++) - { - CRGB color = ColorFromPalette(currentPalette, MIN(heat[j], 240), 255, LINEARBLEND); - setPixelColor(j, color.red, color.green, color.blue); - } return FRAMETIME; } +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1;sx=64,ix=160,m12=1"; // bars + // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb // This function draws color waves with an ever-changing, // widely-varying set of parameters, using a color palette. -uint16_t WS2812FX::mode_colorwaves() -{ +uint16_t mode_colorwaves() { uint16_t duration = 10 + SEGMENT.speed; uint16_t sPseudotime = SEGENV.step; uint16_t sHue16 = SEGENV.aux0; uint8_t brightdepth = beatsin88(341, 96, 224); - uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256)); + uint16_t brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256)); uint8_t msmultiplier = beatsin88(147, 23, 60); - uint16_t hue16 = sHue16; // gHue * 256; - // uint16_t hueinc16 = beatsin88(113, 300, 1500); - uint16_t hueinc16 = beatsin88(113, 60, 300) * SEGMENT.intensity * 10 / 255; // Use the Intensity Slider for the hues + uint16_t hue16 = sHue16;//gHue * 256; + uint16_t hueinc16 = beatsin88(113, 60, 300)*SEGMENT.intensity*10/255; // Use the Intensity Slider for the hues sPseudotime += duration * msmultiplier; sHue16 += duration * beatsin88(400, 5, 9); uint16_t brightnesstheta16 = sPseudotime; - CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0 ; i < SEGLEN; i++) { hue16 += hueinc16; uint8_t hue8 = hue16 >> 8; uint16_t h16_128 = hue16 >> 7; - if (h16_128 & 0x100) - { + if ( h16_128 & 0x100) { hue8 = 255 - (h16_128 >> 1); - } - else - { + } else { hue8 = h16_128 >> 1; } - brightnesstheta16 += brightnessthetainc16; + brightnesstheta16 += brightnessthetainc16; uint16_t b16 = sin16(brightnesstheta16) + 32768; uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; bri8 += (255 - brightdepth); - CRGB newcolor = ColorFromPalette(currentPalette, hue8, bri8); - fastled_col = col_to_crgb(getPixelColor(i)); - - nblend(fastled_col, newcolor, 128); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.blendPixelColor(i, SEGMENT.color_from_palette(hue8, false, PALETTE_SOLID_WRAP, 0, bri8), 128); // 50/50 mix } SEGENV.step = sPseudotime; SEGENV.aux0 = sHue16; + return FRAMETIME; } +static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!"; + // colored stripes pulsing at a defined Beats-Per-Minute (BPM) -uint16_t WS2812FX::mode_bpm() -{ - // CRGB fastled_col; - uint32_t stp = (now / 20) & 0xFF; +uint16_t mode_bpm() { + //CRGB fastled_col; + uint32_t stp = (strip.now / 20) & 0xFF; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for (uint16_t i = 0; i < SEGLEN; i++) - { - // fastled_col = ColorFromPalette(currentPalette, stp + (i * 2), beat - stp + (i * 10)); - // setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0), beat - stp + (i * 10))); // This supports RGBW. + for (int i = 0; i < SEGLEN; i++) { + //fastled_col = ColorFromPalette(SEGPALETTE, stp + (i * 2), beat - stp + (i * 10)); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10))); } + return FRAMETIME; } +static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64"; -uint16_t WS2812FX::mode_fillnoise8() -{ - if (SEGENV.call == 0) - SEGENV.step = random16(12345); - CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) - { + +uint16_t mode_fillnoise8() { + if (SEGENV.call == 0) SEGENV.step = random16(12345); + //CRGB fastled_col; + for (int i = 0; i < SEGLEN; i++) { uint8_t index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); - fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, 255, LINEARBLEND); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } - SEGENV.step += beatsin8(SEGMENT.speed, 1, 6); // 10,1,4 + SEGENV.step += beatsin8(SEGMENT.speed, 1, 6); //10,1,4 return FRAMETIME; } +static const char _data_FX_MODE_FILLNOISE8[] PROGMEM = "Fill Noise@!;!;!"; -uint16_t WS2812FX::mode_noise16_1() -{ - uint16_t scale = 320; // the "zoom factor" for the noise - CRGB fastled_col; - SEGENV.step += (1 + SEGMENT.speed / 16); - for (uint16_t i = 0; i < SEGLEN; i++) - { +uint16_t mode_noise16_1() { + uint16_t scale = 320; // the "zoom factor" for the noise + //CRGB fastled_col; + SEGENV.step += (1 + SEGMENT.speed/16); - uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm - uint16_t shift_y = SEGENV.step / 42; // the y position becomes slowly incremented + for (int i = 0; i < SEGLEN; i++) { + uint16_t shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm + uint16_t shift_y = SEGENV.step/42; // the y position becomes slowly incremented + uint16_t real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm + uint16_t real_y = (i + shift_y) * scale; // the y position becomes slowly incremented + uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + uint8_t index = sin8(noise * 3); // map LED color based on noise data - uint16_t real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm - uint16_t real_y = (i + shift_y) * scale; // the y position becomes slowly incremented - uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented - - uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - - uint8_t index = sin8(noise * 3); // map LED color based on noise data - - fastled_col = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!;!;!"; -uint16_t WS2812FX::mode_noise16_2() -{ - uint16_t scale = 1000; // the "zoom factor" for the noise - // CRGB fastled_col; + +uint16_t mode_noise16_2() { + uint16_t scale = 1000; // the "zoom factor" for the noise + //CRGB fastled_col; SEGENV.step += (1 + (SEGMENT.speed >> 1)); - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { + uint16_t shift_x = SEGENV.step >> 6; // x as a function of time + uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field + uint8_t noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down + uint8_t index = sin8(noise * 3); // map led color based on noise data - uint16_t shift_x = SEGENV.step >> 6; // x as a function of time - - uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field - - uint8_t noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down - - uint8_t index = sin8(noise * 3); // map led color based on noise data - - // fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - // setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), noise)); // This supports RGBW. + //fastled_col = ColorFromPalette(SEGPALETTE, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!"; -uint16_t WS2812FX::mode_noise16_3() -{ - uint16_t scale = 800; // the "zoom factor" for the noise - // CRGB fastled_col; + +uint16_t mode_noise16_3() { + uint16_t scale = 800; // the "zoom factor" for the noise + //CRGB fastled_col; SEGENV.step += (1 + SEGMENT.speed); - for (uint16_t i = 0; i < SEGLEN; i++) - { - - uint16_t shift_x = 4223; // no movement along x and y + for (int i = 0; i < SEGLEN; i++) { + uint16_t shift_x = 4223; // no movement along x and y uint16_t shift_y = 1234; + uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field + uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions + uint32_t real_z = SEGENV.step*8; + uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down + uint8_t index = sin8(noise * 3); // map led color based on noise data - uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field - uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions - uint32_t real_z = SEGENV.step * 8; - - uint8_t noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down - - uint8_t index = sin8(noise * 3); // map led color based on noise data - - // fastled_col = ColorFromPalette(currentPalette, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - // setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), noise)); // This supports RGBW. + //fastled_col = ColorFromPalette(SEGPALETTE, index, noise, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!"; -// https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino -uint16_t WS2812FX::mode_noise16_4() -{ - CRGB fastled_col; - uint32_t stp = (now * SEGMENT.speed) >> 7; - for (uint16_t i = 0; i < SEGLEN; i++) - { + +//https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino +uint16_t mode_noise16_4() { + //CRGB fastled_col; + uint32_t stp = (strip.now * SEGMENT.speed) >> 7; + for (int i = 0; i < SEGLEN; i++) { int16_t index = inoise16(uint32_t(i) << 12, stp); - fastled_col = ColorFromPalette(currentPalette, index); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + //fastled_col = ColorFromPalette(SEGPALETTE, index); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } +static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!"; -// based on https://gist.github.com/kriegsman/5408ecd397744ba0393e -uint16_t WS2812FX::mode_colortwinkle() -{ - uint16_t dataSize = (SEGLEN + 7) >> 3; // 1 bit per LED - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + +//based on https://gist.github.com/kriegsman/5408ecd397744ba0393e +uint16_t mode_colortwinkle() { + uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed CRGB fastled_col, prev; - fract8 fadeUpAmount = _brightness > 28 ? 8 + (SEGMENT.speed >> 2) : 68 - _brightness, fadeDownAmount = _brightness > 28 ? 8 + (SEGMENT.speed >> 3) : 68 - _brightness; - for (uint16_t i = 0; i < SEGLEN; i++) - { - fastled_col = col_to_crgb(getPixelColor(i)); + fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); + fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); + for (uint16_t i = 0; i < SEGLEN; i++) { + fastled_col = SEGMENT.getPixelColor(i); prev = fastled_col; uint16_t index = i >> 3; - uint8_t bitNum = i & 0x07; + uint8_t bitNum = i & 0x07; bool fadeUp = bitRead(SEGENV.data[index], bitNum); - if (fadeUp) - { + if (fadeUp) { CRGB incrementalColor = fastled_col; incrementalColor.nscale8_video(fadeUpAmount); fastled_col += incrementalColor; - if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) - { + if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { bitWrite(SEGENV.data[index], bitNum, false); } - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - if (col_to_crgb(getPixelColor(i)) == prev) - { // fix "stuck" pixels + if (SEGMENT.getPixelColor(i) == RGBW32(prev.r, prev.g, prev.b, 0)) { //fix "stuck" pixels fastled_col += fastled_col; - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, fastled_col); } - } - else - { + } else { fastled_col.nscale8(255 - fadeDownAmount); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, fastled_col); } } - for (uint16_t j = 0; j <= SEGLEN / 50; j++) - { - if (random8() <= SEGMENT.intensity) - { - for (uint8_t times = 0; times < 5; times++) - { // attempt to spawn a new pixel 5 times + for (uint16_t j = 0; j <= SEGLEN / 50; j++) { + if (random8() <= SEGMENT.intensity) { + for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times int i = random16(SEGLEN); - if (getPixelColor(i) == 0) - { - fastled_col = ColorFromPalette(currentPalette, random8(), 64, NOBLEND); + if (SEGMENT.getPixelColor(i) == 0) { + fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); uint16_t index = i >> 3; - uint8_t bitNum = i & 0x07; + uint8_t bitNum = i & 0x07; bitWrite(SEGENV.data[index], bitNum, true); - setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - break; // only spawn 1 new pixel per frame per 50 LEDs + SEGMENT.setPixelColor(i, fastled_col); + break; //only spawn 1 new pixel per frame per 50 LEDs } } } } return FRAMETIME_FIXED; } +static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade speed,Spawn speed;;!;;m12=0"; //pixels -// Calm effect, like a lake at night -uint16_t WS2812FX::mode_lake() -{ - uint8_t sp = SEGMENT.speed / 10; - int wave1 = beatsin8(sp + 2, -64, 64); - int wave2 = beatsin8(sp + 1, -64, 64); - uint8_t wave3 = beatsin8(sp + 2, 0, 80); - // CRGB fastled_col; - for (uint16_t i = 0; i < SEGLEN; i++) +//Calm effect, like a lake at night +uint16_t mode_lake() { + uint8_t sp = SEGMENT.speed/10; + int wave1 = beatsin8(sp +2, -64,64); + int wave2 = beatsin8(sp +1, -64,64); + uint8_t wave3 = beatsin8(sp +2, 0,80); + //CRGB fastled_col; + + for (int i = 0; i < SEGLEN; i++) { - int index = cos8((i * 15) + wave1) / 2 + cubicwave8((i * 23) + wave2) / 2; + int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; uint8_t lum = (index > wave3) ? index - wave3 : 0; - // fastled_col = ColorFromPalette(currentPalette, map(index,0,255,0,240), lum, LINEARBLEND); - // setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(map(index, 0, 255, 0, 240), false, PALETTE_SOLID_WRAP, 0), lum)); // This supports RGBW. + //fastled_col = ColorFromPalette(SEGPALETTE, map(index,0,255,0,240), lum, LINEARBLEND); + //SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum)); } + return FRAMETIME; } +static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!"; + // meteor effect // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain -uint16_t WS2812FX::mode_meteor() -{ - if (!SEGENV.allocateData(SEGLEN)) - return mode_static(); // allocation failed +uint16_t mode_meteor() { + if (SEGLEN == 1) return mode_static(); + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed - byte *trail = SEGENV.data; + byte* trail = SEGENV.data; - byte meteorSize = 1 + SEGLEN / 10; - uint16_t counter = now * ((SEGMENT.speed >> 2) + 8); + byte meteorSize= 1+ SEGLEN / 10; + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t in = counter * SEGLEN >> 16; // fade all leds to colors[1] in LEDs one step - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { if (random8() <= 255 - SEGMENT.intensity) { byte meteorTrailDecay = 128 + random8(127); trail[i] = scale8(trail[i], meteorTrailDecay); - setPixelColor(i, color_from_palette(trail[i], false, true, 255)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i])); } } // draw meteor - for (int j = 0; j < meteorSize; j++) - { + for (int j = 0; j < meteorSize; j++) { uint16_t index = in + j; - if (index >= SEGLEN) - { - index = (in + j - SEGLEN); + if (index >= SEGLEN) { + index -= SEGLEN; } - trail[index] = 240; - setPixelColor(index, color_from_palette(trail[index], false, true, 255)); + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255)); } return FRAMETIME; } +static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!"; + // smooth meteor effect // send a meteor from begining to to the end of the strip with a trail that randomly decays. // adapted from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectMeteorRain -uint16_t WS2812FX::mode_meteor_smooth() -{ - if (!SEGENV.allocateData(SEGLEN)) - return mode_static(); // allocation failed +uint16_t mode_meteor_smooth() { + if (SEGLEN == 1) return mode_static(); + if (!SEGENV.allocateData(SEGLEN)) return mode_static(); //allocation failed - byte *trail = SEGENV.data; + byte* trail = SEGENV.data; - byte meteorSize = 1 + SEGLEN / 10; - uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN - 1); + byte meteorSize= 1+ SEGLEN / 10; + uint16_t in = map((SEGENV.step >> 6 & 0xFF), 0, 255, 0, SEGLEN -1); // fade all leds to colors[1] in LEDs one step - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { if (trail[i] != 0 && random8() <= 255 - SEGMENT.intensity) { - int change = 3 - random8(12); // change each time between -8 and +3 + int change = 3 - random8(12); //change each time between -8 and +3 trail[i] += change; - if (trail[i] > 245) - trail[i] = 0; - if (trail[i] > 240) - trail[i] = 240; - setPixelColor(i, color_from_palette(trail[i], false, true, 255)); + if (trail[i] > 245) trail[i] = 0; + if (trail[i] > 240) trail[i] = 240; + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i])); } } // draw meteor - for (int j = 0; j < meteorSize; j++) - { + for (int j = 0; j < meteorSize; j++) { uint16_t index = in + j; - if (in + j >= SEGLEN) - { - index = (in + j - SEGLEN); + if (index >= SEGLEN) { + index -= SEGLEN; } - setPixelColor(index, color_blend(getPixelColor(index), color_from_palette(240, false, true, 255), 48)); trail[index] = 240; + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255)); } - SEGENV.step += SEGMENT.speed + 1; + SEGENV.step += SEGMENT.speed +1; return FRAMETIME; } +static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!;!"; -// Railway Crossing / Christmas Fairy lights -uint16_t WS2812FX::mode_railway() -{ - uint16_t dur = 40 + (255 - SEGMENT.speed) * 10; + +//Railway Crossing / Christmas Fairy lights +uint16_t mode_railway() { + if (SEGLEN == 1) return mode_static(); + uint16_t dur = (256 - SEGMENT.speed) * 40; uint16_t rampdur = (dur * SEGMENT.intensity) >> 8; if (SEGENV.step > dur) { - // reverse direction + //reverse direction SEGENV.step = 0; SEGENV.aux0 = !SEGENV.aux0; } @@ -2533,131 +2357,119 @@ uint16_t WS2812FX::mode_railway() if (rampdur != 0) { uint16_t p0 = (SEGENV.step * 255) / rampdur; - if (p0 < 255) - pos = p0; + if (p0 < 255) pos = p0; } - if (SEGENV.aux0) - pos = 255 - pos; - for (uint16_t i = 0; i < SEGLEN; i += 2) + if (SEGENV.aux0) pos = 255 - pos; + for (int i = 0; i < SEGLEN; i += 2) { - setPixelColor(i, color_from_palette(255 - pos, false, false, 255)); - if (i < SEGLEN - 1) + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); // do not use color 1 or 2, always use palette + if (i < SEGLEN -1) { - setPixelColor(i + 1, color_from_palette(pos, false, false, 255)); + SEGMENT.setPixelColor(i + 1, SEGMENT.color_from_palette(pos, false, false, 255)); // do not use color 1 or 2, always use palette } } SEGENV.step += FRAMETIME; return FRAMETIME; } +static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;1,2;!"; -// Water ripple -// propagation velocity from speed -// drop rate from intensity -// 4 bytes -typedef struct Ripple -{ - int8_t state; // WLEDSR AC has uint8_t while value can be negative??? +//Water ripple +//propagation velocity from speed +//drop rate from intensity + +//4 bytes +typedef struct Ripple { + uint8_t state; uint8_t color; uint16_t pos; } ripple; #ifdef ESP8266 -#define MAX_RIPPLES 56 + #define MAX_RIPPLES 56 #else -#define MAX_RIPPLES 100 + #define MAX_RIPPLES 100 #endif -uint16_t WS2812FX::ripple_base(bool rainbow) +uint16_t ripple_base() { - uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 + uint16_t maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 uint16_t dataSize = sizeof(ripple) * maxRipples; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Ripple *ripples = reinterpret_cast(SEGENV.data); + Ripple* ripples = reinterpret_cast(SEGENV.data); - // ranbow background or chosen background, all very dim. - if (rainbow) - { - if (SEGENV.call == 0) - { - SEGENV.aux0 = random8(); - SEGENV.aux1 = random8(); - } - if (SEGENV.aux0 == SEGENV.aux1) - { - SEGENV.aux1 = random8(); - } - else if (SEGENV.aux1 > SEGENV.aux0) - { - SEGENV.aux0++; - } - else - { - SEGENV.aux0--; - } - fill(color_blend(color_wheel(SEGENV.aux0), BLACK, 235)); - } - else - { - fill(SEGCOLOR(1)); - } - - // draw wave - for (uint16_t i = 0; i < maxRipples; i++) - { + //draw wave + for (int i = 0; i < maxRipples; i++) { uint16_t ripplestate = ripples[i].state; - if (ripplestate) - { - uint8_t rippledecay = (SEGMENT.speed >> 4) + 1; // faster decay if faster propagation + if (ripplestate) { + uint8_t rippledecay = (SEGMENT.speed >> 4) +1; //faster decay if faster propagation uint16_t rippleorigin = ripples[i].pos; - uint32_t col = color_from_palette(ripples[i].color, false, false, 255); - uint16_t propagation = ((ripplestate / rippledecay - 1) * SEGMENT.speed); + uint32_t col = SEGMENT.color_from_palette(ripples[i].color, false, false, 255); + uint16_t propagation = ((ripplestate/rippledecay - 1) * (SEGMENT.speed + 1)); int16_t propI = propagation >> 8; uint8_t propF = propagation & 0xFF; - int16_t left = rippleorigin - propI - 1; - uint8_t amp = (ripplestate < 17) ? triwave8((ripplestate - 1) * 8) : map(ripplestate, 17, 255, 255, 2); + uint8_t amp = (ripplestate < 17) ? triwave8((ripplestate-1)*8) : map(ripplestate,17,255,255,2); - for (int16_t v = left; v < left + 4; v++) + #ifndef WLED_DISABLE_2D + if (SEGMENT.is2D()) { + uint16_t cx = rippleorigin >> 8; + uint16_t cy = rippleorigin & 0xFF; + uint8_t mag = scale8(cubicwave8((propF>>2)), amp); + if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag)); + } else + #endif { - uint8_t mag = scale8(cubicwave8((propF >> 2) + (v - left) * 64), amp); - if (v < SEGLEN && v >= 0) - { - setPixelColor(v, color_blend(getPixelColor(v), col, mag)); - } - int16_t w = left + propI * 2 + 3 - (v - left); - if (w < SEGLEN && w >= 0) - { - setPixelColor(w, color_blend(getPixelColor(w), col, mag)); + int16_t left = rippleorigin - propI -1; + for (int16_t v = left; v < left +4; v++) { + uint8_t mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp); + SEGMENT.setPixelColor(v, color_blend(SEGMENT.getPixelColor(v), col, mag)); // TODO + int16_t w = left + propI*2 + 3 -(v-left); + SEGMENT.setPixelColor(w, color_blend(SEGMENT.getPixelColor(w), col, mag)); // TODO } } ripplestate += rippledecay; ripples[i].state = (ripplestate > 254) ? 0 : ripplestate; - } - else // randomly create new wave - { - if (random16(IBN + 10000) <= SEGMENT.intensity) - { + } else {//randomly create new wave + if (random16(IBN + 10000) <= SEGMENT.intensity) { ripples[i].state = 1; - ripples[i].pos = random16(SEGLEN); - ripples[i].color = random8(); // color + ripples[i].pos = SEGMENT.is2D() ? ((random8(SEGENV.virtualWidth())<<8) | (random8(SEGENV.virtualHeight()))) : random16(SEGLEN); + ripples[i].color = random8(); //color } } } + return FRAMETIME; } #undef MAX_RIPPLES -uint16_t WS2812FX::mode_ripple(void) -{ - return ripple_base(false); -} -uint16_t WS2812FX::mode_ripple_rainbow(void) -{ - return ripple_base(true); +uint16_t mode_ripple(void) { + if (SEGLEN == 1) return mode_static(); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); + return ripple_base(); } +static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #,,,,,Overlay;,!;!;12"; + + +uint16_t mode_ripple_rainbow(void) { + if (SEGLEN == 1) return mode_static(); + if (SEGENV.call ==0) { + SEGENV.aux0 = random8(); + SEGENV.aux1 = random8(); + } + if (SEGENV.aux0 == SEGENV.aux1) { + SEGENV.aux1 = random8(); + } else if (SEGENV.aux1 > SEGENV.aux0) { + SEGENV.aux0++; + } else { + SEGENV.aux0--; + } + SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235)); + return ripple_base(); +} +static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!;12"; + // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a // @@ -2669,7 +2481,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) // incandescent bulbs change color as they get dim down. #define COOL_LIKE_INCANDESCENT 1 -CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) +CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) uint16_t ticks = ms / SEGENV.aux0; @@ -2682,40 +2494,32 @@ CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool // Overall twinkle density. // 0 (NONE lit) to 8 (ALL lit at once). // Default is 5. - uint8_t twinkleDensity = (SEGMENT.intensity >> 5) + 1; + uint8_t twinkleDensity = (SEGMENT.intensity >> 5) +1; uint8_t bright = 0; - if (((slowcycle8 & 0x0E) / 2) < twinkleDensity) - { + if (((slowcycle8 & 0x0E)/2) < twinkleDensity) { uint8_t ph = fastcycle8; // This is like 'triwave8', which produces a // symmetrical up-and-down triangle sawtooth waveform, except that this // function produces a triangle wave with a faster attack and a slower decay - if (cat) // twinklecat, variant where the leds instantly turn on + if (cat) //twinklecat, variant where the leds instantly turn on { bright = 255 - ph; - } - else - { // vanilla twinklefox - if (ph < 86) - { - bright = ph * 3; - } - else - { + } else { //vanilla twinklefox + if (ph < 86) { + bright = ph * 3; + } else { ph -= 86; - bright = 255 - (ph + (ph / 2)); + bright = 255 - (ph + (ph/2)); } } } uint8_t hue = slowcycle8 - salt; CRGB c; - if (bright > 0) - { - c = ColorFromPalette(currentPalette, hue, bright, NOBLEND); - if (COOL_LIKE_INCANDESCENT == 1) - { + if (bright > 0) { + c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND); + if(COOL_LIKE_INCANDESCENT == 1) { // This code takes a pixel, and if its in the 'fading down' // part of the cycle, it adjusts the color a little bit like the // way that incandescent bulbs fade toward 'red' as they dim. @@ -2726,9 +2530,7 @@ CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool c.b = qsub8(c.b, cooling * 2); } } - } - else - { + } else { c = CRGB::Black; } return c; @@ -2739,7 +2541,7 @@ CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool // "CalculateOneTwinkle" on each pixel. It then displays // either the twinkle color of the background color, // whichever is brighter. -uint16_t WS2812FX::twinklefox_base(bool cat) +uint16_t twinklefox_base(bool cat) { // "PRNG16" is the pseudorandom number generator // It MUST be reset to the same starting value each time @@ -2748,40 +2550,31 @@ uint16_t WS2812FX::twinklefox_base(bool cat) uint16_t PRNG16 = 11337; // Calculate speed - if (SEGMENT.speed > 100) - SEGENV.aux0 = 3 + ((255 - SEGMENT.speed) >> 3); - else - SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1); + if (SEGMENT.speed > 100) SEGENV.aux0 = 3 + ((255 - SEGMENT.speed) >> 3); + else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1); // Set up the background color, "bg". - CRGB bg; - bg = col_to_crgb(SEGCOLOR(1)); + CRGB bg = CRGB(SEGCOLOR(1)); uint8_t bglight = bg.getAverageLight(); - if (bglight > 64) - { + if (bglight > 64) { bg.nscale8_video(16); // very bright, so scale to 1/16th - } - else if (bglight > 16) - { + } else if (bglight > 16) { bg.nscale8_video(64); // not that bright, so scale to 1/4th - } - else - { + } else { bg.nscale8_video(86); // dim, scale to 1/3rd. } uint8_t backgroundBrightness = bg.getAverageLight(); - for (uint16_t i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number - uint16_t myclockoffset16 = PRNG16; // use that number as clock offset + uint16_t myclockoffset16= PRNG16; // use that number as clock offset PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths) - uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF) >> 4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; - uint32_t myclock30 = (uint32_t)((now * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; - uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel + uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08; + uint32_t myclock30 = (uint32_t)((strip.now * myspeedmultiplierQ5_3) >> 3) + myclockoffset16; + uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel // We now have the adjusted 'clock' for this pixel, now we call // the function that computes what color the pixel should be based @@ -2790,117 +2583,113 @@ uint16_t WS2812FX::twinklefox_base(bool cat) uint8_t cbright = c.getAverageLight(); int16_t deltabright = cbright - backgroundBrightness; - if (deltabright >= 32 || (!bg)) - { + if (deltabright >= 32 || (!bg)) { // If the new pixel is significantly brighter than the background color, // use the new color. - setPixelColor(i, c.red, c.green, c.blue); - } - else if (deltabright > 0) - { + SEGMENT.setPixelColor(i, c.red, c.green, c.blue); + } else if (deltabright > 0) { // If the new pixel is just slightly brighter than the background color, // mix a blend of the new color and the background color - setPixelColor(i, color_blend(crgb_to_col(bg), crgb_to_col(c), deltabright * 8)); - } - else - { + SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), deltabright * 8)); + } else { // if the new pixel is not at all brighter than the background color, // just use the background color. - setPixelColor(i, bg.r, bg.g, bg.b); + SEGMENT.setPixelColor(i, bg.r, bg.g, bg.b); } } return FRAMETIME; } -uint16_t WS2812FX::mode_twinklefox() + +uint16_t mode_twinklefox() { return twinklefox_base(false); } +static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!"; -uint16_t WS2812FX::mode_twinklecat() + +uint16_t mode_twinklecat() { return twinklefox_base(true); } +static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!"; -// inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes -#define HALLOWEEN_EYE_SPACE 3 -#define HALLOWEEN_EYE_WIDTH 1 -uint16_t WS2812FX::mode_halloween_eyes() +//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes +uint16_t mode_halloween_eyes() { - uint16_t eyeLength = (2 * HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; - if (eyeLength > SEGLEN) - return mode_static(); // bail if segment too short + if (SEGLEN == 1) return mode_static(); + const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; + const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); + const uint16_t HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; + uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; + if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short - fill(SEGCOLOR(1)); // fill background + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background uint8_t state = SEGENV.aux1 >> 8; uint16_t stateTime = SEGENV.call; - if (stateTime == 0) - stateTime = 2000; + if (stateTime == 0) stateTime = 2000; - if (state == 0) - { // spawn eyes - SEGENV.aux0 = random16(0, SEGLEN - eyeLength); // start pos - SEGENV.aux1 = random8(); // color + if (state == 0) { //spawn eyes + SEGENV.aux0 = random16(0, maxWidth - eyeLength - 1); //start pos + SEGENV.aux1 = random8(); //color + if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices state = 1; } - if (state < 2) - { // fade eyes - uint16_t startPos = SEGENV.aux0; + if (state < 2) { //fade eyes + uint16_t startPos = SEGENV.aux0; uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE; - uint32_t fadestage = (now - SEGENV.step) * 255 / stateTime; - if (fadestage > 255) - fadestage = 255; - uint32_t c = color_blend(color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); + uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime; + if (fadestage > 255) fadestage = 255; + uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage); - for (uint16_t i = 0; i < HALLOWEEN_EYE_WIDTH; i++) - { - setPixelColor(startPos + i, c); - setPixelColor(start2ndEye + i, c); + for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) { + if (strip.isMatrix) { + SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c); + SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c); + } else { + SEGMENT.setPixelColor(startPos + i, c); + SEGMENT.setPixelColor(start2ndEye + i, c); + } } } - if (now - SEGENV.step > stateTime) - { + if (strip.now - SEGENV.step > stateTime) { state++; - if (state > 2) - state = 0; + if (state > 2) state = 0; - if (state < 2) - { - stateTime = 100 + (255 - SEGMENT.intensity) * 10; // eye fade time - } - else - { - uint16_t eyeOffTimeBase = (255 - SEGMENT.speed) * 10; + if (state < 2) { + stateTime = 100 + SEGMENT.intensity*10; //eye fade time + } else { + uint16_t eyeOffTimeBase = (256 - SEGMENT.speed)*10; stateTime = eyeOffTimeBase + random16(eyeOffTimeBase); } - SEGENV.step = now; + SEGENV.step = strip.now; SEGENV.call = stateTime; } - SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); // save state + SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state return FRAMETIME; } +static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time,,,,,Overlay;!,!;!;12"; -// Speed slider sets amount of LEDs lit, intensity sets unlit -uint16_t WS2812FX::mode_static_pattern() + +//Speed slider sets amount of LEDs lit, intensity sets unlit +uint16_t mode_static_pattern() { uint16_t lit = 1 + SEGMENT.speed; uint16_t unlit = 1 + SEGMENT.intensity; bool drawingLit = true; uint16_t cnt = 0; - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, (drawingLit) ? color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, (drawingLit) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); cnt++; - if (cnt >= ((drawingLit) ? lit : unlit)) - { + if (cnt >= ((drawingLit) ? lit : unlit)) { cnt = 0; drawingLit = !drawingLit; } @@ -2908,58 +2697,55 @@ uint16_t WS2812FX::mode_static_pattern() return FRAMETIME; } +static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,!;!;;pal=0"; -uint16_t WS2812FX::mode_tri_static_pattern() + +uint16_t mode_tri_static_pattern() { - uint8_t segSize = (SEGMENT.intensity >> 5) + 1; + uint8_t segSize = (SEGMENT.intensity >> 5) +1; uint8_t currSeg = 0; uint16_t currSegCount = 0; - for (uint16_t i = 0; i < SEGLEN; i++) - { - if (currSeg % 3 == 0) - { - setPixelColor(i, SEGCOLOR(0)); - } - else if (currSeg % 3 == 1) - { - setPixelColor(i, SEGCOLOR(1)); - } - else - { - setPixelColor(i, (SEGCOLOR(2) > 0 ? SEGCOLOR(2) : WHITE)); + for (int i = 0; i < SEGLEN; i++) { + if ( currSeg % 3 == 0 ) { + SEGMENT.setPixelColor(i, SEGCOLOR(0)); + } else if( currSeg % 3 == 1) { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); + } else { + SEGMENT.setPixelColor(i, (SEGCOLOR(2) > 0 ? SEGCOLOR(2) : WHITE)); } currSegCount += 1; - if (currSegCount >= segSize) - { - currSeg += 1; + if (currSegCount >= segSize) { + currSeg +=1; currSegCount = 0; } } return FRAMETIME; } +static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tri@,Size;1,2,3;;;pal=0"; -uint16_t WS2812FX::spots_base(uint16_t threshold) + +uint16_t spots_base(uint16_t threshold) { - fill(SEGCOLOR(1)); + if (SEGLEN == 1) return mode_static(); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); uint16_t maxZones = SEGLEN >> 2; uint16_t zones = 1 + ((SEGMENT.intensity * maxZones) >> 8); uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; - for (uint16_t z = 0; z < zones; z++) + for (int z = 0; z < zones; z++) { uint16_t pos = offset + z * zoneLen; - for (uint16_t i = 0; i < zoneLen; i++) + for (int i = 0; i < zoneLen; i++) { uint16_t wave = triwave16((i * 0xFFFF) / zoneLen); - if (wave > threshold) - { + if (wave > threshold) { uint16_t index = 0 + pos + i; - uint8_t s = (wave - threshold) * 255 / (0xFFFF - threshold); - setPixelColor(index, color_blend(color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - s)); + uint8_t s = (wave - threshold)*255 / (0xFFFF - threshold); + SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); } } } @@ -2967,138 +2753,135 @@ uint16_t WS2812FX::spots_base(uint16_t threshold) return FRAMETIME; } -// Intensity slider sets number of "lights", speed sets LEDs per light -uint16_t WS2812FX::mode_spots() + +//Intensity slider sets number of "lights", speed sets LEDs per light +uint16_t mode_spots() { return spots_base((255 - SEGMENT.speed) << 8); } +static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width,,,,,Overlay;!,!;!"; -// Intensity slider sets number of "lights", LEDs per light fade in and out -uint16_t WS2812FX::mode_spots_fade() + +//Intensity slider sets number of "lights", LEDs per light fade in and out +uint16_t mode_spots_fade() { - uint16_t counter = now * ((SEGMENT.speed >> 2) + 8); + uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t t = triwave16(counter); uint16_t tr = (t >> 1) + (t >> 2); return spots_base(tr); } +static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width,,,,,Overlay;!,!;!"; -// each needs 12 bytes -typedef struct Ball -{ + +//each needs 12 bytes +typedef struct Ball { unsigned long lastBounceTime; float impactVelocity; float height; } ball; /* - * Bouncing Balls Effect - */ -uint16_t WS2812FX::mode_bouncing_balls(void) -{ - // allocate segment data - uint16_t maxNumBalls = 16; +* Bouncing Balls Effect +*/ +uint16_t mode_bouncing_balls(void) { + if (SEGLEN == 1) return mode_static(); + //allocate segment data + const uint16_t strips = SEGMENT.nrOfVStrips(); // adapt for 2D + const size_t maxNumBalls = 16; uint16_t dataSize = sizeof(ball) * maxNumBalls; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed - Ball *balls = reinterpret_cast(SEGENV.data); + Ball* balls = reinterpret_cast(SEGENV.data); - // number of balls based on intensity setting to max of 7 (cycles colors) - // non-chosen color is a random color - uint8_t numBalls = int(((SEGMENT.intensity * (maxNumBalls - 0.8f)) / 255) + 1); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(2) ? BLACK : SEGCOLOR(1)); - float gravity = -9.81; // standard value of gravity - float impactVelocityStart = sqrt(-2 * gravity); + // virtualStrip idea by @ewowi (Ewoud Wijma) + // requires virtual strip # to be embedded into upper 16 bits of index in setPixelColor() + // the following functions will not work on virtual strips: fill(), fade_out(), fadeToBlack(), blur() + struct virtualStrip { + static void runStrip(size_t stripNr, Ball* balls) { + // number of balls based on intensity setting to max of 7 (cycles colors) + // non-chosen color is a random color + uint16_t numBalls = (SEGMENT.intensity * (maxNumBalls - 1)) / 255 + 1; // minimum 1 ball + const float gravity = -9.81f; // standard value of gravity + const bool hasCol2 = SEGCOLOR(2); + const unsigned long time = millis(); - unsigned long time = millis(); + if (SEGENV.call == 0) { + for (size_t i = 0; i < maxNumBalls; i++) balls[i].lastBounceTime = time; + } - if (SEGENV.call == 0) - { - for (uint8_t i = 0; i < maxNumBalls; i++) - balls[i].lastBounceTime = time; - } + for (size_t i = 0; i < numBalls; i++) { + float timeSinceLastBounce = (time - balls[i].lastBounceTime)/((255-SEGMENT.speed)/64 +1); + float timeSec = timeSinceLastBounce/1000.0f; + balls[i].height = (0.5f * gravity * timeSec + balls[i].impactVelocity) * timeSec; // avoid use pow(x, 2) - its extremely slow ! - bool hasCol2 = SEGCOLOR(2); - fill(hasCol2 ? BLACK : SEGCOLOR(1)); + if (balls[i].height <= 0.0f) { + balls[i].height = 0.0f; + //damping for better effect using multiple balls + float dampening = 0.9f - float(i)/float(numBalls * numBalls); // avoid use pow(x, 2) - its extremely slow ! + balls[i].impactVelocity = dampening * balls[i].impactVelocity; + balls[i].lastBounceTime = time; - for (uint8_t i = 0; i < numBalls; i++) - { - float timeSinceLastBounce = (time - balls[i].lastBounceTime) / ((255 - SEGMENT.speed) * 8 / 256 + 1); - balls[i].height = 0.5 * gravity * pow(timeSinceLastBounce / 1000, 2.0) + balls[i].impactVelocity * timeSinceLastBounce / 1000; + if (balls[i].impactVelocity < 0.015f) { + float impactVelocityStart = sqrtf(-2.0f * gravity) * random8(5,11)/10.0f; // randomize impact velocity + balls[i].impactVelocity = impactVelocityStart; + } + } else if (balls[i].height > 1.0f) { + continue; // do not draw OOB ball + } - if (balls[i].height < 0) - { // start bounce - balls[i].height = 0; - // damping for better effect using multiple balls - float dampening = 0.90 - float(i) / pow(numBalls, 2); - balls[i].impactVelocity = dampening * balls[i].impactVelocity; - balls[i].lastBounceTime = time; + uint32_t color = SEGCOLOR(0); + if (SEGMENT.palette) { + color = SEGMENT.color_wheel(i*(256/MAX(numBalls, 8))); + } else if (hasCol2) { + color = SEGCOLOR(i % NUM_COLORS); + } - if (balls[i].impactVelocity < 0.015) - { - balls[i].impactVelocity = impactVelocityStart; + int pos = roundf(balls[i].height * (SEGLEN - 1)); + if (SEGLEN<32) SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color); // encode virtual strip into index + else SEGMENT.setPixelColor(balls[i].height + (stripNr+1)*10.0f, color); } } + }; - uint32_t color = SEGCOLOR(0); - if (SEGMENT.palette) - { - color = color_wheel(i * (256 / MAX(numBalls, 8))); - } - else if (hasCol2) - { - color = SEGCOLOR(i % NUM_COLORS); - } - - uint16_t pos = round(balls[i].height * (SEGLEN - 1)); - setPixelColor(pos, color); - } + for (int stripNr=0; stripNr pos; i--) - { // WLEDSR bugfix - setPixelColor(i, color1); - if (dual) - setPixelColor(SEGLEN - 1 - i, color2); + } else { + for (int i = SEGENV.aux0; i > pos ; i--) { + SEGMENT.setPixelColor(i, color1); + if (dual) SEGMENT.setPixelColor(SEGLEN-1-i, color2); } } SEGENV.aux0 = pos; @@ -3107,228 +2890,223 @@ uint16_t WS2812FX::sinelon_base(bool dual, bool rainbow = false) return FRAMETIME; } -uint16_t WS2812FX::mode_sinelon(void) -{ + +uint16_t mode_sinelon(void) { return sinelon_base(false); } +static const char _data_FX_MODE_SINELON[] PROGMEM = "Sinelon@!,Trail;!,!,!;!"; -uint16_t WS2812FX::mode_sinelon_dual(void) -{ + +uint16_t mode_sinelon_dual(void) { return sinelon_base(true); } +static const char _data_FX_MODE_SINELON_DUAL[] PROGMEM = "Sinelon Dual@!,Trail;!,!,!;!"; -uint16_t WS2812FX::mode_sinelon_rainbow(void) -{ + +uint16_t mode_sinelon_rainbow(void) { return sinelon_base(false, true); } +static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,Trail;,,!;!"; -// Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 -uint16_t WS2812FX::mode_glitter() -{ - mode_palette(); - if (SEGMENT.intensity > random8()) - { - setPixelColor(random16(SEGLEN), ULTRAWHITE); +// utility function that will add random glitter to SEGMENT +void glitter_base(uint8_t intensity, uint32_t col = ULTRAWHITE) { + if (intensity > random8()) { + if (SEGMENT.is2D()) { + SEGMENT.setPixelColorXY(random16(SEGMENT.virtualWidth()),random16(SEGMENT.virtualHeight()), col); + } else { + SEGMENT.setPixelColor(random16(SEGLEN), col); + } } - - return FRAMETIME; } -// each needs 12 bytes -// Spark type is used for popcorn, 1D fireworks, and drip -typedef struct Spark +//Glitter with palette background, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 +uint16_t mode_glitter() { - float pos; - float vel; + if (!SEGMENT.check2) mode_palette(); // use "* Color 1" palette for solid background (replacing "Solid glitter") + glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE); + return FRAMETIME; +} +static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels + + +//Solid colour background with glitter +uint16_t mode_solid_glitter() +{ + SEGMENT.fill(SEGCOLOR(0)); + glitter_base(SEGMENT.intensity, SEGCOLOR(2) ? SEGCOLOR(2) : ULTRAWHITE); + return FRAMETIME; +} +static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;Bg,,Glitter color;;;m12=0"; + + +//each needs 19 bytes +//Spark type is used for popcorn, 1D fireworks, and drip +typedef struct Spark { + float pos, posX; + float vel, velX; uint16_t col; uint8_t colIndex; } spark; +#define maxNumPopcorn 21 // max 21 on 16 segment ESP8266 /* - * POPCORN - * modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h - */ -uint16_t WS2812FX::mode_popcorn(void) -{ - // allocate segment data - uint16_t maxNumPopcorn = 21; // max 21 on 16 segment ESP8266 +* POPCORN +* modified from https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Popcorn.h +*/ +uint16_t mode_popcorn(void) { + if (SEGLEN == 1) return mode_static(); + //allocate segment data + uint16_t strips = SEGMENT.nrOfVStrips(); uint16_t dataSize = sizeof(spark) * maxNumPopcorn; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed - Spark *popcorn = reinterpret_cast(SEGENV.data); - - float gravity = -0.0001 - (SEGMENT.speed / 200000.0); // m/s/s - gravity *= SEGLEN; + Spark* popcorn = reinterpret_cast(SEGENV.data); bool hasCol2 = SEGCOLOR(2); - fill(hasCol2 ? BLACK : SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(hasCol2 ? BLACK : SEGCOLOR(1)); - uint8_t numPopcorn = SEGMENT.intensity * maxNumPopcorn / 255; - if (numPopcorn == 0) - numPopcorn = 1; + struct virtualStrip { + static void runStrip(uint16_t stripNr, Spark* popcorn) { + float gravity = -0.0001 - (SEGMENT.speed/200000.0); // m/s/s + gravity *= SEGLEN; - for (uint8_t i = 0; i < numPopcorn; i++) - { - if (popcorn[i].pos >= 0.0f) - { // if kernel is active, update its position - popcorn[i].pos += popcorn[i].vel; - popcorn[i].vel += gravity; - } - else - { // if kernel is inactive, randomly pop it - if (random8() < 2) - { // POP!!! - popcorn[i].pos = 0.01f; + uint8_t numPopcorn = SEGMENT.intensity*maxNumPopcorn/255; + if (numPopcorn == 0) numPopcorn = 1; - uint16_t peakHeight = 128 + random8(128); // 0-255 - peakHeight = (peakHeight * (SEGLEN - 1)) >> 8; - popcorn[i].vel = sqrt(-2.0 * gravity * peakHeight); + for(int i = 0; i < numPopcorn; i++) { + if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position + popcorn[i].pos += popcorn[i].vel; + popcorn[i].vel += gravity; + } else { // if kernel is inactive, randomly pop it + if (random8() < 2) { // POP!!! + popcorn[i].pos = 0.01f; - if (SEGMENT.palette) - { - popcorn[i].colIndex = random8(); + uint16_t peakHeight = 128 + random8(128); //0-255 + peakHeight = (peakHeight * (SEGLEN -1)) >> 8; + popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight); + + if (SEGMENT.palette) + { + popcorn[i].colIndex = random8(); + } else { + byte col = random8(0, NUM_COLORS); + if (!SEGCOLOR(2) || !SEGCOLOR(col)) col = 0; + popcorn[i].colIndex = col; + } + } } - else - { - byte col = random8(0, NUM_COLORS); - if (!hasCol2 || !SEGCOLOR(col)) - col = 0; - popcorn[i].colIndex = col; + if (popcorn[i].pos >= 0.0f) { // draw now active popcorn (either active before or just popped) + uint32_t col = SEGMENT.color_wheel(popcorn[i].colIndex); + if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex); + uint16_t ledIndex = popcorn[i].pos; + if (ledIndex < SEGLEN) SEGMENT.setPixelColor(indexToVStrip(ledIndex, stripNr), col); } } } - if (popcorn[i].pos >= 0.0f) - { // draw now active popcorn (either active before or just popped) - uint32_t col = color_wheel(popcorn[i].colIndex); - if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) - col = SEGCOLOR(popcorn[i].colIndex); - uint16_t ledIndex = popcorn[i].pos; - if (ledIndex < SEGLEN) - setPixelColor(ledIndex, col); - } - } + }; + + for (int stripNr=0; stripNr> 1; // max 127 + uint8_t rndval = valrange >> 1; //max 127 - // step (how much to move closer to target per frame) coarsely set by speed + //step (how much to move closer to target per frame) coarsely set by speed uint8_t speedFactor = 4; - if (SEGMENT.speed > 252) - { // epilepsy + if (SEGMENT.speed > 252) { //epilepsy speedFactor = 1; - } - else if (SEGMENT.speed > 99) - { // regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms) + } else if (SEGMENT.speed > 99) { //regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms) speedFactor = 2; - } - else if (SEGMENT.speed > 49) - { // slower fade + } else if (SEGMENT.speed > 49) { //slower fade speedFactor = 3; - } // else 4 (slowest) + } //else 4 (slowest) uint16_t numCandles = (multi) ? SEGLEN : 1; - for (uint16_t i = 0; i < numCandles; i++) + for (int i = 0; i < numCandles; i++) { - uint16_t d = 0; // data location + uint16_t d = 0; //data location uint8_t s = SEGENV.aux0, s_target = SEGENV.aux1, fadeStep = SEGENV.step; - if (i > 0) - { - d = (i - 1) * 3; - s = SEGENV.data[d]; - s_target = SEGENV.data[d + 1]; - fadeStep = SEGENV.data[d + 2]; + if (i > 0) { + d = (i-1) *3; + s = SEGENV.data[d]; s_target = SEGENV.data[d+1]; fadeStep = SEGENV.data[d+2]; } - if (fadeStep == 0) - { // init vals - s = 128; - s_target = 130 + random8(4); - fadeStep = 1; + if (fadeStep == 0) { //init vals + s = 128; s_target = 130 + random8(4); fadeStep = 1; } bool newTarget = false; - if (s_target > s) - { // fade up + if (s_target > s) { //fade up s = qadd8(s, fadeStep); - if (s >= s_target) - newTarget = true; - } - else - { + if (s >= s_target) newTarget = true; + } else { s = qsub8(s, fadeStep); - if (s <= s_target) - newTarget = true; + if (s <= s_target) newTarget = true; } - if (newTarget) - { - s_target = random8(rndval) + random8(rndval); // between 0 and rndval*2 -2 = 252 - if (s_target < (rndval >> 1)) - s_target = (rndval >> 1) + random8(rndval); + if (newTarget) { + s_target = random8(rndval) + random8(rndval); //between 0 and rndval*2 -2 = 252 + if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval); uint8_t offset = (255 - valrange); s_target += offset; uint8_t dif = (s_target > s) ? s_target - s : s - s_target; fadeStep = dif >> speedFactor; - if (fadeStep == 0) - fadeStep = 1; + if (fadeStep == 0) fadeStep = 1; } - if (i > 0) - { - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); + if (i > 0) { + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); - SEGENV.data[d] = s; - SEGENV.data[d + 1] = s_target; - SEGENV.data[d + 2] = fadeStep; - } - else - { - for (uint16_t j = 0; j < SEGLEN; j++) - { - setPixelColor(j, color_blend(SEGCOLOR(1), color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); + SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; + } else { + for (int j = 0; j < SEGLEN; j++) { + SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); } - SEGENV.aux0 = s; - SEGENV.aux1 = s_target; - SEGENV.step = fadeStep; + SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep; } } return FRAMETIME_FIXED; } -uint16_t WS2812FX::mode_candle() + +uint16_t mode_candle() { return candle(false); } +static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;01;sx=96,ix=224,pal=0"; -uint16_t WS2812FX::mode_candle_multi() + +uint16_t mode_candle_multi() { return candle(true); } +static const char _data_FX_MODE_CANDLE_MULTI[] PROGMEM = "Candle Multi@!,!;!,!;!;;sx=96,ix=224,pal=0"; + /* / Fireworks in starburst effect @@ -3336,94 +3114,83 @@ uint16_t WS2812FX::mode_candle_multi() / Speed sets frequency of new starbursts, intensity is the intensity of the burst */ #ifdef ESP8266 -#define STARBURST_MAX_FRAG 8 // 52 bytes / star + #define STARBURST_MAX_FRAG 8 //52 bytes / star #else -#define STARBURST_MAX_FRAG 10 // 60 bytes / star + #define STARBURST_MAX_FRAG 10 //60 bytes / star #endif -// each needs 20+STARBURST_MAX_FRAG*4 bytes -typedef struct particle -{ - CRGB color; - uint32_t birth = 0; - uint32_t last = 0; - float vel = 0; - uint16_t pos = -1; - float fragment[STARBURST_MAX_FRAG]; +//each needs 20+STARBURST_MAX_FRAG*4 bytes +typedef struct particle { + CRGB color; + uint32_t birth =0; + uint32_t last =0; + float vel =0; + uint16_t pos =-1; + float fragment[STARBURST_MAX_FRAG]; } star; -uint16_t WS2812FX::mode_starburst(void) -{ - uint16_t maxData = FAIR_DATA_PER_SEG; // ESP8266: 256 ESP32: 640 - uint8_t segs = getActiveSegmentsNum(); - if (segs <= (MAX_NUM_SEGMENTS / 2)) - maxData *= 2; // ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs - if (segs <= (MAX_NUM_SEGMENTS / 4)) - maxData *= 2; // ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs - uint16_t maxStars = maxData / sizeof(star); // ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg +uint16_t mode_starburst(void) { + if (SEGLEN == 1) return mode_static(); + uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 + uint8_t segs = strip.getActiveSegmentsNum(); + if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs + if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs + uint16_t maxStars = maxData / sizeof(star); //ESP8266: max. 4/9/19 stars/seg, ESP32: max. 10/21/42 stars/seg uint8_t numStars = 1 + (SEGLEN >> 3); - if (numStars > maxStars) - numStars = maxStars; + if (numStars > maxStars) numStars = maxStars; uint16_t dataSize = sizeof(star) * numStars; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed uint32_t it = millis(); - star *stars = reinterpret_cast(SEGENV.data); + star* stars = reinterpret_cast(SEGENV.data); - float maxSpeed = 375.0f; // Max velocity - float particleIgnition = 250.0f; // How long to "flash" - float particleFadeTime = 1500.0f; // Fade out time + float maxSpeed = 375.0f; // Max velocity + float particleIgnition = 250.0f; // How long to "flash" + float particleFadeTime = 1500.0f; // Fade out time for (int j = 0; j < numStars; j++) { // speed to adjust chance of a burst, max is nearly always. - if (random8((144 - (SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) + if (random8((144-(SEGMENT.speed >> 1))) == 0 && stars[j].birth == 0) { // Pick a random color and location. - uint16_t startPos = random16(SEGLEN - 1); - float multiplier = (float)(random8()) / 255.0 * 1.0; + uint16_t startPos = (SEGLEN > 1) ? random16(SEGLEN-1) : 0; + float multiplier = (float)(random8())/255.0 * 1.0; - stars[j].color = col_to_crgb(color_wheel(random8())); + stars[j].color = CRGB(SEGMENT.color_wheel(random8())); stars[j].pos = startPos; - stars[j].vel = maxSpeed * (float)(random8()) / 255.0 * multiplier; + stars[j].vel = maxSpeed * (float)(random8())/255.0 * multiplier; stars[j].birth = it; stars[j].last = it; // more fragments means larger burst effect - int num = random8(3, 6 + (SEGMENT.intensity >> 5)); + int num = random8(3,6 + (SEGMENT.intensity >> 5)); - for (int i = 0; i < STARBURST_MAX_FRAG; i++) - { - if (i < num) - stars[j].fragment[i] = startPos; - else - stars[j].fragment[i] = -1; + for (int i=0; i < STARBURST_MAX_FRAG; i++) { + if (i < num) stars[j].fragment[i] = startPos; + else stars[j].fragment[i] = -1; } } } - fill(SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); - for (int j = 0; j < numStars; j++) + for (int j=0; j> 1; - if (stars[j].fragment[i] > 0) - { - // all fragments travel right, will be mirrored on other side - stars[j].fragment[i] += stars[j].vel * dt * (float)var / 3.0; + if (stars[j].fragment[i] > 0) { + //all fragments travel right, will be mirrored on other side + stars[j].fragment[i] += stars[j].vel * dt * (float)var/3.0; } } stars[j].last = it; - stars[j].vel -= 3 * stars[j].vel * dt; + stars[j].vel -= 3*stars[j].vel*dt; } CRGB c = stars[j].color; @@ -3431,53 +3198,40 @@ uint16_t WS2812FX::mode_starburst(void) // If the star is brand new, it flashes white briefly. // Otherwise it just fades over time. float fade = 0.0f; - float age = it - stars[j].birth; + float age = it-stars[j].birth; - if (age < particleIgnition) - { - c = col_to_crgb(color_blend(WHITE, crgb_to_col(c), 254.5f * ((age / particleIgnition)))); - } - else - { + if (age < particleIgnition) { + c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), 254.5f*((age / particleIgnition)))); + } else { // Figure out how much to fade and shrink the star based on // its age relative to its lifetime - if (age > particleIgnition + particleFadeTime) - { - fade = 1.0f; // Black hole, all faded out + if (age > particleIgnition + particleFadeTime) { + fade = 1.0f; // Black hole, all faded out stars[j].birth = 0; - c = col_to_crgb(SEGCOLOR(1)); - } - else - { + c = CRGB(SEGCOLOR(1)); + } else { age -= particleIgnition; - fade = (age / particleFadeTime); // Fading star - byte f = 254.5f * fade; - c = col_to_crgb(color_blend(crgb_to_col(c), SEGCOLOR(1), f)); + fade = (age / particleFadeTime); // Fading star + byte f = 254.5f*fade; + c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), f)); } } - float particleSize = (1.0 - fade) * 2; + float particleSize = (1.0f - fade) * 2.0f; - for (uint8_t index = 0; index < STARBURST_MAX_FRAG * 2; index++) - { + for (size_t index=0; index < STARBURST_MAX_FRAG*2; index++) { bool mirrored = index & 0x1; uint8_t i = index >> 1; - if (stars[j].fragment[i] > 0) - { + if (stars[j].fragment[i] > 0) { float loc = stars[j].fragment[i]; - if (mirrored) - loc -= (loc - stars[j].pos) * 2; + if (mirrored) loc -= (loc-stars[j].pos)*2; int start = loc - particleSize; int end = loc + particleSize; - if (start < 0) - start = 0; - if (start == end) - end++; - if (end > SEGLEN) - end = SEGLEN; - for (int p = start; p < end; p++) - { - setPixelColor(p, c.r, c.g, c.b); + if (start < 0) start = 0; + if (start == end) end++; + if (end > SEGLEN) end = SEGLEN; + for (int p = start; p < end; p++) { + SEGMENT.setPixelColor(p, c.r, c.g, c.b); } } } @@ -3485,436 +3239,434 @@ uint16_t WS2812FX::mode_starburst(void) return FRAMETIME; } #undef STARBURST_MAX_FRAG +static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments,,,,,Overlay;,!;!;;pal=11,m12=0"; + /* * Exploding fireworks effect * adapted from: http://www.anirama.com/1000leds/1d-fireworks/ + * adapted for 2D WLED by blazoncek (Blaz Kristan (AKA blazoncek)) */ -uint16_t WS2812FX::mode_exploding_fireworks(void) +uint16_t mode_exploding_fireworks(void) { - // allocate segment data - uint16_t maxData = FAIR_DATA_PER_SEG; // ESP8266: 256 ESP32: 640 - uint8_t segs = getActiveSegmentsNum(); - if (segs <= (MAX_NUM_SEGMENTS / 2)) - maxData *= 2; // ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs - if (segs <= (MAX_NUM_SEGMENTS / 4)) - maxData *= 2; // ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs - int maxSparks = maxData / sizeof(spark); // ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg + if (SEGLEN == 1) return mode_static(); + const uint16_t cols = strip.isMatrix ? SEGMENT.virtualWidth() : 1; + const uint16_t rows = strip.isMatrix ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); - uint16_t numSparks = min(2 + (SEGLEN >> 1), maxSparks); + //allocate segment data + uint16_t maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 + uint8_t segs = strip.getActiveSegmentsNum(); + if (segs <= (strip.getMaxSegments() /2)) maxData *= 2; //ESP8266: 512 if <= 8 segs ESP32: 1280 if <= 16 segs + if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs + int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg + + uint16_t numSparks = min(2 + ((rows*cols) >> 1), maxSparks); uint16_t dataSize = sizeof(spark) * numSparks; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed + float *dying_gravity = reinterpret_cast(SEGENV.data + dataSize); - if (dataSize != SEGENV.aux1) - { // reset to flare if sparks were reallocated + if (dataSize != SEGENV.aux1) { //reset to flare if sparks were reallocated (it may be good idea to reset segment if bounds change) + *dying_gravity = 0.0f; SEGENV.aux0 = 0; SEGENV.aux1 = dataSize; } - fill(BLACK); + SEGMENT.fade_out(252); - bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED); - // have fireworks start in either direction based on intensity - SEGMENT.setOption(SEG_OPTION_REVERSED, SEGENV.step); + Spark* sparks = reinterpret_cast(SEGENV.data); + Spark* flare = sparks; //first spark is flare data - Spark *sparks = reinterpret_cast(SEGENV.data); - Spark *flare = sparks; // first spark is flare data + float gravity = -0.0004f - (SEGMENT.speed/800000.0f); // m/s/s + gravity *= rows; - float gravity = -0.0004 - (SEGMENT.speed / 800000.0); // m/s/s - gravity *= SEGLEN; - - if (SEGENV.aux0 < 2) - { // FLARE - if (SEGENV.aux0 == 0) - { // init flare + if (SEGENV.aux0 < 2) { //FLARE + if (SEGENV.aux0 == 0) { //init flare flare->pos = 0; - uint16_t peakHeight = 75 + random8(180); // 0-255 - peakHeight = (peakHeight * (SEGLEN - 1)) >> 8; - flare->vel = sqrt(-2.0 * gravity * peakHeight); - flare->col = 255; // brightness - + flare->posX = strip.isMatrix ? random16(2,cols-3) : (SEGMENT.intensity > random8()); // will enable random firing side on 1D + uint16_t peakHeight = 75 + random8(180); //0-255 + peakHeight = (peakHeight * (rows -1)) >> 8; + flare->vel = sqrtf(-2.0f * gravity * peakHeight); + flare->velX = strip.isMatrix ? (random8(9)-4)/32.f : 0; // no X velocity on 1D + flare->col = 255; //brightness SEGENV.aux0 = 1; } // launch - if (flare->vel > 12 * gravity) - { + if (flare->vel > 12 * gravity) { // flare - setPixelColor(int(flare->pos), flare->col, flare->col, flare->col); - - flare->pos += flare->vel; - flare->pos = constrain(flare->pos, 0, SEGLEN - 1); - flare->vel += gravity; - flare->col -= 2; + if (strip.isMatrix) SEGMENT.setPixelColorXY(int(flare->posX), rows - uint16_t(flare->pos) - 1, flare->col, flare->col, flare->col); + else SEGMENT.setPixelColor(int(flare->posX) ? rows - int(flare->pos) - 1 : int(flare->pos), flare->col, flare->col, flare->col); + flare->pos += flare->vel; + flare->posX += flare->velX; + flare->pos = constrain(flare->pos, 0, rows-1); + flare->posX = constrain(flare->posX, 0, cols-strip.isMatrix); + flare->vel += gravity; + flare->col -= 2; + } else { + SEGENV.aux0 = 2; // ready to explode } - else - { - SEGENV.aux0 = 2; // ready to explode - } - } - else if (SEGENV.aux0 < 4) - { + } else if (SEGENV.aux0 < 4) { /* * Explode! * * Explosion happens where the flare ended. * Size is proportional to the height. */ - int nSparks = flare->pos; - nSparks = constrain(nSparks, 0, numSparks); - static float dying_gravity; + int nSparks = flare->pos + random8(4); + nSparks = constrain(nSparks, 4, numSparks); // initialize sparks - if (SEGENV.aux0 == 2) - { - for (int i = 1; i < nSparks; i++) - { - sparks[i].pos = flare->pos; - sparks[i].vel = (float(random16(0, 20000)) / 10000.0) - 0.9; // from -0.9 to 1.1 - sparks[i].col = 345; // abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright - // sparks[i].col = constrain(sparks[i].col, 0, 345); + if (SEGENV.aux0 == 2) { + for (int i = 1; i < nSparks; i++) { + sparks[i].pos = flare->pos; + sparks[i].posX = flare->posX; + sparks[i].vel = (float(random16(20001)) / 10000.0f) - 0.9f; // from -0.9 to 1.1 + sparks[i].vel *= rows<32 ? 0.5f : 1; // reduce velocity for smaller strips + sparks[i].velX = strip.isMatrix ? (float(random16(10001)) / 10000.0f) - 0.5f : 0; // from -0.5 to 0.5 + sparks[i].col = 345;//abs(sparks[i].vel * 750.0); // set colors before scaling velocity to keep them bright + //sparks[i].col = constrain(sparks[i].col, 0, 345); sparks[i].colIndex = random8(); - sparks[i].vel *= flare->pos / SEGLEN; // proportional to height - sparks[i].vel *= -gravity * 50; + sparks[i].vel *= flare->pos/rows; // proportional to height + sparks[i].velX *= strip.isMatrix ? flare->posX/cols : 0; // proportional to width + sparks[i].vel *= -gravity *50; } - // sparks[1].col = 345; // this will be our known spark - dying_gravity = gravity / 2; + //sparks[1].col = 345; // this will be our known spark + *dying_gravity = gravity/2; SEGENV.aux0 = 3; } - if (sparks[1].col > 4) - { //&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks - for (int i = 1; i < nSparks; i++) - { - sparks[i].pos += sparks[i].vel; - sparks[i].vel += dying_gravity; - if (sparks[i].col > 3) - sparks[i].col -= 4; + if (sparks[1].col > 4) {//&& sparks[1].pos > 0) { // as long as our known spark is lit, work with all the sparks + for (int i = 1; i < nSparks; i++) { + sparks[i].pos += sparks[i].vel; + sparks[i].posX += sparks[i].velX; + sparks[i].vel += *dying_gravity; + sparks[i].velX += strip.isMatrix ? *dying_gravity : 0; + if (sparks[i].col > 3) sparks[i].col -= 4; - if (sparks[i].pos > 0 && sparks[i].pos < SEGLEN) - { + if (sparks[i].pos > 0 && sparks[i].pos < rows) { + if (strip.isMatrix && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; uint16_t prog = sparks[i].col; - uint32_t spColor = (SEGMENT.palette) ? color_wheel(sparks[i].colIndex) : SEGCOLOR(0); - CRGB c = CRGB::Black; // HeatColor(sparks[i].col); - if (prog > 300) - { // fade from white to spark color - c = col_to_crgb(color_blend(spColor, WHITE, (prog - 300) * 5)); - } - else if (prog > 45) - { // fade from spark color to black - c = col_to_crgb(color_blend(BLACK, spColor, prog - 45)); + uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0); + CRGB c = CRGB::Black; //HeatColor(sparks[i].col); + if (prog > 300) { //fade from white to spark color + c = CRGB(color_blend(spColor, WHITE, (prog - 300)*5)); + } else if (prog > 45) { //fade from spark color to black + c = CRGB(color_blend(BLACK, spColor, prog - 45)); uint8_t cooling = (300 - prog) >> 5; c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - setPixelColor(int(sparks[i].pos), c.red, c.green, c.blue); + if (strip.isMatrix) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); + else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c.red, c.green, c.blue); } } - dying_gravity *= .99; // as sparks burn out they fall slower + SEGMENT.blur(16); + *dying_gravity *= .8f; // as sparks burn out they fall slower + } else { + SEGENV.aux0 = 6 + random8(10); //wait for this many frames } - else - { - SEGENV.aux0 = 6 + random8(10); // wait for this many frames - } - } - else - { + } else { SEGENV.aux0--; - if (SEGENV.aux0 < 4) - { - SEGENV.aux0 = 0; // back to flare - SEGENV.step = actuallyReverse ^ (SEGMENT.intensity > random8()); // decide firing side + if (SEGENV.aux0 < 4) { + SEGENV.aux0 = 0; //back to flare } } - SEGMENT.setOption(SEG_OPTION_REVERSED, actuallyReverse); - return FRAMETIME; } #undef MAX_SPARKS +static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128"; + /* * Drip Effect * ported of: https://www.youtube.com/watch?v=sru2fXh4r7k */ -uint16_t WS2812FX::mode_drip(void) +uint16_t mode_drip(void) { - // allocate segment data - uint8_t numDrops = 4; - uint16_t dataSize = sizeof(spark) * numDrops; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + if (SEGLEN == 1) return mode_static(); + //allocate segment data + uint16_t strips = SEGMENT.nrOfVStrips(); + const int maxNumDrops = 4; + uint16_t dataSize = sizeof(spark) * maxNumDrops; + if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + Spark* drops = reinterpret_cast(SEGENV.data); - fill(SEGCOLOR(1)); + if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); - Spark *drops = reinterpret_cast(SEGENV.data); + struct virtualStrip { + static void runStrip(uint16_t stripNr, Spark* drops) { - numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 + uint8_t numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 - float gravity = -0.0005 - (SEGMENT.speed / 50000.0); - gravity *= SEGLEN; - int sourcedrop = 12; + float gravity = -0.0005 - (SEGMENT.speed/50000.0); + gravity *= max(1, SEGLEN-1); + int sourcedrop = 12; - for (uint8_t j = 0; j < numDrops; j++) - { - if (drops[j].colIndex == 0) - { // init - drops[j].pos = SEGLEN - 1; // start at end - drops[j].vel = 0; // speed - drops[j].col = sourcedrop; // brightness - drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing) - } - - setPixelColor(SEGLEN - 1, color_blend(BLACK, SEGCOLOR(0), sourcedrop)); // water source - if (drops[j].colIndex == 1) - { - if (drops[j].col > 255) - drops[j].col = 255; - setPixelColor(uint16_t(drops[j].pos), color_blend(BLACK, SEGCOLOR(0), drops[j].col)); - - drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling - - if (random8() < drops[j].col / 10) - { // random drop - drops[j].colIndex = 2; // fall - drops[j].col = 255; - } - } - if (drops[j].colIndex > 1) - { // falling - if (drops[j].pos > 0) - { // fall until end of segment - drops[j].pos += drops[j].vel; - if (drops[j].pos < 0) - drops[j].pos = 0; - drops[j].vel += gravity; // gravity is negative - - for (uint16_t i = 1; i < 7 - drops[j].colIndex; i++) - { // some minor math so we don't expand bouncing droplets - uint16_t pos = constrain(uint16_t(drops[j].pos) + i, 0, SEGLEN - 1); // this is BAD, returns a pos >= SEGLEN occasionally - setPixelColor(pos, color_blend(BLACK, SEGCOLOR(0), drops[j].col / i)); // spread pixel with fade while falling + for (int j=0;j 2) - { // during bounce, some water is on the floor - setPixelColor(0, color_blend(SEGCOLOR(0), BLACK, drops[j].col)); - } - } - else - { // we hit bottom - if (drops[j].colIndex > 2) - { // already hit once, so back to forming - drops[j].colIndex = 0; - drops[j].col = sourcedrop; - } - else - { + SEGMENT.setPixelColor(indexToVStrip(SEGLEN-1, stripNr), color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source + if (drops[j].colIndex==1) { + if (drops[j].col>255) drops[j].col=255; + SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col)); - if (drops[j].colIndex == 2) - { // init bounce - drops[j].vel = -drops[j].vel / 4; // reverse velocity with damping - drops[j].pos += drops[j].vel; + drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling + + if (random8() < drops[j].col/10) { // random drop + drops[j].colIndex=2; //fall + drops[j].col=255; + } + } + if (drops[j].colIndex > 1) { // falling + if (drops[j].pos > 0) { // fall until end of segment + drops[j].pos += drops[j].vel; + if (drops[j].pos < 0) drops[j].pos = 0; + drops[j].vel += gravity; // gravity is negative + + for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets + uint16_t pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally + SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling + } + + if (drops[j].colIndex > 2) { // during bounce, some water is on the floor + SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,drops[j].col)); + } + } else { // we hit bottom + if (drops[j].colIndex > 2) { // already hit once, so back to forming + drops[j].colIndex = 0; + drops[j].col = sourcedrop; + + } else { + + if (drops[j].colIndex==2) { // init bounce + drops[j].vel = -drops[j].vel/4;// reverse velocity with damping + drops[j].pos += drops[j].vel; + } + drops[j].col = sourcedrop*2; + drops[j].colIndex = 5; // bouncing + } } - drops[j].col = sourcedrop * 2; - drops[j].colIndex = 5; // bouncing } } } - } + }; + + for (int stripNr=0; stripNr(SEGENV.data); + if (!SEGENV.allocateData(dataSize * strips)) return mode_static(); //allocation failed + Tetris* drops = reinterpret_cast(SEGENV.data); - // initialize dropping on first call or segment full - if (SEGENV.call == 0 || SEGENV.aux1 >= SEGLEN) - { - SEGENV.aux1 = 0; // reset brick stack size - SEGENV.step = 0; - fill(SEGCOLOR(1)); - return 250; // short wait - } + //if (SEGENV.call == 0) SEGMENT.fill(SEGCOLOR(1)); // will fill entire segment (1D or 2D), then use drop->step = 0 below - if (SEGENV.step == 0) - { // init - drop->speed = 0.0238 * (SEGMENT.speed ? (SEGMENT.speed >> 2) + 1 : random8(6, 64)); // set speed - drop->pos = SEGLEN; // start at end of segment (no need to subtract 1) - drop->col = color_from_palette(random8(0, 15) << 4, false, false, 0); // limit color choices so there is enough HUE gap - SEGENV.step = 1; // drop state (0 init, 1 forming, 2 falling) - SEGENV.aux0 = (SEGMENT.intensity ? (SEGMENT.intensity >> 5) + 1 : random8(1, 5)) * (1 + (SEGLEN >> 6)); // size of brick - } + // virtualStrip idea by @ewowi (Ewoud Wijma) + // requires virtual strip # to be embedded into upper 16 bits of index in setPixelcolor() + // the following functions will not work on virtual strips: fill(), fade_out(), fadeToBlack(), blur() + struct virtualStrip { + static void runStrip(size_t stripNr, Tetris *drop) { + // initialize dropping on first call or segment full + if (SEGENV.call == 0) { + drop->stack = 0; // reset brick stack size + drop->step = millis() + 2000; // start by fading out strip + if (SEGMENT.check1) drop->col = 0;// use only one color from palette + } - if (SEGENV.step == 1) - { // forming - if (random8() >> 6) - { // random drop - SEGENV.step = 2; // fall + if (drop->step == 0) { // init brick + // speed calcualtion: a single brick should reach bottom of strip in X seconds + // if the speed is set to 1 this should take 5s and at 255 it should take 0.25s + // as this is dependant on SEGLEN it should be taken into account and the fact that effect runs every FRAMETIME s + int speed = SEGMENT.speed ? SEGMENT.speed : random8(1,255); + speed = map(speed, 1, 255, 5000, 250); // time taken for full (SEGLEN) drop + drop->speed = float(SEGLEN * FRAMETIME) / float(speed); // set speed + drop->pos = SEGLEN; // start at end of segment (no need to subtract 1) + if (!SEGMENT.check1) drop->col = random8(0,15)<<4; // limit color choices so there is enough HUE gap + drop->step = 1; // drop state (0 init, 1 forming, 2 falling) + drop->brick = (SEGMENT.intensity ? (SEGMENT.intensity>>5)+1 : random8(1,5)) * (1+(SEGLEN>>6)); // size of brick + } + + if (drop->step == 1) { // forming + if (random8()>>6) { // random drop + drop->step = 2; // fall + } + } + + if (drop->step == 2) { // falling + if (drop->pos > drop->stack) { // fall until top of stack + drop->pos -= drop->speed; // may add gravity as: speed += gravity + if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack; + for (int i = int(drop->pos); i < SEGLEN; i++) { + uint32_t col = ipos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); + SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col); + } + } else { // we hit bottom + drop->step = 0; // proceed with next brick, go back to init + drop->stack += drop->brick; // increase the stack size + if (drop->stack >= SEGLEN) drop->step = millis() + 2000; // fade out stack + } + } + + if (drop->step > 2) { // fade strip + drop->brick = 0; // reset brick size (no more growing) + if (drop->step > millis()) { + // allow fading of virtual strip + for (int i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend + } else { + drop->stack = 0; // reset brick stack size + drop->step = 0; // proceed with next brick + if (SEGMENT.check1) drop->col += 8; // gradually increase palette index + } + } } - } + }; + + for (int stripNr=0; stripNr 1) - { // falling - if (drop->pos > SEGENV.aux1) - { // fall until top of stack - drop->pos -= drop->speed; // may add gravity as: speed += gravity - if (int(drop->pos) < SEGENV.aux1) - drop->pos = SEGENV.aux1; - for (uint16_t i = int(drop->pos); i < SEGLEN; i++) - setPixelColor(i, i < int(drop->pos) + SEGENV.aux0 ? drop->col : SEGCOLOR(1)); - } - else - { // we hit bottom - SEGENV.step = 0; // go back to init - SEGENV.aux1 += SEGENV.aux0; // increase the stack size - if (SEGENV.aux1 >= SEGLEN) - return 1000; // wait for a second - } - } return FRAMETIME; } +static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width,,,,One color;!,!;!;;sx=0,ix=0,pal=11,m12=1"; + /* / Plasma Effect / adapted from https://github.com/atuline/FastLED-Demos/blob/master/plasma/plasma.ino */ -uint16_t WS2812FX::mode_plasma(void) -{ +uint16_t mode_plasma(void) { // initialize phases on start - if (SEGENV.call == 0) - { - SEGENV.aux0 = random8(0, 2); // add a bit of randomness + if (SEGENV.call == 0) { + SEGENV.aux0 = random8(0,2); // add a bit of randomness } - uint8_t thisPhase = beatsin8(6 + SEGENV.aux0, -64, 64); - uint8_t thatPhase = beatsin8(7 + SEGENV.aux0, -64, 64); + uint8_t thisPhase = beatsin8(6+SEGENV.aux0,-64,64); + uint8_t thatPhase = beatsin8(7+SEGENV.aux0,-64,64); - for (int i = 0; i < SEGLEN; i++) - { // For each of the LED's in the strand, set color & brightness based on a wave as follows: - uint8_t colorIndex = cubicwave8((i * (2 + 3 * (SEGMENT.speed >> 5)) + thisPhase) & 0xFF) / 2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. - + cos8((i * (1 + 2 * (SEGMENT.speed >> 5)) + thatPhase) & 0xFF) / 2; // factor=15 // Hey, you can even change the frequencies if you wish. - uint8_t thisBright = qsub8(colorIndex, beatsin8(7, 0, (128 - (SEGMENT.intensity >> 1)))); - // CRGB color = ColorFromPalette(currentPalette, colorIndex, thisBright, LINEARBLEND); - // setPixelColor(i, color.red, color.green, color.blue); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0), thisBright)); // This supports RGBW. + for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows: + uint8_t colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change. + + cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish. + uint8_t thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1)))); + //CRGB color = ColorFromPalette(SEGPALETTE, colorIndex, thisBright, LINEARBLEND); + //SEGMENT.setPixelColor(i, color.red, color.green, color.blue); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0, thisBright)); } return FRAMETIME; } +static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase,!;!;!"; + /* * Percentage display * Intesity values from 0-100 turn on the leds. */ -uint16_t WS2812FX::mode_percent(void) -{ +uint16_t mode_percent(void) { - uint8_t percent = MAX(0, MIN(200, SEGMENT.intensity)); + uint8_t percent = SEGMENT.intensity; + percent = constrain(percent, 0, 200); uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 : SEGLEN * (200 - percent) / 100.0; uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)); - if (SEGMENT.speed == 255) - size = 255; + if (SEGMENT.speed == 255) size = 255; - if (percent < 100) - { - for (uint16_t i = 0; i < SEGLEN; i++) - { - if (i < SEGENV.step) - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } - else - { - setPixelColor(i, SEGCOLOR(1)); - } + if (percent <= 100) { + for (int i = 0; i < SEGLEN; i++) { + if (i < SEGENV.aux1) { + if (SEGMENT.check1) + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,0,100,0,255), false, false, 0)); + else + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } + else { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); + } } - } - else - { - for (uint16_t i = 0; i < SEGLEN; i++) - { - if (i < (SEGLEN - SEGENV.step)) - { - setPixelColor(i, SEGCOLOR(1)); - } - else - { - setPixelColor(i, color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); - } + } else { + for (int i = 0; i < SEGLEN; i++) { + if (i < (SEGLEN - SEGENV.aux1)) { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); + } + else { + if (SEGMENT.check1) + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,100,200,255,0), false, false, 0)); + else + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); + } } } - if (active_leds > SEGENV.step) - { // smooth transition to the target value - SEGENV.step += size; - if (SEGENV.step > active_leds) - SEGENV.step = active_leds; - } - else if (active_leds < SEGENV.step) - { - if (SEGENV.step > size) - SEGENV.step -= size; - else - SEGENV.step = 0; - if (SEGENV.step < active_leds) - SEGENV.step = active_leds; + if(active_leds > SEGENV.aux1) { // smooth transition to the target value + SEGENV.aux1 += size; + if (SEGENV.aux1 > active_leds) SEGENV.aux1 = active_leds; + } else if (active_leds < SEGENV.aux1) { + if (SEGENV.aux1 > size) SEGENV.aux1 -= size; else SEGENV.aux1 = 0; + if (SEGENV.aux1 < active_leds) SEGENV.aux1 = active_leds; } - return FRAMETIME; + return FRAMETIME; } +static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!"; + /* -/ Modulates the brightness similar to a heartbeat -*/ -uint16_t WS2812FX::mode_heartbeat(void) -{ - uint8_t bpm = 40 + (SEGMENT.speed >> 4); - uint32_t msPerBeat = (60000 / bpm); + * Modulates the brightness similar to a heartbeat + * (unimplemented?) tries to draw an ECG aproximation on a 2D matrix + */ +uint16_t mode_heartbeat(void) { + uint8_t bpm = 40 + (SEGMENT.speed >> 3); + uint32_t msPerBeat = (60000L / bpm); uint32_t secondBeat = (msPerBeat / 3); - uint32_t bri_lower = SEGENV.aux1; + unsigned long beatTimer = strip.now - SEGENV.step; + bri_lower = bri_lower * 2042 / (2048 + SEGMENT.intensity); SEGENV.aux1 = bri_lower; - unsigned long beatTimer = millis() - SEGENV.step; - if ((beatTimer > secondBeat) && !SEGENV.aux0) - { // time for the second beat? - SEGENV.aux1 = UINT16_MAX; // full bri + if ((beatTimer > secondBeat) && !SEGENV.aux0) { // time for the second beat? + SEGENV.aux1 = UINT16_MAX; //3/4 bri SEGENV.aux0 = 1; } - if (beatTimer > msPerBeat) - { // time to reset the beat timer? - SEGENV.aux1 = UINT16_MAX; // full bri + if (beatTimer > msPerBeat) { // time to reset the beat timer? + SEGENV.aux1 = UINT16_MAX; //full bri SEGENV.aux0 = 0; - SEGENV.step = millis(); + SEGENV.step = strip.now; } - for (uint16_t i = 0; i < SEGLEN; i++) - { - setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); } return FRAMETIME; } +static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;01;m12=1"; + // "Pacifica" // Gentle, blue-green ocean waves. @@ -3940,96 +3692,14 @@ uint16_t WS2812FX::mode_heartbeat(void) // // Modified for WLED, based on https://github.com/FastLED/FastLED/blob/master/examples/Pacifica/Pacifica.ino // -uint16_t WS2812FX::mode_pacifica() -{ - uint32_t nowOld = now; - - CRGBPalette16 pacifica_palette_1 = - {0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, - 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50}; - CRGBPalette16 pacifica_palette_2 = - {0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, - 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F}; - CRGBPalette16 pacifica_palette_3 = - {0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, - 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF}; - - if (SEGMENT.palette) - { - pacifica_palette_1 = currentPalette; - pacifica_palette_2 = currentPalette; - pacifica_palette_3 = currentPalette; - } - - // Increment the four "color index start" counters, one for each wave layer. - // Each is incremented at a different speed, and the speeds vary over time. - uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; - // static uint16_t sCIStart1, sCIStart2, sCIStart3, sCIStart4; - // uint32_t deltams = 26 + (SEGMENT.speed >> 3); - uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7); - uint64_t deltat = (now >> 2) + ((now * SEGMENT.speed) >> 7); - now = deltat; - - uint16_t speedfactor1 = beatsin16(3, 179, 269); - uint16_t speedfactor2 = beatsin16(4, 179, 269); - uint32_t deltams1 = (deltams * speedfactor1) / 256; - uint32_t deltams2 = (deltams * speedfactor2) / 256; - uint32_t deltams21 = (deltams1 + deltams2) / 2; - sCIStart1 += (deltams1 * beatsin88(1011, 10, 13)); - sCIStart2 -= (deltams21 * beatsin88(777, 8, 11)); - sCIStart3 -= (deltams1 * beatsin88(501, 5, 7)); - sCIStart4 -= (deltams2 * beatsin88(257, 4, 6)); - SEGENV.aux0 = sCIStart1; - SEGENV.aux1 = sCIStart2; - SEGENV.step = sCIStart4; - SEGENV.step = (SEGENV.step << 16) + sCIStart3; - - // Clear out the LED array to a dim background blue-green - // fill(132618); - - uint8_t basethreshold = beatsin8(9, 55, 65); - uint8_t wave = beat8(7); - - for (uint16_t i = 0; i < SEGLEN; i++) - { - CRGB c = CRGB(2, 6, 10); - // Render each of four layers, with different scales and speeds, that vary over time - c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0 - beat16(301)); - c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401)); - c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256, beatsin8(9, 10, 38), 0 - beat16(503)); - c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256, beatsin8(8, 10, 28), beat16(601)); - - // Add extra 'white' to areas where the four layers of light have lined up brightly - uint8_t threshold = scale8(sin8(wave), 20) + basethreshold; - wave += 7; - uint8_t l = c.getAverageLight(); - if (l > threshold) - { - uint8_t overage = l - threshold; - uint8_t overage2 = qadd8(overage, overage); - c += CRGB(overage, overage2, qadd8(overage2, overage2)); - } - - // deepen the blues and greens - c.blue = scale8(c.blue, 145); - c.green = scale8(c.green, 200); - c |= CRGB(2, 5, 7); - - setPixelColor(i, c.red, c.green, c.blue); - } - - now = nowOld; - return FRAMETIME; -} - // Add one layer of waves into the led array -CRGB WS2812FX::pacifica_one_layer(uint16_t i, CRGBPalette16 &p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) +CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff) { uint16_t ci = cistart; uint16_t waveangle = ioff; uint16_t wavescale_half = (wavescale >> 1) + 20; - waveangle += ((120 + SEGMENT.intensity) * i); // original 250 * i + waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i uint16_t s16 = sin16(waveangle) + 32768; uint16_t cs = scale16(s16, wavescale_half) + wavescale_half; ci += (cs * i); @@ -4038,276 +3708,320 @@ CRGB WS2812FX::pacifica_one_layer(uint16_t i, CRGBPalette16 &p, uint16_t cistart return ColorFromPalette(p, sindex8, bri, LINEARBLEND); } -// Solid colour background with glitter -uint16_t WS2812FX::mode_solid_glitter() +uint16_t mode_pacifica() { - fill(SEGCOLOR(0)); + uint32_t nowOld = strip.now; - if (SEGMENT.intensity > random8()) - { - setPixelColor(random16(SEGLEN), ULTRAWHITE); + CRGBPalette16 pacifica_palette_1 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x14554B, 0x28AA50 }; + CRGBPalette16 pacifica_palette_2 = + { 0x000507, 0x000409, 0x00030B, 0x00030D, 0x000210, 0x000212, 0x000114, 0x000117, + 0x000019, 0x00001C, 0x000026, 0x000031, 0x00003B, 0x000046, 0x0C5F52, 0x19BE5F }; + CRGBPalette16 pacifica_palette_3 = + { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, + 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; + + if (SEGMENT.palette) { + pacifica_palette_1 = SEGPALETTE; + pacifica_palette_2 = SEGPALETTE; + pacifica_palette_3 = SEGPALETTE; } + + // Increment the four "color index start" counters, one for each wave layer. + // Each is incremented at a different speed, and the speeds vary over time. + uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; + uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7); + uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7); + strip.now = deltat; + + uint16_t speedfactor1 = beatsin16(3, 179, 269); + uint16_t speedfactor2 = beatsin16(4, 179, 269); + uint32_t deltams1 = (deltams * speedfactor1) / 256; + uint32_t deltams2 = (deltams * speedfactor2) / 256; + uint32_t deltams21 = (deltams1 + deltams2) / 2; + sCIStart1 += (deltams1 * beatsin88(1011,10,13)); + sCIStart2 -= (deltams21 * beatsin88(777,8,11)); + sCIStart3 -= (deltams1 * beatsin88(501,5,7)); + sCIStart4 -= (deltams2 * beatsin88(257,4,6)); + SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2; + SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3; + + // Clear out the LED array to a dim background blue-green + //SEGMENT.fill(132618); + + uint8_t basethreshold = beatsin8( 9, 55, 65); + uint8_t wave = beat8( 7 ); + + for (int i = 0; i < SEGLEN; i++) { + CRGB c = CRGB(2, 6, 10); + // Render each of four layers, with different scales and speeds, that vary over time + c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); + c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503)); + c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601)); + + // Add extra 'white' to areas where the four layers of light have lined up brightly + uint8_t threshold = scale8( sin8( wave), 20) + basethreshold; + wave += 7; + uint8_t l = c.getAverageLight(); + if (l > threshold) { + uint8_t overage = l - threshold; + uint8_t overage2 = qadd8(overage, overage); + c += CRGB(overage, overage2, qadd8(overage2, overage2)); + } + + //deepen the blues and greens + c.blue = scale8(c.blue, 145); + c.green = scale8(c.green, 200); + c |= CRGB( 2, 5, 7); + + SEGMENT.setPixelColor(i, c.red, c.green, c.blue); + } + + strip.now = nowOld; return FRAMETIME; } +static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=51"; + /* * Mode simulates a gradual sunrise */ -uint16_t WS2812FX::mode_sunrise() -{ - // speed 0 - static sun - // speed 1 - 60: sunrise time in minutes - // speed 60 - 120 : sunset time in minutes - 60; - // speed above: "breathing" rise and set - if (SEGENV.call == 0 || SEGMENT.speed != SEGENV.aux0) - { - SEGENV.step = millis(); // save starting time, millis() because now can change from sync +uint16_t mode_sunrise() { + if (SEGLEN == 1) return mode_static(); + //speed 0 - static sun + //speed 1 - 60: sunrise time in minutes + //speed 60 - 120 : sunset time in minutes - 60; + //speed above: "breathing" rise and set + if (SEGENV.call == 0 || SEGMENT.speed != SEGENV.aux0) { + SEGENV.step = millis(); //save starting time, millis() because now can change from sync SEGENV.aux0 = SEGMENT.speed; } - fill(0); + SEGMENT.fill(BLACK); uint16_t stage = 0xFFFF; - uint32_t s10SinceStart = (millis() - SEGENV.step) / 100; // tenths of seconds + uint32_t s10SinceStart = (millis() - SEGENV.step) /100; //tenths of seconds - if (SEGMENT.speed > 120) - { // quick sunrise and sunset - uint16_t counter = (now >> 1) * (((SEGMENT.speed - 120) >> 1) + 1); + if (SEGMENT.speed > 120) { //quick sunrise and sunset + uint16_t counter = (strip.now >> 1) * (((SEGMENT.speed -120) >> 1) +1); stage = triwave16(counter); - } - else if (SEGMENT.speed) - { // sunrise + } else if (SEGMENT.speed) { //sunrise uint8_t durMins = SEGMENT.speed; - if (durMins > 60) - durMins -= 60; + if (durMins > 60) durMins -= 60; uint32_t s10Target = durMins * 600; - if (s10SinceStart > s10Target) - s10SinceStart = s10Target; + if (s10SinceStart > s10Target) s10SinceStart = s10Target; stage = map(s10SinceStart, 0, s10Target, 0, 0xFFFF); - if (SEGMENT.speed > 60) - stage = 0xFFFF - stage; // sunset + if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset } - for (uint16_t i = 0; i <= SEGLEN / 2; i++) + for (int i = 0; i <= SEGLEN/2; i++) { - // default palette is Fire - uint32_t c = color_from_palette(0, false, true, 255); // background + //default palette is Fire + uint32_t c = SEGMENT.color_from_palette(0, false, true, 255); //background uint16_t wave = triwave16((i * stage) / SEGLEN); wave = (wave >> 8) + ((wave * SEGMENT.intensity) >> 15); - if (wave > 240) - { // clipped, full white sun - c = color_from_palette(240, false, true, 255); + if (wave > 240) { //clipped, full white sun + c = SEGMENT.color_from_palette( 240, false, true, 255); + } else { //transition + c = SEGMENT.color_from_palette(wave, false, true, 255); } - else - { // transition - c = color_from_palette(wave, false, true, 255); - } - setPixelColor(i, c); - setPixelColor(SEGLEN - i - 1, c); + SEGMENT.setPixelColor(i, c); + SEGMENT.setPixelColor(SEGLEN - i - 1, c); } return FRAMETIME; } +static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],Width;;!;;sx=60"; + /* * Effects by Andrew Tuline */ -uint16_t WS2812FX::phased_base(uint8_t moder) -{ // We're making sine waves here. By Andrew Tuline. +uint16_t phased_base(uint8_t moder) { // We're making sine waves here. By Andrew Tuline. - uint8_t allfreq = 16; // Base frequency. - // float* phasePtr = reinterpret_cast(SEGENV.step); // Phase change value gets calculated. - static float phase = 0; // phasePtr[0]; - uint8_t cutOff = (255 - SEGMENT.intensity); // You can change the number of pixels. AKA INTENSITY (was 192). - uint8_t modVal = 5; // SEGMENT.custom1/8+1; // You can change the modulus. AKA Custom1 (was 5). + uint8_t allfreq = 16; // Base frequency. + float *phase = reinterpret_cast(&SEGENV.step); // Phase change value gets calculated (float fits into unsigned long). + uint8_t cutOff = (255-SEGMENT.intensity); // You can change the number of pixels. AKA INTENSITY (was 192). + uint8_t modVal = 5;//SEGMENT.fft1/8+1; // You can change the modulus. AKA FFT1 (was 5). - uint8_t index = now / 64; // Set color rotation speed - phase += SEGMENT.speed / 32.0; // You can change the speed of the wave. AKA SPEED (was .4) - // phasePtr[0] = phase; + uint8_t index = strip.now/64; // Set color rotation speed + *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) - for (int i = 0; i < SEGLEN; i++) - { - if (moder == 1) - modVal = (inoise8(i * 10 + i * 10) / 16); // Let's randomize our mod length with some Perlin noise. - uint16_t val = (i + 1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that leds[0] is used. - if (modVal == 0) - modVal = 1; - val += phase * (i % modVal + 1) / 2; // This sets the varying phase change of the waves. By Andrew Tuline. - uint8_t b = cubicwave8(val); // Now we make an 8 bit sinewave. - b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light. - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, false, 0), b)); + for (int i = 0; i < SEGLEN; i++) { + if (moder == 1) modVal = (inoise8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. + uint16_t val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used. + if (modVal == 0) modVal = 1; + val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline. + uint8_t b = cubicwave8(val); // Now we make an 8 bit sinewave. + b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light. + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), b)); index += 256 / SEGLEN; - if (SEGLEN > 256) - index++; // Correction for segments longer than 256 LEDs + if (SEGLEN > 256) index ++; // Correction for segments longer than 256 LEDs } return FRAMETIME; } -uint16_t WS2812FX::mode_phased(void) -{ + +uint16_t mode_phased(void) { return phased_base(0); } +static const char _data_FX_MODE_PHASED[] PROGMEM = "Phased@!,!;!,!;!"; -uint16_t WS2812FX::mode_phased_noise(void) -{ + +uint16_t mode_phased_noise(void) { return phased_base(1); } +static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!"; -uint16_t WS2812FX::mode_twinkleup(void) -{ // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. - random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - for (int i = 0; i < SEGLEN; i++) - { - uint8_t pixBri = beatsin8(SEGMENT.speed / 2 + random8() / 4, SEGMENT.custom1, 255, 0, random8()); // Every pixel gets a different timebase. +uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. + random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - /* - if (i < SEGLEN/3) { // You can use this to shape a 1D candle, where it's brightest at 1/3rd of SEGLEN and dull at both ends. - pixBri = pixBri*i/(SEGLEN/3); - } else { - pixBri = pixBri*(SEGLEN-i)/SEGLEN; - } - */ - - if (random8() > SEGMENT.intensity) - pixBri = 0; - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(random8() + now / 100, false, PALETTE_SOLID_WRAP, 0), pixBri)); + for (int i = 0; i < SEGLEN; i++) { + uint8_t ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work. + uint8_t pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed)); + if (random8() > SEGMENT.intensity) pixBri = 0; + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); } return FRAMETIME; } +static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0"; + // Peaceful noise that's slow and with gradually changing palettes. Does not support WLED palettes or default colours or controls. -uint16_t WS2812FX::mode_noisepal(void) -{ // Slow noise palette by Andrew Tuline. - uint16_t scale = 15 + (SEGMENT.intensity >> 2); // default was 30 - // #define scale 30 +uint16_t mode_noisepal(void) { // Slow noise palette by Andrew Tuline. + uint16_t scale = 15 + (SEGMENT.intensity >> 2); //default was 30 + //#define scale 30 - uint16_t dataSize = sizeof(CRGBPalette16) * 2; // allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes) - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + uint16_t dataSize = sizeof(CRGBPalette16) * 2; //allocate space for 2 Palettes (2 * 16 * 3 = 96 bytes) + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - CRGBPalette16 *palettes = reinterpret_cast(SEGENV.data); + CRGBPalette16* palettes = reinterpret_cast(SEGENV.data); - uint16_t changePaletteMs = 4000 + SEGMENT.speed * 10; // between 4 - 6.5sec + uint16_t changePaletteMs = 4000 + SEGMENT.speed *10; //between 4 - 6.5sec if (millis() - SEGENV.step > changePaletteMs) { SEGENV.step = millis(); uint8_t baseI = random8(); - palettes[1] = CRGBPalette16(CHSV(baseI + random8(64), 255, random8(128, 255)), CHSV(baseI + 128, 255, random8(128, 255)), CHSV(baseI + random8(92), 192, random8(128, 255)), CHSV(baseI + random8(92), 255, random8(128, 255))); + palettes[1] = CRGBPalette16(CHSV(baseI+random8(64), 255, random8(128,255)), CHSV(baseI+128, 255, random8(128,255)), CHSV(baseI+random8(92), 192, random8(128,255)), CHSV(baseI+random8(92), 255, random8(128,255))); } CRGB color; - // EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms) - nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations. + //EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms) + nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations. - if (SEGMENT.palette > 0) - palettes[0] = currentPalette; + if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE; - for (int i = 0; i < SEGLEN; i++) - { - uint8_t index = inoise8(i * scale, SEGENV.aux0 + i * scale); // Get a value from the noise function. I'm using both x and y axis. - color = ColorFromPalette(palettes[0], index, 255, LINEARBLEND); // Use the my own palette. - setPixelColor(i, color.red, color.green, color.blue); + for (int i = 0; i < SEGLEN; i++) { + uint8_t index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. + color = ColorFromPalette(palettes[0], index, 255, LINEARBLEND); // Use the my own palette. + SEGMENT.setPixelColor(i, color.red, color.green, color.blue); } - SEGENV.aux0 += beatsin8(10, 1, 4); // Moving along the distance. Vary it a bit with a sine wave. + SEGENV.aux0 += beatsin8(10,1,4); // Moving along the distance. Vary it a bit with a sine wave. return FRAMETIME; } +static const char _data_FX_MODE_NOISEPAL[] PROGMEM = "Noise Pal@!,Scale;;!"; + // Sine waves that have controllable phase change speed, frequency and cutoff. By Andrew Tuline. -// SEGMENT.speed ->Speed, SEGMENT.intensity -> Frequency (SEGMENT.custom1 -> Color change, SEGMENT.custom2 -> PWM cutoff) +// SEGMENT.speed ->Speed, SEGMENT.intensity -> Frequency (SEGMENT.fft1 -> Color change, SEGMENT.fft2 -> PWM cutoff) // -uint16_t WS2812FX::mode_sinewave(void) -{ // Adjustable sinewave. By Andrew Tuline - // #define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0 +uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tuline + //#define qsuba(x, b) ((x>b)?x-b:0) // Analog Unsigned subtraction macro. if result <0, then => 0 - uint16_t colorIndex = now / 32; //(256 - SEGMENT.custom1); // Amount of colour change. + uint16_t colorIndex = strip.now /32;//(256 - SEGMENT.fft1); // Amount of colour change. - SEGENV.step += SEGMENT.speed / 16; // Speed of animation. - uint16_t freq = SEGMENT.intensity / 4; // SEGMENT.custom2/8; // Frequency of the signal. + SEGENV.step += SEGMENT.speed/16; // Speed of animation. + uint16_t freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. - for (int i = 0; i < SEGLEN; i++) - { // For each of the LED's in the strand, set a brightness based on a wave as follows: - int pixBri = cubicwave8((i * freq) + SEGENV.step); // qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. - // setPixCol(i, i*colorIndex/255, pixBri); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i * colorIndex / 255, false, PALETTE_SOLID_WRAP, 0), pixBri)); + for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: + int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. + //setPixCol(i, i*colorIndex/255, pixBri); + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri)); } return FRAMETIME; } +static const char _data_FX_MODE_SINEWAVE[] PROGMEM = "Sine"; + /* * Best of both worlds from Palette and Spot effects. By Aircoookie */ -uint16_t WS2812FX::mode_flow(void) +uint16_t mode_flow(void) { uint16_t counter = 0; if (SEGMENT.speed != 0) { - counter = now * ((SEGMENT.speed >> 2) + 1); + counter = strip.now * ((SEGMENT.speed >> 2) +1); counter = counter >> 8; } - uint16_t maxZones = SEGLEN / 6; // only looks good if each zone has at least 6 LEDs + uint16_t maxZones = SEGLEN / 6; //only looks good if each zone has at least 6 LEDs uint16_t zones = (SEGMENT.intensity * maxZones) >> 8; - if (zones & 0x01) - zones++; // zones must be even - if (zones < 2) - zones = 2; + if (zones & 0x01) zones++; //zones must be even + if (zones < 2) zones = 2; uint16_t zoneLen = SEGLEN / zones; uint16_t offset = (SEGLEN - zones * zoneLen) >> 1; - fill(color_from_palette(-counter, false, true, 255)); + SEGMENT.fill(SEGMENT.color_from_palette(-counter, false, true, 255)); - for (uint16_t z = 0; z < zones; z++) + for (int z = 0; z < zones; z++) { uint16_t pos = offset + z * zoneLen; - for (uint16_t i = 0; i < zoneLen; i++) + for (int i = 0; i < zoneLen; i++) { uint8_t colorIndex = (i * 255 / zoneLen) - counter; - uint16_t led = (z & 0x01) ? i : (zoneLen - 1) - i; - if (IS_REVERSE) - led = (zoneLen - 1) - led; - setPixelColor(pos + led, color_from_palette(colorIndex, false, true, 255)); + uint16_t led = (z & 0x01) ? i : (zoneLen -1) -i; + if (SEGMENT.reverse) led = (zoneLen -1) -led; + SEGMENT.setPixelColor(pos + led, SEGMENT.color_from_palette(colorIndex, false, true, 255)); } } return FRAMETIME; } +static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //vertical + /* * Dots waving around in a sine/pendulum motion. * Little pixel birds flying in a circle. By Aircoookie */ -uint16_t WS2812FX::mode_chunchun(void) +uint16_t mode_chunchun(void) { - fill(SEGCOLOR(1)); - uint16_t counter = now * (6 + (SEGMENT.speed >> 4)); - uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment + if (SEGLEN == 1) return mode_static(); + SEGMENT.fade_out(254); // add a bit of trail + uint16_t counter = strip.now * (6 + (SEGMENT.speed >> 4)); + uint16_t numBirds = 2 + (SEGLEN >> 3); // 2 + 1/8 of a segment uint16_t span = (SEGMENT.intensity << 8) / numBirds; - for (uint16_t i = 0; i < numBirds; i++) + for (int i = 0; i < numBirds; i++) { counter -= span; uint16_t megumin = sin16(counter) + 0x8000; - uint32_t bird = (megumin * SEGLEN) >> 16; - uint32_t c = color_from_palette((i * 255) / numBirds, false, false, 0); // no palette wrapping - setPixelColor(bird, c); + uint16_t bird = uint32_t(megumin * SEGLEN) >> 16; + uint32_t c = SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping + bird = constrain(bird, 0, SEGLEN-1); + SEGMENT.setPixelColor(bird, c); } return FRAMETIME; } +static const char _data_FX_MODE_CHUNCHUN[] PROGMEM = "Chunchun@!,Gap size;!,!;!"; -// 13 bytes -typedef struct Spotlight -{ + +//13 bytes +typedef struct Spotlight { float speed; uint8_t colorIdx; int16_t position; @@ -4316,17 +4030,17 @@ typedef struct Spotlight uint8_t type; } spotlight; -#define SPOT_TYPE_SOLID 0 -#define SPOT_TYPE_GRADIENT 1 +#define SPOT_TYPE_SOLID 0 +#define SPOT_TYPE_GRADIENT 1 #define SPOT_TYPE_2X_GRADIENT 2 -#define SPOT_TYPE_2X_DOT 3 -#define SPOT_TYPE_3X_DOT 4 -#define SPOT_TYPE_4X_DOT 5 -#define SPOT_TYPES_COUNT 6 +#define SPOT_TYPE_2X_DOT 3 +#define SPOT_TYPE_3X_DOT 4 +#define SPOT_TYPE_4X_DOT 5 +#define SPOT_TYPES_COUNT 6 #ifdef ESP8266 -#define SPOT_MAX_COUNT 17 // Number of simultaneous waves + #define SPOT_MAX_COUNT 17 //Number of simultaneous waves #else -#define SPOT_MAX_COUNT 49 // Number of simultaneous waves + #define SPOT_MAX_COUNT 49 //Number of simultaneous waves #endif /* @@ -4336,60 +4050,51 @@ typedef struct Spotlight * * By Steve Pomeroy @xxv */ -uint16_t WS2812FX::mode_dancing_shadows(void) +uint16_t mode_dancing_shadows(void) { - uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 + if (SEGLEN == 1) return mode_static(); + uint8_t numSpotlights = map(SEGMENT.intensity, 0, 255, 2, SPOT_MAX_COUNT); // 49 on 32 segment ESP32, 17 on 16 segment ESP8266 bool initialize = SEGENV.aux0 != numSpotlights; SEGENV.aux0 = numSpotlights; uint16_t dataSize = sizeof(spotlight) * numSpotlights; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Spotlight *spotlights = reinterpret_cast(SEGENV.data); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Spotlight* spotlights = reinterpret_cast(SEGENV.data); - fill(BLACK); + SEGMENT.fill(BLACK); unsigned long time = millis(); bool respawn = false; - for (uint8_t i = 0; i < numSpotlights; i++) - { - if (!initialize) - { + for (size_t i = 0; i < numSpotlights; i++) { + if (!initialize) { // advance the position of the spotlight int16_t delta = (float)(time - spotlights[i].lastUpdateTime) * - (spotlights[i].speed * ((1.0 + SEGMENT.speed) / 100.0)); + (spotlights[i].speed * ((1.0 + SEGMENT.speed)/100.0)); - if (abs(delta) >= 1) - { + if (abs(delta) >= 1) { spotlights[i].position += delta; spotlights[i].lastUpdateTime = time; } - respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (SEGLEN + 2)) || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2)); + respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (SEGLEN + 2)) + || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2)); } - if (initialize || respawn) - { + if (initialize || respawn) { spotlights[i].colorIdx = random8(); spotlights[i].width = random8(1, 10); - spotlights[i].speed = 1.0 / random8(4, 50); + spotlights[i].speed = 1.0/random8(4, 50); - if (initialize) - { + if (initialize) { spotlights[i].position = random16(SEGLEN); spotlights[i].speed *= random8(2) ? 1.0 : -1.0; - } - else - { - if (random8(2)) - { + } else { + if (random8(2)) { spotlights[i].position = SEGLEN + spotlights[i].width; spotlights[i].speed *= -1.0; - } - else - { + }else { spotlights[i].position = -spotlights[i].width; } } @@ -4398,80 +4103,61 @@ uint16_t WS2812FX::mode_dancing_shadows(void) spotlights[i].type = random8(SPOT_TYPES_COUNT); } - uint32_t color = color_from_palette(spotlights[i].colorIdx, false, false, 0); + uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 255); int start = spotlights[i].position; - if (spotlights[i].width <= 1) - { - if (start >= 0 && start < SEGLEN) - { - blendPixelColor(start, color, 128); + if (spotlights[i].width <= 1) { + if (start >= 0 && start < SEGLEN) { + SEGMENT.blendPixelColor(start, color, 128); } - } - else - { - switch (spotlights[i].type) - { - case SPOT_TYPE_SOLID: - for (uint8_t j = 0; j < spotlights[i].width; j++) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, 128); + } else { + switch (spotlights[i].type) { + case SPOT_TYPE_SOLID: + for (size_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, 128); + } } - } break; - case SPOT_TYPE_GRADIENT: - for (uint8_t j = 0; j < spotlights[i].width; j++) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, - cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); + case SPOT_TYPE_GRADIENT: + for (size_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); + } } - } break; - case SPOT_TYPE_2X_GRADIENT: - for (uint8_t j = 0; j < spotlights[i].width; j++) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, - cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); + case SPOT_TYPE_2X_GRADIENT: + for (size_t j = 0; j < spotlights[i].width; j++) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); + } } - } break; - case SPOT_TYPE_2X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 2) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, 128); + case SPOT_TYPE_2X_DOT: + for (size_t j = 0; j < spotlights[i].width; j += 2) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, 128); + } } - } break; - case SPOT_TYPE_3X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 3) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, 128); + case SPOT_TYPE_3X_DOT: + for (size_t j = 0; j < spotlights[i].width; j += 3) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, 128); + } } - } break; - case SPOT_TYPE_4X_DOT: - for (uint8_t j = 0; j < spotlights[i].width; j += 4) - { - if ((start + j) >= 0 && (start + j) < SEGLEN) - { - blendPixelColor(start + j, color, 128); + case SPOT_TYPE_4X_DOT: + for (size_t j = 0; j < spotlights[i].width; j += 4) { + if ((start + j) >= 0 && (start + j) < SEGLEN) { + SEGMENT.blendPixelColor(start + j, color, 128); + } } - } break; } } @@ -4479,160 +4165,149 @@ uint16_t WS2812FX::mode_dancing_shadows(void) return FRAMETIME; } +static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!;!"; + /* Imitates a washing machine, rotating same waves forward, then pause, then backward. By Stefan Seegel */ -uint16_t WS2812FX::mode_washing_machine(void) -{ - float speed = tristate_square8(now >> 7, 90, 15); - float quot = 32.0f - ((float)SEGMENT.speed / 16.0f); - speed /= quot; +uint16_t mode_washing_machine(void) { + int speed = tristate_square8(strip.now >> 7, 90, 15); - SEGENV.step += (speed * 128.0f); + SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed); - for (int i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7)); - setPixelColor(i, color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); } return FRAMETIME; } +static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!;;!"; + /* Blends random colors across palette Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e */ -uint16_t WS2812FX::mode_blends(void) -{ +uint16_t mode_blends(void) { uint16_t pixelLen = SEGLEN > UINT8_MAX ? UINT8_MAX : SEGLEN; - uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - uint32_t *pixels = reinterpret_cast(SEGENV.data); + uint16_t dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266 + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + uint32_t* pixels = reinterpret_cast(SEGENV.data); uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128); - uint8_t shift = (now * ((SEGMENT.speed >> 3) + 1)) >> 8; + uint8_t shift = (strip.now * ((SEGMENT.speed >> 3) +1)) >> 8; - for (int i = 0; i < pixelLen; i++) - { - pixels[i] = color_blend(pixels[i], color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed); + for (int i = 0; i < pixelLen; i++) { + pixels[i] = color_blend(pixels[i], SEGMENT.color_from_palette(shift + quadwave8((i + 1) * 16), false, PALETTE_SOLID_WRAP, 255), blendSpeed); shift += 3; } uint16_t offset = 0; - for (int i = 0; i < SEGLEN; i++) - { - setPixelColor(i, pixels[offset++]); - if (offset > pixelLen) - offset = 0; + for (int i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, pixels[offset++]); + if (offset > pixelLen) offset = 0; } return FRAMETIME; } +static const char _data_FX_MODE_BLENDS[] PROGMEM = "Blends@Shift speed,Blend speed;;!"; + /* TV Simulator Modified and adapted to WLED by Def3nder, based on "Fake TV Light for Engineers" by Phillip Burgess https://learn.adafruit.com/fake-tv-light-for-engineers/arduino-sketch */ -// 43 bytes -typedef struct TvSim -{ +//43 bytes +typedef struct TvSim { uint32_t totalTime = 0; - uint32_t fadeTime = 0; + uint32_t fadeTime = 0; uint32_t startTime = 0; - uint32_t elapsed = 0; - uint32_t pixelNum = 0; + uint32_t elapsed = 0; + uint32_t pixelNum = 0; uint16_t sliderValues = 0; - uint32_t sceeneStart = 0; + uint32_t sceeneStart = 0; uint32_t sceeneDuration = 0; uint16_t sceeneColorHue = 0; - uint8_t sceeneColorSat = 0; - uint8_t sceeneColorBri = 0; - uint8_t actualColorR = 0; - uint8_t actualColorG = 0; - uint8_t actualColorB = 0; + uint8_t sceeneColorSat = 0; + uint8_t sceeneColorBri = 0; + uint8_t actualColorR = 0; + uint8_t actualColorG = 0; + uint8_t actualColorB = 0; uint16_t pr = 0; // Prev R, G, B uint16_t pg = 0; uint16_t pb = 0; } tvSim; -uint16_t WS2812FX::mode_tv_simulator(void) -{ +uint16_t mode_tv_simulator(void) { uint16_t nr, ng, nb, r, g, b, i, hue; - uint8_t sat, bri, j; + uint8_t sat, bri, j; - if (!SEGENV.allocateData(sizeof(tvSim))) - return mode_static(); // allocation failed - TvSim *tvSimulator = reinterpret_cast(SEGENV.data); + if (!SEGENV.allocateData(sizeof(tvSim))) return mode_static(); //allocation failed + TvSim* tvSimulator = reinterpret_cast(SEGENV.data); - uint8_t colorSpeed = map(SEGMENT.speed, 0, UINT8_MAX, 1, 20); + uint8_t colorSpeed = map(SEGMENT.speed, 0, UINT8_MAX, 1, 20); uint8_t colorIntensity = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 30); i = SEGMENT.speed << 8 | SEGMENT.intensity; - if (i != tvSimulator->sliderValues) - { + if (i != tvSimulator->sliderValues) { tvSimulator->sliderValues = i; SEGENV.aux1 = 0; } - // create a new sceene - if (((millis() - tvSimulator->sceeneStart) >= tvSimulator->sceeneDuration) || SEGENV.aux1 == 0) - { - tvSimulator->sceeneStart = millis(); // remember the start of the new sceene - tvSimulator->sceeneDuration = random16(60 * 250 * colorSpeed, 60 * 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider) - tvSimulator->sceeneColorHue = random16(0, 768); // random start color-tone for the sceene - tvSimulator->sceeneColorSat = random8(100, 130 + colorIntensity); // random start color-saturation for the sceene - tvSimulator->sceeneColorBri = random8(200, 240); // random start color-brightness for the sceene - SEGENV.aux1 = 1; - SEGENV.aux0 = 0; - } - - // slightly change the color-tone in this sceene - if (SEGENV.aux0 == 0) - { - // hue change in both directions - j = random8(4 * colorIntensity); - hue = (random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative - ((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767); // positive - - // saturation - j = random8(2 * colorIntensity); - sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j; - - // brightness - j = random8(100); - bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j; - - // calculate R,G,B from HSV - // Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/ - { // just to create a local scope for the variables - uint8_t temp[5], n = (hue >> 8) % 3; - uint8_t x = ((((hue & 255) * sat) >> 8) * bri) >> 8; - uint8_t s = ((256 - sat) * bri) >> 8; - temp[0] = temp[3] = s; - temp[1] = temp[4] = x + s; - temp[2] = bri - x; - tvSimulator->actualColorR = temp[n + 2]; - tvSimulator->actualColorG = temp[n + 1]; - tvSimulator->actualColorB = temp[n]; + // create a new sceene + if (((millis() - tvSimulator->sceeneStart) >= tvSimulator->sceeneDuration) || SEGENV.aux1 == 0) { + tvSimulator->sceeneStart = millis(); // remember the start of the new sceene + tvSimulator->sceeneDuration = random16(60* 250* colorSpeed, 60* 750 * colorSpeed); // duration of a "movie sceene" which has similar colors (5 to 15 minutes with max speed slider) + tvSimulator->sceeneColorHue = random16( 0, 768); // random start color-tone for the sceene + tvSimulator->sceeneColorSat = random8 ( 100, 130 + colorIntensity); // random start color-saturation for the sceene + tvSimulator->sceeneColorBri = random8 ( 200, 240); // random start color-brightness for the sceene + SEGENV.aux1 = 1; + SEGENV.aux0 = 0; } - } - // Apply gamma correction, further expand to 16/16/16 - nr = (uint8_t)gamma8(tvSimulator->actualColorR) * 257; // New R/G/B - ng = (uint8_t)gamma8(tvSimulator->actualColorG) * 257; - nb = (uint8_t)gamma8(tvSimulator->actualColorB) * 257; - if (SEGENV.aux0 == 0) - { // initialize next iteration + // slightly change the color-tone in this sceene + if ( SEGENV.aux0 == 0) { + // hue change in both directions + j = random8(4 * colorIntensity); + hue = (random8() < 128) ? ((j < tvSimulator->sceeneColorHue) ? tvSimulator->sceeneColorHue - j : 767 - tvSimulator->sceeneColorHue - j) : // negative + ((j + tvSimulator->sceeneColorHue) < 767 ? tvSimulator->sceeneColorHue + j : tvSimulator->sceeneColorHue + j - 767) ; // positive + + // saturation + j = random8(2 * colorIntensity); + sat = (tvSimulator->sceeneColorSat - j) < 0 ? 0 : tvSimulator->sceeneColorSat - j; + + // brightness + j = random8(100); + bri = (tvSimulator->sceeneColorBri - j) < 0 ? 0 : tvSimulator->sceeneColorBri - j; + + // calculate R,G,B from HSV + // Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/ + { // just to create a local scope for the variables + uint8_t temp[5], n = (hue >> 8) % 3; + uint8_t x = ((((hue & 255) * sat) >> 8) * bri) >> 8; + uint8_t s = ( (256 - sat) * bri) >> 8; + temp[0] = temp[3] = s; + temp[1] = temp[4] = x + s; + temp[2] = bri - x; + tvSimulator->actualColorR = temp[n + 2]; + tvSimulator->actualColorG = temp[n + 1]; + tvSimulator->actualColorB = temp[n ]; + } + } + // Apply gamma correction, further expand to 16/16/16 + nr = (uint8_t)gamma8(tvSimulator->actualColorR) * 257; // New R/G/B + ng = (uint8_t)gamma8(tvSimulator->actualColorG) * 257; + nb = (uint8_t)gamma8(tvSimulator->actualColorB) * 257; + + if (SEGENV.aux0 == 0) { // initialize next iteration SEGENV.aux0 = 1; // randomize total duration and fade duration for the actual color - tvSimulator->totalTime = random16(250, 2500); // Semi-random pixel-to-pixel time - tvSimulator->fadeTime = random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time - if (random8(10) < 3) - tvSimulator->fadeTime = 0; // Force scene cut 30% of time + tvSimulator->totalTime = random16(250, 2500); // Semi-random pixel-to-pixel time + tvSimulator->fadeTime = random16(0, tvSimulator->totalTime); // Pixel-to-pixel transition time + if (random8(10) < 3) tvSimulator->fadeTime = 0; // Force scene cut 30% of time tvSimulator->startTime = millis(); } // end of initialization @@ -4641,28 +4316,23 @@ uint16_t WS2812FX::mode_tv_simulator(void) tvSimulator->elapsed = millis() - tvSimulator->startTime; // fade from prev volor to next color - if (tvSimulator->elapsed < tvSimulator->fadeTime) - { + if (tvSimulator->elapsed < tvSimulator->fadeTime) { r = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pr, nr); g = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pg, ng); b = map(tvSimulator->elapsed, 0, tvSimulator->fadeTime, tvSimulator->pb, nb); - } - else - { // Avoid divide-by-zero in map() + } else { // Avoid divide-by-zero in map() r = nr; g = ng; b = nb; } // set strip color - for (i = 0; i < SEGLEN; i++) - { - setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit + for (i = 0; i < SEGLEN; i++) { + SEGMENT.setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit } // if total duration has passed, remember last color and restart the loop - if (tvSimulator->elapsed >= tvSimulator->totalTime) - { + if ( tvSimulator->elapsed >= tvSimulator->totalTime) { tvSimulator->pr = nr; // Prev RGB = new RGB tvSimulator->pg = ng; tvSimulator->pb = nb; @@ -4671,1003 +4341,665 @@ uint16_t WS2812FX::mode_tv_simulator(void) return FRAMETIME; } +static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;"; + /* Aurora effect */ -// CONFIG +//CONFIG #ifdef ESP8266 -#define W_MAX_COUNT 9 // Number of simultaneous waves + #define W_MAX_COUNT 9 //Number of simultaneous waves #else -#define W_MAX_COUNT 20 // Number of simultaneous waves + #define W_MAX_COUNT 20 //Number of simultaneous waves #endif -#define W_MAX_SPEED 6 // Higher number, higher speed -#define W_WIDTH_FACTOR 6 // Higher number, smaller waves +#define W_MAX_SPEED 6 //Higher number, higher speed +#define W_WIDTH_FACTOR 6 //Higher number, smaller waves -// 24 bytes -class AuroraWave -{ -private: - uint16_t ttl; - CRGB basecolor; - float basealpha; - uint16_t age; - uint16_t width; - float center; - bool goingleft; - float speed_factor; - bool alive = true; +//24 bytes +class AuroraWave { + private: + uint16_t ttl; + CRGB basecolor; + float basealpha; + uint16_t age; + uint16_t width; + float center; + bool goingleft; + float speed_factor; + bool alive = true; -public: - void init(uint32_t segment_length, CRGB color) - { - ttl = random(500, 1501); - basecolor = color; - basealpha = random(60, 101) / (float)100; - age = 0; - width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); // half of width to make math easier - if (!width) - width = 1; - center = random(101) / (float)100 * segment_length; - goingleft = random(0, 2) == 0; - speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255); - alive = true; - } - - CRGB getColorForLED(int ledIndex) - { - if (ledIndex < center - width || ledIndex > center + width) - return 0; // Position out of range of this wave - - CRGB rgb; - - // Offset of this led from center of wave - // The further away from the center, the dimmer the LED - float offset = ledIndex - center; - if (offset < 0) - offset = -offset; - float offsetFactor = offset / width; - - // The age of the wave determines it brightness. - // At half its maximum age it will be the brightest. - float ageFactor = 0.1; - if ((float)age / ttl < 0.5) - { - ageFactor = (float)age / (ttl / 2); - } - else - { - ageFactor = (float)(ttl - age) / ((float)ttl * 0.5); + public: + void init(uint32_t segment_length, CRGB color) { + ttl = random(500, 1501); + basecolor = color; + basealpha = random(60, 101) / (float)100; + age = 0; + width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier + if (!width) width = 1; + center = random(101) / (float)100 * segment_length; + goingleft = random(0, 2) == 0; + speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255); + alive = true; } - // Calculate color based on above factors and basealpha value - float factor = (1 - offsetFactor) * ageFactor * basealpha; - rgb.r = basecolor.r * factor; - rgb.g = basecolor.g * factor; - rgb.b = basecolor.b * factor; + CRGB getColorForLED(int ledIndex) { + if(ledIndex < center - width || ledIndex > center + width) return 0; //Position out of range of this wave - return rgb; - }; + CRGB rgb; - // Change position and age of wave - // Determine if its sill "alive" - void update(uint32_t segment_length, uint32_t speed) - { - if (goingleft) - { - center -= speed_factor * speed; - } - else - { - center += speed_factor * speed; - } + //Offset of this led from center of wave + //The further away from the center, the dimmer the LED + float offset = ledIndex - center; + if (offset < 0) offset = -offset; + float offsetFactor = offset / width; - age++; + //The age of the wave determines it brightness. + //At half its maximum age it will be the brightest. + float ageFactor = 0.1; + if((float)age / ttl < 0.5) { + ageFactor = (float)age / (ttl / 2); + } else { + ageFactor = (float)(ttl - age) / ((float)ttl * 0.5); + } - if (age > ttl) - { - alive = false; - } - else - { - if (goingleft) - { - if (center + width < 0) - { - alive = false; + //Calculate color based on above factors and basealpha value + float factor = (1 - offsetFactor) * ageFactor * basealpha; + rgb.r = basecolor.r * factor; + rgb.g = basecolor.g * factor; + rgb.b = basecolor.b * factor; + + return rgb; + }; + + //Change position and age of wave + //Determine if its sill "alive" + void update(uint32_t segment_length, uint32_t speed) { + if(goingleft) { + center -= speed_factor * speed; + } else { + center += speed_factor * speed; + } + + age++; + + if(age > ttl) { + alive = false; + } else { + if(goingleft) { + if(center + width < 0) { + alive = false; + } + } else { + if(center - width > segment_length) { + alive = false; + } } } - else - { - if (center - width > segment_length) - { - alive = false; - } - } - } - }; + }; - bool stillAlive() - { - return alive; - }; + bool stillAlive() { + return alive; + }; }; -uint16_t WS2812FX::mode_aurora(void) -{ - // aux1 = Wavecount - // aux2 = Intensity in last loop +uint16_t mode_aurora(void) { + //aux1 = Wavecount + //aux2 = Intensity in last loop - AuroraWave *waves; + AuroraWave* waves; - if (SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) - { - // Intensity slider changed or first call +//TODO: I am not sure this is a correct way of handling memory allocation since if it fails on 1st run +// it will display static effect but on second run it may crash ESP since data will be nullptr + + if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) { + //Intensity slider changed or first call SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT); SEGENV.aux0 = SEGMENT.intensity; - if (!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) - { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266 - return mode_static(); // allocation failed + if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266 + return mode_static(); //allocation failed } - waves = reinterpret_cast(SEGENV.data); + waves = reinterpret_cast(SEGENV.data); - for (int i = 0; i < SEGENV.aux1; i++) - { - waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3)))); + for (int i = 0; i < SEGENV.aux1; i++) { + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); } - } - else - { - waves = reinterpret_cast(SEGENV.data); + } else { + waves = reinterpret_cast(SEGENV.data); } - for (int i = 0; i < SEGENV.aux1; i++) - { - // Update values of wave + for (int i = 0; i < SEGENV.aux1; i++) { + //Update values of wave waves[i].update(SEGLEN, SEGMENT.speed); - if (!(waves[i].stillAlive())) - { - // If a wave dies, reinitialize it starts over. - waves[i].init(SEGLEN, col_to_crgb(color_from_palette(random8(), false, false, random(0, 3)))); + if(!(waves[i].stillAlive())) { + //If a wave dies, reinitialize it starts over. + waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); } } - uint8_t backlight = 1; // dimmer backlight if less active colors - if (SEGCOLOR(0)) - backlight++; - if (SEGCOLOR(1)) - backlight++; - if (SEGCOLOR(2)) - backlight++; - // Loop through LEDs to determine color - for (int i = 0; i < SEGLEN; i++) - { + uint8_t backlight = 1; //dimmer backlight if less active colors + if (SEGCOLOR(0)) backlight++; + if (SEGCOLOR(1)) backlight++; + if (SEGCOLOR(2)) backlight++; + //Loop through LEDs to determine color + for (int i = 0; i < SEGLEN; i++) { CRGB mixedRgb = CRGB(backlight, backlight, backlight); - // For each LED we must check each wave if it is "active" at this position. - // If there are multiple waves active on a LED we multiply their values. - for (int j = 0; j < SEGENV.aux1; j++) - { + //For each LED we must check each wave if it is "active" at this position. + //If there are multiple waves active on a LED we multiply their values. + for (int j = 0; j < SEGENV.aux1; j++) { CRGB rgb = waves[j].getColorForLED(i); - if (rgb != CRGB(0)) - { + if(rgb != CRGB(0)) { mixedRgb += rgb; } } - setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); + SEGMENT.setPixelColor(i, mixedRgb[0], mixedRgb[1], mixedRgb[2]); } return FRAMETIME; } +static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50"; -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Start of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Order of routines should be alphabetical for each type. They are: -// -// 1D non-reactive -// 2D non-reactive -// -// 1D volume reactive -// 1D frequency reactive -// -// 2D volume reactive -// 2D frequency reactive -// - -// Sound reactive external variables. -extern int sampleRaw; -extern float sampleAvg; -extern bool samplePeak; -extern uint8_t myVals[32]; -// extern int sampleAgc; -extern int rawSampleAgc; -extern float sampleAgc; -extern uint8_t squelch; -extern byte soundSquelch; -extern byte soundAgc; -extern uint8_t maxVol; -extern uint8_t binNum; - -extern float sampleReal; // "sample" as float, to provide bits that are lost otherwise. Needed for AGC. -extern float multAgc; // sampleReal * multAgc = sampleAgc. Our multiplier - -// FFT based variables -extern float FFT_MajorPeak; -extern float FFT_Magnitude; -extern float fftBin[]; // raw FFT data -extern int fftResult[]; // summary of bins array. 16 summary bins. -extern float fftAvg[]; - -/////////////////////////////////////// -// Helper function(s) // -/////////////////////////////////////// - -double mapf(double x, double in_min, double in_max, double out_min, double out_max); - -//////////////////////////// -// set Pixels // -//////////////////////////// - -void WS2812FX::setPixels(CRGB *leds) -{ // ewowi20210703: use segmentToLogical (rotated and mirrored) to find the right led - for (int i = 0; i < SEGLEN; i++) - { - setPixelColor(i, leds[segmentToLogical(i)].red, leds[segmentToLogical(i)].green, leds[segmentToLogical(i)].blue); - } -} - -///////////////////////////// -// Non-Reactive Routines // -///////////////////////////// +// WLED-SR effects ///////////////////////// // Perlin Move // ///////////////////////// - // 16 bit perlinmove. Use Perlin Noise instead of sinewaves for movement. By Andrew Tuline. // Controls are speed, # of pixels, faderate. - -uint16_t WS2812FX::mode_perlinmove(void) -{ - - fade_out(255 - SEGMENT.custom1); - for (int i = 0; i < SEGMENT.intensity / 16 + 1; i++) - { - uint16_t locn = inoise16(millis() * 128 / (260 - SEGMENT.speed) + i * 15000, millis() * 128 / (260 - SEGMENT.speed)); // Get a new pixel location from moving noise. - uint16_t pixloc = map(locn, 50 * 256, 192 * 256, 0, SEGLEN) % (SEGLEN); // Map that to the length of the strand, and ensure we don't go over. - setPixelColor(pixloc, color_from_palette(pixloc % 255, false, PALETTE_SOLID_WRAP, 0)); +uint16_t mode_perlinmove(void) { + if (SEGLEN == 1) return mode_static(); + SEGMENT.fade_out(255-SEGMENT.custom1); + for (int i = 0; i < SEGMENT.intensity/16 + 1; i++) { + uint16_t locn = inoise16(millis()*128/(260-SEGMENT.speed)+i*15000, millis()*128/(260-SEGMENT.speed)); // Get a new pixel location from moving noise. + uint16_t pixloc = map(locn, 50*256, 192*256, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over. + SEGMENT.setPixelColor(pixloc, SEGMENT.color_from_palette(pixloc%255, false, PALETTE_SOLID_WRAP, 0)); } return FRAMETIME; } // mode_perlinmove() +static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,Fade rate;!,!;!"; + ///////////////////////// // Waveins // ///////////////////////// +// Uses beatsin8() + phase shifting. By: Andrew Tuline +uint16_t mode_wavesins(void) { -uint16_t WS2812FX::mode_wavesins(void) -{ // Uses beatsin8() + phase shifting. By: Andrew Tuline - - for (int i = 0; i < SEGLEN; i++) - { - uint8_t bri = sin8(millis() / 4 + i * (int)SEGMENT.intensity); - // leds[i] = CHSV(beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * SEGMENT.custom3), 255, bri); - leds[segmentToLogical(i)] = ColorFromPalette(currentPalette, beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1 + SEGMENT.custom2, 0, i * SEGMENT.custom3), bri, LINEARBLEND); + for (int i = 0; i < SEGLEN; i++) { + uint8_t bri = sin8(millis()/4 + i * SEGMENT.intensity); + uint8_t index = beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider + //SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND)); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, bri)); } - setPixels(leds); return FRAMETIME; } // mode_waveins() +static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness variation,Starting color,Range of colors,Color variation;!;!"; + ////////////////////////////// // Flow Stripe // ////////////////////////////// +// By: ldirko https://editor.soulmatelights.com/gallery/392-flow-led-stripe , modifed by: Andrew Tuline +uint16_t mode_FlowStripe(void) { -uint16_t WS2812FX::mode_FlowStripe(void) -{ // By: ldirko https://editor.soulmatelights.com/gallery/392-flow-led-stripe , modifed by: Andrew Tuline + const uint16_t hl = SEGLEN * 10 / 13; + uint8_t hue = millis() / (SEGMENT.speed+1); + uint32_t t = millis() / (SEGMENT.intensity/8+1); - const float hl = SEGLEN / 1.3; - uint8_t hue = millis() / (SEGMENT.speed + 1); - int t = millis() / (SEGMENT.intensity / 8 + 1); - - for (int i = 0; i < SEGLEN; i++) - { + for (int i = 0; i < SEGLEN; i++) { int c = (abs(i - hl) / hl) * 127; c = sin8(c); c = sin8(c / 2 + t); - byte b = sin8(c + t / 8); - leds[segmentToLogical(i)] = CHSV(b + hue, 255, 255); + byte b = sin8(c + t/8); + SEGMENT.setPixelColor(i, CHSV(b + hue, 255, 255)); } - setPixels(leds); return FRAMETIME; } // mode_FlowStripe() +static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;"; -////////////////////////////////////////////// -// START of 2D NON-REACTIVE ROUTINES // -////////////////////////////////////////////// -// static uint16_t x = 0; -// static uint16_t y = 0; -// static uint16_t z = 0; -// static int speed2D = 20; +#ifndef WLED_DISABLE_2D +/////////////////////////////////////////////////////////////////////////////// +//*************************** 2D routines *********************************** +#define XY(x,y) SEGMENT.XY(x,y) -// uint8_t colorLoop = 1; -// Scale determines how far apart the pixels in our noise matrix are. Try -// changing these values around to see how it affects the motion of the display. The -// higher the value of scale, the more "zoomed out" the noise iwll be. A value -// of 1 will be so zoomed in, you'll mostly see solid colors. -// static int scale_2d = 30; // scale is set dynamically once we've started up +// Black hole +uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. -// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. -// -// 0 = no spread at all -// 64 = moderate spreading -// 172 = maximum smooth, even spreading -// -// 173..255 = wider spreading, but increasing flicker -// -// Total light is NOT entirely conserved, so many repeated -// calls to 'blur' will also result in the light fading, -// eventually all the way to black; this is by design so that -// it can be used to (slowly) clear the LEDs to black. -void WS2812FX::blur1d(CRGB *leds, fract8 blur_amount) -{ - uint8_t keep = 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - for (uint16_t x = 0; x <= SEGMENT.width; x++) - for (uint16_t y = 0; y <= SEGMENT.height; y++) - { // ewowi20210629: <= to blur all pixels - CRGB cur = leds[XY(x, y)]; - CRGB part = cur; - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if (x > 0) // ewowi20210701: need to test if y test also necessary - leds[XY(x - 1, y)] += part; - // else if (y) - // leds[XY(x,y-1)] += part; - leds[XY(x, y)] = cur; - carryover = part; - } -} + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + uint16_t x, y; -void WS2812FX::blur2d(CRGB *leds, fract8 blur_amount) -{ - blurRows(leds, blur_amount); - blurColumns(leds, blur_amount); -} - -// blurRows: perform a blur1d on every row of a rectangular matrix -void WS2812FX::blurRows(CRGB *leds, fract8 blur_amount) -{ - blur1d(leds, blur_amount); // ewowi20210629: this will do all rows of the segment - // for( uint8_t row = 0; row < height; row++) { - // CRGB* rowbase = leds + (row * width); - // blur1d( rowbase, width, blur_amount); - // } -} - -// blurColumns: perform a blur1d on each column of a rectangular matrix -void WS2812FX::blurColumns(CRGB *leds, fract8 blur_amount) -{ - // blur columns - uint8_t keep = 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - for (uint16_t col = 0; col < SEGMENT.width; col++) - { - CRGB carryover = CRGB::Black; - for (uint16_t i = 0; i < SEGMENT.height; i++) - { - CRGB cur = leds[XY(col, i)]; - CRGB part = cur; - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if (i) - leds[XY(col, i - 1)] += part; - leds[XY(col, i)] = cur; - carryover = part; - } - } -} - -// ewowi20210628: new functions moved from colorutils: add segment awareness - -void WS2812FX::fill_solid(struct CRGB *leds, const struct CRGB &color) -{ - for (uint16_t x = 0; x <= SEGMENT.width; x++) - for (uint16_t y = 0; y <= SEGMENT.height; y++) - { - leds[XY(x, y)] = color; - } -} - -void WS2812FX::fadeToBlackBy(CRGB *leds, uint8_t fadeBy) -{ - nscale8(leds, 255 - fadeBy); -} - -void WS2812FX::nscale8(CRGB *leds, uint8_t scale) -{ - for (uint16_t x = 0; x <= SEGMENT.width; x++) - for (uint16_t y = 0; y <= SEGMENT.height; y++) - { - leds[XY(x, y)].nscale8(scale); - } -} - -uint16_t WS2812FX::XY(uint16_t x, uint16_t y) -{ // ewowi20210703: new XY: segmentToReal: Maps XY in 2D segment to to rotated and mirrored logical index. Works for 1D strips and 2D panels - return segmentToLogical(x % SEGMENT.width + y % SEGMENT.height * SEGMENT.width); -} - -// Use https://wokwi.com/arduino/projects/300565972972995085 to create layout examples -#define RIGHT 1 -#define BOTTOM 1 -#define HORIZONTAL 0 -uint16_t WS2812FX::logicalToPhysical(uint16_t i) -{ // ewowi20210624: previous XY. Maps logical led index to physical led index. Works for 1D strips and 2D panels - // By Sutaburosu (major and minor flip) and Ewoud Wijma (panels) - - int x = i % matrixWidth; - int y = matrixWidth ? i / matrixWidth : 0; - - if (x >= matrixWidth || y >= matrixHeight) - return SEGLEN + 1; // Off the charts, so it's only useable by routines that use leds[x]!!!! - uint16_t major, minor, sz_major, sz_minor; - - // Width, Height and Size of panel. Same as matrixWidth and Height if only one panel - uint16_t panelWidth = (matrixPanels && matrixHorizontalPanels) ? (matrixWidth / matrixHorizontalPanels) : matrixWidth; - uint16_t panelHeight = (matrixPanels && matrixVerticalPanels) ? (matrixHeight / matrixVerticalPanels) : matrixHeight; - uint16_t panelSize = panelWidth * panelHeight; - - // Horizontal and vertical panel number. 0 if only one panel - uint8_t panelHorizontalNr = x / panelWidth; - uint8_t panelVerticalNr = y / panelHeight; - - uint16_t panelFirstLed = 0; // 0 if only one panel - if (panelOrientationHorVert == HORIZONTAL) - { - if (matrixPanels) - panelFirstLed = panelSize * (panelHorizontalNr + matrixHorizontalPanels * panelVerticalNr); - major = x % panelWidth, minor = y % panelHeight, sz_major = panelWidth, sz_minor = panelHeight; - } - else - { // vertical - if (matrixPanels) - panelFirstLed = panelSize * (panelVerticalNr + matrixVerticalPanels * panelHorizontalNr); - major = y % panelHeight, minor = x % panelWidth, sz_major = panelHeight, sz_minor = panelWidth; + // initialize on first call + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); } - bool flipmajor = (panelOrientationHorVert == HORIZONTAL) ? panelFirstLedLeftRight == RIGHT : panelFirstLedTopBottom == BOTTOM; - bool flipminor = (panelOrientationHorVert == HORIZONTAL) ? panelFirstLedTopBottom == BOTTOM : panelFirstLedLeftRight == RIGHT; - // By: Sutaburosu - Who wrote this VERY COOL and VERY short and MUCH better XY() routine. Thanks!! - // flip minor if needed, this needs to be done before flipmajor because minor value needed to identify serpentine row - if (flipminor) - minor = sz_minor - 1 - minor; - if (flipmajor ^ ((minor & 1) && panelSerpentine)) - major = sz_major - 1 - major; // A line of magic. - // &=Binary AND, minor&1 is odd rows, ^=Binary XOR => flapmajor or serpentine (odd) row, but not both (XOR) - - if (panelTranspose) - return major * (uint16_t)sz_minor + minor + panelFirstLed; - else - return minor * (uint16_t)sz_major + major + panelFirstLed; -} - -uint16_t WS2812FX::mode_2DBlackHole() -{ // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline - - fadeToBlackBy(leds, 32); - double t = (float)(millis()) / 128; - for (byte i = 0; i < 8; i++) - { - leds[XY(beatsin8(SEGMENT.custom1 / 8, 0, SEGMENT.width - 1, 0, ((i % 2) ? 128 : 0) + t * i), beatsin8(10, 0, SEGMENT.height - 1, 0, ((i % 2) ? 192 : 64) + t * i))] += CHSV(i * 32, 255, 255); + SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails + unsigned long t = millis()/128; // timebase + // outer stars + for (size_t i = 0; i < 8; i++) { + x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i); + SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } - for (byte i = 0; i < 8; i++) - { - leds[XY(beatsin8(SEGMENT.custom2 / 8, SEGMENT.width / 4, SEGMENT.width - 1 - SEGMENT.width / 4, 0, ((i % 2) ? 128 : 0) + t * i), beatsin8(SEGMENT.custom3 / 8, SEGMENT.height / 4, SEGMENT.height - 1 - SEGMENT.height / 4, 0, ((i % 2) ? 192 : 64) + t * i))] += CHSV(i * 32, 255, 255); + // inner stars + for (size_t i = 0; i < 4; i++) { + x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i); + y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i); + SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255)); } - leds[XY(SEGMENT.width / 2, SEGMENT.height / 2)] = CHSV(0, 0, 255); - blur2d(leds, 16); + // central white dot + SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE); + // blur everything a bit + SEGMENT.blur(16); - setPixels(leds); return FRAMETIME; } // mode_2DBlackHole() +static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid;!;!;2;pal=11"; + //////////////////////////// // 2D Colored Bursts // //////////////////////////// +uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DColoredBursts() -{ // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - bool dot = false; - bool grad = true; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + SEGENV.aux0 = 0; // start with red hue + } - static byte hue = 0; - static byte numLines = 10; + bool dot = SEGMENT.check3; + bool grad = SEGMENT.check1; - hue++; - numLines = SEGMENT.intensity / 16; - fadeToBlackBy(leds, 40); + byte numLines = SEGMENT.intensity/16 + 1; - for (byte i = 0; i < numLines; i++) - { - byte x1 = beatsin8(2 + SEGMENT.speed / 16, 0, (SEGMENT.width - 1)); - byte x2 = beatsin8(1 + SEGMENT.speed / 16, 0, (SEGMENT.width - 1)); - byte y1 = beatsin8(5 + SEGMENT.speed / 16, 0, (SEGMENT.height - 1), 0, i * 24); - byte y2 = beatsin8(3 + SEGMENT.speed / 16, 0, (SEGMENT.height - 1), 0, i * 48 + 64); - CRGB color = ColorFromPalette(currentPalette, i * 255 / numLines + hue, 255, LINEARBLEND); + SEGENV.aux0++; // hue + SEGMENT.fadeToBlackBy(40); + for (size_t i = 0; i < numLines; i++) { + byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1)); + byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1)); + byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24); + byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); + CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); byte xsteps = abs8(x1 - y1) + 1; byte ysteps = abs8(x2 - y2) + 1; byte steps = xsteps >= ysteps ? xsteps : ysteps; - - for (byte i = 1; i <= steps; i++) - { - byte dx = lerp8by8(x1, y1, i * 255 / steps); - byte dy = lerp8by8(x2, y2, i * 255 / steps); - int index = XY(dx, dy); - leds[index] += color; // change to += for brightness look - if (grad) - leds[index] %= (i * 255 / steps); // Draw gradient line + //Draw gradient line + for (size_t j = 1; j <= steps; j++) { + uint8_t rate = j * 255 / steps; + byte dx = lerp8by8(x1, y1, rate); + byte dy = lerp8by8(x2, y2, rate); + //SEGMENT.setPixelColorXY(dx, dy, grad ? color.nscale8_video(255-rate) : color); // use addPixelColorXY for different look + SEGMENT.addPixelColorXY(dx, dy, color); // use setPixelColorXY for different look + if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate); } - if (dot) - { // add white point at the ends of line - leds[XY(x1, x2)] += CRGB::White; - leds[XY(y1, y2)] += CRGB::White; + if (dot) { //add white point at the ends of line + SEGMENT.setPixelColorXY(x1, x2, WHITE); + SEGMENT.setPixelColorXY(y1, y2, DARKSLATEGRAY); } } - blur2d(leds, 4); + if (SEGMENT.custom3) SEGMENT.blur(SEGMENT.custom3/2); - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DColoredBursts() +static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines,,,Blur,Gradient,,Dots;;!;2;c3=16"; + ///////////////////// // 2D DNA // ///////////////////// +uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Ddna(void) -{ // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fadeToBlackBy(leds, 64); - - for (int i = 0; i < SEGMENT.width; i++) - { // change to height if you want to re-orient, and swap the 4 lines below. - // leds[XY(beatsin8(SEGMENT.speed/8, 0, SEGMENT.width-1, 0, i*4), i)] = ColorFromPalette(currentPalette, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND); - // leds[XY(beatsin8(SEGMENT.speed/8, 0, SEGMENT.width-1, 0, i*4+128), i)] = ColorFromPalette(currentPalette,i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND); // 180 degrees (128) out of phase - leds[XY(i, beatsin8(SEGMENT.speed / 8, 0, SEGMENT.height - 1, 0, i * 4))] = ColorFromPalette(currentPalette, i * 5 + millis() / 17, beatsin8(5, 55, 255, 0, i * 10), LINEARBLEND); - leds[XY(i, beatsin8(SEGMENT.speed / 8, 0, SEGMENT.height - 1, 0, i * 4 + 128))] = ColorFromPalette(currentPalette, i * 5 + 128 + millis() / 17, beatsin8(5, 55, 255, 0, i * 10 + 128), LINEARBLEND); // 180 degrees (128) out of phase + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); } - blur2d(leds, SEGMENT.intensity / 8); + SEGMENT.fadeToBlackBy(64); - setPixels(leds); + for (int i = 0; i < cols; i++) { + SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+millis()/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND)); + SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+millis()/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.intensity>>3); return FRAMETIME; } // mode_2Ddna() +static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2"; + ///////////////////////// // 2D DNA Spiral // ///////////////////////// +uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DDNASpiral() -{ // By: ldirko https://editor.soulmatelights.com/gallery/810 , modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - uint8_t speeds = SEGMENT.speed / 2; - uint8_t freq = SEGMENT.intensity / 8; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } - static byte hue = 0; - int ms = millis() / 20; - nscale8(leds, 120); + uint8_t speeds = SEGMENT.speed/2 + 1; + uint8_t freq = SEGMENT.intensity/8; - for (int i = 0; i < SEGMENT.height; i++) - { - int x = beatsin8(speeds, 0, SEGMENT.width - 1, 0, i * freq) + beatsin8(speeds - 7, 0, SEGMENT.width - 1, 0, i * freq + 128); - int x1 = beatsin8(speeds, 0, SEGMENT.width - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, SEGMENT.width - 1, 0, 128 + 64 + i * freq); - hue = i * 128 / SEGMENT.width + ms; // ewowi20210629: not width - 1 to avoid crash if width = 1 - if ((i + ms / 8) & 3) - { - x = x / 2; - x1 = x1 / 2; - byte steps = abs8(x - x1) + 1; - for (byte k = 1; k <= steps; k++) - { - byte dx = lerp8by8(x, x1, k * 255 / steps); - int index = XY(dx, i); - leds[index] += ColorFromPalette(currentPalette, hue, 255, LINEARBLEND); - leds[index] %= (k * 255 / steps); // for draw gradient line + uint32_t ms = millis() / 20; + SEGMENT.fadeToBlackBy(135); + + for (int i = 0; i < rows; i++) { + uint16_t x = beatsin8(speeds, 0, cols - 1, 0, i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, i * freq + 128); + uint16_t x1 = beatsin8(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq); + uint8_t hue = (i * 128 / rows) + ms; + // skip every 4th row every now and then (fade it more) + if ((i + ms / 8) & 3) { + // draw a gradient line between x and x1 + x = x / 2; x1 = x1 / 2; + uint8_t steps = abs8(x - x1) + 1; + for (size_t k = 1; k <= steps; k++) { + uint8_t rate = k * 255 / steps; + uint8_t dx = lerp8by8(x, x1, rate); + //SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate)); + SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look + SEGMENT.fadePixelColorXY(dx, i, rate); } - leds[XY(x, i)] += CRGB::DarkSlateGray; - leds[XY(x1, i)] += CRGB::White; + SEGMENT.setPixelColorXY(x, i, DARKSLATEGRAY); + SEGMENT.setPixelColorXY(x1, i, WHITE); } } - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DDNASpiral() +static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency;;!;2"; + ///////////////////////// // 2D Drift // ///////////////////////// +uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DDrift() -{ // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); -#define CenterX ((SEGMENT.width / 2) - 0.5) -#define CenterY ((SEGMENT.height / 2) - 0.5) - const byte maxDim = max(SEGMENT.width, SEGMENT.height); - fadeToBlackBy(leds, 128); - unsigned long t = millis() / (32 - SEGMENT.speed / 8); - for (float i = 1; i < maxDim / 2; i += 0.25) - { - double angle = radians(t * (maxDim / 2 - i)); - int myX = (int)(CenterX + sin(angle) * i); - int myY = (int)(CenterY + cos(angle) * i); - leds[XY(myX, myY)] += ColorFromPalette(currentPalette, (i * 20) + (t / 20), 255, LINEARBLEND); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); } - blur2d(leds, SEGMENT.intensity / 8); - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. + SEGMENT.fadeToBlackBy(128); + + const uint16_t maxDim = MAX(cols, rows)/2; + unsigned long t = millis() / (32 - (SEGMENT.speed>>3)); + unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup + for (float i = 1; i < maxDim; i += 0.25) { + float angle = radians(t * (maxDim - i)); + uint16_t myX = (cols>>1) + (uint16_t)(sin_t(angle) * i) + (cols%2); + uint16_t myY = (rows>>1) + (uint16_t)(cos_t(angle) * i) + (rows%2); + SEGMENT.setPixelColorXY(myX, myY, ColorFromPalette(SEGPALETTE, (i * 20) + t_20, 255, LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.intensity>>3); + return FRAMETIME; } // mode_2DDrift() +static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount;;!;2"; -///////////////////////// -// 2D Fire2012 // -///////////////////////// - -uint16_t WS2812FX::mode_2Dfire2012(void) -{ // Fire2012 by Mark Kriegsman. Converted to WLED by Andrew Tuline. - - const uint8_t COOLING = 50; - const uint8_t SPARKING = 50; - - CRGBPalette16 currentPalette = CRGBPalette16(CRGB::Black, CRGB::Red, CRGB::Orange, CRGB::Yellow); - - if (millis() - SEGENV.step >= ((256 - SEGMENT.speed) >> 2)) - { - SEGENV.step = millis(); - // static byte *heat = (uint16_t *)dataStore; - - if (!SEGENV.allocateData(sizeof(byte) * 4096)) - return mode_static(); // allocation failed - byte *heat = reinterpret_cast(SEGENV.data); - - for (uint16_t mw = 0; mw < SEGMENT.width; mw++) - { // Move along the width of the flame - - uint16_t cell; - // Step 1. Cool down every cell a little - for (uint16_t mh = 0; mh < SEGMENT.height; mh++) - { - cell = (mw * SEGMENT.width + mh) % 4096; - heat[cell] = qsub8(heat[cell], random16(0, ((COOLING * 10) / SEGMENT.height) + 2)); - } - - // Step 2. Heat from each cell drifts 'up' and diffuses a little - for (uint16_t mh = SEGMENT.height - 1; mh >= 2; mh--) - { - cell = (mw * SEGMENT.width + mh) % 4096; - heat[cell] = (heat[cell - 1] + heat[cell - 2] + heat[cell - 2]) / 3; - } - - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if (random8(0, 255) < SPARKING) - { - uint8_t mh = random8(3); - cell = (mw * SEGMENT.width + mh) % 4096; - heat[cell] = qadd8(heat[cell], random8(160, 255)); - } - - // Step 4. Map from heat cells to LED colors - for (uint16_t mh = 0; mh < SEGMENT.height; mh++) - { - cell = (mw * SEGMENT.width + mh) % 4096; - byte colorindex = scale8(heat[cell], 240); - uint16_t pixelnumber = (SEGMENT.height - 1) - mh; // Flip it upside down. - leds[XY(mw, pixelnumber)] = ColorFromPalette(currentPalette, colorindex, 255); // Otherwise, it was leds[XY(mw,mh)] = . . . - } // for mh - } // for mw - - setPixels(leds); - - } // if millis - - return FRAMETIME; -} // mode_2Dfire2012() ////////////////////////// // 2D Firenoise // ////////////////////////// +uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Dfirenoise(void) -{ // firenoise2d. By Andrew Tuline. Yet another short routine. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - uint16_t xscale = SEGMENT.intensity * 4; - // uint32_t xscale = 600; // How far apart they are - // uint32_t yscale = 1000; // How fast they move - uint32_t yscale = SEGMENT.speed * 8; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint16_t xscale = SEGMENT.intensity*4; + uint32_t yscale = SEGMENT.speed*8; uint8_t indexx = 0; - currentPalette = CRGBPalette16(CRGB(0, 0, 0), CRGB(0, 0, 0), CRGB(0, 0, 0), CRGB(0, 0, 0), - CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, - CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, - CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); - - for (int j = 0; j < SEGMENT.width; j++) - { - for (int i = 0; i < SEGMENT.height; i++) - { - - indexx = inoise8(j * yscale * SEGMENT.height / 255, i * xscale + millis() / 4); // We're moving along our Perlin map. - leds[XY(j, i)] = ColorFromPalette(currentPalette, min(i * (indexx) >> 4, 255), i * 255 / SEGMENT.width, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. - - // This perlin fire is by /u/ldirko - // int a = millis(); - // leds[XY(i,j)] = ColorFromPalette (currentPalette, qsub8(inoise8 (i * 60 , j * 60+ a , a /3), abs8(j - (SEGMENT.height-1)) * 255 / (SEGMENT.height-1)), 255); + SEGPALETTE = CRGBPalette16( CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), CRGB(0,0,0), + CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange, + CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + for (int j=0; j < cols; j++) { + for (int i=0; i < rows; i++) { + indexx = inoise8(j*yscale*rows/255, i*xscale+millis()/4); // We're moving along our Perlin map. + SEGMENT.setPixelColorXY(j, i, ColorFromPalette(SEGPALETTE, min(i*(indexx)>>4, 255), i*255/cols, LINEARBLEND)); // With that value, look up the 8 bit colour palette value and assign it to the current LED. } // for i - } // for j - - setPixels(leds); + } // for j return FRAMETIME; } // mode_2Dfirenoise() +static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2"; + ////////////////////////////// // 2D Frizzles // ////////////////////////////// +uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DFrizzles(void) -{ // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fadeToBlackBy(leds, 16); - for (int i = 8; i > 0; i--) - { // WLEDSR bugfix - leds[XY(beatsin8(SEGMENT.speed / 8 + i, 0, SEGMENT.width - 1), beatsin8(SEGMENT.intensity / 8 - i, 0, SEGMENT.height - 1))] += ColorFromPalette(currentPalette, beatsin8(12, 0, 255), 255, LINEARBLEND); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); } - blur2d(leds, 16); - setPixels(leds); + SEGMENT.fadeToBlackBy(16); + for (size_t i = 8; i > 0; i--) { + SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1), + beatsin8(SEGMENT.intensity/8 - i, 0, rows - 1), + ColorFromPalette(SEGPALETTE, beatsin8(12, 0, 255), 255, LINEARBLEND)); + } + SEGMENT.blur(SEGMENT.custom1>>3); + return FRAMETIME; } // mode_2DFrizzles() +static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur;;!;2"; + /////////////////////////////////////////// // 2D Cellular Automata Game of life // /////////////////////////////////////////// - -typedef struct ColorCount -{ +typedef struct ColorCount { CRGB color; int8_t count; } colorCount; -uint16_t WS2812FX::mode_2Dgameoflife(void) -{ // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color +uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color + if (!strip.isMatrix) return mode_static(); // not a 2D set-up - // slow down based on speed parameter - if (millis() - SEGENV.step >= ((255 - SEGMENT.speed) * 4)) - { - SEGENV.step = millis(); + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint16_t dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled + const uint16_t crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) - CRGB prevLeds[32 * 32]; // MAX_LED causes a panic, but this will do + if (!SEGENV.allocateData(dataSize + sizeof(uint16_t)*crcBufferLen)) return mode_static(); //allocation failed + CRGB *prevLeds = reinterpret_cast(SEGENV.data); + uint16_t *crcBuffer = reinterpret_cast(SEGENV.data + dataSize); - // array of patterns. Needed to identify repeating patterns. A pattern is one iteration of leds, without the color (on/off only) - const int patternsSize = (SEGMENT.width + SEGMENT.height) * 2; // seems to be a good value to catch also repetition in moving patterns - if (!SEGENV.allocateData(sizeof(String) * patternsSize)) - return mode_static(); // allocation failed - String *patterns = reinterpret_cast(SEGENV.data); + CRGB backgroundColor = SEGCOLOR(1); - CRGB backgroundColor = SEGCOLOR(1); + if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { + SEGENV.step = strip.now; + SEGENV.aux0 = 0; + random16_set_seed(millis()>>2); //seed the random generator - static unsigned long resetMillis; // triggers reset if more than 3 seconds from millis() - - if (SEGENV.call == 0) - { // effect starts - // check if no pixels on screen (there could be due to previous effect, which we then take as starting point) - bool allZero = true; - for (int x = 0; x < SEGMENT.width && allZero; x++) - for (int y = 0; y < SEGMENT.height && allZero; y++) - if (leds[XY(x, y)].r > 10 || leds[XY(x, y)].g > 10 || leds[XY(x, y)].b > 10) // looks like some pixels are not completely off - allZero = false; - if (!allZero) - resetMillis = millis(); // avoid reset + //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { + uint8_t state = random8()%2; + if (state == 0) + SEGMENT.setPixelColorXY(x,y, backgroundColor); + else + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); } - // reset leds if effect repeats (wait 3 seconds after repetition) - if (millis() - resetMillis > 3000) - { - resetMillis = millis(); + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) prevLeds[XY(x,y)] = CRGB::Black; + memset(crcBuffer, 0, sizeof(uint16_t)*crcBufferLen); + } else if (strip.now - SEGENV.step < FRAMETIME_FIXED * (uint32_t)map(SEGMENT.speed,0,255,64,4)) { + // update only when appropriate time passes (in 42 FPS slots) + return FRAMETIME; + } - random16_set_seed(millis()); // seed the random generator + //copy previous leds (save previous generation) + //NOTE: using lossy getPixelColor() is a benefit as endlessly repeating patterns will eventually fade out causing a reset + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) prevLeds[XY(x,y)] = SEGMENT.getPixelColorXY(x,y); - // give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) - for (int x = 0; x < SEGMENT.width; x++) - for (int y = 0; y < SEGMENT.height; y++) - { - uint8_t state = random8() % 2; - if (state == 0) - leds[XY(x, y)] = backgroundColor; - else - leds[XY(x, y)] = SEGMENT.intensity < 128 ? (CRGB)color_wheel(random8()) : CRGB(random8(), random8(), random8()); - } + //calculate new leds + for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { - // init patterns - SEGENV.aux0 = 0; // ewowi20210629: pka static! patternsize: round robin index of next slot to add pattern - for (int i = 0; i < patternsSize; i++) - patterns[i] = ""; - } - else - { - // copy previous leds - for (int x = 0; x < SEGMENT.width; x++) - for (int y = 0; y < SEGMENT.height; y++) - prevLeds[XY(x, y)] = leds[XY(x, y)]; + colorCount colorsCount[9]; // count the different colors in the 3*3 matrix + for (int i=0; i<9; i++) colorsCount[i] = {backgroundColor, 0}; // init colorsCount - // calculate new leds - for (int x = 0; x < SEGMENT.width; x++) - for (int y = 0; y < SEGMENT.height; y++) - { - colorCount colorsCount[9]; // count the different colors in the 9*9 matrix - for (int i = 0; i < 9; i++) - colorsCount[i] = {backgroundColor, 0}; // init colorsCount + // iterate through neighbors and count them and their different colors + int neighbors = 0; + for (int i = -1; i <= 1; i++) for (int j = -1; j <= 1; j++) { // iterate through 3*3 matrix + if (i==0 && j==0) continue; // ignore itself + // wrap around segment + int16_t xx = x+i, yy = y+j; + if (x+i < 0) xx = cols-1; else if (x+i >= cols) xx = 0; + if (y+j < 0) yy = rows-1; else if (y+j >= rows) yy = 0; - // iterate through neighbors and count them and their different colors - int neighbors = 0; - for (int i = -1; i <= 1; i++) - for (int j = -1; j <= 1; j++) - { // iterate through 9*9 matrix - uint16_t xy = XY((x + i + SEGMENT.width) % SEGMENT.width, (y + j + SEGMENT.height) % SEGMENT.height); // cell xy to check - - // count different neighbours and colors, except the centre cell - if (xy != XY(x, y) && prevLeds[xy] != backgroundColor) - { - neighbors++; - bool colorFound = false; - int i; - for (i = 0; i < 9 && colorsCount[i].count != 0; i++) - if (colorsCount[i].color == prevLeds[xy]) - { - colorsCount[i].count++; - colorFound = true; - } - - if (!colorFound) - colorsCount[i] = {prevLeds[xy], 1}; // add new color found in the array - } - } // i,j - - // Rules of Life - if ((leds[XY(x, y)] != backgroundColor) && (neighbors < 2)) - leds[XY(x, y)] = backgroundColor; // Loneliness - else if ((leds[XY(x, y)] != backgroundColor) && (neighbors > 3)) - leds[XY(x, y)] = backgroundColor; // Overpopulation - else if ((leds[XY(x, y)] == backgroundColor) && (neighbors == 3)) - { // Reproduction - // find dominantcolor and assign to cell - colorCount dominantColorCount = {backgroundColor, 0}; - for (int i = 0; i < 9 && colorsCount[i].count != 0; i++) - if (colorsCount[i].count > dominantColorCount.count) - dominantColorCount = colorsCount[i]; - if (dominantColorCount.count > 0) - leds[XY(x, y)] = dominantColorCount.color; // assign the dominant color + uint16_t xy = XY(xx, yy); // previous cell xy to check + // count different neighbours and colors + if (prevLeds[xy] != backgroundColor) { + neighbors++; + bool colorFound = false; + int k; + for (k=0; k<9 && colorsCount[i].count != 0; k++) + if (colorsCount[k].color == prevLeds[xy]) { + colorsCount[k].count++; + colorFound = true; } - // else do nothing! - } // x,y + if (!colorFound) colorsCount[k] = {prevLeds[xy], 1}; //add new color found in the array + } + } // i,j - // create new pattern - String pattern = ""; - for (int x = 0; x < SEGMENT.width; x += MAX(SEGMENT.width / 8, 1)) - for (int y = 0; y < SEGMENT.height; y += MAX(SEGMENT.height / 8, 1)) - pattern += leds[XY(x, y)] == backgroundColor ? " " : "o"; // string representation if on/off + // Rules of Life + uint32_t col = uint32_t(prevLeds[XY(x,y)]) & 0x00FFFFFF; // uint32_t operator returns RGBA, we want RGBW -> cut off "alpha" byte + uint32_t bgc = RGBW32(backgroundColor.r, backgroundColor.g, backgroundColor.b, 0); + if ((col != bgc) && (neighbors < 2)) SEGMENT.setPixelColorXY(x,y, bgc); // Loneliness + else if ((col != bgc) && (neighbors > 3)) SEGMENT.setPixelColorXY(x,y, bgc); // Overpopulation + else if ((col == bgc) && (neighbors == 3)) { // Reproduction + // find dominant color and assign it to a cell + colorCount dominantColorCount = {backgroundColor, 0}; + for (int i=0; i<9 && colorsCount[i].count != 0; i++) + if (colorsCount[i].count > dominantColorCount.count) dominantColorCount = colorsCount[i]; + // assign the dominant color w/ a bit of randomness to avoid "gliders" + if (dominantColorCount.count > 0 && random8(128)) SEGMENT.setPixelColorXY(x,y, dominantColorCount.color); + } else if ((col == bgc) && (neighbors == 2) && !random8(128)) { // Mutation + SEGMENT.setPixelColorXY(x,y, SEGMENT.color_from_palette(random8(), false, PALETTE_SOLID_WRAP, 255)); + } + // else do nothing! + } //x,y - // check if repetition of patterns occurs - bool repetition = false; - for (int i = 0; i < patternsSize && !repetition; i++) - repetition = patterns[(SEGENV.aux0 - 1 - i + patternsSize) % patternsSize] == pattern; - - // add current pattern to array and increase index (round robin) - patterns[SEGENV.aux0] = pattern; - SEGENV.aux0 = (SEGENV.aux0 + 1) % patternsSize; - - if (!repetition) - resetMillis = millis(); // if no repetition avoid reset - } // not reset - - setPixels(leds); - } // millis + // calculate CRC16 of leds + uint16_t crc = crc16((const unsigned char*)prevLeds, dataSize); + // check if we had same CRC and reset if needed + bool repetition = false; + for (int i=0; i>1)+1); - int a = millis() / 8; - - for (int x = 0; x < SEGMENT.width; x++) - { - for (int y = 0; y < SEGMENT.height; y++) - { - int index = XY(x, y); - // leds[index].b = sin8((x - 8) * cos8((y + 20) * 4) / 4 + a); - // leds[index].g = (sin8(x * 16 + a / 3) + cos8(y * 8 + a / 2)) / 2; - // leds[index].r = sin8(cos8(x * 8 + a / 3) + sin8(y * 8 + a / 4) + a); - // leds[index] = ColorFromPalette(currentPalette, sin8(cos8(x * 8 + a / 3) + sin8(y * 8 + a / 4) + a), 255, LINEARBLEND); - leds[index] = ColorFromPalette(currentPalette, sin8(cos8(x * SEGMENT.speed / 16 + a / 3) + sin8(y * SEGMENT.intensity / 16 + a / 4) + a), 255, LINEARBLEND); + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8(cos8(x * SEGMENT.speed/16 + a / 3) + sin8(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0)); } } - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DHiphotic() +static const char _data_FX_MODE_2DHIPHOTIC[] PROGMEM = "Hiphotic@X scale,Y scale,,,Speed;!;!;2"; + ///////////////////////// // 2D Julia // ///////////////////////// - // Sliders are: -// // intensity = Maximum number of iterations per pixel. // Custom1 = Location of X centerpoint // Custom2 = Location of Y centerpoint // Custom3 = Size of the area (small value = smaller area) - -typedef struct Julia -{ // We can't use the 'static' keyword for persistent variables, so we have to go the LONG route to support them. +typedef struct Julia { float xcen; float ycen; float xymag; } julia; -uint16_t WS2812FX::mode_2DJulia(void) -{ // An animated Julia set by Andrew Tuline. +uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up - if (!SEGENV.allocateData(sizeof(julia))) - return mode_static(); // We use this method for allocating memory for static variables. - Julia *julias = reinterpret_cast(SEGENV.data); // Because 'static' doesn't work with SEGMENTS. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (!SEGENV.allocateData(sizeof(julia))) return mode_static(); + Julia* julias = reinterpret_cast(SEGENV.data); float reAl; float imAg; - if (SEGENV.call == 0) - { // Reset the center if we've just re-started this animation. + if (SEGENV.call == 0) { // Reset the center if we've just re-started this animation. julias->xcen = 0.; julias->ycen = 0.; julias->xymag = 1.0; - SEGMENT.custom1 = 128; // Make sure the location widgets are centered to start. Too bad - SEGMENT.custom2 = 128; // it doesn't show up on the UI. - SEGMENT.custom3 = 128; + SEGMENT.custom1 = 128; // Make sure the location widgets are centered to start. + SEGMENT.custom2 = 128; + SEGMENT.custom3 = 16; SEGMENT.intensity = 24; } - julias->xcen = julias->xcen + (float)(SEGMENT.custom1 - 128) / 100000.; - julias->ycen = julias->ycen + (float)(SEGMENT.custom2 - 128) / 100000.; - julias->xymag = julias->xymag + (float)(SEGMENT.custom3 - 128) / 100000.; - if (julias->xymag < 0.01) - julias->xymag = 0.01; - if (julias->xymag > 1.0) - julias->xymag = 1.0; + julias->xcen = julias->xcen + (float)(SEGMENT.custom1 - 128)/100000.f; + julias->ycen = julias->ycen + (float)(SEGMENT.custom2 - 128)/100000.f; + julias->xymag = julias->xymag + (float)((SEGMENT.custom3 - 16)<<3)/100000.f; // reduced resolution slider + if (julias->xymag < 0.01f) julias->xymag = 0.01f; + if (julias->xymag > 1.0f) julias->xymag = 1.0f; float xmin = julias->xcen - julias->xymag; float xmax = julias->xcen + julias->xymag; @@ -5675,336 +5007,313 @@ uint16_t WS2812FX::mode_2DJulia(void) float ymax = julias->ycen + julias->xymag; // Whole set should be within -1.2,1.2 to -.8 to 1. - xmin = constrain(xmin, -1.2, 1.2); - xmax = constrain(xmax, -1.2, 1.2); - ymin = constrain(ymin, -.8, 1.0); - ymax = constrain(ymax, -.8, 1.0); + xmin = constrain(xmin, -1.2f, 1.2f); + xmax = constrain(xmax, -1.2f, 1.2f); + ymin = constrain(ymin, -0.8f, 1.0f); + ymax = constrain(ymax, -0.8f, 1.0f); - float dx; // Delta x is mapped to the matrix size. - float dy; // Delta y is mapped to the matrix size. + float dx; // Delta x is mapped to the matrix size. + float dy; // Delta y is mapped to the matrix size. - int maxIterations = 15; // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours. - float maxCalc = 16.0; // How big is each calculation allowed to be before we give up. + int maxIterations = 15; // How many iterations per pixel before we give up. Make it 8 bits to match our range of colours. + float maxCalc = 16.0; // How big is each calculation allowed to be before we give up. + + maxIterations = SEGMENT.intensity/2; - maxIterations = SEGMENT.intensity / 2; // Resize section on the fly for some animaton. - reAl = -0.94299; // PixelBlaze example - imAg = 0.3162; + reAl = -0.94299f; // PixelBlaze example + imAg = 0.3162f; - reAl += sin((float)millis() / 305.) / 20.; - imAg += sin((float)millis() / 405.) / 20.; + reAl += sin_t((float)millis()/305.f)/20.f; + imAg += sin_t((float)millis()/405.f)/20.f; - // Serial.print(reAl,4); Serial.print("\t"); Serial.print(imAg,4); Serial.println(" "); - - dx = (xmax - xmin) / (SEGMENT.width); // Scale the delta x and y values to our matrix size. - dy = (ymax - ymin) / (SEGMENT.height); + dx = (xmax - xmin) / (cols); // Scale the delta x and y values to our matrix size. + dy = (ymax - ymin) / (rows); // Start y float y = ymin; - for (int j = 0; j < SEGMENT.height; j++) - { + for (int j = 0; j < rows; j++) { // Start x float x = xmin; - for (int i = 0; i < SEGMENT.width; i++) - { + for (int i = 0; i < cols; i++) { // Now we test, as we iterate z = z^2 + c does z tend towards infinity? float a = x; float b = y; int iter = 0; - while (iter < maxIterations) - { // Here we determine whether or not we're out of bounds. + while (iter < maxIterations) { // Here we determine whether or not we're out of bounds. float aa = a * a; float bb = b * b; float len = aa + bb; - if (len > maxCalc) - { // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root. - break; // Bail + if (len > maxCalc) { // |z| = sqrt(a^2+b^2) OR z^2 = a^2+b^2 to save on having to perform a square root. + break; // Bail } - // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'. - b = 2 * a * b + imAg; + // This operation corresponds to z -> z^2+c where z=a+ib c=(x,y). Remember to use 'foil'. + b = 2*a*b + imAg; a = aa - bb + reAl; iter++; } // while // We color each pixel based on how long it takes to get to infinity, or black if it never gets there. - if (iter == maxIterations) - { - // leds[XY(i,j)] = CRGB::Black; // Calculation kept on going, so it was within the set. - setPixelColor(XY(i, j), 0); - } - else - { - // leds[XY(i,j)] = CHSV(iter*255/maxIterations,255,255); // Near the edge of the set. - setPixelColor(XY(i, j), color_from_palette(iter * 255 / maxIterations, false, PALETTE_SOLID_WRAP, 0)); + if (iter == maxIterations) { + SEGMENT.setPixelColorXY(i, j, 0); + } else { + SEGMENT.setPixelColorXY(i, j, SEGMENT.color_from_palette(iter*255/maxIterations, false, PALETTE_SOLID_WRAP, 0)); } x += dx; } y += dy; } +// SEGMENT.blur(64); - // blur2d( leds, 64); - - // setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; - } // mode_2DJulia() +static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size;!;!;2;ix=24,c1=128,c2=128,c3=16"; + ////////////////////////////// // 2D Lissajous // ////////////////////////////// +uint16_t mode_2DLissajous(void) { // By: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DLissajous(void) -{ // By: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fadeToBlackBy(leds, SEGMENT.intensity); + SEGMENT.fadeToBlackBy(SEGMENT.intensity); + uint_fast16_t phase = (millis() * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed - for (int i = 0; i < 256; i++) - { - - uint8_t xlocn = sin8(millis() / 2 + i * SEGMENT.speed / 64); - uint8_t ylocn = cos8(millis() / 2 + i * 128 / 64); - - xlocn = map(xlocn, 0, 255, 0, SEGMENT.width - 1); - ylocn = map(ylocn, 0, 255, 0, SEGMENT.height - 1); - leds[XY(xlocn, ylocn)] = ColorFromPalette(currentPalette, millis() / 100 + i, 255, LINEARBLEND); + //for (int i=0; i < 4*(cols+rows); i ++) { + for (int i=0; i < 256; i ++) { + //float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f; + //float ylocn = float(cos8(now/4+i*2)) / 255.0f; + uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32); + uint_fast8_t ylocn = cos8(phase/2 + i*2); + xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding + ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map() + SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(millis()/100+i, false, PALETTE_SOLID_WRAP, 0)); } - setPixels(leds); return FRAMETIME; } // mode_2DLissajous() +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;;c3=15"; + /////////////////////// // 2D Matrix // /////////////////////// +uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Dmatrix(void) -{ // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) - fill_solid(leds, 0); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } - int fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size - int speed = (256 - SEGMENT.speed) >> map(MIN(SEGMENT.height, 150), 0, 150, 0, 3); // slower speeds for small displays + uint8_t fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size + uint8_t speed = (256-SEGMENT.speed) >> map(MIN(rows, 150), 0, 150, 0, 3); // slower speeds for small displays CRGB spawnColor; CRGB trailColor; - - if (SEGMENT.custom2 > 128) - { + if (SEGMENT.check1) { spawnColor = SEGCOLOR(0); trailColor = SEGCOLOR(1); - } - else - { - spawnColor = CRGB(175, 255, 175); - trailColor = CRGB(27, 130, 39); + } else { + spawnColor = CRGB(175,255,175); + trailColor = CRGB(27,130,39); } - if (millis() - SEGENV.step >= speed) - { - SEGENV.step = millis(); - // if (SEGMENT.custom3 < 128) { // check for orientation, slider in first quarter, default orientation - for (int16_t row = SEGMENT.height - 1; row >= 0; row--) - { - for (int16_t col = 0; col < SEGMENT.width; col++) - { - if (leds[XY(col, row)] == spawnColor) - { - leds[XY(col, row)] = trailColor; // create trail - if (row < SEGMENT.height - 1) - leds[XY(col, row + 1)] = spawnColor; + if (strip.now - SEGENV.step >= speed) { + SEGENV.step = strip.now; + for (int row=rows-1; row>=0; row--) { + for (int col=0; col>6)); // get some 2 random moving points - uint8_t x2 = inoise8(millis() * speed, 25355, 685) / 16; - uint8_t y2 = inoise8(millis() * speed, 355, 11685) / 16; + uint8_t x2 = map(inoise8(strip.now * speed, 25355, 685), 0, 255, 0, cols-1); + uint8_t y2 = map(inoise8(strip.now * speed, 355, 11685), 0, 255, 0, rows-1); - uint8_t x3 = inoise8(millis() * speed, 55355, 6685) / 16; - uint8_t y3 = inoise8(millis() * speed, 25355, 22685) / 16; + uint8_t x3 = map(inoise8(strip.now * speed, 55355, 6685), 0, 255, 0, cols-1); + uint8_t y3 = map(inoise8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1); // and one Lissajou function - uint8_t x1 = beatsin8(23 * speed, 0, 15); - uint8_t y1 = beatsin8(28 * speed, 0, 15); - - for (uint16_t y = 0; y < SEGMENT.height; y++) - { - for (uint16_t x = 0; x < SEGMENT.width; x++) - { + uint8_t x1 = beatsin8(23 * speed, 0, cols-1); + uint8_t y1 = beatsin8(28 * speed, 0, rows-1); + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { // calculate distances of the 3 points from actual pixel // and add them together with weightening uint16_t dx = abs(x - x1); uint16_t dy = abs(y - y1); - uint16_t dist = 2 * sqrt((dx * dx) + (dy * dy)); + uint16_t dist = 2 * sqrt16((dx * dx) + (dy * dy)); dx = abs(x - x2); dy = abs(y - y2); - dist += sqrt((dx * dx) + (dy * dy)); + dist += sqrt16((dx * dx) + (dy * dy)); dx = abs(x - x3); dy = abs(y - y3); - dist += sqrt((dx * dx) + (dy * dy)); + dist += sqrt16((dx * dx) + (dy * dy)); // inverse result - byte color = 1000 / dist; // dist=0?1000: 1000 / dist; + byte color = dist ? 1000 / dist : 255; // map color between thresholds - if (color > 0 and color < 60) - { - leds[XY(x, y)] = ColorFromPalette(currentPalette, color * 9, 255); - } - else - { - leds[XY(x, y)] = ColorFromPalette(currentPalette, 0, 255); + if (color > 0 and color < 60) { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(map(color * 9, 9, 531, 0, 255), false, PALETTE_SOLID_WRAP, 0)); + } else { + SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(0, false, PALETTE_SOLID_WRAP, 0)); } // show the 3 points, too - leds[XY(x1, y1)] = CRGB(255, 255, 255); - leds[XY(x2, y2)] = CRGB(255, 255, 255); - leds[XY(x3, y3)] = CRGB(255, 255, 255); + SEGMENT.setPixelColorXY(x1, y1, WHITE); + SEGMENT.setPixelColorXY(x2, y2, WHITE); + SEGMENT.setPixelColorXY(x3, y3, WHITE); } } - setPixels(leds); - return FRAMETIME; } // mode_2Dmetaballs() +static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2"; + ////////////////////// // 2D Noise // ////////////////////// +uint16_t mode_2Dnoise(void) { // By Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Dnoise(void) -{ // By Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - uint8_t scale = SEGMENT.intensity + 2; + const uint16_t scale = SEGMENT.intensity+2; - for (uint16_t y = 0; y < SEGMENT.height; y++) - { - for (uint16_t x = 0; x < SEGMENT.width; x++) - { - uint8_t pixelHue8 = inoise8(x * scale, y * scale, millis() / (16 - SEGMENT.speed / 16)); - leds[XY(x, y)] = ColorFromPalette(currentPalette, pixelHue8); + for (int y = 0; y < rows; y++) { + for (int x = 0; x < cols; x++) { + uint8_t pixelHue8 = inoise8(x * scale, y * scale, millis() / (16 - SEGMENT.speed/16)); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, pixelHue8)); } } - setPixels(leds); return FRAMETIME; } // mode_2Dnoise() +static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2"; + ////////////////////////////// // 2D Plasma Ball // ////////////////////////////// +uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DPlasmaball(void) -{ // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fadeToBlackBy(leds, 64); - double t = millis() / (33 - SEGMENT.speed / 8); - for (uint16_t i = 0; i < SEGMENT.width; i++) - { + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); + + uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float + for (int i = 0; i < cols; i++) { uint16_t thisVal = inoise8(i * 30, t, t); - uint16_t thisMax = map(thisVal, 0, 255, 0, SEGMENT.width); - for (uint16_t j = 0; j < SEGMENT.height; j++) - { + uint16_t thisMax = map(thisVal, 0, 255, 0, cols-1); + for (int j = 0; j < rows; j++) { uint16_t thisVal_ = inoise8(t, j * 30, t); - uint16_t thisMax_ = map(thisVal_, 0, 255, 0, SEGMENT.height); - uint16_t x = (i + thisMax_ - (SEGMENT.width * 2 - SEGMENT.width) / 2); - uint16_t y = (j + thisMax - (SEGMENT.width * 2 - SEGMENT.width) / 2); + uint16_t thisMax_ = map(thisVal_, 0, 255, 0, rows-1); + uint16_t x = (i + thisMax_ - cols / 2); + uint16_t y = (j + thisMax - cols / 2); uint16_t cx = (i + thisMax_); uint16_t cy = (j + thisMax); - leds[XY(i, j)] += ((x - y > -2) && (x - y < 2)) || - ((SEGMENT.width - 1 - x - y) > -2 && (SEGMENT.width - 1 - x - y < 2)) || - (SEGMENT.width - cx == 0) || - (SEGMENT.width - 1 - cx == 0) || - ((SEGMENT.height - cy == 0) || - (SEGMENT.height - 1 - cy == 0)) - ? ColorFromPalette(currentPalette, beat8(5), thisVal, LINEARBLEND) - : CHSV(0, 0, 0); + SEGMENT.addPixelColorXY(i, j, ((x - y > -2) && (x - y < 2)) || + ((cols - 1 - x - y) > -2 && (cols - 1 - x - y < 2)) || + (cols - cx == 0) || + (cols - 1 - cx == 0) || + ((rows - cy == 0) || + (rows - 1 - cy == 0)) ? ColorFromPalette(SEGPALETTE, beat8(5), thisVal, LINEARBLEND) : CRGB::Black); } } - blur2d(leds, 4); + SEGMENT.blur(SEGMENT.custom2>>5); - setPixels(leds); return FRAMETIME; } // mode_2DPlasmaball() +static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fade,Blur;;!;2"; + //////////////////////////////// // 2D Polar Lights // //////////////////////////////// +//static float fmap(const float x, const float in_min, const float in_max, const float out_min, const float out_max) { +// return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min; +//} +uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -static float fmap(const float x, const float in_min, const float in_max, const float out_min, const float out_max) -{ - return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min; -} + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); -uint16_t WS2812FX::mode_2DPolarLights() -{ // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline + CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; - CRGBPalette16 currentPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + SEGENV.step = 0; + } - float adjustHeight = fmap(SEGMENT.height, 8, 32, 28, 12); - - uint16_t adjScale = map(SEGMENT.width, 8, 64, 310, 63); - - static unsigned long timer; // Cannot be uint16_t value (aka aux0) - - if (SEGENV.aux1 != SEGMENT.custom1 / 12) - { // Hacky palette rotation. We need that black. - - SEGENV.aux1 = SEGMENT.custom1; - for (int i = 0; i < 16; i++) - { + float adjustHeight = (float)map(rows, 8, 32, 28, 12); // maybe use mapf() ??? + uint16_t adjScale = map(cols, 8, 64, 310, 63); +/* + if (SEGENV.aux1 != SEGMENT.custom1/12) { // Hacky palette rotation. We need that black. + SEGENV.aux1 = SEGMENT.custom1/12; + for (int i = 0; i < 16; i++) { long ilk; ilk = (long)currentPalette[i].r << 16; ilk += (long)currentPalette[i].g << 8; @@ -6015,379 +5324,981 @@ uint16_t WS2812FX::mode_2DPolarLights() currentPalette[i].b = ilk; } } +*/ + uint16_t _scale = map(SEGMENT.intensity, 0, 255, 30, adjScale); + byte _speed = map(SEGMENT.speed, 0, 255, 128, 16); - uint16_t _scale = map(SEGMENT.intensity, 1, 255, 30, adjScale); - byte _speed = map(SEGMENT.speed, 1, 255, 128, 16); - - for (uint16_t x = 0; x < SEGMENT.width; x++) - { - for (uint16_t y = 0; y < SEGMENT.height; y++) - { - timer++; - leds[XY(x, y)] = ColorFromPalette(currentPalette, - qsub8( - inoise8(SEGENV.aux0 % 2 + x * _scale, - y * 16 + timer % 16, - timer / _speed), - fabs((float)SEGMENT.height / 2 - (float)y) * adjustHeight)); + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + SEGENV.step++; + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(auroraPalette, + qsub8( + inoise8((SEGENV.step%2) + x * _scale, y * 16 + SEGENV.step % 16, SEGENV.step / _speed), + fabsf((float)rows / 2.0f - (float)y) * adjustHeight))); } } - setPixels(leds); return FRAMETIME; } // mode_2DPolarLights() +static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;;;2"; + ///////////////////////// // 2D Pulser // ///////////////////////// +uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DPulser() -{ // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - if (SEGENV.call == 0) - FastLED.clear(); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } - static byte r = 16; - uint16_t a = millis() / (18 - SEGMENT.speed / 16); - byte x = (a / 14) % SEGMENT.width; - byte y = (sin8(a * 5) + sin8(a * 4) + sin8(a * 2)) / 3 * r / 255; - uint16_t index = XY(x, (SEGMENT.height / 2 - r / 2 + y) % SEGMENT.width); - leds[index] = ColorFromPalette(currentPalette, y * 16 - 100, 255, LINEARBLEND); - blur2d(leds, SEGMENT.intensity / 16); + SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); + + uint32_t a = strip.now / (18 - SEGMENT.speed / 16); + uint16_t x = (a / 14) % cols; + uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND)); + + SEGMENT.blur(1 + (SEGMENT.intensity>>4)); - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DPulser() +static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2"; + ///////////////////////// // 2D Sindots // ///////////////////////// +uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DSindots() -{ // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(SEGMENT.custom1>>3); - fadeToBlackBy(leds, 15); byte t1 = millis() / (257 - SEGMENT.speed); // 20; byte t2 = sin8(t1) / 4 * 2; - for (uint16_t i = 0; i < 13; i++) - { - byte x = sin8(t1 + i * SEGMENT.intensity / 8) * (SEGMENT.width - 1) / 255; // max index now 255x15/255=15! - byte y = sin8(t2 + i * SEGMENT.intensity / 8) * (SEGMENT.height - 1) / 255; // max index now 255x15/255=15! - leds[XY(x, y)] = ColorFromPalette(currentPalette, i * 255 / 13, 255, LINEARBLEND); + for (int i = 0; i < 13; i++) { + byte x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! + byte y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND)); } - blur2d(leds, 16); + SEGMENT.blur(SEGMENT.custom2>>3); - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DSindots() +static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fade rate,Blur;;!;2"; + ////////////////////////////// // 2D Squared Swirl // ////////////////////////////// +// custom3 affects the blur amount. +uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160 + // Modifed by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Dsquaredswirl(void) -{ // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160 - // Modifed by: Andrew Tuline - // custom3 affects the blur amount. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } const uint8_t kBorderWidth = 2; - fadeToBlackBy(leds, 24); - // uint8_t blurAmount = dim8_raw( beatsin8(20,64,128) ); //3,64,192 - uint8_t blurAmount = SEGMENT.custom3; - blur2d(leds, blurAmount); + SEGMENT.fadeToBlackBy(24); + + uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider + SEGMENT.blur(blurAmount); // Use two out-of-sync sine waves - uint8_t i = beatsin8(19, kBorderWidth, SEGMENT.width - kBorderWidth); - uint8_t j = beatsin8(22, kBorderWidth, SEGMENT.width - kBorderWidth); - uint8_t k = beatsin8(17, kBorderWidth, SEGMENT.width - kBorderWidth); - uint8_t m = beatsin8(18, kBorderWidth, SEGMENT.height - kBorderWidth); - uint8_t n = beatsin8(15, kBorderWidth, SEGMENT.height - kBorderWidth); - uint8_t p = beatsin8(20, kBorderWidth, SEGMENT.height - kBorderWidth); + uint8_t i = beatsin8(19, kBorderWidth, cols-kBorderWidth); + uint8_t j = beatsin8(22, kBorderWidth, cols-kBorderWidth); + uint8_t k = beatsin8(17, kBorderWidth, cols-kBorderWidth); + uint8_t m = beatsin8(18, kBorderWidth, rows-kBorderWidth); + uint8_t n = beatsin8(15, kBorderWidth, rows-kBorderWidth); + uint8_t p = beatsin8(20, kBorderWidth, rows-kBorderWidth); uint16_t ms = millis(); - leds[XY(i, m)] += ColorFromPalette(currentPalette, ms / 29, 255, LINEARBLEND); - leds[XY(j, n)] += ColorFromPalette(currentPalette, ms / 41, 255, LINEARBLEND); - leds[XY(k, p)] += ColorFromPalette(currentPalette, ms / 73, 255, LINEARBLEND); - - setPixels(leds); + SEGMENT.addPixelColorXY(i, m, ColorFromPalette(SEGPALETTE, ms/29, 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(j, n, ColorFromPalette(SEGPALETTE, ms/41, 255, LINEARBLEND)); + SEGMENT.addPixelColorXY(k, p, ColorFromPalette(SEGPALETTE, ms/73, 255, LINEARBLEND)); return FRAMETIME; } // mode_2Dsquaredswirl() +static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Blur;;!;2"; + ////////////////////////////// // 2D Sun Radiation // ////////////////////////////// +uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DSunradiation(void) -{ // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline - // Does not yet support segments. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - static CRGB chsvLut[256]; - static byte bump[1156]; // Don't go beyond a 32x32 matrix!!! or (SEGMENT.width+2) * (mtrixHeight+2) + if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed + byte *bump = reinterpret_cast(SEGENV.data); - if (SEGMENT.intensity != SEGENV.aux0) - { - SEGENV.aux0 = SEGMENT.intensity; - for (int j = 0; j < 256; j++) - { - chsvLut[j] = HeatColor(j / (3.0 - (float)(SEGMENT.intensity) / 128.)); // 256 pallette color - } + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); } - int t = millis() / 4; + unsigned long t = millis() / 4; int index = 0; - uint8_t someVal = SEGMENT.speed / 4; // Was 25. - for (uint16_t j = 0; j < (SEGMENT.height + 2); j++) - { - for (uint16_t i = 0; i < (SEGMENT.width + 2); i++) - { + uint8_t someVal = SEGMENT.speed/4; // Was 25. + for (int j = 0; j < (rows + 2); j++) { + for (int i = 0; i < (cols + 2); i++) { byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2; bump[index++] = col; } } - int yindex = SEGMENT.width + 3; - int16_t vly = -(SEGMENT.height / 2 + 1); - for (uint16_t y = 0; y < SEGMENT.height; y++) - { + int yindex = cols + 3; + int16_t vly = -(rows / 2 + 1); + for (int y = 0; y < rows; y++) { ++vly; - int16_t vlx = -(SEGMENT.width / 2 + 1); - for (uint16_t x = 0; x < SEGMENT.width; x++) - { + int16_t vlx = -(cols / 2 + 1); + for (int x = 0; x < cols; x++) { ++vlx; int8_t nx = bump[x + yindex + 1] - bump[x + yindex - 1]; - int8_t ny = bump[x + yindex + (SEGMENT.width + 2)] - bump[x + yindex - (SEGMENT.width + 2)]; + int8_t ny = bump[x + yindex + (cols + 2)] - bump[x + yindex - (cols + 2)]; byte difx = abs8(vlx * 7 - nx); byte dify = abs8(vly * 7 - ny); int temp = difx * difx + dify * dify; - int col = 255 - temp / 8; // 8 its a size of effect - if (col < 0) - col = 0; - leds[XY(x, y)] = chsvLut[col]; // thx sutubarosu )) + int col = 255 - temp / 8; //8 its a size of effect + if (col < 0) col = 0; + SEGMENT.setPixelColorXY(x, y, HeatColor(col / (3.0f-(float)(SEGMENT.intensity)/128.f))); } - yindex += (SEGMENT.width + 2); + yindex += (cols + 2); } - setPixels(leds); return FRAMETIME; } // mode_2DSunradiation() +static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Variance,Brightness;;;2"; -///////////////////////// -// * 2D Swirl // -///////////////////////// - -uint16_t WS2812FX::mode_2DSwirl(void) -{ // By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline - - const uint8_t borderWidth = 2; - - blur2d(leds, SEGMENT.custom1); - - uint8_t i = beatsin8(27 * SEGMENT.speed / 255, borderWidth, SEGMENT.height - borderWidth); - uint8_t j = beatsin8(41 * SEGMENT.speed / 255, borderWidth, SEGMENT.width - borderWidth); - uint8_t ni = (SEGMENT.width - 1) - i; - uint8_t nj = (SEGMENT.width - 1) - j; - uint16_t ms = millis(); - - int tmpSound = (soundAgc) ? rawSampleAgc : sampleRaw; - - leds[XY(i, j)] += ColorFromPalette(currentPalette, (ms / 11 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 11, 200, 255); - leds[XY(j, i)] += ColorFromPalette(currentPalette, (ms / 13 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 13, 200, 255); - leds[XY(ni, nj)] += ColorFromPalette(currentPalette, (ms / 17 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 17, 200, 255); - leds[XY(nj, ni)] += ColorFromPalette(currentPalette, (ms / 29 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 29, 200, 255); - leds[XY(i, nj)] += ColorFromPalette(currentPalette, (ms / 37 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 37, 200, 255); - leds[XY(ni, j)] += ColorFromPalette(currentPalette, (ms / 41 + sampleAvg * 4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); // CHSV( ms / 41, 200, 255); - - setPixels(leds); - return FRAMETIME; -} // mode_2DSwirl() ///////////////////////// // 2D Tartan // ///////////////////////// +uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2Dtartan() -{ // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - uint8_t hue; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint8_t hue, bri; + size_t intensity; int offsetX = beatsin16(3, -360, 360); int offsetY = beatsin16(2, -360, 360); + int sharpness = SEGMENT.custom3 / 8; // 0-3 - for (uint16_t x = 0; x < SEGMENT.width; x++) - { - for (uint16_t y = 0; y < SEGMENT.height; y++) - { - uint16_t index = XY(x, y); + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { hue = x * beatsin16(10, 1, 10) + offsetY; - leds[index] = ColorFromPalette(currentPalette, hue, sin8(x * SEGMENT.speed + offsetX) * sin8(x * SEGMENT.speed + offsetX) / 255, LINEARBLEND); + intensity = bri = sin8(x * SEGMENT.speed/2 + offsetX); + for (int i=0; i>= 8*sharpness; + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); hue = y * 3 + offsetX; - leds[index] += ColorFromPalette(currentPalette, hue, sin8(y * SEGMENT.intensity + offsetY) * sin8(y * SEGMENT.intensity + offsetY) / 255, LINEARBLEND); + intensity = bri = sin8(y * SEGMENT.intensity/2 + offsetY); + for (int i=0; i>= 8*sharpness; + SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); } } - setPixels(leds); // Use this ONLY if we're going to display via leds[x] method. return FRAMETIME; } // mode_2DTartan() +static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,Sharpness;;!;2"; + + +///////////////////////// +// 2D spaceships // +///////////////////////// +uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek) + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint32_t tb = strip.now >> 12; // every ~4s + if (tb > SEGENV.step) { + int8_t dir = ++SEGENV.aux0; + dir += (int)random8(3)-1; + if (dir > 7) SEGENV.aux0 = 0; + else if (dir < 0) SEGENV.aux0 = 7; + else SEGENV.aux0 = dir; + SEGENV.step = tb + random8(4); + } + + SEGMENT.fadeToBlackBy(map(SEGMENT.speed, 0, 255, 248, 16)); + SEGMENT.move(SEGENV.aux0, 1); + + for (size_t i = 0; i < 8; i++) { + byte x = beatsin8(12 + i, 2, cols - 3); + byte y = beatsin8(15 + i, 2, rows - 3); + CRGB color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255); + SEGMENT.addPixelColorXY(x, y, color); + if (cols > 24 || rows > 24) { + SEGMENT.addPixelColorXY(x+1, y, color); + SEGMENT.addPixelColorXY(x-1, y, color); + SEGMENT.addPixelColorXY(x, y+1, color); + SEGMENT.addPixelColorXY(x, y-1, color); + } + } + SEGMENT.blur(SEGMENT.intensity>>3); + + return FRAMETIME; +} +static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2"; + + +///////////////////////// +// 2D Crazy Bees // +///////////////////////// +//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek) +#define MAX_BEES 5 +uint16_t mode_2Dcrazybees(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); + + typedef struct Bee { + uint8_t posX, posY, aimX, aimY, hue; + int8_t deltaX, deltaY, signX, signY, error; + void aimed(uint16_t w, uint16_t h) { + random16_set_seed(millis()); + aimX = random8(0, w); + aimY = random8(0, h); + hue = random8(); + deltaX = abs(aimX - posX); + deltaY = abs(aimY - posY); + signX = posX < aimX ? 1 : -1; + signY = posY < aimY ? 1 : -1; + error = deltaX - deltaY; + }; + } bee_t; + + if (!SEGENV.allocateData(sizeof(bee_t)*MAX_BEES)) return mode_static(); //allocation failed + bee_t *bee = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + for (size_t i = 0; i < n; i++) { + bee[i].posX = random8(0, cols); + bee[i].posY = random8(0, rows); + bee[i].aimed(cols, rows); + } + } + + if (millis() > SEGENV.step) { + SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1)); + + SEGMENT.fadeToBlackBy(32); + + for (size_t i = 0; i < n; i++) { + SEGMENT.addPixelColorXY(bee[i].aimX + 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY + 1, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX - 1, bee[i].aimY, CHSV(bee[i].hue, 255, 255)); + SEGMENT.addPixelColorXY(bee[i].aimX, bee[i].aimY - 1, CHSV(bee[i].hue, 255, 255)); + if (bee[i].posX != bee[i].aimX || bee[i].posY != bee[i].aimY) { + SEGMENT.setPixelColorXY(bee[i].posX, bee[i].posY, CRGB(CHSV(bee[i].hue, 60, 255))); + int8_t error2 = bee[i].error * 2; + if (error2 > -bee[i].deltaY) { + bee[i].error -= bee[i].deltaY; + bee[i].posX += bee[i].signX; + } + if (error2 < bee[i].deltaX) { + bee[i].error += bee[i].deltaX; + bee[i].posY += bee[i].signY; + } + } else { + bee[i].aimed(cols, rows); + } + } + SEGMENT.blur(SEGMENT.intensity>>4); + } + return FRAMETIME; +} +static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; + + +///////////////////////// +// 2D Ghost Rider // +///////////////////////// +//// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek) +#define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix) +uint16_t mode_2Dghostrider(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + typedef struct Lighter { + int16_t gPosX; + int16_t gPosY; + uint16_t gAngle; + int8_t angleSpeed; + uint16_t lightersPosX[LIGHTERS_AM]; + uint16_t lightersPosY[LIGHTERS_AM]; + uint16_t Angle[LIGHTERS_AM]; + uint16_t time[LIGHTERS_AM]; + bool reg[LIGHTERS_AM]; + int8_t Vspeed; + } lighter_t; + + if (!SEGENV.allocateData(sizeof(lighter_t))) return mode_static(); //allocation failed + lighter_t *lighter = reinterpret_cast(SEGENV.data); + + const size_t maxLighters = min(cols + rows, LIGHTERS_AM); + + if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { + SEGENV.aux0 = cols; + SEGENV.aux1 = rows; + SEGMENT.fill(BLACK); + random16_set_seed(strip.now); + lighter->angleSpeed = random8(0,20) - 10; + lighter->Vspeed = 5; + lighter->gPosX = (cols/2) * 10; + lighter->gPosY = (rows/2) * 10; + for (size_t i = 0; i < maxLighters; i++) { + lighter->lightersPosX[i] = lighter->gPosX; + lighter->lightersPosY[i] = lighter->gPosY + i; + lighter->time[i] = i * 2; + } + } + + if (millis() > SEGENV.step) { + SEGENV.step = millis() + 1024 / (cols+rows); + + SEGMENT.fadeToBlackBy((SEGMENT.speed>>2)+64); + + CRGB color = CRGB::White; + SEGMENT.wu_pixel(lighter->gPosX * 256 / 10, lighter->gPosY * 256 / 10, color); + + lighter->gPosX += lighter->Vspeed * sin_t(radians(lighter->gAngle)); + lighter->gPosY += lighter->Vspeed * cos_t(radians(lighter->gAngle)); + lighter->gAngle += lighter->angleSpeed; + if (lighter->gPosX < 0) lighter->gPosX = (cols - 1) * 10; + if (lighter->gPosX > (cols - 1) * 10) lighter->gPosX = 0; + if (lighter->gPosY < 0) lighter->gPosY = (rows - 1) * 10; + if (lighter->gPosY > (rows - 1) * 10) lighter->gPosY = 0; + for (size_t i = 0; i < maxLighters; i++) { + lighter->time[i] += random8(5, 20); + if (lighter->time[i] >= 255 || + (lighter->lightersPosX[i] <= 0) || + (lighter->lightersPosX[i] >= (cols - 1) * 10) || + (lighter->lightersPosY[i] <= 0) || + (lighter->lightersPosY[i] >= (rows - 1) * 10)) { + lighter->reg[i] = true; + } + if (lighter->reg[i]) { + lighter->lightersPosY[i] = lighter->gPosY; + lighter->lightersPosX[i] = lighter->gPosX; + lighter->Angle[i] = lighter->gAngle + random(-10, 10); + lighter->time[i] = 0; + lighter->reg[i] = false; + } else { + lighter->lightersPosX[i] += -7 * sin_t(radians(lighter->Angle[i])); + lighter->lightersPosY[i] += -7 * cos_t(radians(lighter->Angle[i])); + } + SEGMENT.wu_pixel(lighter->lightersPosX[i] * 256 / 10, lighter->lightersPosY[i] * 256 / 10, ColorFromPalette(SEGPALETTE, (256 - lighter->time[i]))); + } + SEGMENT.blur(SEGMENT.intensity>>3); + } + + return FRAMETIME; +} +static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,Blur;;!;2"; + + +//////////////////////////// +// 2D Floating Blobs // +//////////////////////////// +//// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek) +#define MAX_BLOBS 8 +uint16_t mode_2Dfloatingblobs(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + typedef struct Blob { + float x[MAX_BLOBS], y[MAX_BLOBS]; + float sX[MAX_BLOBS], sY[MAX_BLOBS]; // speed + float r[MAX_BLOBS]; + bool grow[MAX_BLOBS]; + byte color[MAX_BLOBS]; + } blob_t; + + uint8_t Amount = (SEGMENT.intensity>>5) + 1; // NOTE: be sure to update MAX_BLOBS if you change this + + if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed + blob_t *blob = reinterpret_cast(SEGENV.data); + + if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { + SEGENV.aux0 = cols; // re-initialise if virtual size changes + SEGENV.aux1 = rows; + SEGMENT.fill(BLACK); + for (size_t i = 0; i < MAX_BLOBS; i++) { + blob->r[i] = random8(1, cols>8 ? (cols/4) : 2); + blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x + blob->sY[i] = (float) random8(3, rows) / (float)(256 - SEGMENT.speed); // speed y + blob->x[i] = random8(0, cols-1); + blob->y[i] = random8(0, rows-1); + blob->color[i] = random8(); + blob->grow[i] = (blob->r[i] < 1.f); + if (blob->sX[i] == 0) blob->sX[i] = 1; + if (blob->sY[i] == 0) blob->sY[i] = 1; + } + } + + SEGMENT.fadeToBlackBy(20); + + // Bounce balls around + for (size_t i = 0; i < Amount; i++) { + if (SEGENV.step < millis()) blob->color[i] = add8(blob->color[i], 4); // slowly change color + // change radius if needed + if (blob->grow[i]) { + // enlarge radius until it is >= 4 + blob->r[i] += (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; + if (blob->r[i] >= MIN(cols/4.f,2.f)) { + blob->grow[i] = false; + } + } else { + // reduce radius until it is < 1 + blob->r[i] -= (fabsf(blob->sX[i]) > fabsf(blob->sY[i]) ? fabsf(blob->sX[i]) : fabsf(blob->sY[i])) * 0.05f; + if (blob->r[i] < 1.f) { + blob->grow[i] = true; + } + } + uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); + if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->x[i], blob->y[i], roundf(blob->r[i]), c); + else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c); + // move x + if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); + else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f)); + else blob->x[i] += blob->sX[i]; + // move y + if (blob->y[i] + blob->r[i] >= rows - 1) blob->y[i] += (blob->sY[i] * ((rows - 1 - blob->y[i]) / blob->r[i] + 0.005f)); + else if (blob->y[i] - blob->r[i] <= 0) blob->y[i] += (blob->sY[i] * (blob->y[i] / blob->r[i] + 0.005f)); + else blob->y[i] += blob->sY[i]; + // bounce x + if (blob->x[i] < 0.01f) { + blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->x[i] = 0.01f; + } else if (blob->x[i] > (float)cols - 1.01f) { + blob->sX[i] = (float)random8(3, cols) / (256 - SEGMENT.speed); + blob->sX[i] = -blob->sX[i]; + blob->x[i] = (float)cols - 1.01f; + } + // bounce y + if (blob->y[i] < 0.01f) { + blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->y[i] = 0.01f; + } else if (blob->y[i] > (float)rows - 1.01f) { + blob->sY[i] = (float)random8(3, rows) / (256 - SEGMENT.speed); + blob->sY[i] = -blob->sY[i]; + blob->y[i] = (float)rows - 1.01f; + } + } + SEGMENT.blur(SEGMENT.custom1>>2); + + if (SEGENV.step < millis()) SEGENV.step = millis() + 2000; // change colors every 2 seconds + + return FRAMETIME; +} +#undef MAX_BLOBS +static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8"; + + +//////////////////////////// +// 2D Scrolling text // +//////////////////////////// +uint16_t mode_2Dscrollingtext(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + int letterWidth; + int letterHeight; + switch (map(SEGMENT.custom2, 0, 255, 1, 5)) { + default: + case 1: letterWidth = 4; letterHeight = 6; break; + case 2: letterWidth = 5; letterHeight = 8; break; + case 3: letterWidth = 6; letterHeight = 8; break; + case 4: letterWidth = 7; letterHeight = 9; break; + case 5: letterWidth = 5; letterHeight = 12; break; + } + const bool zero = SEGMENT.check3; + const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; + char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'}; + if (SEGMENT.name) for (size_t i=0,j=0; i31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; + + if (!strlen(text) + || !strncmp_P(text,PSTR("#DATE"),5) + || !strncmp_P(text,PSTR("#DDMM"),5) + || !strncmp_P(text,PSTR("#MMDD"),5) + || !strncmp_P(text,PSTR("#TIME"),5) + || !strncmp_P(text,PSTR("#HHMM"),5)) { // fallback if empty segment name: display date and time + char sec[5]; + byte AmPmHour = hour(localTime); + boolean isitAM = true; + if (useAMPM) { + if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } + if (AmPmHour == 0) { AmPmHour = 12; } + } + if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM")); + else sprintf_P(sec, PSTR(":%02d"), second(localTime)); + if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime)); + else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d"):PSTR("%d.%d"), day(localTime), month(localTime)); + else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d"):PSTR("%d/%d"), month(localTime), day(localTime)); + else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s"):PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec); + else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d"):PSTR("%d:%02d"), AmPmHour, minute(localTime)); + else sprintf_P(text, zero?PSTR("%s %02d, %04d %02d:%02d%s"):PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec); + } + const int numberOfLetters = strlen(text); + + if (SEGENV.step < millis()) { + if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset + else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; + ++SEGENV.aux1 &= 0xFF; // color shift + SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); + if (!SEGMENT.check2) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++ ) + SEGMENT.blendPixelColorXY(x, y, SEGCOLOR(1), 255 - (SEGMENT.custom1>>1)); + } + } + for (int i = 0; i < numberOfLetters; i++) { + if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen + uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); + uint32_t col2 = BLACK; + if (SEGMENT.check1 && SEGMENT.palette == 0) { + col1 = SEGCOLOR(0); + col2 = SEGCOLOR(2); + } + SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); + } + + return FRAMETIME; +} +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,,Gradient,Overlay,0;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; + + +//////////////////////////// +// 2D Drift Rose // +//////////////////////////// +//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek) +uint16_t mode_2Ddriftrose(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + const float CX = (cols-cols%2)/2.f - .5f; + const float CY = (rows-rows%2)/2.f - .5f; + const float L = min(cols, rows) / 2.f; + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); + for (size_t i = 1; i < 37; i++) { + uint32_t x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; + uint32_t y = (CY + (cos_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f; + SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255)); + } + SEGMENT.blur((SEGMENT.intensity>>4)+1); + + return FRAMETIME; +} +static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;2"; + +#endif // WLED_DISABLE_2D + + +/////////////////////////////////////////////////////////////////////////////// +/******************** audio enhanced routines ************************/ +/////////////////////////////////////////////////////////////////////////////// + + +/* use the following code to pass AudioReactive usermod variables to effect + + uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment + bool samplePeak = false; + float FFT_MajorPeak = 1.0; + uint8_t *fftResult = nullptr; + float *fftBin = nullptr; + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + volumeSmth = *(float*) um_data->u_data[0]; + volumeRaw = *(float*) um_data->u_data[1]; + fftResult = (uint8_t*) um_data->u_data[2]; + samplePeak = *(uint8_t*) um_data->u_data[3]; + FFT_MajorPeak = *(float*) um_data->u_data[4]; + my_magnitude = *(float*) um_data->u_data[5]; + maxVol = (uint8_t*) um_data->u_data[6]; // requires UI element (SEGMENT.customX?), changes source element + binNum = (uint8_t*) um_data->u_data[7]; // requires UI element (SEGMENT.customX?), changes source element + fftBin = (float*) um_data->u_data[8]; + } else { + // add support for no audio data + um_data = simulateSound(SEGMENT.soundSim); + } +*/ + + +// a few constants needed for AudioReactive effects + +// for 22Khz sampling +#define MAX_FREQUENCY 11025 // sample frequency / 2 (as per Nyquist criterion) +#define MAX_FREQ_LOG10 4.04238f // log10(MAX_FREQUENCY) + +// for 20Khz sampling +//#define MAX_FREQUENCY 10240 +//#define MAX_FREQ_LOG10 4.0103f + +// for 10Khz sampling +//#define MAX_FREQUENCY 5120 +//#define MAX_FREQ_LOG10 3.71f + + +///////////////////////////////// +// * Ripple Peak // +///////////////////////////////// +uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline. + // This currently has no controls. + #define maxsteps 16 // Case statement wouldn't allow a variable. + + uint16_t maxRipples = 16; + uint16_t dataSize = sizeof(Ripple) * maxRipples; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Ripple* ripples = reinterpret_cast(SEGENV.data); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + #ifdef ESP32 + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + #endif + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + + // printUmData(); + + if (SEGENV.call == 0) { + SEGENV.aux0 = 255; + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; + } + + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + SEGMENT.fade_out(240); // Lower frame rate means less effective fading than FastLED + SEGMENT.fade_out(240); + + for (int i = 0; i < SEGMENT.intensity/16; i++) { // Limit the number of ripples. + if (samplePeak) ripples[i].state = 255; + + switch (ripples[i].state) { + case 254: // Inactive mode + break; + + case 255: // Initialize ripple variables. + ripples[i].pos = random16(SEGLEN); + #ifdef ESP32 + if (FFT_MajorPeak > 1) // log10(0) is "forbidden" (throws exception) + ripples[i].color = (int)(log10f(FFT_MajorPeak)*128); + else ripples[i].color = 0; + #else + ripples[i].color = random8(); + #endif + ripples[i].state = 0; + break; + + case 0: + SEGMENT.setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); + ripples[i].state++; + break; + + case maxsteps: // At the end of the ripples. 254 is an inactive mode. + ripples[i].state = 254; + break; + + default: // Middle of the ripples. + SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + ripples[i].state++; // Next step. + break; + } // switch step + } // for i + + return FRAMETIME; +} // mode_ripplepeak() +static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Max # of ripples,Select bin,Volume (min);!,!;!;1v;c2=0,m12=0,si=0"; // Pixel, Beatsin + + +#ifndef WLED_DISABLE_2D +///////////////////////// +// * 2D Swirl // +///////////////////////// +// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline +uint16_t mode_2DSwirl(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + const uint8_t borderWidth = 2; + + SEGMENT.blur(SEGMENT.custom1); + + uint8_t i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); + uint8_t j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); + uint8_t ni = (cols - 1) - i; + uint8_t nj = (cols - 1) - j; + uint16_t ms = millis(); + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg??? + int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; + + // printUmData(); + + SEGMENT.addPixelColorXY( i, j, ColorFromPalette(SEGPALETTE, (ms / 11 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 11, 200, 255); + SEGMENT.addPixelColorXY( j, i, ColorFromPalette(SEGPALETTE, (ms / 13 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 13, 200, 255); + SEGMENT.addPixelColorXY(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255); + SEGMENT.addPixelColorXY(nj,ni, ColorFromPalette(SEGPALETTE, (ms / 29 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 29, 200, 255); + SEGMENT.addPixelColorXY( i,nj, ColorFromPalette(SEGPALETTE, (ms / 37 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 37, 200, 255); + SEGMENT.addPixelColorXY(ni, j, ColorFromPalette(SEGPALETTE, (ms / 41 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 41, 200, 255); + + return FRAMETIME; +} // mode_2DSwirl() +static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,Bg Swirl;!;2v;ix=64,si=0"; // Beatsin // TODO: color 1 unused? + ///////////////////////// // * 2D Waverly // ///////////////////////// +// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline +uint16_t mode_2DWaverly(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DWaverly(void) -{ // By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fadeToBlackBy(leds, SEGMENT.speed); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + + SEGMENT.fadeToBlackBy(SEGMENT.speed); long t = millis() / 2; - for (uint16_t i = 0; i < SEGMENT.width; i++) - { - // byte thisVal = inoise8(i * 45 , t , t); - // byte thisMax = map(thisVal, 0, 255, 0, SEGMENT.height); + for (int i = 0; i < cols; i++) { + uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; + // use audio if available + if (um_data) { + thisVal /= 32; // reduce intensity of inoise8() + thisVal *= volumeSmth; + } + uint16_t thisMax = map(thisVal, 0, 512, 0, rows); - int tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - - uint16_t thisVal = tmpSound * SEGMENT.intensity / 64 * inoise8(i * 45, t, t) / 64; - uint16_t thisMax = map(thisVal, 0, 512, 0, SEGMENT.height); - - for (uint16_t j = 0; j < thisMax; j++) - { - leds[XY(i, j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); - leds[XY((SEGMENT.width - 1) - i, (SEGMENT.height - 1) - j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); + for (int j = 0; j < thisMax; j++) { + SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); + SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND)); } } - blur2d(leds, 16); + SEGMENT.blur(16); - setPixels(leds); return FRAMETIME; } // mode_2DWaverly() +static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity;;!;2v;ix=64,si=0"; // Beatsin -//////////////////////////////// -// Begin volume routines // -//////////////////////////////// +#endif // WLED_DISABLE_2D + +// float version of map() +static float mapf(float x, float in_min, float in_max, float out_min, float out_max){ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +// Gravity struct requited for GRAV* effects +typedef struct Gravity { + int topLED; + int gravityCounter; +} gravity; /////////////////////// // * GRAVCENTER // /////////////////////// +uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. -typedef struct Gravity -{ - int topLED; - int gravityCounter; -} gravity; + const uint16_t dataSize = sizeof(gravity); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); -uint16_t WS2812FX::mode_gravcenter(void) -{ // Gravcenter. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Gravity *gravcen = reinterpret_cast(SEGENV.data); + //SEGMENT.fade_out(240); + SEGMENT.fade_out(251); // 30% - fade_out(240); - - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0; + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg * 2.0, 0, 32, 0, (float)SEGLEN / 2.0); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN / 2); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed / 32; + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment + uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; - for (int i = 0; i < tempsamp; i++) - { - uint8_t index = inoise8(i * segmentSampleAvg + millis(), 5000 + i * segmentSampleAvg); - setPixelColor(i + SEGLEN / 2, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg * 8)); - setPixelColor(SEGLEN / 2 - i - 1, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg * 8)); + for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp - 1; + gravcen->topLED = tempsamp-1; else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; - if (gravcen->topLED >= 0) - { - setPixelColor(gravcen->topLED + SEGLEN / 2, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); - setPixelColor(SEGLEN / 2 - 1 - gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; return FRAMETIME; } // mode_gravcenter() +static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin + /////////////////////// // * GRAVCENTRIC // /////////////////////// - -uint16_t WS2812FX::mode_gravcentric(void) -{ // Gravcentric. By Andrew Tuline. +uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Gravity *gravcen = reinterpret_cast(SEGENV.data); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); - fade_out(240); - fade_out(240); + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0; - segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling + // printUmData(); - float mySampleAvg = mapf(segmentSampleAvg * 2.0, 0, 32, 0, (float)SEGLEN / 2.0); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN / 2); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed / 32; + //SEGMENT.fade_out(240); + //SEGMENT.fade_out(240); // twice? really? + SEGMENT.fade_out(253); // 50% - for (int i = 0; i < tempsamp; i++) - { - uint8_t index = segmentSampleAvg * 24 + millis() / 200; - setPixelColor(i + SEGLEN / 2, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); - setPixelColor(SEGLEN / 2 - 1 - i, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; + segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling + + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; + + for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp - 1; + gravcen->topLED = tempsamp-1; else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; - if (gravcen->topLED >= 0) - { - setPixelColor(gravcen->topLED + SEGLEN / 2, CRGB::Gray); - setPixelColor(SEGLEN / 2 - 1 - gravcen->topLED, CRGB::Gray); + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; return FRAMETIME; } // mode_gravcentric() +static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=3,si=0"; // Corner, Beatsin + /////////////////////// // * GRAVIMETER // /////////////////////// -#ifndef SR_DEBUG_AGC -uint16_t WS2812FX::mode_gravimeter(void) -{ // Gravmeter. By Andrew Tuline. +uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Gravity *gravcen = reinterpret_cast(SEGENV.data); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); - fade_out(240); + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0; + //SEGMENT.fade_out(240); + SEGMENT.fade_out(249); // 25% + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg * 2.0, 0, 64, 0, (SEGLEN - 1)); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN - 1); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed / 32; + float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; - for (int i = 0; i < tempsamp; i++) - { - uint8_t index = inoise8(i * segmentSampleAvg + millis(), 5000 + i * segmentSampleAvg); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg * 8)); + for (int i=0; i= gravcen->topLED) @@ -6395,779 +6306,552 @@ uint16_t WS2812FX::mode_gravimeter(void) else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; - if (gravcen->topLED > 0) - { - setPixelColor(gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + if (gravcen->topLED > 0) { + SEGMENT.setPixelColor(gravcen->topLED, SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; return FRAMETIME; } // mode_gravimeter() +static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin -#else -// This an abuse of the gravimeter effect for AGC debugging -// instead of sound volume, it uses the AGC gain multiplier as input -uint16_t WS2812FX::mode_gravimeter(void) -{ // Gravmeter. By Andrew Tuline. - - uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Gravity *gravcen = reinterpret_cast(SEGENV.data); - - fade_out(240); - - float tmpSound = multAgc; // AGC gain - if (soundAgc == 0) - { - if ((sampleAvg > 1.0) && (sampleReal > 0.05)) - tmpSound = (float)sampleRaw / sampleReal; // current non-AGC gain - else - tmpSound = ((float)sampleGain / 40.0 * (float)inputLevel / 128.0) + 1.0 / 16.0; // non-AGC gain from presets - } - - if (tmpSound > 2) - tmpSound = ((tmpSound - 2.0) / 2) + 2; // compress ranges > 2 - if (tmpSound > 1) - tmpSound = ((tmpSound - 1.0) / 2) + 1; // compress ranges > 1 - - float segmentSampleAvg = 64.0 * tmpSound * (float)SEGMENT.intensity / 128.0; - float mySampleAvg = mapf(segmentSampleAvg, 0, 128, 0, (SEGLEN - 1)); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN - 1); // Keep the sample from overflowing. - - // tempsamp = SEGLEN - tempsamp; // uncomment to invert direction - segmentSampleAvg = fmax(64.0 - fmin(segmentSampleAvg, 63), 8); // inverted brightness - - uint8_t gravity = 8 - SEGMENT.speed / 32; - - if (sampleAvg > 1) // disable bar "body" if below squelch - { - for (int i = 0; i < tempsamp; i++) - { - uint8_t index = inoise8(i * segmentSampleAvg + millis(), 5000 + i * segmentSampleAvg); - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg * 4.0)); - } - } - if (tempsamp >= gravcen->topLED) - gravcen->topLED = tempsamp; - else if (gravcen->gravityCounter % gravity == 0) - gravcen->topLED--; - - if (gravcen->topLED > 0) - { - setPixelColor(gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); - } - gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; - - return FRAMETIME; -} // mode_gravimeter() -#endif - -////////////////////// -// JBL // -////////////////////// - - -uint16_t pos = 0; -uint8_t hue = 0; -int hueDelay = 0; - -uint8_t red(uint32_t c) -{ - return (c >> 16); -} -uint8_t green(uint32_t c) -{ - return (c >> 8); -} -uint8_t blue(uint32_t c) -{ - return (c); -} - -uint16_t WS2812FX::mode_jbl() -{ - hueDelay++; - - if (hueDelay > SEGMENT.intensity) - { - hueDelay = 0; - hue++; - } - - if (hue > 254) - { - hue = 0; - } - - float speed = 0; - - uint16_t counter = 0; - - if (sampleAgc > (255 - SEGMENT.speed)) - { - speed = 1000; - } - else - { - speed = 20; - }; - - pos += speed; - - counter = pos; - /* counter = (now * ((speed >> 3 ) + 1 )) & 0xFFFF; - counter = counter >> 8; */ - - // counter += speed * now; - counter = counter >> 8; - - for (uint16_t i = 0; i < SEGLEN; i++) - { - uint8_t colorIndex = (i * 255 / SEGLEN) - counter; - - uint32_t paletteColor = color_from_palette(colorIndex, false, true, 255); - - uint8_t r = red(paletteColor); - - CRGB rgb(CHSV(hue, 255, r)); - - setPixelColor(i, rgb.r, rgb.g, rgb.b); - }; - - // setPixelColor(0, rgb.r, rgb.g, rgb.b); - - return FRAMETIME; -} // mode_jbl() ////////////////////// // * JUGGLES // ////////////////////// +uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; -uint16_t WS2812FX::mode_juggles(void) -{ // Juggles. By Andrew Tuline. + SEGMENT.fade_out(224); // 6.25% + uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); - fade_out(224); - int my_sampleAgc = fmax(fmin(sampleAgc, 255.0), 0); - - for (int i = 0; i < SEGMENT.intensity / 32 + 1; i++) - { - setPixelColor(beatsin16(SEGMENT.speed / 4 + i * 2, 0, SEGLEN - 1), color_blend(SEGCOLOR(1), color_from_palette(millis() / 4 + i * 2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc)); + for (size_t i=0; iu_data[1]; - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 % 16; - if (SEGENV.aux0 != secondHand) - { - SEGENV.aux0 = secondHand; - uint8_t tmpSound = (soundAgc) ? rawSampleAgc : sampleRaw; - int pixBri = tmpSound * SEGMENT.intensity / 64; - leds[segmentToLogical(SEGLEN - 1)] = color_blend(SEGCOLOR(1), color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri); - for (int i = 0; i < SEGLEN - 1; i++) - leds[segmentToLogical(i)] = leds[segmentToLogical(i + 1)]; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + int pixBri = volumeRaw * SEGMENT.intensity / 64; + for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left + SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri)); } - setPixels(leds); return FRAMETIME; } // mode_matripix() +static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;!;1v;ix=64,m12=2,si=1"; //,rev=1,mi=1,rY=1,mY=1 Circle, WeWillRockYou, reverseX + ////////////////////// // * MIDNOISE // ////////////////////// +uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. +// Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1. -uint16_t WS2812FX::mode_midnoise(void) -{ // Midnoise. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - // Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1. + SEGMENT.fade_out(SEGMENT.speed); + SEGMENT.fade_out(SEGMENT.speed); - fade_out(SEGMENT.speed); - fade_out(SEGMENT.speed); + float tmpSound2 = volumeSmth * (float)SEGMENT.intensity / 256.0; // Too sensitive. + tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitity/length. - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - float tmpSound2 = tmpSound * (float)SEGMENT.intensity / 256.0; // Too sensitive. - tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitity/length. + int maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2); + if (maxLen >SEGLEN/2) maxLen = SEGLEN/2; - int maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN / 2); - if (maxLen > SEGLEN / 2) - maxLen = SEGLEN / 2; - - for (int i = (SEGLEN / 2 - maxLen); i < (SEGLEN / 2 + maxLen); i++) - { - uint8_t index = inoise8(i * tmpSound + SEGENV.aux0, SEGENV.aux1 + i * tmpSound); // Get a value from the noise function. I'm using both x and y axis. - setPixelColor(i, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + for (int i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { + uint8_t index = inoise8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } - SEGENV.aux0 = SEGENV.aux0 + beatsin8(5, 0, 10); - SEGENV.aux1 = SEGENV.aux1 + beatsin8(4, 0, 10); + SEGENV.aux0=SEGENV.aux0+beatsin8(5,0,10); + SEGENV.aux1=SEGENV.aux1+beatsin8(4,0,10); return FRAMETIME; } // mode_midnoise() +static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Max. length;!,!;!;1v;ix=128,m12=1,si=0"; // Bar, Beatsin + ////////////////////// // * NOISEFIRE // ////////////////////// - // I am the god of hellfire. . . Volume (only) reactive fire routine. Oh, look how short this is. -uint16_t WS2812FX::mode_noisefire(void) -{ // Noisefire. By Andrew Tuline. +uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. + CRGBPalette16 myPal = CRGBPalette16(CHSV(0,255,2), CHSV(0,255,4), CHSV(0,255,8), CHSV(0, 255, 8), // Fire palette definition. Lower value = darker. + CHSV(0, 255, 16), CRGB::Red, CRGB::Red, CRGB::Red, + CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, + CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); - currentPalette = CRGBPalette16(CHSV(0, 255, 2), CHSV(0, 255, 4), CHSV(0, 255, 8), CHSV(0, 255, 8), // Fire palette definition. Lower value = darker. - CHSV(0, 255, 16), CRGB::Red, CRGB::Red, CRGB::Red, - CRGB::DarkOrange, CRGB::DarkOrange, CRGB::Orange, CRGB::Orange, - CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow); + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - for (int i = 0; i < SEGLEN; i++) - { - uint16_t index = inoise8(i * SEGMENT.speed / 64, millis() * SEGMENT.speed / 64 * SEGLEN / 255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. - index = (255 - i * 256 / SEGLEN) * index / (256 - SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. - // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. + if (SEGENV.call == 0) SEGMENT.fill(BLACK); - uint8_t tmpSound = (soundAgc) ? sampleAgc : sampleAvg; + for (int i = 0; i < SEGLEN; i++) { + uint16_t index = inoise8(i*SEGMENT.speed/64,millis()*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. + index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. + // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. - CRGB color = ColorFromPalette(currentPalette, index, tmpSound * 2, LINEARBLEND); // Use the my own palette. - leds[segmentToLogical(i)] = color; + CRGB color = ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND); // Use the my own palette. + SEGMENT.setPixelColor(i, color); } - setPixels(leds); return FRAMETIME; } // mode_noisefire() +static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;1v;m12=2,si=0"; // Circle, Beatsin + /////////////////////// // * Noisemeter // /////////////////////// +uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. -uint16_t WS2812FX::mode_noisemeter(void) -{ // Noisemeter. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; + int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; - uint8_t fadeRate = map(SEGMENT.speed, 0, 255, 224, 255); - fade_out(fadeRate); + //uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255); + uint8_t fadeRate = map(SEGMENT.speed,0,255,200,254); + SEGMENT.fade_out(fadeRate); - float tmpSound = (soundAgc) ? rawSampleAgc : sampleRaw; - float tmpSound2 = tmpSound * 2.0 * (float)SEGMENT.intensity / 255.0; + float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0; int maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. - if (maxLen > SEGLEN) - maxLen = SEGLEN; + if (maxLen <0) maxLen = 0; + if (maxLen >SEGLEN) maxLen = SEGLEN; - tmpSound = soundAgc ? sampleAgc : sampleAvg; // now use smoothed value (sampleAvg or sampleAgc) - for (int i = 0; i < maxLen; i++) - { // The louder the sound, the wider the soundbar. By Andrew Tuline. - uint8_t index = inoise8(i * tmpSound + SEGENV.aux0, SEGENV.aux1 + i * tmpSound); // Get a value from the noise function. I'm using both x and y axis. - setPixelColor(i, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); + for (int i=0; i SEGLEN / 2; i--) - { // Move to the right. - leds[segmentToLogical(i)] = leds[segmentToLogical(i - 1)]; - } - for (int i = 0; i < SEGLEN / 2; i++) - { // Move to the left. - leds[segmentToLogical(i)] = leds[segmentToLogical(i + 1)]; - } + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + int16_t volumeRaw = *(int16_t*)um_data->u_data[1]; + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16; + if (SEGENV.aux0 != secondHand) { + SEGENV.aux0 = secondHand; + + int pixBri = volumeRaw * SEGMENT.intensity / 64; + + SEGMENT.setPixelColor(SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0), pixBri)); + for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } - setPixels(leds); return FRAMETIME; } // mode_pixelwave() +static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;1v;ix=64,m12=2,si=0"; // Circle, Beatsin + ////////////////////// // * PLASMOID // ////////////////////// - -typedef struct Plasphase -{ - int16_t thisphase; - int16_t thatphase; +typedef struct Plasphase { + int16_t thisphase; + int16_t thatphase; } plasphase; -uint16_t WS2812FX::mode_plasmoid(void) -{ // Plasmoid. By Andrew Tuline. +uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. + // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment + if (!SEGENV.allocateData(sizeof(plasphase))) return mode_static(); //allocation failed + Plasphase* plasmoip = reinterpret_cast(SEGENV.data); - uint16_t dataSize = sizeof(plasphase); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Plasphase *plasmoip = reinterpret_cast(SEGENV.data); - - fadeToBlackBy(leds, 64); - - plasmoip->thisphase += beatsin8(6, -4, 4); // You can change direction and speed individually. - plasmoip->thatphase += beatsin8(7, -4, 4); // Two phase values to make a complex pattern. By Andrew Tuline. - - for (int i = 0; i < SEGLEN; i++) - { // For each of the LED's in the strand, set a brightness based on a wave as follows. - // updated, similar to "plasma" effect - softhack007 - uint8_t thisbright = cubicwave8(((i * (1 + (3 * SEGMENT.speed / 32))) + plasmoip->thisphase) & 0xFF) / 2; - thisbright += cos8(((i * (97 + (5 * SEGMENT.speed / 32))) + plasmoip->thatphase) & 0xFF) / 2; // Let's munge the brightness a bit and animate it all with the phases. - - uint8_t colorIndex = thisbright; - int tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - if (tmpSound * SEGMENT.intensity / 64 < thisbright) - { - thisbright = 0; - } - - leds[segmentToLogical(i)] += color_blend(SEGCOLOR(1), color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0), thisbright); + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); } + float volumeSmth = *(float*) um_data->u_data[0]; - setPixels(leds); + SEGMENT.fadeToBlackBy(32); + + plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. + plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. + + for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows. + // updated, similar to "plasma" effect - softhack007 + uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2; + thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. + + uint8_t colorIndex=thisbright; + if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;} + + SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0), thisbright)); + } return FRAMETIME; } // mode_plasmoid() +static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;1v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin + /////////////////////// // * PUDDLEPEAK // /////////////////////// - // Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing. -uint16_t WS2812FX::mode_puddlepeak(void) -{ // Puddlepeak. By Andrew Tuline. +uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. uint16_t size = 0; - uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 255); - uint16_t pos = random(SEGLEN); // Set a random starting position. + uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); + uint16_t pos = random(SEGLEN); // Set a random starting position. - binNum = SEGMENT.custom2; // Select a bin. - maxVol = SEGMENT.custom3 / 4; // Our volume comparator. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + float volumeSmth = *(float*) um_data->u_data[0]; - fade_out(fadeVal); - - if (samplePeak == 1) - { - size = sampleAgc * SEGMENT.intensity / 256 / 4 + 1; // Determine size of the flash based on the volume. - if (pos + size >= SEGLEN) - size = SEGLEN - pos; + if (SEGENV.call == 0) { + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; } - for (int i = 0; i < size; i++) - { // Flash the LED's. - setPixelColor(pos + i, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + SEGMENT.fade_out(fadeVal); + + if (samplePeak == 1) { + size = volumeSmth * SEGMENT.intensity /256 /4 + 1; // Determine size of the flash based on the volume. + if (pos+size>= SEGLEN) size = SEGLEN - pos; + } + + for (int i=0; iu_data[1]; - if (tmpSound > 1) - { - size = tmpSound * SEGMENT.intensity / 256 / 8 + 1; // Determine size of the flash based on the volume. - if (pos + size >= SEGLEN) - size = SEGLEN - pos; + if (volumeRaw > 1) { + size = volumeRaw * SEGMENT.intensity /256 /8 + 1; // Determine size of the flash based on the volume. + if (pos+size >= SEGLEN) size = SEGLEN - pos; } - for (int i = 0; i < size; i++) - { // Flash the LED's. - setPixelColor(pos + i, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0)); + for (int i=0; i(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low. - uint16_t maxRipples = 16; - uint16_t dataSize = sizeof(ripple) * maxRipples; + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + um_data = simulateSound(SEGMENT.soundSim); + } + float volumeSmth = *(float*) um_data->u_data[0]; - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed + myVals[millis()%32] = volumeSmth; // filling values semi randomly - Ripple *ripples = reinterpret_cast(SEGENV.data); + SEGMENT.fade_out(64+(SEGMENT.speed>>1)); - // static uint8_t colour; // Ripple colour is randomized. - // static uint16_t centre; // Center of the current ripple. - // static int8_t steps = -1; // -1 is the initializing step. - - // static uint8_t ripFade = 255; // Starting brightness, which we'll say is SEGENV.aux0. - if (SEGENV.call == 0) - SEGENV.aux0 = 255; - - binNum = SEGMENT.custom2; // Select a bin. - maxVol = SEGMENT.custom3 / 2; // Our volume comparator. - - fade_out(240); // Lower frame rate means less effective fading than FastLED - fade_out(240); - - for (uint16_t i = 0; i < SEGMENT.intensity / 16; i++) - { // Limit the number of ripples. - - if (samplePeak) - { - - ripples[i].state = -1; - } - - switch (ripples[i].state) - { - - case -2: // Inactive mode - break; - - case -1: // Initialize ripple variables. - ripples[i].pos = random16(SEGLEN); - -#ifdef ESP32 - ripples[i].color = (int)(log10f(FFT_MajorPeak) * 128); -#else - ripples[i].color = random8(); -#endif - - ripples[i].state = 0; - break; - - case 0: - setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); - ripples[i].state++; - break; - - case maxsteps: // At the end of the ripples. -2 is an inactive mode. - ripples[i].state = -2; - break; - - default: // Middle of the ripples. - - setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0 / ripples[i].state * 2)); - setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0 / ripples[i].state * 2)); - ripples[i].state++; // Next step. - break; - } // switch step - } // for i + for (int i=0; i startBin) - endBin--; // avoid overlapping - - double sumBin = 0; - - for (int j = startBin; j <= endBin; j++) - { - sumBin += (fftBin[j] < soundSquelch * 1.75) ? 0 : fftBin[j]; // We need some sound temporary squelch for fftBin, because we didn't do it for the raw bins in audio_reactive.h - } - - sumBin = sumBin / (endBin - startBin + 1); // Normalize it. - sumBin = sumBin * (i + 5) / (endBin - startBin + 5); // Disgusting frequency adjustment calculation. Lows were too bright. Am open to quick 'n dirty alternatives. - - sumBin = sumBin * 8; // Need to use the 'log' version for this. Why " * 8" ?? - sumBin *= binScale; // apply gain - - if (sumBin > maxVal) - sumBin = maxVal; // Make sure our bin isn't higher than the max . . which we capped earlier. - - uint8_t bright = constrain(mapf(sumBin, 0, maxVal, 0, 255), 0, 255); // Map the brightness in relation to maxVal and crunch to 8 bits. - - setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i * 8 + millis() / 50, false, PALETTE_SOLID_WRAP, 0), bright)); // 'i' is just an index in the palette. The FFT value, bright, is the intensity. - - } // for i - - return FRAMETIME; -} // mode_binmap() ////////////////////// -// ** Blurz // +// ** Blurz // ////////////////////// +uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. + // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment -uint16_t WS2812FX::mode_blurz(void) -{ // Blurz. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - if (SEGENV.call == 0) - { - fill_solid(leds, 0); + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); SEGENV.aux0 = 0; } - uint8_t blurAmt = SEGMENT.intensity; + int fadeoutDelay = (256 - SEGMENT.speed) / 32; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed); - fade_out(SEGMENT.speed); + SEGENV.step += FRAMETIME; + if (SEGENV.step > SPEED_FORMULA_L) { + uint16_t segLoc = random16(SEGLEN); + SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); + ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16 - uint16_t segLoc = random(SEGLEN); - leds[segmentToLogical(segLoc)] = color_blend(SEGCOLOR(1), color_from_palette(2 * fftResult[SEGENV.aux0 % 16] * 240 / (SEGLEN - 1), false, PALETTE_SOLID_WRAP, 0), 2 * fftResult[SEGENV.aux0 % 16]); - SEGENV.aux0++; - SEGENV.aux0 = SEGENV.aux0 % 16; + SEGENV.step = 1; + SEGMENT.blur(SEGMENT.intensity); + } - blur1d(leds, blurAmt); - - setPixels(leds); return FRAMETIME; } // mode_blurz() +static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color mix;!;1f;m12=0,si=0"; // Pixels, Beatsin + ///////////////////////// // ** DJLight // ///////////////////////// +uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam. + const int mid = SEGLEN / 2; -uint16_t WS2812FX::mode_DJLight(void) -{ // Written by ??? Adapted by Will Tatam. - int NUM_LEDS = SEGLEN; // aka SEGLEN - int mid = NUM_LEDS / 2; + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 + 1 % 64; - - if (SEGENV.aux0 != secondHand) - { // Triggered millis timing. - SEGENV.aux0 = secondHand; - - leds[segmentToLogical(mid)] = CRGB(fftResult[15] / 2, fftResult[5] / 2, fftResult[0] / 2); // 16-> 15 as 16 is out of bounds - leds[segmentToLogical(mid)].fadeToBlackBy(map(fftResult[1 * 4], 0, 255, 255, 10)); // TODO - Update - - // move to the left - for (int i = NUM_LEDS - 1; i > mid; i--) - { - leds[segmentToLogical(i)] = leds[segmentToLogical(i - 1)]; - } - // move to the right - for (int i = 0; i < mid; i++) - { - leds[segmentToLogical(i)] = leds[segmentToLogical(i + 1)]; - } + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. + SEGENV.aux0 = secondHand; + + CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds + SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update + + for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left + for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } - setPixels(leds); return FRAMETIME; } // mode_DJLight() +static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,si=0"; // Circle, Beatsin + //////////////////// // ** Freqmap // //////////////////// - -uint16_t WS2812FX::mode_freqmap(void) -{ // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate. +uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. Would be better if a higher framerate. // Start frequency = 60 Hz and log10(60) = 1.78 - // End frequency = 5120 Hz and lo10(5120) = 3.71 + // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10 - float my_magnitude = FFT_Magnitude / 4.0; - if (soundAgc) - my_magnitude *= multAgc; - if (sampleAvg < 1) - my_magnitude = 0.001; // noise gate closed - mute + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float my_magnitude = *(float*)um_data->u_data[5] / 4.0f; + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) - fade_out(SEGMENT.speed); + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 32; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(SEGMENT.speed); - int locn = (log10f(FFT_MajorPeak) - 1.78) * (float)SEGLEN / (3.71 - 1.78); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. + int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. + if (locn < 1) locn = 0; // avoid underflow + + if (locn >=SEGLEN) locn = SEGLEN-1; + uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. + if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow - if (locn >= SEGLEN) - locn = SEGLEN - 1; - if (locn < 1) - locn = 0; - uint16_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0 / (3.71 - 1.78); // Scale log10 of frequency values to the 255 colour index. uint16_t bright = (int)my_magnitude; - setPixelColor(locn, color_blend(SEGCOLOR(1), color_from_palette(SEGMENT.intensity + pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright)); return FRAMETIME; } // mode_freqmap() +static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin + /////////////////////// // ** Freqmatrix // /////////////////////// +uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*)um_data->u_data[0]; -uint16_t WS2812FX::mode_freqmatrix(void) -{ // Freqmatrix. By Andreas Pleschung. + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 % 16; - - if (SEGENV.aux0 != secondHand) - { + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { SEGENV.aux0 = secondHand; - double sensitivity = mapf(SEGMENT.custom3, 1, 255, 1, 10); - int pixVal = sampleAgc * SEGMENT.intensity / 256 * sensitivity; - if (pixVal > 255) - pixVal = 255; + uint8_t sensitivity = map(SEGMENT.custom3, 0, 31, 1, 10); // reduced resolution slider + int pixVal = (volumeSmth * SEGMENT.intensity * sensitivity) / 256.0f; + if (pixVal > 255) pixVal = 255; - double intensity = map(pixVal, 0, 255, 0, 100) / 100.0; // make a brightness from the last avg + float intensity = map(pixVal, 0, 255, 0, 100) / 100.0f; // make a brightness from the last avg - CRGB color = 0; - CHSV c; + CRGB color = CRGB::Black; - if (FFT_MajorPeak > 5120) - FFT_MajorPeak = 1.0f; + if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1; // MajorPeak holds the freq. value which is most abundant in the last sample. // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz // we will treat everything with less than 65Hz as 0 - // Serial.printf("%5d ", FFT_MajorPeak, 0); - if (FFT_MajorPeak < 80) - { - color = CRGB::Black; - } - else - { - int upperLimit = 20 * SEGMENT.custom2; - int lowerLimit = 2 * SEGMENT.custom1; - int i = lowerLimit != upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; - uint16_t b = 255 * intensity; - if (b > 255) - b = 255; - c = CHSV(i, 240, (uint8_t)b); - color = c; // implicit conversion to RGB supplied by FastLED - } - // Serial.println(color); - leds[segmentToLogical(0)] = color; + if (FFT_MajorPeak < 80) { + color = CRGB::Black; + } else { + int upperLimit = 80 + 42 * SEGMENT.custom2; + int lowerLimit = 80 + 3 * SEGMENT.custom1; + uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t + uint16_t b = 255 * intensity; + if (b > 255) b = 255; + color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED + } // shift the pixels one pixel up - for (int i = SEGLEN; i > 0; i--) - { // Move up - leds[segmentToLogical(i)] = leds[segmentToLogical(i - 1)]; - } - - // fadeval = fade; - - // DISPLAY ARRAY - setPixels(leds); + SEGMENT.setPixelColor(0, color); + for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left } return FRAMETIME; } // mode_freqmatrix() +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin + ////////////////////// // ** Freqpixels // ////////////////////// - // Start frequency = 60 Hz and log10(60) = 1.78 // End frequency = 5120 Hz and lo10(5120) = 3.71 - // SEGMENT.speed select faderate // SEGMENT.intensity select colour index - -uint16_t WS2812FX::mode_freqpixels(void) -{ // Freqpixel. By Andrew Tuline. - - uint16_t fadeRate = 2 * SEGMENT.speed - SEGMENT.speed * SEGMENT.speed / 255; // Get to 255 as quick as you can. - - float my_magnitude = FFT_Magnitude / 16.0; - if (soundAgc) - my_magnitude *= multAgc; - if (sampleAvg < 1) - my_magnitude = 0.001; // noise gate closed - mute - - fade_out(fadeRate); - - for (int i = 0; i < SEGMENT.intensity / 32 + 1; i++) - { - uint16_t locn = random16(0, SEGLEN); - uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78) * 255.0 / (3.71 - 1.78); // Scale log10 of frequency values to the 255 colour index. - setPixelColor(locn, color_blend(SEGCOLOR(1), color_from_palette(SEGMENT.intensity + pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); +uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float my_magnitude = *(float*)um_data->u_data[5] / 16.0f; + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) + + uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can. + + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 64; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate); + + for (int i=0; i < SEGMENT.intensity/32+1; i++) { + uint16_t locn = random16(0,SEGLEN); + uint8_t pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. + if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow + SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + } + return FRAMETIME; } // mode_freqpixels() +static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;;;1f;m12=0,si=0"; // Pixels, Beatsin + ////////////////////// // ** Freqwave // ////////////////////// - // Assign a color to the central (starting pixels) based on the predominant frequencies and the volume. The color is being determined by mapping the MajorPeak from the FFT // and then mapping this to the HSV color circle. Currently we are sampling at 10240 Hz, so the highest frequency we can look at is 5120Hz. // @@ -7180,607 +6864,932 @@ uint16_t WS2812FX::mode_freqpixels(void) // // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. // Depending on the music stream you have you might find it useful to change the frequency mapping. +uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*)um_data->u_data[0]; -uint16_t WS2812FX::mode_freqwave(void) -{ // Freqwave. By Andreas Pleschung. + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } - // Instead of using colorpalettes, This effect works on the HSV color circle with red being the lowest frequency - // - // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. - // Depending on the music stream you have you might find it useful to change the frequency mapping. - - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 % 16; - - // uint8_t secondHand = millis()/(256-SEGMENT.speed) % 10; - if (SEGENV.aux0 != secondHand) - { + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16; + if(SEGENV.aux0 != secondHand) { SEGENV.aux0 = secondHand; - // uint8_t fade = SEGMENT.custom3; - // uint8_t fadeval; + float sensitivity = mapf(SEGMENT.custom3, 1, 31, 1, 10); // reduced resolution slider + float pixVal = volumeSmth * (float)SEGMENT.intensity / 256.0f * sensitivity; + if (pixVal > 255) pixVal = 255; - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - - float sensitivity = mapf(SEGMENT.custom3, 1, 255, 1, 10); - float pixVal = tmpSound * (float)SEGMENT.intensity / 256.0 * sensitivity; - if (pixVal > 255) - pixVal = 255; - - float intensity = mapf(pixVal, 0, 255, 0, 100) / 100.0; // make a brightness from the last avg + float intensity = mapf(pixVal, 0, 255, 0, 100) / 100.0f; // make a brightness from the last avg CRGB color = 0; - CHSV c; - if (FFT_MajorPeak > 5120) - FFT_MajorPeak = 1.0f; + if (FFT_MajorPeak > MAX_FREQUENCY) FFT_MajorPeak = 1.0f; // MajorPeak holds the freq. value which is most abundant in the last sample. // With our sampling rate of 10240Hz we have a usable freq range from roughtly 80Hz to 10240/2 Hz // we will treat everything with less than 65Hz as 0 - // Serial.printf("%5d ", FFT_MajorPeak, 0); - if (FFT_MajorPeak < 80) - { + + if (FFT_MajorPeak < 80) { color = CRGB::Black; - } - else - { - int upperLimit = 20 * SEGMENT.custom2; - int lowerLimit = 2 * SEGMENT.custom1; - int i = lowerLimit != upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; - if (i < 0) - i = 0; + } else { + int upperLimit = 80 + 42 * SEGMENT.custom2; + int lowerLimit = 80 + 3 * SEGMENT.custom1; + uint8_t i = lowerLimit!=upperLimit ? map(FFT_MajorPeak, lowerLimit, upperLimit, 0, 255) : FFT_MajorPeak; // may under/overflow - so we enforce uint8_t uint16_t b = 255.0 * intensity; - if (b > 255) - b = 255; - c = CHSV(i, 240, (uint8_t)b); - color = c; // implicit conversion to RGB supplied by FastLED + if (b > 255) b=255; + color = CHSV(i, 240, (uint8_t)b); // implicit conversion to RGB supplied by FastLED } - // Serial.println(color); - leds[segmentToLogical(SEGLEN / 2)] = color; + SEGMENT.setPixelColor(SEGLEN/2, color); // shift the pixels one pixel outwards - for (int i = SEGLEN; i > SEGLEN / 2; i--) - { // Move to the right. - leds[segmentToLogical(i)] = leds[segmentToLogical(i - 1)]; - } - for (int i = 0; i < SEGLEN / 2; i++) - { // Move to the left. - leds[segmentToLogical(i)] = leds[segmentToLogical(i + 1)]; - } - - // DISPLAY ARRAY - setPixels(leds); + for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; } // mode_freqwave() +static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin + /////////////////////// // ** Gravfreq // /////////////////////// - -uint16_t WS2812FX::mode_gravfreq(void) -{ // Gravfreq. By Andrew Tuline. +uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. uint16_t dataSize = sizeof(gravity); - if (!SEGENV.allocateData(dataSize)) - return mode_static(); // allocation failed - Gravity *gravcen = reinterpret_cast(SEGENV.data); + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Gravity* gravcen = reinterpret_cast(SEGENV.data); - fade_out(240); + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + float FFT_MajorPeak = *(float*)um_data->u_data[4]; + float volumeSmth = *(float*)um_data->u_data[0]; + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) - float tmpSound = (soundAgc) ? sampleAgc : sampleAvg; - float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0; + SEGMENT.fade_out(250); + + float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling - float mySampleAvg = mapf(segmentSampleAvg * 2.0, 0, 32, 0, (float)SEGLEN / 2.0); // map to pixels availeable in current segment - int tempsamp = constrain(mySampleAvg, 0, SEGLEN / 2); // Keep the sample from overflowing. - uint8_t gravity = 8 - SEGMENT.speed / 32; + float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment + int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. + uint8_t gravity = 8 - SEGMENT.speed/32; - for (int i = 0; i < tempsamp; i++) - { + for (int i=0; i= gravcen->topLED) - gravcen->topLED = tempsamp - 1; + gravcen->topLED = tempsamp-1; else if (gravcen->gravityCounter % gravity == 0) gravcen->topLED--; - if (gravcen->topLED >= 0) - { - setPixelColor(gravcen->topLED + SEGLEN / 2, CRGB::Gray); - setPixelColor(SEGLEN / 2 - 1 - gravcen->topLED, CRGB::Gray); + if (gravcen->topLED >= 0) { + SEGMENT.setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray); + SEGMENT.setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray); } gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity; return FRAMETIME; } // mode_gravfreq() +static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin + ////////////////////// // ** Noisemove // ////////////////////// +uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; -uint16_t WS2812FX::mode_noisemove(void) -{ // Noisemove. By: Andrew Tuline + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + //SEGMENT.fade_out(224); // Just in case something doesn't get faded. + int fadeoutDelay = (256 - SEGMENT.speed) / 96; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4); - fade_out(224); // Just in case something doesn't get faded. - - uint8_t numBins = map(SEGMENT.intensity, 0, 255, 0, 16); // Map slider to fftResult bins. - - for (int i = 0; i < numBins; i++) - { // How many active bins are we using. - uint16_t locn = inoise16(millis() * SEGMENT.speed + i * 50000, millis() * SEGMENT.speed); // Get a new pixel location from moving noise. - - locn = map(locn, 7500, 58000, 0, SEGLEN - 1); // Map that to the length of the strand, and ensure we don't go over. - locn = locn % SEGLEN; // Just to be bloody sure. - - setPixelColor(locn, color_blend(SEGCOLOR(1), color_from_palette(i * 64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16] * 4)); + uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins. + for (int i=0; iu_data[4]; + float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; -uint16_t WS2812FX::mode_rocktaves(void) -{ // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline - - fadeToBlackBy(leds, 64); // Just in case something doesn't get faded. + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded. float frTemp = FFT_MajorPeak; - uint8_t octCount = 0; // Octave counter. + uint8_t octCount = 0; // Octave counter. uint8_t volTemp = 0; - float my_magnitude = FFT_Magnitude / 16.0; // scale magnitude to be aligned with scaling of FFT bins - if (soundAgc) - my_magnitude *= multAgc; // apply gain - if (sampleAvg < 1) - my_magnitude = 0.001; // mute + volTemp = 32.0f + my_magnitude * 1.5f; // brightness = volume (overflows are handled in next lines) + if (my_magnitude < 48) volTemp = 0; // We need to squelch out the background noise. + if (my_magnitude > 144) volTemp = 255; // everything above this is full brightness - if (my_magnitude > 32) - volTemp = 255; // We need to squelch out the background noise. - - while (frTemp > 249) - { - octCount++; // This should go up to 5. - frTemp = frTemp / 2; + while ( frTemp > 249 ) { + octCount++; // This should go up to 5. + frTemp = frTemp/2; } - frTemp -= 132; // This should give us a base musical note of C3 - frTemp = fabs(frTemp * 2.1); // Fudge factors to compress octave range starting at 0 and going to 255; + frTemp -=132; // This should give us a base musical note of C3 + frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; - // Serial.print(frTemp); Serial.print("\t"); Serial.print(volTemp); Serial.print("\t");Serial.print(octCount); Serial.print("\t"); Serial.println(FFT_Magnitude); + uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); + i = constrain(i, 0, SEGLEN-1); + SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp)); - // leds[beatsin8(8+octCount*4,0,SEGLEN-1,0,octCount*8)] += CHSV((uint8_t)frTemp,255,volTemp); // Back and forth with different frequencies and phase shift depending on current octave. - - leds[segmentToLogical(mapf(beatsin8(8 + octCount * 4, 0, 255, 0, octCount * 8), 0, 255, 0, SEGLEN - 1))] += color_blend(SEGCOLOR(1), color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp); - - setPixels(leds); return FRAMETIME; -} // mode_rockdaves() +} // mode_rocktaves() +static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1,si=0"; // Bar, Beatsin + /////////////////////// // ** Waterfall // /////////////////////// - // Combines peak detection with FFT_MajorPeak and FFT_Magnitude. +uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline + if (SEGENV.call == 0) SEGMENT.fill(BLACK); -uint16_t WS2812FX::mode_waterfall(void) -{ // Waterfall. By: Andrew Tuline + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t samplePeak = *(uint8_t*)um_data->u_data[3]; + float FFT_MajorPeak = *(float*) um_data->u_data[4]; + uint8_t *maxVol = (uint8_t*)um_data->u_data[6]; + uint8_t *binNum = (uint8_t*)um_data->u_data[7]; + float my_magnitude = *(float*) um_data->u_data[5] / 8.0f; - if (SEGENV.call == 0) - fill_solid(leds, 0); + if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) - binNum = SEGMENT.custom2; // Select a bin. - maxVol = SEGMENT.custom3 / 2; // Our volume comparator. - - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 + 1 % 16; - - if (SEGENV.aux0 != secondHand) - { // Triggered millis timing. - SEGENV.aux0 = secondHand; - - float my_magnitude = FFT_Magnitude / 8.0; - if (soundAgc) - my_magnitude *= multAgc; - if (sampleAvg < 1) - my_magnitude = 0.001; // noise gate closed - mute - - int pixCol = (log10f(FFT_MajorPeak) - 2.26) * 177; // log10 frequency range is from 2.26 to 3.7. Let's scale accordingly. - if (pixCol < 0) - pixCol = 0; - - if (samplePeak) - { - leds[segmentToLogical(SEGLEN - 1)] = CHSV(92, 92, 92); - } - else - { - leds[segmentToLogical(SEGLEN - 1)] = color_blend(SEGCOLOR(1), color_from_palette(pixCol + SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude); - } - for (int i = 0; i < SEGLEN - 1; i++) - leds[segmentToLogical(i)] = leds[segmentToLogical(i + 1)]; + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + SEGENV.aux0 = 255; + SEGMENT.custom1 = *binNum; + SEGMENT.custom2 = *maxVol * 2; + } + + *binNum = SEGMENT.custom1; // Select a bin. + *maxVol = SEGMENT.custom2 / 2; // Our volume comparator. + + uint8_t secondHand = micros() / (256-SEGMENT.speed)/500 + 1 % 16; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. + SEGENV.aux0 = secondHand; + + //uint8_t pixCol = (log10f((float)FFT_MajorPeak) - 2.26f) * 177; // 10Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.7 (5012hz). Let's scale accordingly. + uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly. + if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow + + if (samplePeak) { + SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92)); + } else { + SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); + } + for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left } - setPixels(leds); return FRAMETIME; } // mode_waterfall() +static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;1f;c2=0,m12=2,si=0"; // Circles, Beatsin -////////////////////////////////////////////// -// START of 2D REACTIVE ROUTINES // -////////////////////////////////////////////// +#ifndef WLED_DISABLE_2D ///////////////////////// // ** 2D GEQ // ///////////////////////// +uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::GEQ_base(bool centered_horizontal, bool centered_vertical, bool color_vertical) -{ // By Will Tatam. Refactor by Ewoud Wijma. - fadeToBlackBy(leds, SEGMENT.speed); + const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - bool rippleTime; - if (millis() - SEGENV.step >= 255 - SEGMENT.intensity) - { + if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed + uint16_t *previousBarHeight = reinterpret_cast(SEGENV.data); //array of previous bar heights per frequency band + + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + + if (SEGENV.call == 0) for (int i=0; i= (256U - SEGMENT.intensity)) { SEGENV.step = millis(); rippleTime = true; } - else - rippleTime = false; - static int previousBarHeight[64]; // array of previous bar heights per frequency band + if (SEGENV.call == 0) SEGMENT.fill(BLACK); + int fadeoutDelay = (256 - SEGMENT.speed) / 64; + if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed); - int xCount = SEGMENT.width; - if (centered_vertical) - xCount /= 2; + for (int x=0; x < cols; x++) { + uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); + if (NUM_BANDS < 16) band = map(band, 0, NUM_BANDS - 1, 0, 15); // always use full range. comment out this line to get the previous behaviour. + band = constrain(band, 0, 15); + uint16_t colorIndex = band * 17; + uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here + if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up - for (int x = 0; x < xCount; x++) - { - int band = map(x, 0, xCount - 1, 0, 15); - int barHeight = map(fftResult[band], 0, 255, 0, SEGMENT.height); - if ((barHeight % 2 == 1) && centered_horizontal) - barHeight++; // get an even barHeight if centered_horizontal - int yStartBar = centered_horizontal ? (SEGMENT.height - barHeight) / 2 : 0; // lift up the bar if centered_horizontal - int yStartPeak = centered_horizontal ? (SEGMENT.height - previousBarHeight[x]) / 2 : 0; // lift up the peaks if centered_horizontal + uint32_t ledColor = BLACK; + for (int y=0; y < barHeight; y++) { + if (SEGMENT.check1) //color_vertical / color bars toggle + colorIndex = map(y, 0, rows-1, 0, 255); - for (int y = 0; y < SEGMENT.height; y++) - { - CRGB heightColor = CRGB::Black; - uint16_t colorIndex; - if (color_vertical) - { - if (centered_horizontal) - colorIndex = map(abs(y - (SEGMENT.height - 1) / 2.0), 0, SEGMENT.height / 2 - 1, 0, 255); - else - colorIndex = map(y, 0, SEGMENT.height - 1, 0, 255); - } - else - colorIndex = band * 17; - heightColor = color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); - - CRGB ledColor = CRGB::Black; // if not part of bars or peak, make black (not fade to black) - - // bar - if (y >= yStartBar && y < yStartBar + barHeight) - ledColor = heightColor; - - // low and high peak (must exist && on peak position && only below if centered_horizontal effect) - if ((previousBarHeight[x] > 0) && (SEGMENT.intensity < 255) && (y == yStartPeak || y == yStartPeak + previousBarHeight[x] - 1) && (centered_horizontal || y != yStartPeak)) - ledColor = SEGCOLOR(2) == CRGB::Black ? heightColor : CRGB(SEGCOLOR(2)); // low peak - - if (centered_vertical) - { - leds[XY(SEGMENT.width / 2 + x, SEGMENT.height - 1 - y)] = ledColor; - leds[XY(SEGMENT.width / 2 - 1 - x, SEGMENT.height - 1 - y)] = ledColor; - } - else - leds[XY(x, SEGMENT.height - 1 - y)] = ledColor; + ledColor = SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0); + SEGMENT.setPixelColorXY(x, rows-1 - y, ledColor); } + if (previousBarHeight[x] > 0) + SEGMENT.setPixelColorXY(x, rows - previousBarHeight[x], (SEGCOLOR(2) != BLACK) ? SEGCOLOR(2) : ledColor); - if (rippleTime) - previousBarHeight[x] -= centered_horizontal ? 2 : 1; // delay/ripple effect - if (barHeight > previousBarHeight[x]) - previousBarHeight[x] = barHeight; // drive the peak up + if (rippleTime && previousBarHeight[x]>0) previousBarHeight[x]--; //delay/ripple effect } - setPixels(leds); return FRAMETIME; -} // GEQ_base - -uint16_t WS2812FX::mode_2DGEQ(void) -{ // By Will Tatam. Code reduction by Ewoud Wijma. - return GEQ_base(false, false, false); } // mode_2DGEQ() +static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,,Color bars;!,,Peaks;!;2f;c1=255,c2=64,pal=11,si=0"; // Beatsin -///////////////////////// -// ** 2D CenterBars // -///////////////////////// - -uint16_t WS2812FX::mode_2DCenterBars(void) -{ // Written by Scott Marley Adapted by Spiro-C.. - return GEQ_base(SEGMENT.custom1 > 128, SEGMENT.custom2 > 128, SEGMENT.custom3 > 128); -} // mode_2DCenterBars() ///////////////////////// // ** 2D Funky plank // ///////////////////////// +uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam. + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_2DFunkyPlank(void) -{ // Written by ??? Adapted by Will Tatam. + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); - int barWidth = (SEGMENT.width / NUMB_BANDS); + int barWidth = (cols / NUMB_BANDS); int bandInc = 1; - if (barWidth == 0) - { + if (barWidth == 0) { // Matrix narrower than fft bands barWidth = 1; - bandInc = (NUMB_BANDS / SEGMENT.width); + bandInc = (NUMB_BANDS / cols); } - uint8_t secondHand = micros() / (256 - SEGMENT.speed) / 500 + 1 % 64; + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + // add support for no audio + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; - if (SEGENV.aux0 != secondHand) - { // Triggered millis timing. + if (SEGENV.call == 0) { + SEGMENT.fill(BLACK); + } + + uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 64; + if (SEGENV.aux0 != secondHand) { // Triggered millis timing. SEGENV.aux0 = secondHand; // display values of int b = 0; - for (int band = 0; band < NUMB_BANDS; band += bandInc) - { - int hue = fftResult[band]; - int v = map(fftResult[band], 0, 255, 10, 255); - // if(hue > 0) Serial.printf("Band: %u Value: %u\n", band, hue); - for (int w = 0; w < barWidth; w++) - { - int xpos = (barWidth * b) + w; - leds[XY(xpos, 0)] = CHSV(hue, 255, v); + for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) { + int hue = fftResult[band % 16]; + int v = map(fftResult[band % 16], 0, 255, 10, 255); + for (int w = 0; w < barWidth; w++) { + int xpos = (barWidth * b) + w; + SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v)); } - b++; } // Update the display: - for (int i = (SEGMENT.height - 1); i > 0; i--) - { - for (int j = (SEGMENT.width - 1); j >= 0; j--) - { - int src = XY(j, (i - 1)); - int dst = XY(j, i); - leds[dst] = leds[src]; + for (int i = (rows - 1); i > 0; i--) { + for (int j = (cols - 1); j >= 0; j--) { + SEGMENT.setPixelColorXY(j, i, SEGMENT.getPixelColorXY(j, i-1)); } } } - setPixels(leds); return FRAMETIME; } // mode_2DFunkyPlank +static const char _data_FX_MODE_2DFUNKYPLANK[] PROGMEM = "Funky Plank@Scroll speed,,# of bands;;;2f;si=0"; // Beatsin -uint16_t WS2812FX::mode_2DAkemi(void) -{ - uint16_t counter = (now * ((SEGMENT.speed >> 2) + 2)) & 0xFFFF; +///////////////////////// +// 2D Akemi // +///////////////////////// +static uint8_t akemi[] PROGMEM = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,2,2,3,3,3,3,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,2,3,3,0,0,0,0,0,0,3,3,2,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,2,3,0,0,0,6,5,5,4,0,0,0,3,2,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,0,6,6,5,5,5,5,4,4,0,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,0,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,7,7,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,2, + 0,2,2,2,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,2,2,2,0, + 0,0,0,3,2,0,0,0,6,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,2,2,0,0,0, + 0,0,0,3,2,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,2,3,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,3,0,3,3,0,0,3,3,0,3,3,0,0,0,0,2,2,0,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,2,0,3,2,0,0,3,2,0,3,2,0,0,0,0,2,3,0,0,0,0, + 0,0,0,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,2,3,0,0,0,0,0, + 0,0,0,0,0,3,2,2,2,2,0,0,0,3,2,0,0,3,2,0,0,0,3,2,2,2,3,0,0,0,0,0, + 0,0,0,0,0,0,3,3,3,0,0,0,0,3,2,0,0,3,2,0,0,0,0,3,3,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +uint16_t mode_2DAkemi(void) { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + uint16_t counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - // Akemi - uint8_t akemi[32][32] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 0, 0, 0, 0, 0, 0, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 0, 6, 5, 5, 4, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, 6, 6, 5, 5, 5, 5, 4, 4, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 3, 2, 3, 6, 5, 5, 7, 7, 5, 5, 5, 5, 7, 7, 5, 5, 4, 3, 2, 3, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 2, 3, 1, 3, 6, 5, 1, 7, 7, 7, 5, 5, 1, 7, 7, 7, 5, 4, 3, 1, 3, 2, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 8, 3, 1, 3, 6, 5, 1, 7, 7, 7, 5, 5, 1, 7, 7, 7, 5, 4, 3, 1, 3, 8, 9, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 8, 3, 1, 3, 6, 5, 5, 1, 1, 5, 5, 5, 5, 1, 1, 5, 5, 4, 3, 1, 3, 8, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 2, 3, 1, 3, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3, 1, 3, 2, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 3, 2, 3, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2, 3, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 5, 5, 5, 5, 7, 7, 5, 5, 5, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2}, - {0, 2, 2, 2, 0, 0, 0, 0, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 0, 0, 2, 2, 2, 0}, - {0, 0, 0, 3, 2, 0, 0, 0, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 2, 2, 0, 0, 0}, - {0, 0, 0, 3, 2, 0, 0, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 2, 3, 0, 0, 0}, - {0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 3, 3, 0, 3, 3, 0, 0, 3, 3, 0, 3, 3, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0}, - {0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 3, 2, 0, 3, 2, 0, 0, 3, 2, 0, 3, 2, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 2, 3, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 3, 2, 2, 2, 2, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 3, 2, 2, 2, 3, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - }; + const float lightFactor = 0.15f; + const float normalFactor = 0.4f; - // draw and color Akemi - for (int y = 0; y < SEGMENT.height; y++) - for (int x = 0; x < SEGMENT.width; x++) - { - CRGB color = BLACK; - CRGB faceColor = color_wheel(counter); - CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; // default warmish white 0xABA8FF; //0xFF52e5;// - CRGB soundColor = ORANGE; - double lightFactor = 0.15; - double normalFactor = 0.4; - double base = fftResult[0] / 255.0; - switch (akemi[y * 32 / SEGMENT.height][x * 32 / SEGMENT.width]) - { - case 0: - color = BLACK; - break; - case 3: - armsAndLegsColor.r *= lightFactor; - armsAndLegsColor.g *= lightFactor; - armsAndLegsColor.b *= lightFactor; - color = armsAndLegsColor; - break; // light arms and legs 0x9B9B9B - case 2: - armsAndLegsColor.r *= normalFactor; - armsAndLegsColor.g *= normalFactor; - armsAndLegsColor.b *= normalFactor; - color = armsAndLegsColor; - break; // normal arms and legs 0x888888 - case 1: - color = armsAndLegsColor; - break; // dark arms and legs 0x686868 - case 6: - faceColor.r *= lightFactor; - faceColor.g *= lightFactor; - faceColor.b *= lightFactor; - color = faceColor; - break; // light face 0x31AAFF - case 5: - faceColor.r *= normalFactor; - faceColor.g *= normalFactor; - faceColor.b *= normalFactor; - color = faceColor; - break; // normal face 0x0094FF - case 4: - color = faceColor; - break; // dark face 0x007DC6 - case 7: - color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; - break; // eyes and mouth default white - case 8: - if (base > 0.4) - { - soundColor.r *= base; - soundColor.g *= base; - soundColor.b *= base; - color = soundColor; - } - else - color = armsAndLegsColor; - break; - default: - color = BLACK; - } + um_data_t *um_data; + if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + um_data = simulateSound(SEGMENT.soundSim); + } + uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; + float base = fftResult[0]/255.0f; - if (SEGMENT.intensity > 128 && fftResult[0] > 128) // dance if base is high - { - leds[XY(x, 0)] = BLACK; - leds[XY(x, y + 1)] = color; - } - else - leds[XY(x, y)] = color; + //draw and color Akemi + for (int y=0; y < rows; y++) for (int x=0; x < cols; x++) { + CRGB color; + CRGB soundColor = ORANGE; + CRGB faceColor = SEGMENT.color_wheel(counter); + CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; //default warmish white 0xABA8FF; //0xFF52e5;// + uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols] + switch (ak) { + case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B + case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888 + case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868 + case 6: faceColor.r *= lightFactor; faceColor.g *= lightFactor; faceColor.b *= lightFactor; color=faceColor; break; //light face 0x31AAFF + case 5: faceColor.r *= normalFactor; faceColor.g *= normalFactor; faceColor.b *= normalFactor; color=faceColor; break; //normal face 0x0094FF + case 4: color = faceColor; break; //dark face 0x007DC6 + case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white + case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break; + default: color = BLACK; break; } - // add geq left and right - for (int x = 0; x < SEGMENT.width / 8; x++) - { - int band = x * SEGMENT.width / 8; - int barHeight = map(fftResult[band], 0, 255, 0, 17 * SEGMENT.height / 32); - CRGB color = color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); - - for (int y = 0; y < barHeight; y++) - { - leds[XY(x, SEGMENT.height / 2 - y)] = color; - leds[XY(SEGMENT.width - 1 - x, SEGMENT.height / 2 - y)] = color; - } + if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high + SEGMENT.setPixelColorXY(x, 0, BLACK); + SEGMENT.setPixelColorXY(x, y+1, color); + } else + SEGMENT.setPixelColorXY(x, y, color); } - setPixels(leds); + //add geq left and right + if (um_data && fftResult) { + for (int x=0; x < cols/8; x++) { + uint16_t band = x * cols/8; + band = constrain(band, 0, 15); + uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); + CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); + + for (int y=0; y < barHeight; y++) { + SEGMENT.setPixelColorXY(x, rows/2-y, color); + SEGMENT.setPixelColorXY(cols-1-x, rows/2-y, color); + } + } + } return FRAMETIME; } // mode_2DAkemi +static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;2f;si=0"; //beatsin -// 3D !!!!!!!!!! -float distance(uint16_t x1, uint16_t y1, uint16_t z1, uint16_t x2, uint16_t y2, uint16_t z2) -{ - return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2)); -} +// Distortion waves - ldirko +// https://editor.soulmatelights.com/gallery/1089-distorsion-waves +// adapted for WLED by @blazoncek +uint16_t mode_2Ddistortionwaves() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up -uint16_t WS2812FX::mode_3DRipples(void) -{ - float ripple_interval = 1.3 * (SEGMENT.intensity / 128.0); + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - fill(CRGB::Black); + uint8_t speed = SEGMENT.speed/32; + uint8_t scale = SEGMENT.intensity/32; - // workaround to get width, height and depth - uint16_t mW = strip.matrixWidth; - uint16_t mH = 1; - uint16_t mD = 1; - // balance dimensions - while (mW > mH) - { - if (mH < mD) - mH++; - else - mD++; - mW = strip.matrixWidth / mH / mD; - } + uint8_t w = 2; - for (int z = 0; z < mD; z++) - { - for (int x = 0; x < mW; x++) - { - float d = distance(3.5, 3.5, 0, x, z, 0) / 9.899495 * mH; - uint16_t height = floor(mH / 2.0 + sinf(d / ripple_interval + SEGENV.call / ((256.0 - SEGMENT.speed) / 20.0)) * mH / 2.0); // between 0 and 8 + uint16_t a = millis()/32; + uint16_t a2 = a/2; + uint16_t a3 = a/3; - setPixelColor(x + height * mW + z * mW * mH, color_from_palette(SEGENV.call, true, PALETTE_SOLID_WRAP, 0)); + uint16_t cx = beatsin8(10-speed,0,cols-1)*scale; + uint16_t cy = beatsin8(12-speed,0,rows-1)*scale; + uint16_t cx1 = beatsin8(13-speed,0,cols-1)*scale; + uint16_t cy1 = beatsin8(15-speed,0,rows-1)*scale; + uint16_t cx2 = beatsin8(17-speed,0,cols-1)*scale; + uint16_t cy2 = beatsin8(14-speed,0,rows-1)*scale; + + uint16_t xoffs = 0; + for (int x = 0; x < cols; x++) { + xoffs += scale; + uint16_t yoffs = 0; + + for (int y = 0; y < rows; y++) { + yoffs += scale; + + byte rdistort = cos8((cos8(((x<<3)+a )&255)+cos8(((y<<3)-a2)&255)+a3 )&255)>>1; + byte gdistort = cos8((cos8(((x<<3)-a2)&255)+cos8(((y<<3)+a3)&255)+a+32 )&255)>>1; + byte bdistort = cos8((cos8(((x<<3)+a3)&255)+cos8(((y<<3)-a) &255)+a2+64)&255)>>1; + + byte valueR = rdistort+ w* (a- ( ((xoffs - cx) * (xoffs - cx) + (yoffs - cy) * (yoffs - cy))>>7 )); + byte valueG = gdistort+ w* (a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 )); + byte valueB = bdistort+ w* (a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 )); + + valueR = gamma8(cos8(valueR)); + valueG = gamma8(cos8(valueG)); + valueB = gamma8(cos8(valueB)); + + SEGMENT.setPixelColorXY(x, y, RGBW32(valueR, valueG, valueB, 0)); } } return FRAMETIME; -} // mode_3DRipples +} +static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2"; -uint16_t WS2812FX::mode_3DSphereMove(void) -{ - uint16_t origin_x, origin_y, origin_z, d; - float diameter; - fill(CRGB::Black); +//Soap +//@Stepko +//Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick +// adapted for WLED by @blazoncek +uint16_t mode_2Dsoap() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up - uint32_t interval = SEGENV.call / ((256.0 - SEGMENT.speed) / 20.0); + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); - // workaround to get width, height and depth - uint16_t mW = strip.matrixWidth; - uint16_t mH = 1; - uint16_t mD = 1; - // balance dimensions - while (mW > mH) - { - if (mH < mD) - mH++; - else - mD++; - mW = strip.matrixWidth / mH / mD; + const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped + if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed + + uint8_t *noise3d = reinterpret_cast(SEGENV.data); + uint32_t *noise32_x = reinterpret_cast(SEGENV.data + dataSize); + uint32_t *noise32_y = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)); + uint32_t *noise32_z = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)*2); + const uint32_t scale32_x = 160000U/cols; + const uint32_t scale32_y = 160000U/rows; + const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2; + const uint8_t smoothness = MIN(250,SEGMENT.intensity); // limit as >250 produces very little changes + + // init + if (SEGENV.call == 0) { + *noise32_x = random16(); + *noise32_y = random16(); + *noise32_z = random16(); + } else { + *noise32_x += mov; + *noise32_y += mov; + *noise32_z += mov; } - origin_x = 3.5 + sinf(interval) * 2.5; - origin_y = 3.5 + cosf(interval) * 2.5; - origin_z = 3.5 + cosf(interval) * 2.0; - - diameter = 2.0 + sinf(interval / 3.0); - - for (int x = 0; x < mW; x++) - { - for (int y = 0; y < mH; y++) - { - for (int z = 0; z < mD; z++) - { - d = distance(x, y, z, origin_x, origin_y, origin_z); - - if (d > diameter && d < diameter + 1) - { - setPixelColor(x + y * mW + z * mW * mH, color_from_palette(SEGENV.call, true, PALETTE_SOLID_WRAP, 0)); - } + for (int i = 0; i < cols; i++) { + int32_t ioffset = scale32_x * (i - cols / 2); + for (int j = 0; j < rows; j++) { + int32_t joffset = scale32_y * (j - rows / 2); + uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; + noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness); + } + } + // init also if dimensions changed + if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) { + SEGMENT.aux0 = cols; + SEGMENT.aux1 = rows; + for (int i = 0; i < cols; i++) { + for (int j = 0; j < rows; j++) { + SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); } } } + int zD; + int zF; + int amplitude; + int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4; + int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4; + CRGB ledsbuff[MAX(cols,rows)]; + + amplitude = (cols >= 16) ? (cols-8)/8 : 1; + for (int y = 0; y < rows; y++) { + int amount = ((int)noise3d[XY(0,y)] - 128) * 2 * amplitude + 256*shiftX; + int delta = abs(amount) >> 8; + int fraction = abs(amount) & 255; + for (int x = 0; x < cols; x++) { + if (amount < 0) { + zD = x - delta; + zF = zD - 1; + } else { + zD = x + delta; + zF = zD + 1; + } + CRGB PixelA = CRGB::Black; + if ((zD >= 0) && (zD < cols)) PixelA = SEGMENT.getPixelColorXY(zD, y); + else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zD),y)]*3); + CRGB PixelB = CRGB::Black; + if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y); + else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3); + ledsbuff[x] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + } + for (int x = 0; x < cols; x++) SEGMENT.setPixelColorXY(x, y, ledsbuff[x]); + } + + amplitude = (rows >= 16) ? (rows-8)/8 : 1; + for (int x = 0; x < cols; x++) { + int amount = ((int)noise3d[XY(x,0)] - 128) * 2 * amplitude + 256*shiftY; + int delta = abs(amount) >> 8; + int fraction = abs(amount) & 255; + for (int y = 0; y < rows; y++) { + if (amount < 0) { + zD = y - delta; + zF = zD - 1; + } else { + zD = y + delta; + zF = zD + 1; + } + CRGB PixelA = CRGB::Black; + if ((zD >= 0) && (zD < rows)) PixelA = SEGMENT.getPixelColorXY(x, zD); + else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zD))]*3); + CRGB PixelB = CRGB::Black; + if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF); + else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3); + ledsbuff[y] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + } + for (int y = 0; y < rows; y++) SEGMENT.setPixelColorXY(x, y, ledsbuff[y]); + } + return FRAMETIME; -} // mode_3DSphereMove +} +static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; + + +//Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21 +//Octopus (https://editor.soulmatelights.com/gallery/671-octopus) +//Stepko and Sutaburosu +// adapted for WLED by @blazoncek +uint16_t mode_2Doctopus() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint8_t mapp = 180 / MAX(cols,rows); + + typedef struct { + uint8_t angle; + uint8_t radius; + } map_t; + + const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped + if (!SEGENV.allocateData(dataSize + 2)) return mode_static(); //allocation failed + + map_t *rMap = reinterpret_cast(SEGENV.data); + uint8_t *offsX = reinterpret_cast(SEGENV.data + dataSize); + uint8_t *offsY = reinterpret_cast(SEGENV.data + dataSize + 1); + + // re-init if SEGMENT dimensions or offset changed + if (SEGENV.call == 0 || SEGENV.aux0 != cols || SEGENV.aux1 != rows || SEGMENT.custom1 != *offsX || SEGMENT.custom2 != *offsY) { + SEGENV.step = 0; // t + SEGENV.aux0 = cols; + SEGENV.aux1 = rows; + *offsX = SEGMENT.custom1; + *offsY = SEGMENT.custom2; + const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255; + const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255; + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI + rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu + } + } + } + + SEGENV.step += SEGMENT.speed / 32 + 1; // 1-4 range + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + byte angle = rMap[XY(x,y)].angle; + byte radius = rMap[XY(x,y)].radius; + //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); + uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); + intensity = map(intensity*intensity, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display + CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); + SEGMENT.setPixelColorXY(x, y, c); + } + } + return FRAMETIME; +} +static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs;;!;2;"; + + +//Waving Cell +//@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells) +// adapted for WLED by @blazoncek +uint16_t mode_2Dwavingcell() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + uint32_t t = millis()/(257-SEGMENT.speed); + uint8_t aX = SEGMENT.custom1/16 + 9; + uint8_t aY = SEGMENT.custom2/16 + 1; + uint8_t aZ = SEGMENT.custom3 + 1; + for (int x = 0; x < cols; x++) for (int y = 0; y + #include "const.h" #define FASTLED_INTERNAL //remove annoying pragma messages @@ -38,6 +40,9 @@ #define DEFAULT_SPEED (uint8_t)128 #define DEFAULT_INTENSITY (uint8_t)128 #define DEFAULT_COLOR (uint32_t)0xFFAA00 +#define DEFAULT_C1 (uint8_t)128 +#define DEFAULT_C2 (uint8_t)128 +#define DEFAULT_C3 (uint8_t)16 #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) @@ -54,38 +59,41 @@ /* Not used in all effects yet */ #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) -#define FRAMETIME _frametime +//#define FRAMETIME _frametime +#define FRAMETIME strip.getFrameTime() /* each segment uses 52 bytes of SRAM memory, so if you're application fails because of insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ #ifdef ESP8266 #define MAX_NUM_SEGMENTS 16 - /* How many color transitions can run at once */ - #define MAX_NUM_TRANSITIONS 8 /* How much data bytes all segments combined may allocate */ - #define MAX_SEGMENT_DATA 4096 + #define MAX_SEGMENT_DATA 5120 #else #ifndef MAX_NUM_SEGMENTS #define MAX_NUM_SEGMENTS 32 #endif - #define MAX_NUM_TRANSITIONS 24 - #define MAX_SEGMENT_DATA 20480 + #if defined(ARDUINO_ARCH_ESP32S2) + #define MAX_SEGMENT_DATA 24576 + #else + #define MAX_SEGMENT_DATA 32767 + #endif #endif /* How much data bytes each segment should max allocate to leave enough space for other segments, assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ -#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS) +#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments()) -// NEED WORKAROUND TO ACCESS PRIVATE CLASS VARIABLE '_frametime' #define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15) #define NUM_COLORS 3 /* number of colors per segment */ -#define SEGMENT _segments[_segment_index] -#define SEGCOLOR(x) _colors_t[x] -#define SEGENV _segment_runtimes[_segment_index] -#define SEGLEN _virtualSegmentLength -#define SEGACT SEGMENT.stop -#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN +#define SEGMENT strip._segments[strip.getCurrSegmentId()] +#define SEGENV strip._segments[strip.getCurrSegmentId()] +//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) +//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() +#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ +#define SEGPALETTE strip._currentPalette +#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ +#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) // some common colors #define RED (uint32_t)0xFF0000 @@ -100,27 +108,25 @@ #define ORANGE (uint32_t)0xFF3000 #define PINK (uint32_t)0xFF1493 #define ULTRAWHITE (uint32_t)0xFFFFFFFF +#define DARKSLATEGRAY (uint32_t)0x2F4F4F +#define DARKSLATEGREY (uint32_t)0x2F4F4F // options // bit 7: segment is in transition mode -// bits 4-6: See WLEDSR below +// bits 4-6: TBD // bit 3: mirror effect within segment // bit 2: segment is on // bit 1: reverse segment // bit 0: segment is selected -#define NO_OPTIONS (uint8_t)0x00 -#define TRANSITIONAL (uint8_t)0x80 -#define MIRROR (uint8_t)0x08 -#define SEGMENT_ON (uint8_t)0x04 -#define REVERSE (uint8_t)0x02 -#define SELECTED (uint8_t)0x01 -#define IS_TRANSITIONAL ((SEGMENT.options & TRANSITIONAL) == TRANSITIONAL) -#define IS_MIRROR ((SEGMENT.options & MIRROR ) == MIRROR ) -#define IS_SEGMENT_ON ((SEGMENT.options & SEGMENT_ON ) == SEGMENT_ON ) -#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) -#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) - -#define MODE_COUNT 191// WLEDSR: First 128 for AC (incl reserved), rest for SR +#define NO_OPTIONS (uint16_t)0x0000 +#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed +#define REVERSE_Y_2D (uint16_t)0x0200 +#define MIRROR_Y_2D (uint16_t)0x0100 +#define TRANSITIONAL (uint16_t)0x0080 +#define MIRROR (uint16_t)0x0008 +#define SEGMENT_ON (uint16_t)0x0004 +#define REVERSE (uint16_t)0x0002 +#define SELECTED (uint16_t)0x0001 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -141,7 +147,7 @@ #define FX_MODE_SAW 16 #define FX_MODE_TWINKLE 17 #define FX_MODE_DISSOLVE 18 -#define FX_MODE_DISSOLVE_RANDOM 19 +#define FX_MODE_DISSOLVE_RANDOM 19 // candidate for removal (use Dissolve with with check 3) #define FX_MODE_SPARKLE 20 #define FX_MODE_FLASH_SPARKLE 21 #define FX_MODE_HYPER_SPARKLE 22 @@ -170,12 +176,12 @@ #define FX_MODE_FIRE_FLICKER 45 #define FX_MODE_GRADIENT 46 #define FX_MODE_LOADING 47 -#define FX_MODE_POLICE 48 // candidate for removal (after below three) +// #define FX_MODE_POLICE 48 // removed in 0.14! #define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity) #define FX_MODE_TWO_DOTS 50 #define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity) #define FX_MODE_RUNNING_DUAL 52 -#define FX_MODE_HALLOWEEN 53 // candidate for removal +// #define FX_MODE_HALLOWEEN 53 // removed in 0.14! #define FX_MODE_TRICOLOR_CHASE 54 #define FX_MODE_TRICOLOR_WIPE 55 #define FX_MODE_TRICOLOR_FADE 56 @@ -225,7 +231,7 @@ #define FX_MODE_HEARTBEAT 100 #define FX_MODE_PACIFICA 101 #define FX_MODE_CANDLE_MULTI 102 -#define FX_MODE_SOLID_GLITTER 103 +#define FX_MODE_SOLID_GLITTER 103 // candidate for removal (use glitter) #define FX_MODE_SUNRISE 104 #define FX_MODE_PHASED 105 #define FX_MODE_TWINKLEUP 106 @@ -236,28 +242,24 @@ #define FX_MODE_CHUNCHUN 111 #define FX_MODE_DANCING_SHADOWS 112 #define FX_MODE_WASHING_MACHINE 113 -#define FX_MODE_CANDY_CANE 114 // candidate for removal +// #define FX_MODE_CANDY_CANE 114 // removed in 0.14! #define FX_MODE_BLENDS 115 #define FX_MODE_TV_SIMULATOR 116 -#define FX_MODE_DYNAMIC_SMOOTH 117 +#define FX_MODE_DYNAMIC_SMOOTH 117 // candidate for removal (check3 in dynamic) -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Start of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// new 0.14 2D effects +#define FX_MODE_2DSPACESHIPS 118 //gap fill +#define FX_MODE_2DCRAZYBEES 119 //gap fill +#define FX_MODE_2DGHOSTRIDER 120 //gap fill +#define FX_MODE_2DBLOBS 121 //gap fill +#define FX_MODE_2DSCROLLTEXT 122 //gap fill +#define FX_MODE_2DDRIFTROSE 123 //gap fill +#define FX_MODE_2DDISTORTIONWAVES 124 //gap fill +#define FX_MODE_2DSOAP 125 //gap fill +#define FX_MODE_2DOCTOPUS 126 //gap fill +#define FX_MODE_2DWAVINGCELL 127 //gap fill -#define DEFAULT_Custom1 (uint8_t)128 -#define DEFAULT_Custom2 (uint8_t)128 -#define DEFAULT_Custom3 (uint8_t)128 - -// bits 4-6: WLEDSR: used for rotation and reverse -#define ROTATED2D (uint8_t)0x10 //0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, 0x20, 0x40, 0x80, 0xF0 -#define REVERSE2D (uint8_t)0x40 //0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, 0x20, 0x40, 0x80, 0xF0 -#define IS_REVERSE2D ((SEGMENT.options & REVERSE2D ) == REVERSE2D ) -#define IS_ROTATED2D ((SEGMENT.options & ROTATED2D ) == ROTATED2D ) - -//these id's should never change as they are saved in api commands of presets -//currently 118 to 127 is reserved for future AC effects (use Reserved keyword in Json) -//they must match the order of the effects in JSON_mode_names (if unused, use Reserved keyword in Json) +// WLED-SR effects (SR compatible IDs !!!) #define FX_MODE_PIXELS 128 #define FX_MODE_PIXELWAVE 129 #define FX_MODE_JUGGLES 130 @@ -294,14 +296,14 @@ #define FX_MODE_2DCENTERBARS 161 #define FX_MODE_2DPULSER 162 #define FX_MODE_BLURZ 163 -#define FX_MODE_2DDRIFT 164 -#define FX_MODE_2DWAVERLY 165 -#define FX_MODE_2DSUNRADIATION 166 -#define FX_MODE_2DCOLOREDBURSTS 167 -#define FX_MODE_2DJULIA 168 -#define FX_MODE_2DPOOLNOISE 169 //reserved in JSON_mode_names -#define FX_MODE_2DTWISTER 170 //reserved in JSON_mode_names -#define FX_MODE_2DCAELEMENTATY 171 //reserved in JSON_mode_names +#define FX_MODE_2DDRIFT 164 +#define FX_MODE_2DWAVERLY 165 +#define FX_MODE_2DSUNRADIATION 166 +#define FX_MODE_2DCOLOREDBURSTS 167 +#define FX_MODE_2DJULIA 168 +// #define FX_MODE_2DPOOLNOISE 169 //have been removed in WLED SR in the past because of low mem but should be added back +// #define FX_MODE_2DTWISTER 170 //have been removed in WLED SR in the past because of low mem but should be added back +// #define FX_MODE_2DCAELEMENTATY 171 //have been removed in WLED SR in the past because of low mem but should be added back #define FX_MODE_2DGAMEOFLIFE 172 #define FX_MODE_2DTARTAN 173 #define FX_MODE_2DPOLARLIGHTS 174 @@ -317,1179 +319,610 @@ #define FX_MODE_WAVESINS 184 #define FX_MODE_ROCKTAVES 185 #define FX_MODE_2DAKEMI 186 -#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects -#define FX_MODE_3DRIPPLES 188 -#define FX_MODE_3DSphereMove 189 -#define FX_MODE_JBL 190 +#define MODE_COUNT 187 -#define floatNull -32768 //WLEDSR Custom Effects -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// End of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef enum mapping1D2D { + M12_Pixels = 0, + M12_pBar = 1, + M12_pArc = 2, + M12_pCorner = 3 +} mapping1D2D_t; -class WS2812FX { - typedef uint16_t (WS2812FX::*mode_ptr)(void); +// segment, 80 bytes +typedef struct Segment { + public: + uint16_t start; // start index / start X coordinate 2D (left) + uint16_t stop; // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0 + uint16_t offset; + uint8_t speed; + uint8_t intensity; + uint8_t palette; + uint8_t mode; + union { + uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected + struct { + bool selected : 1; // 0 : selected + bool reverse : 1; // 1 : reversed + bool on : 1; // 2 : is On + bool mirror : 1; // 3 : mirrored + bool freeze : 1; // 4 : paused/frozen + bool reset : 1; // 5 : indicates that Segment runtime requires reset + bool transitional: 1; // 6 : transitional (there is transition occuring) + bool reverse_y : 1; // 7 : reversed Y (2D) + bool mirror_y : 1; // 8 : mirrored Y (2D) + bool transpose : 1; // 9 : transposed (2D, swapped X & Y) + uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...) + uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off") + uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups + }; + }; + uint8_t grouping, spacing; + uint8_t opacity; + uint32_t colors[NUM_COLORS]; + uint8_t cct; //0==1900K, 255==10091K + uint8_t custom1, custom2; // custom FX parameters/sliders + struct { + uint8_t custom3 : 5; // reduced range slider (0-31) + bool check1 : 1; // checkmark 1 + bool check2 : 1; // checkmark 2 + bool check3 : 1; // checkmark 3 + }; + uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows + uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows + char *name; - // pre show callback - typedef void (*show_callback) (void); + // runtime data + unsigned long next_time; // millis() of next update + uint32_t step; // custom "step" var + uint32_t call; // call counter + uint16_t aux0; // custom var + uint16_t aux1; // custom var + byte *data; // effect data pointer + static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) + + private: + union { + uint8_t _capabilities; + struct { + bool _isRGB : 1; + bool _hasW : 1; + bool _isCCT : 1; + bool _manualW : 1; + uint8_t _reserved : 4; + }; + }; + uint16_t _dataLen; + static uint16_t _usedSegmentData; + + // perhaps this should be per segment, not static + static CRGBPalette16 _randomPalette; + static CRGBPalette16 _newRandomPalette; + static unsigned long _lastPaletteChange; + + // transition data, valid only if transitional==true, holds values during transition (72 bytes) + struct Transition { + uint32_t _colorT[NUM_COLORS]; + uint8_t _briT; // temporary brightness + uint8_t _cctT; // temporary CCT + CRGBPalette16 _palT; // temporary palette + uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible) + uint8_t _modeP; // previous mode/effect + //uint16_t _aux0, _aux1; // previous mode/effect runtime data + //uint32_t _step, _call; // previous mode/effect runtime data + //byte *_data; // previous mode/effect runtime data + unsigned long _start; // must accommodate millis() + uint16_t _dur; + Transition(uint16_t dur=750) + : _briT(255) + , _cctT(127) + , _palT(CRGBPalette16(CRGB::Black)) + , _prevPaletteBlends(0) + , _modeP(FX_MODE_STATIC) + , _start(millis()) + , _dur(dur) + {} + Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o) + : _briT(b) + , _cctT(c) + , _palT(CRGBPalette16(CRGB::Black)) + , _prevPaletteBlends(0) + , _modeP(FX_MODE_STATIC) + , _start(millis()) + , _dur(d) + { + for (size_t i=0; i> n) & 0x01); } + inline bool isSelected(void) const { return selected; } + inline bool isActive(void) const { return stop > start; } + inline bool is2D(void) const { return (width()>1 && height()>1); } + inline bool hasRGB(void) const { return _isRGB; } + inline bool hasWhite(void) const { return _hasW; } + inline bool isCCT(void) const { return _isCCT; } + inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D) + inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1) + inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels + inline uint16_t groupLength(void) const { return grouping + spacing; } + inline uint8_t getLightCapabilities(void) const { return _capabilities; } + + static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } + static void addUsedSegmentData(int len) { _usedSegmentData += len; } + static void handleRandomPalette(); + + void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255); + bool setColor(uint8_t slot, uint32_t c); //returns true if changed + void setCCT(uint16_t k); + void setOpacity(uint8_t o); + void setOption(uint8_t n, bool val); + void setMode(uint8_t fx, bool loadDefaults = false); + void setPalette(uint8_t pal); + uint8_t differs(Segment& b) const; + void refreshLightCapabilities(void); + + // runtime data functions + inline uint16_t dataSize(void) const { return _dataLen; } + bool allocateData(size_t len); + void deallocateData(void); + void resetIfRequired(void); + /** + * Flags that before the next effect is calculated, + * the internal segment state should be reset. + * Call resetIfRequired before calling the next effect function. + * Safe to call from interrupts and network requests. + */ + inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true) + + // transition functions + void startTransition(uint16_t dur); // transition has to start before actual segment values change + void handleTransition(void); + uint16_t progress(void); //transition progression between 0-65535 + uint8_t currentBri(uint8_t briNew, bool useCct = false); + uint8_t currentMode(uint8_t modeNew); + uint32_t currentColor(uint8_t slot, uint32_t colorNew); + CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); + CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); + + // 1D strip + uint16_t virtualLength(void) const; + void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color + void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline + void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void setPixelColor(float i, uint32_t c, bool aa = true); + void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } + void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColor(int i); + // 1D support functions (some implement 2D as well) + void blur(uint8_t); + void fill(uint32_t c); + void fade_out(uint8_t r); + void fadeToBlackBy(uint8_t fadeBy); + void blendPixelColor(int n, uint32_t color, uint8_t blend); + void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColor(int n, uint32_t color, bool fast = false); + void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline + void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline + void fadePixelColor(uint16_t n, uint8_t fade); + uint8_t get_random_wheel_index(uint8_t pos); + uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); + uint32_t color_wheel(uint8_t pos); + + // 2D matrix + uint16_t virtualWidth(void) const; + uint16_t virtualHeight(void) const; + uint16_t nrOfVStrips(void) const; + #ifndef WLED_DISABLE_2D + uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment + void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color + void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); + void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } + void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColorXY(uint16_t x, uint16_t y); + // 2D support functions + void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); + void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColorXY(int x, int y, uint32_t color, bool fast = false); + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline + void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } + void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade); + void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight) + void blurRow(uint16_t row, fract8 blur_amount); + void blurCol(uint16_t col, fract8 blur_amount); + void moveX(int8_t delta, bool wrap = false); + void moveY(int8_t delta, bool wrap = false); + void move(uint8_t dir, uint8_t delta, bool wrap = false); + void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); + void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline + void wu_pixel(uint32_t x, uint32_t y, CRGB c); + void blur1d(fract8 blur_amount); // blur all rows in 1 dimension + void blur2d(fract8 blur_amount) { blur(blur_amount); } + void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } + void nscale8(uint8_t scale); + #else + uint16_t XY(uint16_t x, uint16_t y) { return x; } + void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } + void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } + void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } + void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } + void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } + uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } + void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } + void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } + void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); } + void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } + void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } + void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} + void blurRow(uint16_t row, fract8 blur_amount) {} + void blurCol(uint16_t col, fract8 blur_amount) {} + void moveX(int8_t delta, bool wrap = false) {} + void moveY(int8_t delta, bool wrap = false) {} + void move(uint8_t dir, uint8_t delta, bool wrap = false) {} + void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {} + void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} + void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} + #endif +} segment; +//static int segSize = sizeof(Segment); + +// main "strip" class +class WS2812FX { // 96 bytes + typedef uint16_t (*mode_ptr)(void); // pointer to mode function + typedef void (*show_callback)(void); // pre show callback + typedef struct ModeData { + uint8_t _id; // mode (effect) id + mode_ptr _fcn; // mode (effect) function + const char *_data; // mode (effect) name and its UI control data + ModeData(uint8_t id, uint16_t (*fcn)(void), const char *data) : _id(id), _fcn(fcn), _data(data) {} + } mode_data_t; static WS2812FX* instance; - // segment parameters public: - // FastLED array, so we can refer to leds[i] instead of getPixel() and setPixel() - CRGB leds[MAX_LEDS+1]; // See const.h for a value of 1500. The plus 1 is just in case we go over with XY(). - - typedef struct Segment { // 31 (32 in memory) bytes - uint16_t start; - uint16_t stop; //segment invalid if stop == 0 - uint16_t offset; - uint8_t speed; - uint8_t intensity; - uint8_t custom1; // WLEDSR - uint8_t custom2; // WLEDSR - uint8_t custom3; // WLEDSR - uint8_t palette; - uint8_t mode; - uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected - uint8_t grouping, spacing; - uint8_t opacity; - uint32_t colors[NUM_COLORS]; - uint8_t cct; //0==1900K, 255==10091K - uint8_t _capabilities; - uint16_t width; // WLEDSR ewowi20210624: add width/height and startX/Y stopX/Y for 2D segments - uint16_t height; // WLEDSR - uint16_t startX; // WLEDSR - uint16_t startY; // WLEDSR - uint16_t stopX; // WLEDSR - uint16_t stopY; // WLEDSR - - char *name; - bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed - if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false; - if (c == colors[slot]) return false; - uint8_t b = (slot == 1) ? cct : opacity; - ColorTransition::startTransition(b, colors[slot], instance->_transitionDur, segn, slot); - colors[slot] = c; return true; - } - void setCCT(uint16_t k, uint8_t segn) { - if (segn >= MAX_NUM_SEGMENTS) return; - if (k > 255) { //kelvin value, convert to 0-255 - if (k < 1900) k = 1900; - if (k > 10091) k = 10091; - k = (k - 1900) >> 5; - } - if (cct == k) return; - ColorTransition::startTransition(cct, colors[1], instance->_transitionDur, segn, 1); - cct = k; - } - void setOpacity(uint8_t o, uint8_t segn) { - if (segn >= MAX_NUM_SEGMENTS) return; - if (opacity == o) return; - ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0); - opacity = o; - } - void setOption(uint8_t n, bool val, uint8_t segn = 255) - { - bool prevOn = false; - if (n == SEG_OPTION_ON) { - prevOn = getOption(SEG_OPTION_ON); - if (!val && prevOn) { //fade off - ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0); - } - } - - if (val) { - options |= 0x01 << n; - } else - { - options &= ~(0x01 << n); - } - - if (n == SEG_OPTION_ON && val && !prevOn) { //fade on - ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0); - } - } - bool getOption(uint8_t n) - { - return ((options >> n) & 0x01); - } - inline bool isSelected() - { - return getOption(0); - } - inline bool isActive() - { - return stop > start; - } - inline uint16_t length() - { - return (stopX - startX + 1) * (stopY - startY + 1); //WLEDSR: calculate length using SEGMENT x/y. Used by SEGLEN - } - inline uint16_t groupLength() - { - return grouping + spacing; - } - uint16_t virtualLength() - { - uint16_t groupLen = groupLength(); - uint16_t vLength = (length() + groupLen - 1) / groupLen; - if (options & MIRROR) - vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED - return vLength; - } - uint8_t differs(Segment& b); - inline uint8_t getLightCapabilities() {return _capabilities;} - void refreshLightCapabilities(); - } segment; - - // segment runtime parameters - typedef struct Segment_runtime { // 28 bytes - unsigned long next_time; // millis() of next update - uint32_t step; // custom "step" var - uint32_t call; // call counter - uint16_t aux0; // custom var - uint16_t aux1; // custom var - byte* data = nullptr; - bool allocateData(uint16_t len){ - if (data && _dataLen == len) return true; //already allocated - deallocateData(); - if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory - // if possible use SPI RAM on ESP32 - #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) - if (psramFound()) - data = (byte*) ps_malloc(len); - else - #endif - data = (byte*) malloc(len); - if (!data) return false; //allocation failed - WS2812FX::instance->_usedSegmentData += len; - _dataLen = len; - memset(data, 0, len); - return true; - } - void deallocateData(){ - free(data); - data = nullptr; - WS2812FX::instance->_usedSegmentData -= _dataLen; - _dataLen = 0; - } - - /** - * If reset of this segment was request, clears runtime - * settings of this segment. - * Must not be called while an effect mode function is running - * because it could access the data buffer and this method - * may free that data buffer. - */ - void resetIfRequired() { - if (_requiresReset) { - next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; - deallocateData(); - _requiresReset = false; - } - } - - /** - * Flags that before the next effect is calculated, - * the internal segment state should be reset. - * Call resetIfRequired before calling the next effect function. - * Safe to call from interrupts and network requests. - */ - inline void markForReset() { _requiresReset = true; } - private: - uint16_t _dataLen = 0; - bool _requiresReset = false; - } segment_runtime; - - typedef struct ColorTransition { // 12 bytes - uint32_t colorOld = 0; - uint32_t transitionStart; - uint16_t transitionDur; - uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel - uint8_t briOld = 0; - static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) { - if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return; - if (instance->_brightness == 0) return; //do not need transitions if master bri is off - if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either - uint8_t tIndex = 0xFF; //none found - uint16_t tProgression = 0; - uint8_t s = segn + (slot << 6); //merge slot and segment into one byte - - for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) { - uint8_t tSeg = instance->transitions[i].segment; - //see if this segment + color already has a running transition - if (tSeg == s) { - tIndex = i; break; - } - if (tSeg == 0xFF) { //free transition - tIndex = i; tProgression = 0xFFFF; - } - } - - if (tIndex == 0xFF) { //no slot found yet - for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) { - //find most progressed transition to overwrite - uint16_t prog = instance->transitions[i].progress(); - if (prog > tProgression) { - tIndex = i; tProgression = prog; - } - } - } - - ColorTransition& t = instance->transitions[tIndex]; - if (t.segment == s) //this is an active transition on the same segment+color - { - bool wasTurningOff = (oldBri == 0); - t.briOld = t.currentBri(wasTurningOff, slot); - t.colorOld = t.currentColor(oldCol); - } else { - t.briOld = oldBri; - t.colorOld = oldCol; - uint8_t prevSeg = t.segment & 0x3F; - if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false); - } - t.transitionDur = dur; - t.transitionStart = millis(); - t.segment = s; - instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true); - //refresh immediately, required for Solid mode - if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart; - } - uint16_t progress(bool allowEnd = false) { //transition progression between 0-65535 - uint32_t timeNow = millis(); - if (timeNow - transitionStart > transitionDur) { - if (allowEnd) { - uint8_t segn = segment & 0x3F; - if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false); - segment = 0xFF; - } - return 0xFFFF; - } - uint32_t elapsed = timeNow - transitionStart; - uint32_t prog = elapsed * 0xFFFF / transitionDur; - return (prog > 0xFFFF) ? 0xFFFF : prog; - } - uint32_t currentColor(uint32_t colorNew) { - return instance->color_blend(colorOld, colorNew, progress(true), true); - } - uint8_t currentBri(bool turningOff = false, uint8_t slot = 0) { - uint8_t segn = segment & 0x3F; - if (segn >= MAX_NUM_SEGMENTS) return 0; - uint8_t briNew = instance->_segments[segn].opacity; - if (slot == 0) { - if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0; - } else { //transition slot 1 brightness for CCT transition - briNew = instance->_segments[segn].cct; - } - uint32_t prog = progress() + 1; - return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16; - } - } color_transition; - - WS2812FX() { + WS2812FX() : + paletteFade(0), + paletteBlend(0), + milliampsPerLed(55), + cctBlending(0), + ablMilliampsMax(ABL_MILLIAMPS_DEFAULT), + currentMilliamps(0), + now(millis()), + timebase(0), + isMatrix(false), +#ifndef WLED_DISABLE_2D + panels(1), +#endif + // semi-private (just obscured) used in effect functions through macros + _currentPalette(CRGBPalette16(CRGB::Black)), + _colors_t{0,0,0}, + _virtualSegmentLength(0), + // true private variables + _length(DEFAULT_LED_COUNT), + _brightness(DEFAULT_BRIGHTNESS), + _transitionDur(750), + _targetFps(WLED_FPS), + _frametime(FRAMETIME_FIXED), + _cumulativeFps(2), + _isServicing(false), + _isOffRefreshRequired(false), + _hasWhiteChannel(false), + _triggered(false), + _modeCount(MODE_COUNT), + _callback(nullptr), + customMappingTable(nullptr), + customMappingSize(0), + _lastShow(0), + _segment_index(0), + _mainSegment(0), + _queuedChangesSegId(255), + _qStart(0), + _qStop(0), + _qStartY(0), + _qStopY(0), + _qGrouping(0), + _qSpacing(0), + _qOffset(0) + { WS2812FX::instance = this; - //assign each member of the _mode[] array to its respective function reference - _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; - _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; - _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; - _mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random; - _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; - _mode[FX_MODE_COLOR_SWEEP] = &WS2812FX::mode_color_sweep; - _mode[FX_MODE_DYNAMIC] = &WS2812FX::mode_dynamic; - _mode[FX_MODE_RAINBOW] = &WS2812FX::mode_rainbow; - _mode[FX_MODE_RAINBOW_CYCLE] = &WS2812FX::mode_rainbow_cycle; - _mode[FX_MODE_SCAN] = &WS2812FX::mode_scan; - _mode[FX_MODE_DUAL_SCAN] = &WS2812FX::mode_dual_scan; - _mode[FX_MODE_FADE] = &WS2812FX::mode_fade; - _mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase; - _mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow; - _mode[FX_MODE_SAW] = &WS2812FX::mode_saw; - _mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle; - _mode[FX_MODE_DISSOLVE] = &WS2812FX::mode_dissolve; - _mode[FX_MODE_DISSOLVE_RANDOM] = &WS2812FX::mode_dissolve_random; - _mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle; - _mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle; - _mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle; - _mode[FX_MODE_STROBE] = &WS2812FX::mode_strobe; - _mode[FX_MODE_STROBE_RAINBOW] = &WS2812FX::mode_strobe_rainbow; - _mode[FX_MODE_MULTI_STROBE] = &WS2812FX::mode_multi_strobe; - _mode[FX_MODE_BLINK_RAINBOW] = &WS2812FX::mode_blink_rainbow; - _mode[FX_MODE_ANDROID] = &WS2812FX::mode_android; - _mode[FX_MODE_CHASE_COLOR] = &WS2812FX::mode_chase_color; - _mode[FX_MODE_CHASE_RANDOM] = &WS2812FX::mode_chase_random; - _mode[FX_MODE_CHASE_RAINBOW] = &WS2812FX::mode_chase_rainbow; - _mode[FX_MODE_CHASE_FLASH] = &WS2812FX::mode_chase_flash; - _mode[FX_MODE_CHASE_FLASH_RANDOM] = &WS2812FX::mode_chase_flash_random; - _mode[FX_MODE_CHASE_RAINBOW_WHITE] = &WS2812FX::mode_chase_rainbow_white; - _mode[FX_MODE_COLORFUL] = &WS2812FX::mode_colorful; - _mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light; - _mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random; - _mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color; - _mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora; - _mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random; - _mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner; - _mode[FX_MODE_COMET] = &WS2812FX::mode_comet; - _mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks; - _mode[FX_MODE_RAIN] = &WS2812FX::mode_rain; - _mode[FX_MODE_TETRIX] = &WS2812FX::mode_tetrix; - _mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; - _mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient; - _mode[FX_MODE_LOADING] = &WS2812FX::mode_loading; - _mode[FX_MODE_POLICE] = &WS2812FX::mode_police; - _mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy; - _mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots; - _mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle; - _mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual; - _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; - _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; - _mode[FX_MODE_TRICOLOR_WIPE] = &WS2812FX::mode_tricolor_wipe; - _mode[FX_MODE_TRICOLOR_FADE] = &WS2812FX::mode_tricolor_fade; - _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; - _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; - _mode[FX_MODE_LIGHTNING] = &WS2812FX::mode_lightning; - _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; - _mode[FX_MODE_MULTI_COMET] = &WS2812FX::mode_multi_comet; - _mode[FX_MODE_DUAL_LARSON_SCANNER] = &WS2812FX::mode_dual_larson_scanner; - _mode[FX_MODE_RANDOM_CHASE] = &WS2812FX::mode_random_chase; - _mode[FX_MODE_OSCILLATE] = &WS2812FX::mode_oscillate; - _mode[FX_MODE_FIRE_2012] = &WS2812FX::mode_fire_2012; - _mode[FX_MODE_PRIDE_2015] = &WS2812FX::mode_pride_2015; - _mode[FX_MODE_BPM] = &WS2812FX::mode_bpm; - _mode[FX_MODE_JUGGLE] = &WS2812FX::mode_juggle; - _mode[FX_MODE_PALETTE] = &WS2812FX::mode_palette; - _mode[FX_MODE_COLORWAVES] = &WS2812FX::mode_colorwaves; - _mode[FX_MODE_FILLNOISE8] = &WS2812FX::mode_fillnoise8; - _mode[FX_MODE_NOISE16_1] = &WS2812FX::mode_noise16_1; - _mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2; - _mode[FX_MODE_NOISE16_3] = &WS2812FX::mode_noise16_3; - _mode[FX_MODE_NOISE16_4] = &WS2812FX::mode_noise16_4; - _mode[FX_MODE_COLORTWINKLE] = &WS2812FX::mode_colortwinkle; - _mode[FX_MODE_LAKE] = &WS2812FX::mode_lake; - _mode[FX_MODE_METEOR] = &WS2812FX::mode_meteor; - _mode[FX_MODE_METEOR_SMOOTH] = &WS2812FX::mode_meteor_smooth; - _mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway; - _mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple; - _mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes; - _mode[FX_MODE_STATIC_PATTERN] = &WS2812FX::mode_static_pattern; - _mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern; - _mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots; - _mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade; - _mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter; - _mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle; - _mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst; - _mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks; - _mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls; - _mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon; - _mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual; - _mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow; - _mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn; - _mode[FX_MODE_DRIP] = &WS2812FX::mode_drip; - _mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma; - _mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent; - _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; - _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; - _mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica; - _mode[FX_MODE_CANDLE_MULTI] = &WS2812FX::mode_candle_multi; - _mode[FX_MODE_SOLID_GLITTER] = &WS2812FX::mode_solid_glitter; - _mode[FX_MODE_SUNRISE] = &WS2812FX::mode_sunrise; - _mode[FX_MODE_PHASED] = &WS2812FX::mode_phased; - _mode[FX_MODE_TWINKLEUP] = &WS2812FX::mode_twinkleup; - _mode[FX_MODE_NOISEPAL] = &WS2812FX::mode_noisepal; - _mode[FX_MODE_SINEWAVE] = &WS2812FX::mode_sinewave; - _mode[FX_MODE_PHASEDNOISE] = &WS2812FX::mode_phased_noise; - _mode[FX_MODE_FLOW] = &WS2812FX::mode_flow; - _mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun; - _mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows; - _mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine; - _mode[FX_MODE_CANDY_CANE] = &WS2812FX::mode_candy_cane; - _mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends; - _mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator; - _mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth; - - -// Technical notes -// =============== -// Defines which effects are incuded in the build. Currently only WLEDSR_LARGE used. -// It is possible to just comment any effect you don't like, but it is recommended to use the ifdefs. -// Note that this is the only place to activate or de-activate effects. No need to (un)comment anything else then - // _mode[] = &WS2812FX::mode_; -// in this version, commented effects will show in the UI but will not be executed / compiled, -// instead of that, the blink effect will be shown. This is done in WS2812FX::service() in fx_fcn.cpp -// to do: find out if the effect list can only be loaded with effects which are uncommented here. -// Note: as in this version, numbering is made future prove (e.g. range for AC, range for SR, fixed numbering). -// If you have old presets, they need to be converted to the new numbering. -// Harry Baas made a VB script to convert them. -// Note: If all SR effects are disabled: 2.5% of 1.310.720 bytes = 32.768 bytes are saved. -// Note: the order of numbering is based on the order in which effects are added, so newer effects get higher numbers - -#define WLEDSR_SMALL 1 -#define WLEDSR_MEDIUM 1 -#define WLEDSR_LARGE 1 - -//WLED AC effects disabled by WLEDSR setting -#ifdef WLEDSR_LARGE - _mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox; - _mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat; -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Start of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - _mode[FX_MODE_PIXELS] = &WS2812FX::mode_pixels; - _mode[FX_MODE_PIXELWAVE] = &WS2812FX::mode_pixelwave; - _mode[FX_MODE_JUGGLES] = &WS2812FX::mode_juggles; - _mode[FX_MODE_MATRIPIX] = &WS2812FX::mode_matripix; - _mode[FX_MODE_GRAVIMETER] = &WS2812FX::mode_gravimeter; - _mode[FX_MODE_PLASMOID] = &WS2812FX::mode_plasmoid; - _mode[FX_MODE_PUDDLES] = &WS2812FX::mode_puddles; - _mode[FX_MODE_MIDNOISE] = &WS2812FX::mode_midnoise; - _mode[FX_MODE_NOISEMETER] = &WS2812FX::mode_noisemeter; - _mode[FX_MODE_FREQWAVE] = &WS2812FX::mode_freqwave; - _mode[FX_MODE_FREQMATRIX] = &WS2812FX::mode_freqmatrix; - _mode[FX_MODE_2DGEQ] = &WS2812FX::mode_2DGEQ; - _mode[FX_MODE_WATERFALL] = &WS2812FX::mode_waterfall; - _mode[FX_MODE_FREQPIXELS] = &WS2812FX::mode_freqpixels; - _mode[FX_MODE_BINMAP] = &WS2812FX::mode_binmap; - _mode[FX_MODE_NOISEFIRE] = &WS2812FX::mode_noisefire; - _mode[FX_MODE_PUDDLEPEAK] = &WS2812FX::mode_puddlepeak; - _mode[FX_MODE_NOISEMOVE] = &WS2812FX::mode_noisemove; - _mode[FX_MODE_2DNOISE] = &WS2812FX::mode_2Dnoise; - _mode[FX_MODE_PERLINMOVE] = &WS2812FX::mode_perlinmove; - _mode[FX_MODE_RIPPLEPEAK] = &WS2812FX::mode_ripplepeak; - _mode[FX_MODE_2DFIRENOISE] = &WS2812FX::mode_2Dfirenoise; - _mode[FX_MODE_2DSQUAREDSWIRL] = &WS2812FX::mode_2Dsquaredswirl; - _mode[FX_MODE_2DFIRE2012] = &WS2812FX::mode_2Dfire2012; - _mode[FX_MODE_2DDNA] = &WS2812FX::mode_2Ddna; - _mode[FX_MODE_2DMATRIX] = &WS2812FX::mode_2Dmatrix; - _mode[FX_MODE_2DMETABALLS] = &WS2812FX::mode_2Dmetaballs; - _mode[FX_MODE_FREQMAP] = &WS2812FX::mode_freqmap; - _mode[FX_MODE_GRAVCENTER] = &WS2812FX::mode_gravcenter; - _mode[FX_MODE_GRAVCENTRIC] = &WS2812FX::mode_gravcentric; - _mode[FX_MODE_GRAVFREQ] = &WS2812FX::mode_gravfreq; - _mode[FX_MODE_DJLIGHT] = &WS2812FX::mode_DJLight; - _mode[FX_MODE_2DFUNKYPLANK] = &WS2812FX::mode_2DFunkyPlank; - _mode[FX_MODE_2DCENTERBARS] = &WS2812FX::mode_2DCenterBars; - _mode[FX_MODE_2DPULSER] = &WS2812FX::mode_2DPulser; - _mode[FX_MODE_BLURZ] = &WS2812FX::mode_blurz; - _mode[FX_MODE_2DSUNRADIATION] = &WS2812FX::mode_2DSunradiation; - _mode[FX_MODE_2DWAVERLY] = &WS2812FX::mode_2DWaverly; - _mode[FX_MODE_2DDRIFT] = &WS2812FX::mode_2DDrift; - _mode[FX_MODE_2DCOLOREDBURSTS] = &WS2812FX::mode_2DColoredBursts; - _mode[FX_MODE_2DTARTAN] = &WS2812FX::mode_2Dtartan; - _mode[FX_MODE_2DPOLARLIGHTS] = &WS2812FX::mode_2DPolarLights; - _mode[FX_MODE_2DSWIRL] = &WS2812FX::mode_2DSwirl; - _mode[FX_MODE_2DLISSAJOUS] = &WS2812FX::mode_2DLissajous; - _mode[FX_MODE_2DFRIZZLES] = &WS2812FX::mode_2DFrizzles; - _mode[FX_MODE_2DPLASMABALL] = &WS2812FX::mode_2DPlasmaball; - _mode[FX_MODE_FLOWSTRIPE] = &WS2812FX::mode_FlowStripe; - _mode[FX_MODE_2DHIPHOTIC] = &WS2812FX::mode_2DHiphotic; - _mode[FX_MODE_2DSINDOTS] = &WS2812FX::mode_2DSindots; - _mode[FX_MODE_2DDNASPIRAL] = &WS2812FX::mode_2DDNASpiral; - _mode[FX_MODE_2DBLACKHOLE] = &WS2812FX::mode_2DBlackHole; - _mode[FX_MODE_WAVESINS] = &WS2812FX::mode_wavesins; - _mode[FX_MODE_ROCKTAVES] = &WS2812FX::mode_rocktaves; - _mode[FX_MODE_2DAKEMI] = &WS2812FX::mode_2DAkemi; - _mode[FX_MODE_CUSTOMEFFECT] = &WS2812FX::mode_customEffect; //WLEDSR Custom Effects - _mode[FX_MODE_3DRIPPLES] = &WS2812FX::mode_3DRipples; - _mode[FX_MODE_3DSphereMove] = &WS2812FX::mode_3DSphereMove; - _mode[FX_MODE_JBL] = &WS2812FX::mode_jbl; - -#ifdef WLEDSR_LARGE - // _mode[FX_MODE_2DPOOLNOISE] = &WS2812FX::mode_2DPoolnoise; //code not in fx.cpp - // _mode[FX_MODE_2DTWISTER] = &WS2812FX::mode_2DTwiser; //code not in fx.cpp - // _mode[FX_MODE_2DCAElementary] = &WS2812FX::mode_2DCAElementary; //code not in fx.cpp - - _mode[FX_MODE_2DJULIA] = &WS2812FX::mode_2DJulia; - _mode[FX_MODE_2DGAMEOFLIFE] = &WS2812FX::mode_2Dgameoflife; -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// End of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - - _brightness = DEFAULT_BRIGHTNESS; - currentPalette = CRGBPalette16(CRGB::Black); - targetPalette = CloudColors_p; - ablMilliampsMax = ABL_MILLIAMPS_DEFAULT; - currentMilliamps = 0; - timebase = 0; - resetSegments(); + _mode.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) + _modeData.reserve(_modeCount); // allocate memory to prevent initial fragmentation (does not increase size()) + if (_mode.capacity() <= 1 || _modeData.capacity() <= 1) _modeCount = 1; // memory allocation failed only show Solid + else setupEffectData(); } + ~WS2812FX() { + if (customMappingTable) delete[] customMappingTable; + _mode.clear(); + _modeData.clear(); + _segments.clear(); +#ifndef WLED_DISABLE_2D + panel.clear(); +#endif + customPalettes.clear(); + } + + static WS2812FX* getInstance(void) { return instance; } + void +#ifdef WLED_DEBUG + printSize(), +#endif finalizeInit(), service(void), - blur(uint8_t), - fill(uint32_t), - fade_out(uint8_t r), setMode(uint8_t segid, uint8_t m), - setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setColor(uint8_t slot, uint32_t c), setCCT(uint16_t k), setBrightness(uint8_t b, bool direct = false), setRange(uint16_t i, uint16_t i2, uint32_t col), - setShowCallback(show_callback cb), - setTransition(uint16_t t), setTransitionMode(bool t), - calcGammaTable(float), - trigger(void), - setReset(uint8_t n), - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX), + purgeSegments(bool force = false), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(uint8_t n), restartRuntime(), resetSegments(), makeAutoSegments(bool forceReset = false), fixInvalidSegments(), - setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), + setPixelColor(int n, uint32_t c), show(void), - setTargetFps(uint8_t fps), - deserializeMap(uint8_t n=0); + setTargetFps(uint8_t fps); - inline void setPixelColor(int n, uint32_t c) {setPixelColor(n, byte(c>>16), byte(c>>8), byte(c), byte(c>>24));} + void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } + void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) + void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp + void setupEffectData(void); // add default effects to the list; defined in FX.cpp + + // outsmart the compiler :) by correctly overloading + inline void setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } + inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. + inline void setShowCallback(show_callback cb) { _callback = cb; } + inline void setTransition(uint16_t t) { _transitionDur = t; } + inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); } bool - gammaCorrectBri = false, - gammaCorrectCol = true, - // REMOVED IN 7b969bb - // applyToAllSelected = true, - // setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t p), //WLEDSR: add c1,c2,c3 checkSegmentAlignment(void), hasRGBWBus(void), hasCCTBus(void), // return true if the strip is being sent pixel updates - isUpdating(void); + isUpdating(void), + deserializeMap(uint8_t n=0); + + inline bool isServicing(void) { return _isServicing; } + inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} + inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} uint8_t - paletteFade = 0, - paletteBlend = 0, - milliampsPerLed = 55, - autoWhiteMode = RGBW_MODE_DUAL, - cctBlending = 0, - getBrightness(void), - getModeCount(void), - getPaletteCount(void), - getMaxSegments(void), + paletteFade, + paletteBlend, + milliampsPerLed, + cctBlending, getActiveSegmentsNum(void), getFirstSelectedSegId(void), - getMainSegmentId(void), getLastActiveSegmentId(void), - getTargetFps(void), - setPixelSegment(uint8_t n), - gamma8(uint8_t), - gamma8_cal(uint8_t, float), - get_random_wheel_index(uint8_t); + getActiveSegsLightCapabilities(bool selectedOnly = false), + setPixelSegment(uint8_t n); - inline uint8_t sin_gap(uint16_t in) { - if (in & 0x100) return 0; - return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0 - } - - int8_t - tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec); + inline uint8_t getBrightness(void) { return _brightness; } + inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments + inline uint8_t getCurrSegmentId(void) { return _segment_index; } + inline uint8_t getMainSegmentId(void) { return _mainSegment; } + inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count + inline uint8_t getTargetFps() { return _targetFps; } + inline uint8_t getModeCount() { return _modeCount; } uint16_t ablMilliampsMax, currentMilliamps, - triwave16(uint16_t), - getLengthTotal(void), getLengthPhysical(void), - getFps(), - getMinShowDelay(); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC + getLengthTotal(void), // will include virtual/nonexistent pixels in matrix + getFps(); + + inline uint16_t getFrameTime(void) { return _frametime; } + inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } + inline uint16_t getLength(void) { return _length; } // 2D matrix may have less pixels than W*H + inline uint16_t getTransition(void) { return _transitionDur; } uint32_t now, timebase, - color_wheel(uint8_t), - color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255), - color_blend(uint32_t,uint32_t,uint16_t,bool b16=false), - currentColor(uint32_t colorNew, uint8_t tNr), - gamma32(uint32_t), - getLastShow(void), getPixelColor(uint16_t); - WS2812FX::Segment - &getSegment(uint8_t n), - &getFirstSelectedSeg(void), - &getMainSegment(void); + inline uint32_t getLastShow(void) { return _lastShow; } + inline uint32_t segColor(uint8_t i) { return _colors_t[i]; } - WS2812FX::Segment* - getSegments(void); + const char * + getModeData(uint8_t id = 0) { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } - // builtin modes - uint16_t - mode_static(void), - mode_blink(void), - mode_blink_rainbow(void), - mode_strobe(void), - mode_strobe_rainbow(void), - mode_color_wipe(void), - mode_color_sweep(void), - mode_color_wipe_random(void), - mode_color_sweep_random(void), - mode_random_color(void), - mode_dynamic(void), - mode_breath(void), - mode_fade(void), - mode_scan(void), - mode_dual_scan(void), - mode_theater_chase(void), - mode_theater_chase_rainbow(void), - mode_rainbow(void), - mode_rainbow_cycle(void), - mode_running_lights(void), - mode_saw(void), - mode_twinkle(void), - mode_dissolve(void), - mode_dissolve_random(void), - mode_sparkle(void), - mode_flash_sparkle(void), - mode_hyper_sparkle(void), - mode_multi_strobe(void), - mode_android(void), - mode_chase_color(void), - mode_chase_random(void), - mode_chase_rainbow(void), - mode_chase_flash(void), - mode_chase_flash_random(void), - mode_chase_rainbow_white(void), - mode_colorful(void), - mode_traffic_light(void), - mode_running_color(void), - mode_aurora(void), - mode_running_random(void), - mode_larson_scanner(void), - mode_comet(void), - mode_fireworks(void), - mode_rain(void), - mode_tetrix(void), - mode_halloween(void), - mode_fire_flicker(void), - mode_gradient(void), - mode_loading(void), - mode_police(void), - mode_fairy(void), - mode_two_dots(void), - mode_fairytwinkle(void), - mode_running_dual(void), - mode_bicolor_chase(void), - mode_tricolor_chase(void), - mode_tricolor_wipe(void), - mode_tricolor_fade(void), - mode_lightning(void), - mode_icu(void), - mode_multi_comet(void), - mode_dual_larson_scanner(void), - mode_random_chase(void), - mode_oscillate(void), - mode_fire_2012(void), - mode_pride_2015(void), - mode_bpm(void), - mode_juggle(void), - mode_palette(void), - mode_colorwaves(void), - mode_fillnoise8(void), - mode_noise16_1(void), - mode_noise16_2(void), - mode_noise16_3(void), - mode_noise16_4(void), - mode_colortwinkle(void), - mode_lake(void), - mode_meteor(void), - mode_meteor_smooth(void), - mode_railway(void), - mode_ripple(void), - mode_twinklefox(void), - mode_twinklecat(void), - mode_halloween_eyes(void), - mode_static_pattern(void), - mode_tri_static_pattern(void), - mode_spots(void), - mode_spots_fade(void), - mode_glitter(void), - mode_candle(void), - mode_starburst(void), - mode_exploding_fireworks(void), - mode_bouncing_balls(void), - mode_sinelon(void), - mode_sinelon_dual(void), - mode_sinelon_rainbow(void), - mode_popcorn(void), - mode_drip(void), - mode_plasma(void), - mode_percent(void), - mode_ripple_rainbow(void), - mode_heartbeat(void), - mode_pacifica(void), - mode_candle_multi(void), - mode_solid_glitter(void), - mode_sunrise(void), - mode_phased(void), - mode_twinkleup(void), - mode_noisepal(void), - mode_sinewave(void), - mode_phased_noise(void), - mode_flow(void), - mode_chunchun(void), - mode_dancing_shadows(void), - mode_washing_machine(void), - mode_candy_cane(void), - mode_blends(void), - mode_tv_simulator(void), - mode_dynamic_smooth(void); + const char ** + getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Start of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Segment& getSegment(uint8_t id); + inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } + inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } + inline Segment* getSegments(void) { return &(_segments[0]); } + + // 2D support (panels) + bool + isMatrix; + +#ifndef WLED_DISABLE_2D + #define WLED_MAX_PANELS 64 + uint8_t + panels; + + typedef struct panel_t { + uint16_t xOffset; // x offset relative to the top left of matrix in LEDs + uint16_t yOffset; // y offset relative to the top left of matrix in LEDs + uint8_t width; // width of the panel + uint8_t height; // height of the panel + union { + uint8_t options; + struct { + bool bottomStart : 1; // starts at bottom? + bool rightStart : 1; // starts on right? + bool vertical : 1; // is vertical? + bool serpentine : 1; // is serpentine? + }; + }; + panel_t() + : xOffset(0) + , yOffset(0) + , width(8) + , height(8) + , options(0) + {} + } Panel; + std::vector panel; +#endif void - fade2black(uint8_t r), - set2DSegment(uint8_t n), - setStripOrPanelWidthAndHeight(), - noise8_help(uint8_t), - mapNoiseToLEDsUsingPalette(), - blur1d( CRGB* leds, fract8 blur_amount), - blur2d( CRGB* leds, fract8 blur_amount), - blurRows( CRGB* leds, fract8 blur_amount), - blurColumns(CRGB* leds, fract8 blur_amount), - fill_solid( struct CRGB * leds, const struct CRGB& color), - fadeToBlackBy( CRGB* leds, uint8_t fadeBy), - nscale8( CRGB* leds, uint8_t scale), - setPixels(CRGB* leds); + setUpMatrix(), + setPixelColorXY(int x, int y, uint32_t c); - bool - segmentsAreIdentical(Segment* a, Segment* b); + // outsmart the compiler :) by correctly overloading + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - uint16_t - XY(uint16_t, uint16_t), // ewowi20210624: new XY: segmentToLogical: Maps XY in 2D segment to logical index. Works for 1D strips and 2D panels - logicalToPhysical(uint16_t); // ewowi20210624: previous XY. Maps logical led index to physical led index. Works for 1D strips and 2D panels + uint32_t + getPixelColorXY(uint16_t, uint16_t); - uint16_t - matrixWidth = 30, - matrixHeight = 1; + // end 2D support - uint8_t - matrixPanels, - matrixHorizontalPanels = 1, - matrixVerticalPanels = 1, - stripOrMatrixPanel = 0; + void loadCustomPalettes(void); // loads custom palettes from JSON + CRGBPalette16 _currentPalette; // palette used for current effect (includes transition) + std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class - uint8_t - panelFirstLedTopBottom, - panelFirstLedLeftRight, - panelOrientationHorVert, - panelSerpentine, - panelTranspose; - - uint16_t - mode_pixels(void), - mode_pixelwave(void), - mode_juggles(void), - mode_matripix(void), - mode_gravimeter(void), - mode_plasmoid(void), - mode_puddles(void), - mode_midnoise(void), - mode_noisemeter(void), - mode_freqwave(void), - mode_freqmatrix(void), - mode_2DGEQ(void), - mode_waterfall(void), - mode_freqpixels(void), - mode_binmap(void), - mode_noisefire(void), - mode_puddlepeak(void), - mode_noisemove(void), - mode_2Dnoise(void), - mode_perlinmove(void), - mode_ripplepeak(void), - mode_2Dfirenoise(void), - mode_2Dsquaredswirl(void), - mode_2Dfire2012(void), - mode_2Ddna(void), - mode_2Dmatrix(void), - mode_2Dmetaballs(void), - mode_freqmap(void), - mode_gravcenter(void), - mode_gravcentric(void), - mode_gravfreq(void), - mode_DJLight(void), - mode_2DFunkyPlank(void), - mode_2DCenterBars(void), - mode_2DPulser(void), - mode_blurz(void), - mode_2Dgameoflife(void), - mode_2Dtartan(void), - mode_2DPolarLights(void), - mode_2DSwirl(void), - mode_2DLissajous(void), - mode_2DFrizzles(void), - mode_2DPlasmaball(void), - mode_FlowStripe(void), - mode_2DHiphotic(void), - mode_2DSindots(void), - mode_2DDNASpiral(void), - mode_2DBlackHole(void), - mode_wavesins(void), - mode_rocktaves(void), - mode_2DAkemi(void), - mode_2DSunradiation(void), - mode_2DWaverly(void), - mode_2DDrift(void), - mode_2DColoredBursts(void), - mode_2DJulia(void), - mode_customEffect(void), //WLEDSR Custom Effects - mode_3DRipples(void), - mode_jbl(void), - mode_3DSphereMove(void); - // mode_2DPoolnoise(void), - // mode_2DTwister(void); - // mode_2DCAElementary(void); - - uint16_t - GEQ_base(bool, bool, bool); //private??? - - uint16_t - _lengthRaw; //private? not in AC (anymore) - - bool - _skipFirstMode; //private? not in AC (anymore) - - //WLEDSR Custom Effects - float arti_external_function(uint8_t function, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull, float par4 = floatNull, float par5 = floatNull); - float arti_get_external_variable(uint8_t variable, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull); - void arti_set_external_variable(float value, uint8_t variable, float par1 = floatNull, float par2 = floatNull, float par3 = floatNull); - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// End of Audio Reactive fork (WLEDSR) // -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) + // and color transitions + uint32_t _colors_t[3]; // color used for effect (includes transition) + uint16_t _virtualSegmentLength; + std::vector _segments; + friend class Segment; private: - uint32_t crgb_to_col(CRGB fastled); - CRGB col_to_crgb(uint32_t); - CRGBPalette16 currentPalette; - CRGBPalette16 targetPalette; + uint16_t _length; + uint8_t _brightness; + uint16_t _transitionDur; - uint16_t _length, _virtualSegmentLength; - uint16_t _rand16seed; - uint8_t _brightness; - uint16_t _usedSegmentData = 0; - uint16_t _transitionDur = 750; + uint8_t _targetFps; + uint16_t _frametime; + uint16_t _cumulativeFps; - uint8_t _targetFps = 42; - uint16_t _frametime = (1000/42); - uint16_t _cumulativeFps = 2; + // will require only 1 byte + struct { + bool _isServicing : 1; + bool _isOffRefreshRequired : 1; //periodic refresh is required for the strip to remain off. + bool _hasWhiteChannel : 1; + bool _triggered : 1; + }; - bool - _isOffRefreshRequired = false, //periodic refresh is required for the strip to remain off. - _hasWhiteChannel = false, - _triggered; + uint8_t _modeCount; + std::vector _mode; // SRAM footprint: 4 bytes per element + std::vector _modeData; // mode (effect) name and its slider control data array - mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + show_callback _callback; - show_callback _callback = nullptr; + uint16_t* customMappingTable; + uint16_t customMappingSize; - // mode helper functions - uint16_t - blink(uint32_t, uint32_t, bool strobe, bool), - candle(bool), - color_wipe(bool, bool), - dynamic(bool), - scan(bool), - running_base(bool,bool), - larson_scanner(bool), - sinelon_base(bool,bool), - dissolve(uint32_t), - chase(uint32_t, uint32_t, uint32_t, bool), - gradient_base(bool), - ripple_base(bool), - police_base(uint32_t, uint32_t), - running(uint32_t, uint32_t, bool theatre=false), - tricolor_chase(uint32_t, uint32_t), - twinklefox_base(bool), - spots_base(uint16_t), - phased_base(uint8_t); + unsigned long _lastShow; - CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat); - CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff); + uint8_t _segment_index; + uint8_t _mainSegment; + uint8_t _queuedChangesSegId; + uint16_t _qStart, _qStop, _qStartY, _qStopY; + uint8_t _qGrouping, _qSpacing; + uint16_t _qOffset; + + uint8_t + estimateCurrentAndLimitBri(void); void - blendPixelColor(uint16_t n, uint32_t color, uint8_t blend), - startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot), - estimateCurrentAndLimitBri(void), - load_gradient_palette(uint8_t), - handle_palette(void); - - uint16_t* customMappingTable = nullptr; - uint16_t customMappingSize = 0; - - uint32_t _lastPaletteChange = 0; - uint32_t _lastShow = 0; - - uint32_t _colors_t[3]; - uint8_t _bri_t; - - bool _no_rgb = false; - - uint8_t _segment_index = 0; - uint8_t _segment_index_palette_last = 99; - uint8_t _mainSegment; - - segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 27 bytes per element - //WLEDSR: add f1,2,3 - // start, stop, offset, speed, intensity, custom1, custom2, custom3, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities - {0, 7, 0, DEFAULT_SPEED, DEFAULT_INTENSITY, DEFAULT_Custom1, DEFAULT_Custom2, DEFAULT_Custom3, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0} - }; - segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element - friend class Segment_runtime; - - ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element - friend class ColorTransition; - - uint16_t - segmentToLogical(uint16_t i), - transitionProgress(uint8_t tNr); - public: - inline bool hasWhiteChannel(void) {return _hasWhiteChannel;} - inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} + setUpSegmentFromQueuedChanges(void); }; -//10 names per line -// WLEDSR: second part -// Technical notes -// =============== -// If an effect name is followed by an @, slider and color control is effective. -// See setEffectParameters in index.js for implementation -// If not effective then: -// - For AC effects (id<128) 2 sliders and 3 colors and the palette will be shown -// - For SR effects (id>128) 5 sliders and 3 colors and the palette will be shown -// If effective (@) -// - a ; seperates slider controls (left) from color controls (middle) and palette control (right) -// - if left, middle or right is empty no controls are shown -// - a , seperates slider controls (max 5) or color controls (max 3). Palette has only one value -// - a ! means that the default is used. -// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3 -// - Blazoncek default values: e.g. Sensitivity=128 -// - For colors: Fx color, Background color, Custom -// - For palette: prompt Color palette -// -// Note: In this version only SR-effects have been specified. -// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle. -// If a color is specified, the 1,2 or 3 is replaced by that specification. -// Note: Effects can override default pattern behaviour -// - FadeToBlack can override the background setting -// - Defining SEGCOL() can override a specific palette using these values (e.g. Color Gradient) - - -const char JSON_mode_names[] PROGMEM = R"=====([ -"Solid@;!;", -"Blink@!,;!,!,;!", -"Breathe@!,;!,!;!", -"Wipe@!,!;!,!,;!", -"Wipe Random@!,;1,2,3;!", -"Random Colors@!,Fade time;1,2,3;!", -"Sweep@!,!;!,!,;!", -"Dynamic@!,!;1,2,3;!", -"Colorloop@!,Saturation;1,2,3;!", -"Rainbow@!,Size;1,2,3;!", -"Scan@!,# of dots;!,!,;!", -"Scan Dual@!,# of dots;!,!,;!", -"Fade@!,;!,!,;!", -"Theater@!,Gap size;!,!,;!", -"Theater Rainbow@!,Gap size;1,2,3;!", -"Running@!,Wave width;!,!,;!", -"Saw@!,Width;!,!,;!", -"Twinkle@!,;!,!,;!", -"Dissolve@Repeat speed,Dissolve speed;!,!,;!", -"Dissolve Rnd@Repeat speed,Dissolve speed;,!,;!", -"Sparkle@!,;!,!,;!", -"Sparkle Dark@!,!;Bg,Fx,;!", -"Sparkle+@!,!;Bg,Fx,;!", -"Strobe@!,;!,!,;!", -"Strobe Rainbow@!,;,!,;!", -"Strobe Mega@!,!;!,!,;!", -"Blink Rainbow@Frequency,Blink duration;!,!,;!", -"Android@!,Width;!,!,;!", -"Chase@!,Width;!,!,!;!", -"Chase Random@!,Width;!,,!;!", -"Chase Rainbow@!,Width;!,!,;0", -"Chase Flash@!,;Bg,Fx,!;!", -"Chase Flash Rnd@!,;,Fx,;!", -"Rainbow Runner@!,Size;Bg,,;!", -"Colorful@!,Saturation;1,2,3;!", -"Traffic Light@!,;,!,;!", -"Sweep Random", -"Chase 2@!,Width;!,!,;!", -"Aurora@!=24,!;1,2,3;!=50", -"Stream", -"Scanner", -"Lighthouse", -"Fireworks@Sharpness=96,Frequency=192;!,2,;!=11", -"Rain@Fade rate=128,Frequency=128;!,2,;!", -"Tetrix@!=224,Width=0;!,!,;!=11", -"Fire Flicker@!,!;!,,;!", -"Gradient@!,Spread=16;!,!,;!", -"Loading@!,Fade=16;!,!,;!", -"Police@!,Width;,Bg,;0", -"Fairy", -"Two Dots@!,Dot size;1,2,Bg;!", -"Fairy Twinkle", -"Running Dual", -"Halloween", -"Tri Chase@!,Size;1,2,3;0", -"Tri Wipe@!,Width;1,2,3;0", -"Tri Fade", -"Lightning", -"ICU", -"Multi Comet", -"Scanner Dual", -"Stream 2", -"Oscillate", -"Pride 2015@!,;;", -"Juggle@!=16,Trail=240;!,!,;!", -"Palette@!,;1,2,3;!", -"Fire 2012@Spark rate=120,Decay=64;1,2,3;!", -"Colorwaves", -"Bpm@!=64,;1,2,3;!", -"Fill Noise", -"Noise 1", -"Noise 2", -"Noise 3", -"Noise 4", -"Colortwinkles@Fade speed,Spawn speed;1,2,3;!", -"Lake@!,;1,2,3;!", -"Meteor@!,Trail length;!,!,;!", -"Meteor Smooth@!,Trail length;!,!,;!", -"Railway", -"Ripple", -"Twinklefox", -"Twinklecat", -"Halloween Eyes", -"Solid Pattern@Fg size,Bg size;Fg,Bg,;!=0", -"Solid Pattern Tri@,Size;1,2,3;!=0", -"Spots@Spread,Width;!,!,;!", -"Spots Fade@Spread,Width;!,!,;!", -"Glitter", -"Candle@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Fireworks Starburst", -"Fireworks 1D@Gravity,Firing side;!,!,;!", -"Bouncing Balls@Gravity,# of balls;!,!,;!", -"Sinelon", -"Sinelon Dual", -"Sinelon Rainbow", -"Popcorn", -"Drip@Gravity,# of drips;!,!;!", -"Plasma@Phase,;1,2,3;!", -"Percent@,% of fill;!,!,;!", -"Ripple Rainbow", -"Heartbeat@!,!;!,!,;!", -"Pacifica", -"Candle Multi@Flicker rate=96,Flicker intensity=224;!,!,;0", -"Solid Glitter@,!;!,,;0", -"Sunrise@Time [min]=60,;;0", -"Phased", -"Twinkleup@!,Intensity;!,!,;!", -"Noise Pal", -"Sine", -"Phased Noise", -"Flow", -"Chunchun@!,Gap size;!,!,;!", -"Dancing Shadows@!,# of shadows;!,,;!", -"Washing Machine", -"Candy Cane@!,Width;;", -"Blends@Shift speed,Blend speed;1,2,3,!", -"TV Simulator", -"Dynamic Smooth", -"Reserved0", -"Reserved1", -"Reserved2", -"Reserved3", -"Reserved4", -"Reserved5", -"Reserved6", -"Reserved7", -"Reserved8", -"Reserved9", -" ♪ Pixels@Fade rate,# of pixels;,!;!", -" ♪ Pixelwave@!,Sensitivity=64;!,!;!", -" ♪ Juggles@!,# of balls;,!;!", -" ♪ Matripix@!,Brightness=64;,!;!", -" ♪ Gravimeter@Rate of fall,Sensitivity=128;,!;!", -" ♪ Plasmoid@Phase=128,# of pixels=128;,!;!", -" ♪ Puddles@Fade rate,Puddle size;!,!;!", -" ♪ Midnoise@Fade rate,Maximum length=128;,!;!", -" ♪ Noisemeter@Fade rate,Width=128;!,!;!", -" ♫ Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;", -" ♫ Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;", -" ♫ 2D GEQ@Bar speed,Ripple decay;,,Peak Color;!", -" ♫ Waterfall@!,Adjust color,,Select bin, Volume (minimum);!,!;!", -" ♫ Freqpixels@Fade rate,Starting colour and # of pixels;;", -" ♫ Binmap@;!,!;!", -" ♪ Noisefire@!,!;;", -" ♪ Puddlepeak@Fade rate,Puddle size,,Select bin,Volume (minimum);!,!;!", -" ♫ Noisemove@Speed of perlin movement,Fade rate;,!;!", -"2D Noise@Speed,Scale;;!", -"Perlin Move@!,# of pixels,fade rate;,!;!", -" ♪ Ripple Peak@Fade rate,Max # of ripples,,Select bin,Volume (minimum);!,!;!", -"2D FireNoise@X scale,Y scale;;", -"2D Squared Swirl@,,,,Blur;,,;!", -"2D Fire2012@Speed;;", -"2D DNA@Scroll speed,Blur;;!", -"2D Matrix@Falling speed,Spawning rate,Trail,Custom color ☑;Spawn,Trail;", -"2D Metaballs@;;", -" ♫ Freqmap@Fade rate,Starting color;,!;!", -" ♪ Gravcenter@Rate of fall,Sensitivity=128;,!;!", -" ♪ Gravcentric@Rate of fall,Sensitivity=128;!;!", -" ♫ Gravfreq@Rate of fall,Sensivity=128;,!;!", -" ♫ DJ Light@Speed;;", -" ♫ 2D Funky Plank@Scroll speed,,# of bands;;", -" ♫ 2D CenterBars@Bar speed=250,Ripple decay=250,Center ↔ ☑=192,Center ↕ ☑=192, Color ↕ ☑=192;,,Peak Color;!=11", -"2D Pulser@Speed,Blur;;!", -" ♫ Blurz@Fade rate,Blur amount;,Color mix;!", -"2D Drift@Rotation speed,Blur amount;;!", -" ♪ 2D Waverly@Amplification,Sensitivity=64;;!", -"2D Sun Radiation@Variance,Brightness;;", -"2D Colored Bursts@Speed,Number of lines;;!", -"2D Julia@,Max iterations per pixel,X center,Y center,Area size;;!", -"Reserved for PoolNoise", -"Reserved for Twister", -"Reserved for Elementary", -"2D Game Of Life@!,Use Palette ☑;!,!;!", -"2D Tartan@X scale,Y scale;;!", -"2D Polar Lights@Speed,X scale,Palette;;", -" ♪ 2D Swirl@!,Sensitivity=64,Blur;,Bg Swirl;!", -"2D Lissajous@X frequency,Fadetime;;!", -"2D Frizzles@X frequency,Y frequency;;!", -"2D Plasma Ball@Speed;;!", -"Flow Stripe@Hue speed,Effect speed;;", -"2D Hiphotic@X scale,Y scale;;!", -"2D Sindots@Speed,Dot distance;;!", -"2D DNA Spiral@Speed,Frequency;;!", -"2D Black Hole@Outer X frequency,Inner X frequency,Inner Y frequency;;", -"Wavesins@Speed,Brightness variation,Starting Color,Range of Colors,Color variation;;!", -" ♫ Rocktaves@;,!;!", -" ♫ 2D Akemi@Color speed,Dance ☑;Head palette,Arms & Legs,Eyes & Mouth;Face palette", -" ⚙️ Custom Effect@Speed,Intensity,Custom 1, Custom 2, Custom 3;!;!", -"3D Ripples@Speed=128,Interval=128;!;!", -"3D Sphere Move@Speed=128,Interval=128;!;!", -" ♪ JBL@Sensitivity,Color change speed;!,!;!" -])====="; - -//WLEDSR: second part (not SR specific, but in latest SR, not in AC (Pallettes added in WLEDSR from Retro Clown->END)) -const char JSON_palette_names[] PROGMEM = R"=====([ -"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean", -"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash", -"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", -"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", -"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", -"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf", -"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide", -"Candy2" -])====="; +extern const char JSON_mode_names[]; +extern const char JSON_palette_names[]; #endif