Merge branch 'fx-blending'

This commit is contained in:
Frank 2023-09-05 18:37:55 +02:00
commit ddbe883d47
8 changed files with 427 additions and 262 deletions

View File

@ -604,11 +604,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
* Dissolve function * Dissolve function
*/ */
uint16_t dissolve(uint32_t color) { 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 (int j = 0; j <= SEGLEN / 15; j++) { for (int j = 0; j <= SEGLEN / 15; j++) {
if (random8() <= SEGMENT.intensity) { if (random8() <= SEGMENT.intensity) {
for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times
@ -764,7 +759,7 @@ uint16_t mode_android(void) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); 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 > (SEGMENT.intensity*SEGLEN)/255)
{ {
SEGENV.aux0 = 1; SEGENV.aux0 = 1;
} else } else
@ -814,7 +809,7 @@ static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=
*/ */
uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palette) { 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 counter = strip.now * ((SEGMENT.speed >> 2) + 1);
uint16_t a = counter * SEGLEN >> 16; uint16_t a = (counter * SEGLEN) >> 16;
bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM); bool chase_random = (SEGMENT.mode == FX_MODE_CHASE_RANDOM);
if (chase_random) { if (chase_random) {
@ -828,7 +823,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
SEGENV.step = a; SEGENV.step = a;
// Use intensity setting to vary chase up to 1/2 string length // Use intensity setting to vary chase up to 1/2 string length
uint8_t size = 1 + (SEGMENT.intensity * SEGLEN >> 10); uint8_t size = 1 + ((SEGMENT.intensity * SEGLEN) >> 10);
uint16_t b = a + size; //"trail" of chase, filled with color1 uint16_t b = a + size; //"trail" of chase, filled with color1
if (b > SEGLEN) b -= SEGLEN; if (b > SEGLEN) b -= SEGLEN;
@ -1116,8 +1111,9 @@ static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;
uint16_t larson_scanner(bool dual) { uint16_t larson_scanner(bool dual) {
if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8); uint16_t counter = strip.now * ((SEGMENT.speed >> 2) +8);
uint16_t index = counter * SEGLEN >> 16; uint16_t index = (counter * SEGLEN) >> 16;
SEGMENT.fade_out(SEGMENT.intensity); SEGMENT.fade_out(SEGMENT.intensity);
@ -1204,7 +1200,6 @@ uint16_t mode_fireworks() {
const uint16_t height = SEGMENT.virtualHeight(); const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX; SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX; SEGENV.aux1 = UINT16_MAX;
} }
@ -1305,24 +1300,23 @@ static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;0
* Gradient run base function * Gradient run base function
*/ */
uint16_t gradient_base(bool loading) { uint16_t gradient_base(bool loading) {
if (SEGLEN == 1) return mode_static();
uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1); uint16_t counter = strip.now * ((SEGMENT.speed >> 2) + 1);
uint16_t pp = counter * SEGLEN >> 16; uint16_t pp = (counter * SEGLEN) >> 16;
if (SEGENV.call == 0) pp = 0; if (SEGENV.call == 0) pp = 0;
float val; //0.0 = sec 1.0 = pri int val; //0 = sec 1 = pri
float brd = loading ? SEGMENT.intensity : SEGMENT.intensity/2; int brd = 1 + loading ? SEGMENT.intensity/2 : SEGMENT.intensity/4;
if (brd <1.0) brd = 1.0; //if (brd < 1) brd = 1;
int p1 = pp-SEGLEN; int p1 = pp-SEGLEN;
int p2 = pp+SEGLEN; int p2 = pp+SEGLEN;
for (int i = 0; i < SEGLEN; i++) for (int i = 0; i < SEGLEN; i++) {
{ if (loading) {
if (loading)
{
val = abs(((i>pp) ? p2:pp) - i); val = abs(((i>pp) ? p2:pp) - i);
} else { } else {
val = MIN(abs(pp-i),MIN(abs(p1-i),abs(p2-i))); val = min(abs(pp-i),min(abs(p1-i),abs(p2-i)));
} }
val = (brd > val) ? val/brd * 255 : 255; val = (brd > val) ? (val * 255) / brd : 255;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val)); SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val));
} }
@ -1903,12 +1897,8 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
//eight colored dots, weaving in and out of sync with each other //eight colored dots, weaving in and out of sync with each other
uint16_t mode_juggle(void) { uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static(); if (SEGLEN == 1) return mode_static();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4)); SEGMENT.fadeToBlackBy(192 - (3*SEGMENT.intensity/4));
CRGB fastled_col; CRGB fastled_col;
byte dothue = 0; byte dothue = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
@ -2073,12 +2063,9 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!";
// colored stripes pulsing at a defined Beats-Per-Minute (BPM) // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint16_t mode_bpm() { uint16_t mode_bpm() {
//CRGB fastled_col;
uint32_t stp = (strip.now / 20) & 0xFF; uint32_t stp = (strip.now / 20) & 0xFF;
uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); uint8_t beat = beatsin8(SEGMENT.speed, 64, 255);
for (int i = 0; i < SEGLEN; i++) { 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))); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
} }
@ -2475,12 +2462,6 @@ static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wav
// //
// TwinkleFOX: Twinkling 'holiday' lights that fade in and out. // TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
// Colors are chosen from a palette. Read more about this effect using the link above! // Colors are chosen from a palette. Read more about this effect using the link above!
// If COOL_LIKE_INCANDESCENT is set to 1, colors will
// fade out slighted 'reddened', similar to how
// incandescent bulbs change color as they get dim down.
#define COOL_LIKE_INCANDESCENT 1
CRGB 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) // Overall twinkle speed (changed)
@ -2519,7 +2500,7 @@ CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
CRGB c; CRGB c;
if (bright > 0) { if (bright > 0) {
c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND); c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND);
if(COOL_LIKE_INCANDESCENT == 1) { if (!SEGMENT.check1) {
// This code takes a pixel, and if its in the 'fading down' // 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 // part of the cycle, it adjusts the color a little bit like the
// way that incandescent bulbs fade toward 'red' as they dim. // way that incandescent bulbs fade toward 'red' as they dim.
@ -2605,14 +2586,14 @@ uint16_t mode_twinklefox()
{ {
return twinklefox_base(false); return twinklefox_base(false);
} }
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!"; static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!";
uint16_t mode_twinklecat() uint16_t mode_twinklecat()
{ {
return twinklefox_base(true); return twinklefox_base(true);
} }
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!"; static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!";
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
@ -2930,7 +2911,7 @@ uint16_t mode_glitter()
static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!,,,,,Overlay;1,2,Glitter color;!;;pal=0,m12=0"; //pixels
//Solid colour background with glitter //Solid colour background with glitter (can be replaced by Glitter)
uint16_t mode_solid_glitter() uint16_t mode_solid_glitter()
{ {
SEGMENT.fill(SEGCOLOR(0)); SEGMENT.fill(SEGCOLOR(0));
@ -3020,8 +3001,7 @@ static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!,,,,,Overlay;!,!
uint16_t candle(bool multi) uint16_t candle(bool multi)
{ {
if (multi) if (multi && SEGLEN > 1) {
{
//allocate segment data //allocate segment data
uint16_t dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266) uint16_t dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266)
if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed
@ -3590,8 +3570,8 @@ uint16_t mode_percent(void) {
uint8_t percent = SEGMENT.intensity; uint8_t percent = SEGMENT.intensity;
percent = constrain(percent, 0, 200); percent = constrain(percent, 0, 200);
uint16_t active_leds = (percent < 100) ? SEGLEN * percent / 100.0 uint16_t active_leds = (percent < 100) ? roundf(SEGLEN * percent / 100.0f)
: SEGLEN * (200 - percent) / 100.0; : roundf(SEGLEN * (200 - percent) / 100.0f);
uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11)); uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11));
if (SEGMENT.speed == 255) size = 255; if (SEGMENT.speed == 255) size = 255;
@ -4582,11 +4562,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
uint16_t x, y; uint16_t x, y;
// initialize on first call
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
unsigned long t = millis()/128; // timebase unsigned long t = millis()/128; // timebase
// outer stars // outer stars
@ -4621,7 +4596,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
SEGENV.aux0 = 0; // start with red hue SEGENV.aux0 = 0; // start with red hue
} }
@ -4673,12 +4647,7 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(64); SEGMENT.fadeToBlackBy(64);
for (int i = 0; i < cols; i++) { 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 ), 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.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));
@ -4744,12 +4713,7 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(128); SEGMENT.fadeToBlackBy(128);
const uint16_t maxDim = MAX(cols, rows)/2; const uint16_t maxDim = MAX(cols, rows)/2;
unsigned long t = millis() / (32 - (SEGMENT.speed>>3)); unsigned long t = millis() / (32 - (SEGMENT.speed>>3));
unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup unsigned long t_20 = t/20; // softhack007: pre-calculating this gives about 10% speedup
@ -4809,10 +4773,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(16); SEGMENT.fadeToBlackBy(16);
for (size_t i = 8; i > 0; i--) { for (size_t i = 8; i > 0; i--) {
SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1), SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1),
@ -5256,12 +5216,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2);
uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float uint_fast32_t t = (millis() * 8) / (256 - SEGMENT.speed); // optimized to avoid float
for (int i = 0; i < cols; i++) { for (int i = 0; i < cols; i++) {
uint16_t thisVal = inoise8(i * 30, t, t); uint16_t thisVal = inoise8(i * 30, t, t);
@ -5352,12 +5307,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
uint32_t a = strip.now / (18 - SEGMENT.speed / 16); uint32_t a = strip.now / (18 - SEGMENT.speed / 16);
uint16_t x = (a / 14) % cols; uint16_t x = (a / 14) % cols;
uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0); uint16_t y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0);
@ -5410,14 +5360,9 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
const uint8_t kBorderWidth = 2; const uint8_t kBorderWidth = 2;
SEGMENT.fadeToBlackBy(24); SEGMENT.fadeToBlackBy(24);
uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider uint8_t blurAmount = SEGMENT.custom3>>1; // reduced resolution slider
SEGMENT.blur(blurAmount); SEGMENT.blur(blurAmount);
@ -5538,10 +5483,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
uint32_t tb = strip.now >> 12; // every ~4s uint32_t tb = strip.now >> 12; // every ~4s
if (tb > SEGENV.step) { if (tb > SEGENV.step) {
int8_t dir = ++SEGENV.aux0; int8_t dir = ++SEGENV.aux0;
@ -5607,7 +5548,6 @@ uint16_t mode_2Dcrazybees(void) {
bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data); bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data);
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
bee[i].posX = random8(0, cols); bee[i].posX = random8(0, cols);
bee[i].posY = random8(0, rows); bee[i].posY = random8(0, rows);
@ -5616,7 +5556,7 @@ uint16_t mode_2Dcrazybees(void) {
} }
if (millis() > SEGENV.step) { if (millis() > SEGENV.step) {
SEGENV.step = millis() + (FRAMETIME * 8 / ((SEGMENT.speed>>5)+1)); SEGENV.step = millis() + (FRAMETIME * 16 / ((SEGMENT.speed>>4)+1));
SEGMENT.fadeToBlackBy(32); SEGMENT.fadeToBlackBy(32);
@ -5679,9 +5619,9 @@ uint16_t mode_2Dghostrider(void) {
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; SEGENV.aux0 = cols;
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
SEGMENT.fill(BLACK);
random16_set_seed(strip.now); random16_set_seed(strip.now);
lighter->angleSpeed = random8(0,20) - 10; lighter->angleSpeed = random8(0,20) - 10;
lighter->gAngle = random16();
lighter->Vspeed = 5; lighter->Vspeed = 5;
lighter->gPosX = (cols/2) * 10; lighter->gPosX = (cols/2) * 10;
lighter->gPosY = (rows/2) * 10; lighter->gPosY = (rows/2) * 10;
@ -5689,6 +5629,7 @@ uint16_t mode_2Dghostrider(void) {
lighter->lightersPosX[i] = lighter->gPosX; lighter->lightersPosX[i] = lighter->gPosX;
lighter->lightersPosY[i] = lighter->gPosY + i; lighter->lightersPosY[i] = lighter->gPosY + i;
lighter->time[i] = i * 2; lighter->time[i] = i * 2;
lighter->reg[i] = false;
} }
} }
@ -5763,7 +5704,7 @@ uint16_t mode_2Dfloatingblobs(void) {
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; // re-initialise if virtual size changes SEGENV.aux0 = cols; // re-initialise if virtual size changes
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
SEGMENT.fill(BLACK); //SEGMENT.fill(BLACK);
for (size_t i = 0; i < MAX_BLOBS; i++) { for (size_t i = 0; i < MAX_BLOBS; i++) {
blob->r[i] = random8(1, cols>8 ? (cols/4) : 2); blob->r[i] = random8(1, cols>8 ? (cols/4) : 2);
blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x blob->sX[i] = (float) random8(3, cols) / (float)(256 - SEGMENT.speed); // speed x
@ -5777,7 +5718,7 @@ uint16_t mode_2Dfloatingblobs(void) {
} }
} }
SEGMENT.fadeToBlackBy(20); SEGMENT.fadeToBlackBy((SEGMENT.custom2>>3)+1);
// Bounce balls around // Bounce balls around
for (size_t i = 0; i < Amount; i++) { for (size_t i = 0; i < Amount; i++) {
@ -5833,7 +5774,7 @@ uint16_t mode_2Dfloatingblobs(void) {
return FRAMETIME; return FRAMETIME;
} }
#undef MAX_BLOBS #undef MAX_BLOBS
static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8"; static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;!;!;2;c1=8";
//////////////////////////// ////////////////////////////
@ -5845,8 +5786,8 @@ uint16_t mode_2Dscrollingtext(void) {
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
int letterWidth; int letterWidth, rotLW;
int letterHeight; int letterHeight, rotLH;
switch (map(SEGMENT.custom2, 0, 255, 1, 5)) { switch (map(SEGMENT.custom2, 0, 255, 1, 5)) {
default: default:
case 1: letterWidth = 4; letterHeight = 6; break; case 1: letterWidth = 4; letterHeight = 6; break;
@ -5855,59 +5796,84 @@ uint16_t mode_2Dscrollingtext(void) {
case 4: letterWidth = 7; letterHeight = 9; break; case 4: letterWidth = 7; letterHeight = 9; break;
case 5: letterWidth = 5; letterHeight = 12; break; case 5: letterWidth = 5; letterHeight = 12; break;
} }
const bool zero = SEGMENT.check3; // letters are rotated
const int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-letterHeight)/2; if (((SEGMENT.custom3+1)>>3) % 2) {
rotLH = letterWidth;
rotLW = letterHeight;
} else {
rotLW = letterWidth;
rotLH = letterHeight;
}
char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'}; char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i]; if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
const bool zero = strchr(text, '0') != nullptr;
const int numberOfLetters = strlen(text);
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]; char sec[5];
byte AmPmHour = hour(localTime); int AmPmHour = hour(localTime);
boolean isitAM = true; bool isitAM = true;
if (useAMPM) { if (useAMPM) {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; } if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; } if (AmPmHour == 0) { AmPmHour = 12; }
sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
} else {
sprintf_P(sec, PSTR(":%02d"), second(localTime));
} }
if (useAMPM) sprintf_P(sec, PSTR(" %2s"), (isitAM ? "AM" : "PM"));
else sprintf_P(sec, PSTR(":%02d"), second(localTime)); if (!numberOfLetters) { // fallback if empty segment name: display date and time
sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
} else {
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)); 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("#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("#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("#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 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()) { const unsigned long now = millis(); // reduce millis() calls
if ((numberOfLetters * letterWidth) > cols) ++SEGENV.aux0 %= (numberOfLetters * letterWidth) + cols; // offset int width = (numberOfLetters * rotLW);
else SEGENV.aux0 = (cols + (numberOfLetters * letterWidth))/2; int yoffset = map(SEGMENT.intensity, 0, 255, -rows/2, rows/2) + (rows-rotLH)/2;
if (width <= cols) {
// scroll vertically (e.g. ^^ Way out ^^) if it fits
int speed = map(SEGMENT.speed, 0, 255, 5000, 1000);
int frac = now % speed + 1;
if (SEGMENT.intensity == 255) {
yoffset = (2 * frac * rows)/speed - rows;
} else if (SEGMENT.intensity == 0) {
yoffset = rows - (2 * frac * rows)/speed;
}
}
if (SEGENV.step < now) {
// calculate start offset
if (width > cols) {
if (SEGMENT.check3) {
if (SEGENV.aux0 == 0) SEGENV.aux0 = width + cols - 1;
else --SEGENV.aux0;
} else ++SEGENV.aux0 %= width + cols;
} else SEGENV.aux0 = (cols + width)/2;
++SEGENV.aux1 &= 0xFF; // color shift ++SEGENV.aux1 &= 0xFF; // color shift
SEGENV.step = millis() + map(SEGMENT.speed, 0, 255, 10*FRAMETIME_FIXED, 2*FRAMETIME_FIXED); SEGENV.step = now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms
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));
}
} }
if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
for (int i = 0; i < numberOfLetters; i++) { for (int i = 0; i < numberOfLetters; i++) {
if (int(cols) - int(SEGENV.aux0) + letterWidth*(i+1) < 0) continue; // don't draw characters off-screen int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i;
if (xoffset + rotLW < 0) continue; // don't draw characters off-screen
uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0); uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
uint32_t col2 = BLACK; uint32_t col2 = BLACK;
if (SEGMENT.check1 && SEGMENT.palette == 0) { if (SEGMENT.check1 && SEGMENT.palette == 0) {
col1 = SEGCOLOR(0); col1 = SEGCOLOR(0);
col2 = SEGCOLOR(2); col2 = SEGCOLOR(2);
} }
SEGMENT.drawCharacter(text[i], int(cols) - int(SEGENV.aux0) + letterWidth*i, yoffset, letterWidth, letterHeight, col1, col2); SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, (SEGMENT.custom3+1)>>3);
} }
return FRAMETIME; 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"; static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,c3=0,rev=0,mi=0,rY=0,mY=0";
//////////////////////////// ////////////////////////////
@ -5924,10 +5890,6 @@ uint16_t mode_2Ddriftrose(void) {
const float CY = (rows-rows%2)/2.f - .5f; const float CY = (rows-rows%2)/2.f - .5f;
const float L = min(cols, rows) / 2.f; const float L = min(cols, rows) / 2.f;
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3)); SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
for (size_t i = 1; i < 37; i++) { 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 x = (CX + (sin_t(radians(i * 10)) * (beatsin8(i, 0, L*2)-L))) * 255.f;
@ -6100,8 +6062,6 @@ uint16_t mode_2DSwirl(void) {
float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg??? float volumeSmth = *(float*) um_data->u_data[0]; //ewowi: use instead of sampleAvg???
int16_t volumeRaw = *(int16_t*) um_data->u_data[1]; 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( 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( 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(ni,nj, ColorFromPalette(SEGPALETTE, (ms / 17 + volumeSmth*4), volumeRaw * SEGMENT.intensity / 64, LINEARBLEND)); //CHSV( ms / 17, 200, 255);
@ -6124,10 +6084,6 @@ uint16_t mode_2DWaverly(void) {
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.fill(BLACK);
}
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6175,6 +6131,7 @@ typedef struct Gravity {
// * GRAVCENTER // // * GRAVCENTER //
/////////////////////// ///////////////////////
uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
const uint16_t dataSize = sizeof(gravity); const uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6193,7 +6150,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling 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 available in current segment float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0f); // map to pixels available in current segment
uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32; uint8_t gravity = 8 - SEGMENT.speed/32;
@ -6223,6 +6180,7 @@ static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,
// * GRAVCENTRIC // // * GRAVCENTRIC //
/////////////////////// ///////////////////////
uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline. uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity); uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6241,10 +6199,10 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew
//SEGMENT.fade_out(240); // twice? really? //SEGMENT.fade_out(240); // twice? really?
SEGMENT.fade_out(253); // 50% SEGMENT.fade_out(253); // 50%
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0; float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling 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 float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing. int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32; uint8_t gravity = 8 - SEGMENT.speed/32;
@ -6274,6 +6232,7 @@ static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fal
// * GRAVIMETER // // * GRAVIMETER //
/////////////////////// ///////////////////////
uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity); uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
@ -6320,6 +6279,7 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,
// * JUGGLES // // * JUGGLES //
////////////////////// //////////////////////
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6343,6 +6303,7 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;
// * MATRIPIX // // * MATRIPIX //
////////////////////// //////////////////////
uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data; um_data_t *um_data;
@ -6374,6 +6335,7 @@ static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;
// * MIDNOISE // // * MIDNOISE //
////////////////////// //////////////////////
uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1. // Changing xdist to SEGENV.aux0 and ydist to SEGENV.aux1.
um_data_t *um_data; um_data_t *um_data;
@ -6477,6 +6439,7 @@ static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Wid
// * PIXELWAVE // // * PIXELWAVE //
////////////////////// //////////////////////
uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline. uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
@ -6552,6 +6515,7 @@ static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels
/////////////////////// ///////////////////////
// Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing. // Andrew's crappy peak detector. If I were 40+ years younger, I'd learn signal processing.
uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t size = 0; uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254);
@ -6595,6 +6559,7 @@ static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Pud
// * PUDDLES // // * PUDDLES //
////////////////////// //////////////////////
uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline. uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t size = 0; uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254); uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position. uint16_t pos = random16(SEGLEN); // Set a random starting position.
@ -6626,6 +6591,7 @@ static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle si
// * PIXELS // // * PIXELS //
////////////////////// //////////////////////
uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline. uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed if (!SEGENV.allocateData(32*sizeof(uint8_t))) return mode_static(); //allocation failed
uint8_t *myVals = reinterpret_cast<uint8_t*>(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. uint8_t *myVals = reinterpret_cast<uint8_t*>(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.
@ -6659,6 +6625,7 @@ static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels
// ** Blurz // // ** Blurz //
////////////////////// //////////////////////
uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment // even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data; um_data_t *um_data;
@ -6695,6 +6662,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color
// ** DJLight // // ** DJLight //
///////////////////////// /////////////////////////
uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam. uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam.
if (SEGLEN == 1) return mode_static();
const int mid = SEGLEN / 2; const int mid = SEGLEN / 2;
um_data_t *um_data; um_data_t *um_data;
@ -6728,6 +6696,7 @@ static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,s
// ** Freqmap // // ** Freqmap //
//////////////////// ////////////////////
uint16_t 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.
if (SEGLEN == 1) return mode_static();
// Start frequency = 60 Hz and log10(60) = 1.78 // Start frequency = 60 Hz and log10(60) = 1.78
// End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10 // End frequency = MAX_FREQUENCY in Hz and lo10(MAX_FREQUENCY) = MAX_FREQ_LOG10
@ -6764,6 +6733,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting
// ** Freqmatrix // // ** Freqmatrix //
/////////////////////// ///////////////////////
uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6829,24 +6799,27 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
} }
float FFT_MajorPeak = *(float*)um_data->u_data[4]; float FFT_MajorPeak = *(float*)um_data->u_data[4];
float my_magnitude = *(float*)um_data->u_data[5] / 16.0f; float my_magnitude = *(float*)um_data->u_data[5] / 16.0f;
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception) if (FFT_MajorPeak < 1) FFT_MajorPeak = 1.0f; // 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. // this code translates to speed * (2 - speed/255) which is a) speed*2 or b) speed (when speed is 255)
// and since fade_out() can only take 0-255 it will behave incorrectly when speed > 127
//uint16_t fadeRate = 2*SEGMENT.speed - SEGMENT.speed*SEGMENT.speed/255; // Get to 255 as quick as you can.
uint16_t fadeRate = SEGMENT.speed*SEGMENT.speed; // Get to 255 as quick as you can.
fadeRate = map(fadeRate, 0, 65535, 1, 255);
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64; int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fade_out(fadeRate); 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. 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 if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
uint16_t locn = random16(0,SEGLEN);
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
} }
return FRAMETIME; return FRAMETIME;
} // mode_freqpixels() } // 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 static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;!,!,;!;1f;m12=0,si=0"; // Pixels, Beatsin
////////////////////// //////////////////////
@ -6865,6 +6838,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta
// 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. // 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. // 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. uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6921,7 +6895,7 @@ static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effec
// ** Gravfreq // // ** Gravfreq //
/////////////////////// ///////////////////////
uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
uint16_t dataSize = sizeof(gravity); uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data); Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
@ -6938,9 +6912,9 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
SEGMENT.fade_out(250); SEGMENT.fade_out(250);
float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f; float segmentSampleAvg = volumeSmth * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment float mySampleAvg = mapf(segmentSampleAvg*2.0f, 0,32, 0, (float)SEGLEN/2.0f); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing. int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32; uint8_t gravity = 8 - SEGMENT.speed/32;
@ -6973,6 +6947,7 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sens
// ** Noisemove // // ** Noisemove //
////////////////////// //////////////////////
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6980,8 +6955,6 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
} }
uint8_t *fftResult = (uint8_t*)um_data->u_data[2]; uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
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; int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4); if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
@ -7001,6 +6974,7 @@ static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin
// ** Rocktaves // // ** Rocktaves //
////////////////////// //////////////////////
uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7009,7 +6983,6 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
float FFT_MajorPeak = *(float*) um_data->u_data[4]; float FFT_MajorPeak = *(float*) um_data->u_data[4];
float my_magnitude = *(float*) um_data->u_data[5] / 16.0f; float my_magnitude = *(float*) um_data->u_data[5] / 16.0f;
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded. SEGMENT.fadeToBlackBy(16); // Just in case something doesn't get faded.
float frTemp = FFT_MajorPeak; float frTemp = FFT_MajorPeak;
@ -7025,7 +6998,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp = frTemp/2; frTemp = frTemp/2;
} }
frTemp -=132; // This should give us a base musical note of C3 frTemp -= 132.0f; // 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; frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
@ -7042,7 +7015,7 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1
/////////////////////// ///////////////////////
// Combines peak detection with FFT_MajorPeak and FFT_Magnitude. // Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
if (SEGENV.call == 0) SEGMENT.fill(BLACK); if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
@ -7117,7 +7090,6 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
rippleTime = true; rippleTime = true;
} }
if (SEGENV.call == 0) SEGMENT.fill(BLACK);
int fadeoutDelay = (256 - SEGMENT.speed) / 64; int fadeoutDelay = (256 - SEGMENT.speed) / 64;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed); if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(SEGMENT.speed);

View File

@ -381,6 +381,26 @@ typedef struct Segment {
byte *data; // effect data pointer byte *data; // effect data pointer
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions) static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
typedef struct TemporarySegmentData {
uint16_t _optionsT;
uint32_t _colorT[NUM_COLORS];
uint8_t _speedT;
uint8_t _intensityT;
uint8_t _custom1T, _custom2T; // custom FX parameters/sliders
struct {
uint8_t _custom3T : 5; // reduced range slider (0-31)
bool _check1T : 1; // checkmark 1
bool _check2T : 1; // checkmark 2
bool _check3T : 1; // checkmark 3
};
uint16_t _aux0T;
uint16_t _aux1T;
uint32_t _stepT;
uint32_t _callT;
uint8_t *_dataT;
uint16_t _dataLenT;
} tmpsegd_t;
private: private:
union { union {
uint8_t _capabilities; uint8_t _capabilities;
@ -396,43 +416,33 @@ typedef struct Segment {
static uint16_t _usedSegmentData; static uint16_t _usedSegmentData;
// perhaps this should be per segment, not static // perhaps this should be per segment, not static
static CRGBPalette16 _randomPalette; static CRGBPalette16 _randomPalette; // actual random palette
static CRGBPalette16 _newRandomPalette; static CRGBPalette16 _newRandomPalette; // target random palette
static unsigned long _lastPaletteChange; static unsigned long _lastPaletteChange; // last random palette change time in millis()
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
#endif
// transition data, valid only if transitional==true, holds values during transition (72 bytes) // transition data, valid only if transitional==true, holds values during transition (72 bytes)
struct Transition { struct Transition {
#ifndef WLED_DISABLE_MODE_BLEND
tmpsegd_t _segT; // previous segment environment
uint8_t _modeT; // previous mode/effect
#else
uint32_t _colorT[NUM_COLORS]; uint32_t _colorT[NUM_COLORS];
#endif
uint8_t _briT; // temporary brightness uint8_t _briT; // temporary brightness
uint8_t _cctT; // temporary CCT uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette CRGBPalette16 _palT; // temporary palette
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible) 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() unsigned long _start; // must accommodate millis()
uint16_t _dur; uint16_t _dur;
Transition(uint16_t dur=750) Transition(uint16_t dur=750)
: _briT(255) : _palT(CRGBPalette16(CRGB::Black))
, _cctT(127)
, _palT(CRGBPalette16(CRGB::Black))
, _prevPaletteBlends(0) , _prevPaletteBlends(0)
, _modeP(FX_MODE_STATIC)
, _start(millis()) , _start(millis())
, _dur(dur) , _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<NUM_COLORS; i++) _colorT[i] = o[i];
}
} *_t; } *_t;
public: public:
@ -471,6 +481,9 @@ typedef struct Segment {
_t(nullptr) _t(nullptr)
{ {
//refreshLightCapabilities(); //refreshLightCapabilities();
#ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this);
#endif
} }
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) { Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
@ -482,14 +495,14 @@ typedef struct Segment {
Segment(Segment &&orig) noexcept; // move constructor Segment(Segment &&orig) noexcept; // move constructor
~Segment() { ~Segment() {
//#ifdef WLED_DEBUG #ifdef WLED_DEBUG
//Serial.print(F("Destroying segment:")); //Serial.printf("-- Destroying segment: %p\n", this);
//if (name) Serial.printf(" %s (%p)", name, name); //if (name) Serial.printf(" %s (%p)", name, name);
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
//Serial.println(); //Serial.println();
//#endif #endif
if (name) { delete[] name; name = nullptr; } if (name) { delete[] name; name = nullptr; }
if (_t) { transitional = false; delete _t; _t = nullptr; } stopTransition();
deallocateData(); deallocateData();
} }
@ -515,6 +528,9 @@ typedef struct Segment {
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; } static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; } static void addUsedSegmentData(int len) { _usedSegmentData += len; }
#ifndef WLED_DISABLE_MODE_BLEND
static void modeBlend(bool blend) { _modeBlend = blend; }
#endif
static void handleRandomPalette(); 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); 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);
@ -542,7 +558,12 @@ typedef struct Segment {
// transition functions // transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change void startTransition(uint16_t dur); // transition has to start before actual segment values change
void stopTransition(void);
void handleTransition(void); void handleTransition(void);
#ifndef WLED_DISABLE_MODE_BLEND
void swapSegenv(tmpsegd_t &tmpSegD);
void restoreSegenv(tmpsegd_t &tmpSegD);
#endif
uint16_t progress(void); //transition progression between 0-65535 uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentBri(uint8_t briNew, bool useCct = false);
uint8_t currentMode(uint8_t modeNew); uint8_t currentMode(uint8_t modeNew);
@ -604,9 +625,9 @@ typedef struct Segment {
void fill_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, 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 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, uint32_t color, uint32_t col2 = 0, uint8_t rotate = 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) { 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 drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, uint8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c); void wu_pixel(uint32_t x, uint32_t y, CRGB c);
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); } void blur2d(fract8 blur_amount) { blur(blur_amount); }

View File

@ -216,23 +216,29 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
y *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels
if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally for (int g = 0; g < grouping; g++) { // groupping horizontally
uint16_t xX = (x+g), yY = (y+j); uint16_t xX = (x+g), yY = (y+j);
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
strip.setPixelColorXY(start + xX, startY + yY, col); #ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
if (mirror) { //set the corresponding horizontally mirrored pixel if (mirror) { //set the corresponding horizontally mirrored pixel
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
} }
if (mirror_y) { //set the corresponding vertically mirrored pixel if (mirror_y) { //set the corresponding vertically mirrored pixel
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
} }
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col); strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
} }
} }
} }
@ -565,7 +571,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
// draws a raster font character on canvas // draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM // only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) { void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, uint8_t rotate) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries chr -= 32; // align with font table entries
@ -578,9 +584,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
//if (w<5 || w>6 || h!=8) return; //if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height for (int i = 0; i<h; i++) { // character height
int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen
uint8_t bits = 0; uint8_t bits = 0;
switch (font) { switch (font) {
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
@ -592,8 +595,15 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
} }
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
for (int j = 0; j<w; j++) { // character width for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j; int x0, y0;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen switch (rotate) {
case 3: x0 = x + (h-1) - i; y0 = y + (w-1) - j; break;
case 2: x0 = x + j; y0 = y + (h-1) - i; break;
case 1: x0 = x + i; y0 = y + j; break;
default: x0 = x + (w-1) - j; y0 = y + i; break;
}
if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
setPixelColorXY(x0, y0, col); setPixelColorXY(x0, y0, col);
} }
} }

View File

@ -81,9 +81,13 @@ CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR);
CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR); CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR);
unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
#endif
// copy constructor // copy constructor
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --")); //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition transitional = false; // copied segment cannot be in transition
name = nullptr; name = nullptr;
@ -92,12 +96,12 @@ Segment::Segment(const Segment &orig) {
_t = nullptr; _t = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } //if (orig._t) { _t = new Transition(orig._t->_dur); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --")); //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
@ -108,12 +112,17 @@ Segment::Segment(Segment &&orig) noexcept {
// copy assignment // copy assignment
Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copying segment --")); //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
transitional = false; // copied segment cannot be in transition transitional = false; // copied segment cannot be in transition
if (name) delete[] name; if (name) delete[] name;
if (_t) delete _t; if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
}
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
@ -133,12 +142,18 @@ Segment& Segment::operator= (const Segment &orig) {
// move assignment // move assignment
Segment& Segment::operator= (Segment &&orig) noexcept { Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Moving segment --")); //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
transitional = false; // just temporary transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data deallocateData(); // free old runtime data
if (_t) { delete _t; _t = nullptr; } if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif
delete _t;
_t = nullptr;
}
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr; orig.name = nullptr;
@ -151,12 +166,18 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
bool Segment::allocateData(size_t len) { bool Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
deallocateData(); deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
// not enough memory
DEBUG_PRINTF("!!! Effect RAM depleted: %d/%d !!!\n", len, Segment::getUsedSegmentData());
return false;
}
// do not use SPI RAM on ESP32 since it is slow // do not use SPI RAM on ESP32 since it is slow
data = (byte*) malloc(len); data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed
Segment::addUsedSegmentData(len); Segment::addUsedSegmentData(len);
//DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data);
_dataLen = len; _dataLen = len;
memset(data, 0, len); memset(data, 0, len);
return true; return true;
@ -164,9 +185,11 @@ bool Segment::allocateData(size_t len) {
void Segment::deallocateData() { void Segment::deallocateData() {
if (!data) return; if (!data) return;
//DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data);
free(data); free(data);
data = nullptr; data = nullptr;
Segment::addUsedSegmentData(-_dataLen); // WARNING it looks like we have a memory leak somewhere
Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
_dataLen = 0; _dataLen = 0;
} }
@ -179,7 +202,7 @@ void Segment::deallocateData() {
*/ */
void Segment::resetIfRequired() { void Segment::resetIfRequired() {
if (!reset) return; if (!reset) return;
//DEBUG_PRINTF("-- Segment reset: %p\n", this);
deallocateData(); deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; reset = false;
@ -215,7 +238,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
CHSV(random8(), random8(160, 255), random8(128, 255)), CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255))); CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis(); _lastPaletteChange = millis();
handleRandomPalette(); // do initial blend handleRandomPalette(); // do a 1st pass of blend
} }
targetPalette = _randomPalette; targetPalette = _randomPalette;
break;} break;}
@ -270,11 +293,8 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
void Segment::startTransition(uint16_t dur) { void Segment::startTransition(uint16_t dur) {
if (!dur) { if (!dur) {
transitional = false; if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
if (_t) { else transitional = false;
delete _t;
_t = nullptr;
}
return; return;
} }
if (transitional && _t) return; // already in transition no need to store anything if (transitional && _t) return; // already in transition no need to store anything
@ -283,44 +303,165 @@ void Segment::startTransition(uint16_t dur) {
_t = new Transition(dur); // no previous transition running _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
_t->_palT = _palT;
_t->_briT = on ? opacity : 0; _t->_briT = on ? opacity : 0;
_t->_cctT = cct; _t->_cctT = cct;
_t->_palT = _palT; #ifndef WLED_DISABLE_MODE_BLEND
_t->_modeP = mode; swapSegenv(_t->_segT);
_t->_modeT = mode;
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
_t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen;
}
}
#else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
free(_t->_segT._dataT);
_t->_segT._dataT = nullptr;
}
#endif
delete _t;
_t = nullptr;
}
}
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition();
}
// transition progression between 0-65535 // transition progression between 0-65535
uint16_t Segment::progress() { uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU; if (transitional && _t) {
unsigned long timeNow = millis(); unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU; if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
} }
return 0xFFFFU;
}
#ifndef WLED_DISABLE_MODE_BLEND
void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
if (!_t) return;
//DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg);
tmpSeg._optionsT = options;
for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i];
tmpSeg._speedT = speed;
tmpSeg._intensityT = intensity;
tmpSeg._custom1T = custom1;
tmpSeg._custom2T = custom2;
tmpSeg._custom3T = custom3;
tmpSeg._check1T = check1;
tmpSeg._check2T = check2;
tmpSeg._check3T = check3;
tmpSeg._aux0T = aux0;
tmpSeg._aux1T = aux1;
tmpSeg._stepT = step;
tmpSeg._callT = call;
tmpSeg._dataT = data;
tmpSeg._dataLenT = _dataLen;
if (&tmpSeg != &(_t->_segT)) {
// swap SEGENV with transitional data
options = _t->_segT._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = _t->_segT._colorT[i];
speed = _t->_segT._speedT;
intensity = _t->_segT._intensityT;
custom1 = _t->_segT._custom1T;
custom2 = _t->_segT._custom2T;
custom3 = _t->_segT._custom3T;
check1 = _t->_segT._check1T;
check2 = _t->_segT._check2T;
check3 = _t->_segT._check3T;
aux0 = _t->_segT._aux0T;
aux1 = _t->_segT._aux1T;
step = _t->_segT._stepT;
call = _t->_segT._callT;
data = _t->_segT._dataT;
_dataLen = _t->_segT._dataLenT;
}
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg);
if (_t && &(_t->_segT) != &tmpSeg) {
// update possibly changed variables to keep old effect running correctly
_t->_segT._aux0T = aux0;
_t->_segT._aux1T = aux1;
_t->_segT._stepT = step;
_t->_segT._callT = call;
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
}
options = tmpSeg._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
speed = tmpSeg._speedT;
intensity = tmpSeg._intensityT;
custom1 = tmpSeg._custom1T;
custom2 = tmpSeg._custom2T;
custom3 = tmpSeg._custom3T;
check1 = tmpSeg._check1T;
check2 = tmpSeg._check2T;
check3 = tmpSeg._check3T;
aux0 = tmpSeg._aux0T;
aux1 = tmpSeg._aux1T;
step = tmpSeg._stepT;
call = tmpSeg._callT;
data = tmpSeg._dataT;
_dataLen = tmpSeg._dataLenT;
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
}
#endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
uint32_t prog = progress(); uint32_t prog = progress();
if (transitional && _t && prog < 0xFFFFU) { if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else {
return briNew;
} }
return briNew;
} }
uint8_t Segment::currentMode(uint8_t newMode) { uint8_t Segment::currentMode(uint8_t newMode) {
return (progress()>32767U) ? newMode : _t->_modeP; // change effect in the middle of transition #ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
if (prog < 0xFFFFU) return _t->_modeT;
#endif
return newMode;
} }
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
#ifndef WLED_DISABLE_MODE_BLEND
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
#else
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
#endif
} }
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal); loadPalette(targetPalette, pal);
if (transitional && _t && progress() < 0xFFFFU) { if (progress() < 0xFFFFU) {
// blend palettes // blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms // minimum blend time is 100ms maximum is 65535ms
@ -332,18 +473,6 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
return targetPalette; return targetPalette;
} }
void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress();
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
if (_progress == 0xFFFFU) {
delete _t;
_t = nullptr;
}
}
}
// relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY) // relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY)
void Segment::handleRandomPalette() { void Segment::handleRandomPalette() {
// just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette) // just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette)
@ -426,6 +555,7 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) { void Segment::setOpacity(uint8_t o) {
if (opacity == o) return; if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o; opacity = o;
stateChanged = true; // send UDP/WS broadcast stateChanged = true; // send UDP/WS broadcast
} }
@ -444,7 +574,6 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
if (fx != mode) { if (fx != mode) {
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
mode = fx; mode = fx;
// load default values from effect string // load default values from effect string
if (loadDefaults) { if (loadDefaults) {
int16_t sOpt; int16_t sOpt;
@ -625,6 +754,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
i += start; // starting pixel in a group i += start; // starting pixel in a group
uint32_t tmpCol = col;
// set all the pixels in the group // set all the pixels in the group
for (int j = 0; j < grouping; j++) { for (int j = 0; j < grouping; j++) {
uint16_t indexSet = i + ((reverse) ? -j : j); uint16_t indexSet = i + ((reverse) ? -j : j);
@ -633,11 +763,17 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
uint16_t indexMir = stop - indexSet + start - 1; uint16_t indexMir = stop - indexSet + start - 1;
indexMir += offset; // offset/phase indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap if (indexMir >= stop) indexMir -= len; // wrap
strip.setPixelColor(indexMir, col); #ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexMir, tmpCol);
} }
indexSet += offset; // offset/phase indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap if (indexSet >= stop) indexSet -= len; // wrap
strip.setPixelColor(indexSet, col); #ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexSet, tmpCol);
} }
} }
} }
@ -843,7 +979,7 @@ void Segment::fade_out(uint8_t rate) {
const uint16_t rows = virtualHeight(); // will be 1 for 1D const uint16_t rows = virtualHeight(); // will be 1 for 1D
rate = (255-rate) >> 1; rate = (255-rate) >> 1;
float mappedRate = float(rate) +1.1; float mappedRate = float(rate) +1.1f;
uint32_t color = colors[1]; // SEGCOLOR(1); // target color uint32_t color = colors[1]; // SEGCOLOR(1); // target color
int w2 = W(color); int w2 = W(color);
@ -1079,8 +1215,10 @@ void WS2812FX::service() {
// reset the segment runtime data if needed // reset the segment runtime data if needed
seg.resetIfRequired(); seg.resetIfRequired();
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time // last condition ensures all solid segments are updated at the same time
if (seg.isActive() && (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))) if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{ {
doShow = true; doShow = true;
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
@ -1095,10 +1233,25 @@ void WS2812FX::service() {
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// effect blending (execute previous effect) // Effect blending
// actual code may be a bit more involved as effects have runtime data including allocated memory // When two effects are being blended, each may have different segment data, this
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress()); // data needs to be saved first and then restored before running previous/transitional mode.
delay = (*_mode[seg.currentMode(seg.mode)])(); // The blending will largely depend on the effect behaviour since actual output (LEDs) may be
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
uint16_t d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
#endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
} }
@ -1273,9 +1426,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (m >= getModeCount()) m = getModeCount() - 1; if (m >= getModeCount()) m = getModeCount() - 1;
if (_segments[segid].mode != m) { if (_segments[segid].mode != m) {
_segments[segid].startTransition(_transitionDur); // set effect transitions _segments[segid].setMode(m); // do not load defaults
_segments[segid].markForReset();
_segments[segid].mode = m;
} }
} }

View File

@ -148,6 +148,10 @@ void BusDigital::show() {
pix += _skip; pix += _skip;
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
} }
#if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
} }
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
} }

View File

@ -29,8 +29,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
id = strip.getSegmentsNum()-1; // segments are added at the end of list id = strip.getSegmentsNum()-1; // segments are added at the end of list
} }
//DEBUG_PRINTLN("-- JSON deserialize segment.");
Segment& seg = strip.getSegment(id); Segment& seg = strip.getSegment(id);
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
Segment prev = seg; //make a backup so we can tell if something changed Segment prev = seg; //make a backup so we can tell if something changed
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
uint16_t start = elem["start"] | seg.start; uint16_t start = elem["start"] | seg.start;
if (stop < 0) { if (stop < 0) {
@ -979,9 +982,10 @@ void serializeNodes(JsonObject root)
// deserializes mode data string into JsonArray // deserializes mode data string into JsonArray
void serializeModeData(JsonArray fxdata) void serializeModeData(JsonArray fxdata)
{ {
char lineBuffer[128]; char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) { for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127); strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@'); char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) fxdata.add(dataPtr+1); if (dataPtr) fxdata.add(dataPtr+1);
@ -992,10 +996,12 @@ void serializeModeData(JsonArray fxdata)
// deserializes mode names string into JsonArray // deserializes mode names string into JsonArray
// also removes effect data extensions (@...) from deserialised names // also removes effect data extensions (@...) from deserialised names
void serializeModeNames(JsonArray arr) { void serializeModeNames(JsonArray arr)
char lineBuffer[128]; {
char lineBuffer[256];
for (size_t i = 0; i < strip.getModeCount(); i++) { for (size_t i = 0; i < strip.getModeCount(); i++) {
strncpy_P(lineBuffer, strip.getModeData(i), 127); strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* dataPtr = strchr(lineBuffer,'@'); char* dataPtr = strchr(lineBuffer,'@');
if (dataPtr) *dataPtr = 0; // terminate mode data after name if (dataPtr) *dataPtr = 0; // terminate mode data after name

View File

@ -37,12 +37,12 @@ void applyValuesToSelectedSegs()
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;} if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;} if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;} if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;} if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]); uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]); uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;} if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;} if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
} }
} }

View File

@ -239,7 +239,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
if (mode < strip.getModeCount()) { if (mode < strip.getModeCount()) {
char lineBuffer[256]; char lineBuffer[256];
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
strcpy_P(lineBuffer, strip.getModeData(mode)); strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
size_t len = strlen(lineBuffer); size_t len = strlen(lineBuffer);
size_t j = 0; size_t j = 0;
for (; j < maxLen && j < len; j++) { for (; j < maxLen && j < len; j++) {
@ -367,9 +368,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t extractModeDefaults(uint8_t mode, const char *segVar) int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{ {
if (mode < strip.getModeCount()) { if (mode < strip.getModeCount()) {
char lineBuffer[128] = ""; char lineBuffer[256];
strncpy_P(lineBuffer, strip.getModeData(mode), 127); strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
lineBuffer[127] = '\0'; // terminate string lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
if (lineBuffer[0] != 0) { if (lineBuffer[0] != 0) {
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
if (!startPtr) return -1; if (!startPtr) return -1;