diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2ee772ce..f46f002b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,8 +9,8 @@ ], "dependsOrder": "sequence", "problemMatcher": [ - "$platformio", - ], + "$platformio" + ] }, { "type": "PlatformIO", @@ -18,7 +18,7 @@ "task": "Build", "group": { "kind": "build", - "isDefault": true, + "isDefault": true }, "problemMatcher": [ "$platformio" diff --git a/CHANGELOG.md b/CHANGELOG.md index 2af6bf16..2e8d8110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ ## WLED changelog +#### Build 2309120 till build 2311030 +- WLED version 0.15.0-a0 +- Implement global JSON API boolean toggle. +- Sort presets by ID +- Fix #3496 +- Improved random bg image and added random bg image options (@WoodyLetsCode, #3481) +- Audio palettes (Audioreactive usermod, credit @netmindz) +- Better UI tooltips (@ajotnac, #3464) +- Better effect filters (filter dropdown) +- Fix udp sync (fix for #3487) +- Power button override (solves #3431) +- Additional HTTP request throttling (ESP8266) +- Additional UI/UX improvements +- Segment class optimisations (internal) +- ESP-NOW sync +- ESP-NOW Wiz remote JSON overrides (similar to IR JSON) +- Gamma correction for custom palettes (#3399). +- Restore presets from browser local storage +- Optional effect blending +- Restructured UDP Sync (internal) + - Remove sync receive + - Sync clarification +- Disallow 2D effects on non-2D segments +- Return of 2 audio simulations +- Bugfix in sync #3344 (internal) + - remove excessive segments + - ignore inactive segments if not syncing bounds + - send UDP/WS on segment change + - pop_back() when removing last segment + #### Build 2310010, build 2310130 - Release of WLED version 0.14.0 "Hoshi" - Bugfixes for #3400, #3403, #3405 diff --git a/package-lock.json b/package-lock.json index e775dc41..55fc0b17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0", + "version": "0.15.0-a0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3135c5f3..68bd16b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0", + "version": "0.15.0-a0", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index f3caa585..fa6bf3bb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -181,6 +181,9 @@ lib_deps = IRremoteESP8266 @ 2.8.2 makuna/NeoPixelBus @ 2.7.5 https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7 + # ESP-NOW library (includes mandatory QuickDebug library) + ; gmag11/QuickESPNow @ 0.6.2 + https://github.com/blazoncek/QuickESPNow.git#optional-debug #For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line #TFT_eSPI #For compatible OLED display uncomment following @@ -217,7 +220,8 @@ build_flags = ; restrict to minimal mime-types -DMIMETYPE_MINIMAL ; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html) - ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" + ; decrease code cache size and increase IRAM to fit all pixel functions + -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'" ; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown lib_deps = diff --git a/usermods/PWM_fan/usermod_PWM_fan.h b/usermods/PWM_fan/usermod_PWM_fan.h index f7fe0e10..05b6d9b3 100644 --- a/usermods/PWM_fan/usermod_PWM_fan.h +++ b/usermods/PWM_fan/usermod_PWM_fan.h @@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod { uint8_t tachoUpdateSec = 30; float targetTemperature = 35.0; uint8_t minPWMValuePct = 0; + uint8_t maxPWMValuePct = 100; uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups. uint8_t pwmValuePct = 0; + // constant values + static const uint8_t _pwmMaxValue = 255; + static const uint8_t _pwmMaxStepCount = 7; + float _pwmTempStepSize = 0.5f; + // strings to reduce flash memory usage (used more than twice) static const char _name[]; static const char _enabled[]; @@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod { static const char _temperature[]; static const char _tachoUpdateSec[]; static const char _minPWMValuePct[]; + static const char _maxPWMValuePct[]; static const char _IRQperRotation[]; static const char _speed[]; static const char _lock[]; @@ -156,31 +163,25 @@ class PWMFanUsermod : public Usermod { void setFanPWMbasedOnTemperature(void) { float temp = getActualTemperature(); - float difftemp = temp - targetTemperature; - // Default to run fan at full speed. - int newPWMvalue = 255; - int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100); - int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100; + // dividing minPercent and maxPercent into equal pwmvalue sizes + int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100); + int pwmStep = calculatePwmStep(temp - targetTemperature); + // minimum based on full speed - not entered MaxPercent + int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100; + updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize); + } - if ((temp == NAN) || (temp <= -100.0)) { + uint8_t calculatePwmStep(float diffTemp){ + if ((diffTemp == NAN) || (diffTemp <= -100.0)) { DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255.")); - } else if (difftemp <= 0.0) { - // Temperature is below target temperature. Run fan at minimum speed. - newPWMvalue = pwmMinimumValue; - } else if (difftemp <= 0.5) { - newPWMvalue = pwmMinimumValue + pwmStep; - } else if (difftemp <= 1.0) { - newPWMvalue = pwmMinimumValue + 2*pwmStep; - } else if (difftemp <= 1.5) { - newPWMvalue = pwmMinimumValue + 3*pwmStep; - } else if (difftemp <= 2.0) { - newPWMvalue = pwmMinimumValue + 4*pwmStep; - } else if (difftemp <= 2.5) { - newPWMvalue = pwmMinimumValue + 5*pwmStep; - } else if (difftemp <= 3.0) { - newPWMvalue = pwmMinimumValue + 6*pwmStep; + return _pwmMaxStepCount; } - updateFanSpeed(newPWMvalue); + if(diffTemp <=0){ + return 0; + } + int calculatedStep = (diffTemp / _pwmTempStepSize)+1; + // anything greater than max stepcount gets max + return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep); } public: @@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod { top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec; top[FPSTR(_temperature)] = targetTemperature; top[FPSTR(_minPWMValuePct)] = minPWMValuePct; + top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct; top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation; DEBUG_PRINTLN(F("Autosave config saved.")); } @@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod { targetTemperature = top[FPSTR(_temperature)] | targetTemperature; minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct; minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking + maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct; + maxPWMValuePct = (uint8_t) min(100,max((int)minPWMValuePct,(int)maxPWMValuePct)); // bounds checking numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation; numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking @@ -389,6 +393,7 @@ const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin"; const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C"; const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s"; const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent"; +const char PWMFanUsermod::_maxPWMValuePct[] PROGMEM = "max-PWM-percent"; const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation"; const char PWMFanUsermod::_speed[] PROGMEM = "speed"; const char PWMFanUsermod::_lock[] PROGMEM = "lock"; diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 215ec6e9..55fe0169 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -51,6 +51,8 @@ #define PLOT_PRINTF(x...) #endif +#define MAX_PALETTES 3 + // use audio source class (ESP32 specific) #include "audio_source.h" constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !) @@ -614,6 +616,8 @@ class AudioReactive : public Usermod { // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) bool enabled = false; bool initDone = false; + bool addPalettes = false; + int8_t palettes = 0; // variables for UDP sound sync WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!) @@ -652,10 +656,15 @@ class AudioReactive : public Usermod { static const char _inputLvl[]; static const char _analogmic[]; static const char _digitalmic[]; + static const char _addPalettes[]; static const char UDP_SYNC_HEADER[]; static const char UDP_SYNC_HEADER_v1[]; // private methods + void removeAudioPalettes(void); + void createAudioPalettes(void); + CRGB getCRGBForBand(int x, int pal); + void fillAudioPalettes(void); //////////////////// // Debug support // @@ -1199,6 +1208,7 @@ class AudioReactive : public Usermod { } if (enabled) connectUDPSoundSync(); + if (enabled && addPalettes) createAudioPalettes(); initDone = true; } @@ -1361,6 +1371,7 @@ class AudioReactive : public Usermod { lastTime = millis(); } + fillAudioPalettes(); } @@ -1613,13 +1624,28 @@ class AudioReactive : public Usermod { if (usermod[FPSTR(_enabled)].is()) { enabled = usermod[FPSTR(_enabled)].as(); if (prevEnabled != enabled) onUpdateBegin(!enabled); + if (addPalettes) { + // add/remove custom/audioreactive palettes + if (prevEnabled && !enabled) removeAudioPalettes(); + if (!prevEnabled && enabled) createAudioPalettes(); + } } if (usermod[FPSTR(_inputLvl)].is()) { inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as())); } } + if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as()) { + // handle removal of custom palettes from JSON call so we don't break things + removeAudioPalettes(); + } } + void onStateChange(uint8_t callMode) { + if (initDone && enabled && addPalettes && palettes==0 && strip.customPalettes.size()<10) { + // if palettes were removed during JSON call re-add them + createAudioPalettes(); + } + } /* * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. @@ -1660,6 +1686,7 @@ class AudioReactive : public Usermod { { JsonObject top = root.createNestedObject(FPSTR(_name)); top[FPSTR(_enabled)] = enabled; + top[FPSTR(_addPalettes)] = addPalettes; #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); @@ -1712,8 +1739,11 @@ class AudioReactive : public Usermod { { JsonObject top = root[FPSTR(_name)]; bool configComplete = !top.isNull(); + bool oldEnabled = enabled; + bool oldAddPalettes = addPalettes; configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); + configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes); #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); @@ -1747,6 +1777,11 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort); configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled); + if (initDone) { + // add/remove custom/audioreactive palettes + if ((oldAddPalettes && !addPalettes) || (oldAddPalettes && !enabled)) removeAudioPalettes(); + if ((addPalettes && !oldAddPalettes && enabled) || (addPalettes && !oldEnabled && enabled)) createAudioPalettes(); + } // else setup() will create palettes return configComplete; } @@ -1822,6 +1857,92 @@ class AudioReactive : public Usermod { } }; +void AudioReactive::removeAudioPalettes(void) { + DEBUG_PRINTLN(F("Removing audio palettes.")); + while (palettes>0) { + strip.customPalettes.pop_back(); + DEBUG_PRINTLN(palettes); + palettes--; + } + DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size()); +} + +void AudioReactive::createAudioPalettes(void) { + DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size()); + if (palettes) return; + DEBUG_PRINTLN(F("Adding audio palettes.")); + for (int i=0; i= palettes) lastCustPalette -= palettes; + for (size_t pal=0; pal> 3; //1 bit per LED + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + if (SEGENV.call == 0) { + memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up + SEGENV.aux0 = 1; + } + 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); + for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times + unsigned i = random16(SEGLEN); + unsigned index = i >> 3; + unsigned bitNum = i & 0x07; + bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (SEGENV.aux0) { //dissolve to primary/palette - if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) /*|| wa*/) { + if (fadeUp) { if (color == SEGCOLOR(0)) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } else { SEGMENT.setPixelColor(i, color); } + bitWrite(SEGENV.data[index], bitNum, false); 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 (!fadeUp) { + SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; + bitWrite(SEGENV.data[index], bitNum, true); + } } } } @@ -628,6 +642,7 @@ uint16_t dissolve(uint32_t color) { if (SEGENV.step > (255 - SEGMENT.speed) + 15U) { SEGENV.aux0 = !SEGENV.aux0; SEGENV.step = 0; + memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading } else { SEGENV.step++; } @@ -681,7 +696,7 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!; * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_flash_sparkle(void) { - if (!SEGMENT.check2) for(uint16_t i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } @@ -816,7 +831,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett 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 = SEGMENT.get_random_wheel_index(SEGENV.aux0); + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); } color1 = SEGMENT.color_wheel(SEGENV.aux0); } @@ -1056,7 +1071,7 @@ uint16_t mode_chase_flash_random(void) { SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; if (SEGENV.aux1 == 0) { - SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); } } return delay; @@ -2182,7 +2197,7 @@ uint16_t mode_colortwinkle() { CRGB fastled_col, prev; 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++) { + for (int i = 0; i < SEGLEN; i++) { fastled_col = SEGMENT.getPixelColor(i); prev = fastled_col; uint16_t index = i >> 3; @@ -2209,9 +2224,9 @@ uint16_t mode_colortwinkle() { } } - for (uint16_t j = 0; j <= SEGLEN / 50; j++) { + for (unsigned 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 (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times int i = random16(SEGLEN); if (SEGMENT.getPixelColor(i) == 0) { fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); @@ -2590,14 +2605,14 @@ uint16_t mode_twinklefox() { return twinklefox_base(false); } -static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!"; +static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!"; uint16_t mode_twinklecat() { return twinklefox_base(true); } -static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!"; +static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!"; uint16_t mode_halloween_eyes() @@ -4755,7 +4770,7 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4789,7 +4804,7 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4841,7 +4856,7 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4862,7 +4877,7 @@ 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4907,7 +4922,7 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4933,7 +4948,7 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur a // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4967,7 +4982,7 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -4994,7 +5009,7 @@ typedef struct ColorCount { } colorCount; 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5100,7 +5115,7 @@ static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2 // 2D Hiphotic // ///////////////////////// uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5132,7 +5147,7 @@ typedef struct Julia { } julia; uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5238,7 +5253,7 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p // 2D Lissajous // ////////////////////////////// uint16_t mode_2DLissajous(void) { // By: Andrew Tuline - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5266,7 +5281,7 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F // 2D Matrix // /////////////////////// uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5341,7 +5356,7 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra // 2D Metaballs // ///////////////////////// uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline. - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5400,7 +5415,7 @@ 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5423,7 +5438,7 @@ 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5463,7 +5478,7 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5514,7 +5529,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale; // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5536,7 +5551,7 @@ 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5567,7 +5582,7 @@ static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fa // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5601,7 +5616,7 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5651,7 +5666,7 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5690,7 +5705,7 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5733,7 +5748,7 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2 //// 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5805,7 +5820,7 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; //// 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5895,7 +5910,7 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate, //// 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -5993,7 +6008,7 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail; // 2D Scrolling text // //////////////////////////// uint16_t mode_2Dscrollingtext(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6040,6 +6055,8 @@ uint16_t mode_2Dscrollingtext(void) { 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 if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour); + else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime)); } const int numberOfLetters = strlen(text); @@ -6093,7 +6110,7 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off //////////////////////////// //// 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6247,7 +6264,7 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma ///////////////////////// // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -6291,7 +6308,7 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B ///////////////////////// // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7278,7 +7295,7 @@ static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color, // ** 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); const uint16_t cols = SEGMENT.virtualWidth(); @@ -7336,7 +7353,7 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# // ** 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7428,7 +7445,7 @@ static uint8_t akemi[] PROGMEM = { }; uint16_t mode_2DAkemi(void) { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7496,7 +7513,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea // 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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7551,7 +7568,7 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@ //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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7663,7 +7680,7 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; //Stepko and Sutaburosu // adapted for WLED by @blazoncek uint16_t mode_2Doctopus() { - if (!strip.isMatrix) return mode_static(); // not a 2D set-up + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); @@ -7719,7 +7736,7 @@ static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offse //@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 + if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); diff --git a/wled00/FX.h b/wled00/FX.h index c9306e80..3d4e9844 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -109,20 +109,15 @@ #define PINK (uint32_t)0xFF1493 #define ULTRAWHITE (uint32_t)0xFFFFFFFF #define DARKSLATEGRAY (uint32_t)0x2F4F4F -#define DARKSLATEGREY (uint32_t)0x2F4F4F +#define DARKSLATEGREY DARKSLATEGRAY -// options -// bit 7: segment is in transition mode -// bits 4-6: TBD -// bit 3: mirror effect within segment -// bit 2: segment is on -// bit 1: reverse segment -// bit 0: segment is selected +// segment options #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 TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed +#define MIRROR_Y_2D (uint16_t)0x0080 +#define REVERSE_Y_2D (uint16_t)0x0040 +#define RESET_REQ (uint16_t)0x0020 +#define FROZEN (uint16_t)0x0010 #define MIRROR (uint16_t)0x0008 #define SEGMENT_ON (uint16_t)0x0004 #define REVERSE (uint16_t)0x0002 @@ -348,12 +343,11 @@ typedef struct Segment { 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") + bool reverse_y : 1; // 6 : reversed Y (2D) + bool mirror_y : 1; // 7 : mirrored Y (2D) + bool transpose : 1; // 8 : transposed (2D, swapped X & Y) + uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...) + uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off") uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups }; }; @@ -484,7 +478,6 @@ typedef struct Segment { _dataLen(0), _t(nullptr) { - //refreshLightCapabilities(); #ifdef WLED_DEBUG //Serial.printf("-- Creating segment: %p\n", this); #endif @@ -519,6 +512,7 @@ typedef struct Segment { inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool isSelected(void) const { return selected; } + inline bool isInTransition(void) const { return _t != nullptr; } 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; } @@ -569,33 +563,33 @@ typedef struct Segment { void restoreSegenv(tmpsegd_t &tmpSegD); #endif 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); + uint8_t currentBri(bool useCct = false); + uint8_t currentMode(void); + uint32_t currentColor(uint8_t slot); 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 + inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } + inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } 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); } + inline 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); } + inline 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); + inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } + inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } + inline void addPixelColor(int n, uint32_t color, bool fast = false) { setPixelColor(n, color_add(getPixelColor(n), color, fast)); } + inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } + inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } + inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } 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); @@ -606,19 +600,20 @@ typedef struct Segment { #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 + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } 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); } + inline 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); } + inline 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); + inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); } + inline 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); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); } + inline 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); } + inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } + inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } 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); @@ -628,43 +623,43 @@ typedef struct Segment { 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 + 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, int8_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, CRGB c2, int8_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 + 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 + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_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 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)); } + inline void blur2d(fract8 blur_amount) { blur(blur_amount); } + inline 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, uint32_t = 0, int8_t = 0) {} - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} - void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} + inline uint16_t XY(uint16_t x, uint16_t y) { return x; } + inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } + inline 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); } + inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); } + inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } + inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } + inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } + inline 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); } + inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } + inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } + inline void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} + inline void blurRow(uint16_t row, fract8 blur_amount) {} + inline void blurCol(uint16_t col, fract8 blur_amount) {} + inline void moveX(int8_t delta, bool wrap = false) {} + inline void moveY(int8_t delta, bool wrap = false) {} + inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} + inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} + inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} + inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {} #endif } segment; //static int segSize = sizeof(Segment); @@ -759,23 +754,22 @@ class WS2812FX { // 96 bytes setCCT(uint16_t k), setBrightness(uint8_t b, bool direct = false), setRange(uint16_t i, uint16_t i2, uint32_t col), - setTransitionMode(bool t), 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, uint32_t c), show(void), - setTargetFps(uint8_t fps); - - 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 + setTargetFps(uint8_t fps), + addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name), // add effect to the list; defined in FX.cpp + setupEffectData(void); // add default effects to the list; defined in FX.cpp + inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); } + inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } + inline 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)); } + inline void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) // 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); } @@ -881,16 +875,14 @@ class WS2812FX { // 96 bytes std::vector panel; #endif - void - setUpMatrix(), - setPixelColorXY(int x, int y, uint32_t c); + void setUpMatrix(); // 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)); } + inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, c); } + inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - uint32_t - getPixelColorXY(uint16_t, uint16_t); + inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);} // end 2D support diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 60a7e68f..d2622f90 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() { #ifdef WLED_DEBUG DEBUG_PRINT(F("Matrix ledmap:")); - for (uint16_t i=0; i= _length) return; - busses.setPixelColor(index, col); -} - -// returns RGBW values of pixel -uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { -#ifndef WLED_DISABLE_2D - uint16_t index = (y * Segment::maxWidth + x); -#else - uint16_t index = x; -#endif - if (index < customMappingSize) index = customMappingTable[index]; - if (index >= _length) return 0; - return busses.getPixelColor(index); -} /////////////////////////////////////////////////////////// // Segment:: routines @@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { #ifndef WLED_DISABLE_2D // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) -uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) { +uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y) +{ uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1) return isActive() ? (x%width) + (y%height) * width : 0; } -void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) +void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col) { if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint8_t _bri_t = currentBri(on ? opacity : 0); + uint8_t _bri_t = currentBri(); if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); @@ -289,7 +265,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) } // returns RGBW values of pixel -uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { +uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) { if (!isActive()) return 0; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit if (reverse ) x = virtualWidth() - x - 1; @@ -301,41 +277,9 @@ uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) { return strip.getPixelColorXY(start + x, startY + y); } -// Blends the specified color with the existing pixel color. -void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { - setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); -} - -// Adds the specified color with the existing pixel color perserving color balance. -void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { - if (!isActive()) return; // not active - if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint32_t col = getPixelColorXY(x,y); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } - setPixelColorXY(x, y, col); -} - -void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { - if (!isActive()) return; // not active - CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); - setPixelColorXY(x, y, pix); -} - // blurRow: perform a blur on a row of a rectangular matrix void Segment::blurRow(uint16_t row, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -344,7 +288,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) { uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint_fast16_t x = 0; x < cols; x++) { + for (unsigned x = 0; x < cols; x++) { CRGB cur = getPixelColorXY(x, row); CRGB before = cur; // remember color before blur CRGB part = cur; @@ -363,7 +307,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) { // blurCol: perform a blur on a column of a rectangular matrix void Segment::blurCol(uint16_t col, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -372,7 +316,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) { uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint_fast16_t y = 0; y < rows; y++) { + for (unsigned y = 0; y < rows; y++) { CRGB cur = getPixelColorXY(col, y); CRGB part = cur; CRGB before = cur; // remember color before blur @@ -391,7 +335,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) { // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); const uint16_t dim1 = vertical ? rows : cols; @@ -401,7 +345,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { const float keep = 3.f - 2.f*seep; // 1D box blur CRGB tmp[dim1]; - for (uint16_t j = 0; j < dim1; j++) { + for (int j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow @@ -417,7 +361,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { b = (curr.b*keep + (prev.b + next.b)*seep) / 3; tmp[j] = CRGB(r,g,b); } - for (uint16_t j = 0; j < dim1; j++) { + for (int j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; setPixelColorXY(x, y, tmp[j]); @@ -440,7 +384,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::blur1d(fract8 blur_amount) { const uint16_t rows = virtualHeight(); - for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount); + for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount); } void Segment::moveX(int8_t delta, bool wrap) { @@ -498,7 +442,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { } void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active + if (!isActive() || radius == 0) return; // not active // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; @@ -523,7 +467,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active + if (!isActive() || radius == 0) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); for (int16_t y = -radius; y <= radius; y++) { @@ -540,7 +484,7 @@ void Segment::nscale8(uint8_t scale) { if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); - for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale)); } } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index cd4a4f14..cac3687f 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -89,25 +89,22 @@ bool Segment::_modeBlend = false; Segment::Segment(const Segment &orig) { //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); - transitional = false; // copied segment cannot be in transition + _t = nullptr; // copied segment cannot be in transition name = nullptr; data = nullptr; _dataLen = 0; - _t = nullptr; 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._t) { _t = new Transition(orig._t->_dur); } } // move constructor Segment::Segment(Segment &&orig) noexcept { //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); - orig.transitional = false; // old segment cannot be in transition any more + orig._t = nullptr; // old segment cannot be in transition any more orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; - orig._t = nullptr; } // copy assignment @@ -115,27 +112,17 @@ Segment& Segment::operator= (const Segment &orig) { //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this); if (this != &orig) { // clean destination - transitional = false; // copied segment cannot be in transition - if (name) delete[] name; - if (_t) { - #ifndef WLED_DISABLE_MODE_BLEND - if (_t->_segT._dataT) free(_t->_segT._dataT); - #endif - delete _t; - } + if (name) { delete[] name; name = nullptr; } + stopTransition(); deallocateData(); // copy source memcpy((void*)this, (void*)&orig, sizeof(Segment)); - transitional = false; // erase pointers to allocated data - name = nullptr; data = nullptr; _dataLen = 0; - _t = nullptr; // copy source data 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._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } } return *this; } @@ -144,27 +131,19 @@ Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (Segment &&orig) noexcept { //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); if (this != &orig) { - transitional = false; // just temporary if (name) { delete[] name; name = nullptr; } // free old name + stopTransition(); deallocateData(); // free old runtime data - 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)); - orig.transitional = false; // old segment cannot be in transition orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; - orig._t = nullptr; + orig._t = nullptr; // old segment cannot be in transition } return *this; } -bool Segment::allocateData(size_t len) { +bool IRAM_ATTR Segment::allocateData(size_t len) { if (data && _dataLen == len) return true; //already allocated //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this); deallocateData(); @@ -185,7 +164,7 @@ bool Segment::allocateData(size_t len) { return true; } -void Segment::deallocateData() { +void IRAM_ATTR Segment::deallocateData() { if (!data) { _dataLen = 0; return; } //DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data); if ((Segment::getUsedSegmentData() > 0) && (_dataLen > 0)) { // check that we don't have a dangling / inconsistent data pointer @@ -217,7 +196,7 @@ void Segment::resetIfRequired() { reset = false; } -CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { +CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; //default palette. Differs depending on effect @@ -237,7 +216,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { switch (pal) { case 0: //default palette. Exceptions for specific effects above targetPalette = PartyColors_p; break; - case 1: {//periodically replace palette with a random one. Transition palette change in 500ms + case 1: {//periodically replace palette with a random one unsigned long timeSinceLastChange = millis() - _lastPaletteChange; if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { _randomPalette = _newRandomPalette; @@ -301,50 +280,48 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { } void Segment::startTransition(uint16_t dur) { - if (!dur) { - if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton() - else transitional = false; + if (dur == 0) { + if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton() return; } - if (transitional && _t) return; // already in transition no need to store anything + if (isInTransition()) return; // already in transition no need to store anything // starting a transition has to occur before change so we get current values 1st _t = new Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data //DEBUG_PRINTF("-- Started transition: %p\n", this); - CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); - _t->_palT = _palT; + loadPalette(_t->_palT, palette); _t->_briT = on ? opacity : 0; _t->_cctT = cct; #ifndef WLED_DISABLE_MODE_BLEND - 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; + if (modeBlending) { + swapSegenv(_t->_segT); + _t->_modeT = mode; + _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) for %p: %p\n", _dataLen, this, _t->_segT._dataT); + memcpy(_t->_segT._dataT, data, _dataLen); + _t->_segT._dataLenT = _dataLen; + } } + } else { + for (size_t i=0; i_segT._colorT[i] = colors[i]; } #else for (size_t i=0; i_colorT[i] = colors[i]; #endif - 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) { + if (isInTransition()) { #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); + //DEBUG_PRINTF("-- Released duplicate data (%d) for %p: %p\n", _t->_segT._dataLenT, this, _t->_segT._dataT); free(_t->_segT._dataT); _t->_segT._dataT = nullptr; _t->_segT._dataLenT = 0; @@ -356,14 +333,13 @@ void Segment::stopTransition() { } void Segment::handleTransition() { - if (!transitional) return; uint16_t _progress = progress(); if (_progress == 0xFFFFU) stopTransition(); } // transition progression between 0-65535 -uint16_t Segment::progress() { - if (transitional && _t) { +uint16_t IRAM_ATTR Segment::progress() { + if (isInTransition()) { unsigned long timeNow = millis(); if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; } @@ -420,8 +396,8 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { _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 !!) + _t->_segT._dataT = data; + _t->_segT._dataLenT = _dataLen; } options = tmpSeg._optionsT; for (size_t i=0; i_cctT * (0xFFFFU - prog)) >> 16; - else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; + uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog; + curBri += (useCct ? _t->_cctT : (on ? _t->_briT : 0)) * (0xFFFFU - prog); + return curBri / 0xFFFFU; } - return briNew; + return (useCct ? cct : (on ? opacity : 0)); } -uint8_t Segment::currentMode(uint8_t newMode) { +uint8_t IRAM_ATTR Segment::currentMode() { #ifndef WLED_DISABLE_MODE_BLEND - uint16_t prog = progress(); // implicit check for transitional & _t in progress() - if (prog < 0xFFFFU) return _t->_modeT; + uint16_t prog = progress(); + if (modeBlending && prog < 0xFFFFU) return _t->_modeT; #endif - return newMode; + return mode; } -uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { +uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) { #ifndef WLED_DISABLE_MODE_BLEND - return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew; + return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot]; #else - return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; + return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot]; #endif } -CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { +CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { loadPalette(targetPalette, pal); - if (progress() < 0xFFFFU) { + uint16_t prog = progress(); + if (strip.paletteFade && prog < 0xFFFFU) { // blend palettes // 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 - unsigned long timeMS = millis() - _t->_start; - uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; + uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); targetPalette = _t->_palT; // copy transitioning/temporary palette } @@ -500,6 +477,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t && (!grp || (grouping == grp && spacing == spc)) && (ofs == UINT16_MAX || ofs == offset)) return; + stateChanged = true; // send UDP/WS broadcast + if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) if (grp) { // prevent assignment of 0 grouping = grp; @@ -510,6 +489,10 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t } if (ofs < UINT16_MAX) offset = ofs; + DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); + DEBUG_PRINT(','); DEBUG_PRINT(i2); + DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y); + DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y); markForReset(); if (boundsUnchanged) return; @@ -564,7 +547,6 @@ void Segment::setCCT(uint16_t k) { void Segment::setOpacity(uint8_t o) { if (opacity == o) return; if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change - DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o); opacity = o; stateChanged = true; // send UDP/WS broadcast } @@ -574,14 +556,16 @@ void Segment::setOption(uint8_t n, bool val) { if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change if (val) options |= 0x01 << n; else options &= ~(0x01 << n); - if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast + if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast } void Segment::setMode(uint8_t fx, bool loadDefaults) { // if we have a valid mode & is not reserved if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx != mode) { - if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions +#ifndef WLED_DISABLE_MODE_BLEND + if (modeBlending) startTransition(strip.getTransition()); // set effect transitions +#endif mode = fx; // load default values from effect string if (loadDefaults) { @@ -595,7 +579,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) { sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); - sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1); + sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 3); sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt; sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt; @@ -619,21 +603,21 @@ void Segment::setPalette(uint8_t pal) { } // 2D matrix -uint16_t Segment::virtualWidth() const { +uint16_t IRAM_ATTR Segment::virtualWidth() const { uint16_t groupLen = groupLength(); uint16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen; if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED return vWidth; } -uint16_t Segment::virtualHeight() const { +uint16_t IRAM_ATTR Segment::virtualHeight() const { uint16_t groupLen = groupLength(); uint16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen; if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED return vHeight; } -uint16_t Segment::nrOfVStrips() const { +uint16_t IRAM_ATTR Segment::nrOfVStrips() const { uint16_t vLen = 1; #ifndef WLED_DISABLE_2D if (is2D()) { @@ -648,7 +632,7 @@ uint16_t Segment::nrOfVStrips() const { } // 1D strip -uint16_t Segment::virtualLength() const { +uint16_t IRAM_ATTR Segment::virtualLength() const { #ifndef WLED_DISABLE_2D if (is2D()) { uint16_t vW = virtualWidth(); @@ -743,7 +727,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) #endif uint16_t len = length(); - uint8_t _bri_t = currentBri(on ? opacity : 0); + uint8_t _bri_t = currentBri(); if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); @@ -820,7 +804,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa) } } -uint32_t Segment::getPixelColor(int i) +uint32_t IRAM_ATTR Segment::getPixelColor(int i) { if (!isActive()) return 0; // not active #ifndef WLED_DISABLE_2D @@ -877,10 +861,11 @@ uint8_t Segment::differs(Segment& b) const { if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS; if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS; - //bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected] - if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT; + //bit pattern: (msb first) + // set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected] + if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT; if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL; - for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; + for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; return d; } @@ -912,7 +897,7 @@ void Segment::refreshLightCapabilities() { segStopIdx = stop; } - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + for (unsigned b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; if (!bus->isOk()) continue; @@ -942,43 +927,12 @@ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D - for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { if (is2D()) setPixelColorXY(x, y, c); else setPixelColor(x, c); } } -// Blends the specified color with the existing pixel color. -void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) { - setPixelColor(n, color_blend(getPixelColor(n), color, blend)); -} - -// Adds the specified color with the existing pixel color perserving color balance. -void Segment::addPixelColor(int n, uint32_t color, bool fast) { - if (!isActive()) return; // not active - uint32_t col = getPixelColor(n); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } - setPixelColor(n, col); -} - -void Segment::fadePixelColor(uint16_t n, uint8_t fade) { - if (!isActive()) return; // not active - CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); - setPixelColor(n, pix); -} - /* * fade out function, higher rate = quicker fade */ @@ -996,7 +950,7 @@ void Segment::fade_out(uint8_t rate) { int g2 = G(color); int b2 = B(color); - for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); int w1 = W(color); int r1 = R(color); @@ -1025,49 +979,40 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D - for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { - if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy)); - else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy)); + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy)); + else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy)); } } /* * blurs segment content, source: FastLED colorutils.cpp */ -void Segment::blur(uint8_t blur_amount) -{ +void Segment::blur(uint8_t blur_amount) { if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - const uint_fast16_t cols = virtualWidth(); - const uint_fast16_t rows = virtualHeight(); - for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows - for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns + const unsigned cols = virtualWidth(); + const unsigned rows = virtualHeight(); + for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows + for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns return; } #endif uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - uint_fast16_t vlength = virtualLength(); - for(uint_fast16_t i = 0; i < vlength; i++) - { - CRGB cur = CRGB(getPixelColor(i)); - CRGB part = cur; - CRGB before = cur; // remember color before blur - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if(i > 0) { + uint32_t carryover = BLACK; + unsigned vlength = virtualLength(); + for (unsigned i = 0; i < vlength; i++) { + uint32_t cur = getPixelColor(i); + uint32_t part = color_fade(cur, seep); + cur = color_add(color_fade(cur, keep), carryover, true); + if (i > 0) { uint32_t c = getPixelColor(i-1); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); + setPixelColor(i-1, color_add(c, part, true)); } - if (before != cur) // optimization: only set pixel if color has changed - setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue); + setPixelColor(i, cur); carryover = part; } } @@ -1080,7 +1025,7 @@ void Segment::blur(uint8_t blur_amount) uint32_t Segment::color_wheel(uint8_t pos) { if (palette) return color_from_palette(pos, false, true, 0); pos = 255 - pos; - if(pos < 85) { + if (pos < 85) { return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); } else if(pos < 170) { pos -= 85; @@ -1091,21 +1036,6 @@ uint32_t Segment::color_wheel(uint8_t pos) { } } -/* - * Returns a new, random wheel index with a minimum distance of 42 from pos. - */ -uint8_t Segment::get_random_wheel_index(uint8_t pos) { - uint8_t r = 0, x = 0, y = 0, d = 0; - - while(d < 42) { - r = random8(); - x = abs(pos - r); - y = 255 - x; - d = MIN(x, y); - } - return r; -} - /* * Gets a single color from the currently selected palette. * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. @@ -1115,24 +1045,21 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) { * @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling) * @returns Single color from palette */ -uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) -{ +uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) { // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { - uint32_t color = currentColor(mcol, colors[mcol]); + uint32_t color = currentColor(mcol); color = gamma32(color); if (pbri == 255) return color; - return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri)); + return color_fade(color, pbri, true); } uint8_t paletteIndex = i; if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); - if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col; + if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" CRGBPalette16 curPal; - if (transitional && _t) curPal = _t->_palT; - else loadPalette(curPal, palette); - fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + curPal = currentPalette(curPal, palette); + CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0); } @@ -1143,8 +1070,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ /////////////////////////////////////////////////////////////////////////////// //do not call this method from system context (network callback) -void WS2812FX::finalizeInit(void) -{ +void WS2812FX::finalizeInit(void) { //reset segment runtimes for (segment &seg : _segments) { seg.markForReset(); @@ -1166,7 +1092,7 @@ void WS2812FX::finalizeInit(void) const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); uint16_t prevLen = 0; - for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { + for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { uint8_t defPin[] = {defDataPins[i]}; uint16_t start = prevLen; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; @@ -1177,7 +1103,7 @@ void WS2812FX::finalizeInit(void) } _length = 0; - for (uint8_t i=0; igetStart() + bus->getLength() > MAX_LEDS) break; @@ -1234,24 +1160,24 @@ void WS2812FX::service() { if (!seg.freeze) { //only run effect function if not frozen _virtualSegmentLength = seg.virtualLength(); - _colors_t[0] = seg.currentColor(0, seg.colors[0]); - _colors_t[1] = seg.currentColor(1, seg.colors[1]); - _colors_t[2] = seg.currentColor(2, seg.colors[2]); - seg.currentPalette(_currentPalette, seg.palette); + _colors_t[0] = seg.currentColor(0); + _colors_t[1] = seg.currentColor(1); + _colors_t[2] = seg.currentColor(2); + seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference - 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]); + if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB); + for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); // Effect blending // When two effects are being blended, each may have different segment data, this - // data needs to be saved first and then restored before running previous/transitional mode. + // data needs to be saved first and then restored before running previous 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 + [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // 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) { + if (modeBlending && 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) @@ -1262,7 +1188,7 @@ void WS2812FX::service() { } #endif seg.call++; - if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition + if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition } seg.next_time = nowUp + delay; @@ -1287,15 +1213,13 @@ void WS2812FX::service() { #endif } -void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) -{ +void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) { if (i < customMappingSize) i = customMappingTable[i]; if (i >= _length) return; busses.setPixelColor(i, col); } -uint32_t WS2812FX::getPixelColor(uint16_t i) -{ +uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) { if (i < customMappingSize) i = customMappingTable[i]; if (i >= _length) return 0; return busses.getPixelColor(i); @@ -1335,13 +1259,13 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() { size_t pLen = 0; //getLengthPhysical(); size_t powerSum = 0; - for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) { + for (unsigned bNum = 0; bNum < busses.getNumBusses(); bNum++) { Bus *bus = busses.getBus(bNum); if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses uint16_t len = bus->getLength(); pLen += len; uint32_t busPowerSum = 0; - for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED + for (unsigned i = 0; i < len; i++) { //sum up the usage of each LED uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling byte r = R(c), g = G(c), b = B(c), w = W(c); @@ -1399,12 +1323,12 @@ void WS2812FX::show(void) { // or async show has a separate buffer (ESP32 RMT and I2S are ok) if (newBri < _brightness) busses.setBrightness(_brightness); - unsigned long now = millis(); - size_t diff = now - _lastShow; + unsigned long showNow = millis(); + size_t diff = showNow - _lastShow; size_t fpsCurr = 200; if (diff > 0) fpsCurr = 1000 / diff; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) - _lastShow = now; + _lastShow = showNow; } /** @@ -1486,8 +1410,7 @@ uint8_t WS2812FX::getActiveSegsLightCapabilities(bool selectedOnly) { return totalLC; } -uint8_t WS2812FX::getFirstSelectedSegId(void) -{ +uint8_t WS2812FX::getFirstSelectedSegId(void) { size_t i = 0; for (segment &seg : _segments) { if (seg.isActive() && seg.isSelected()) return i; @@ -1597,10 +1520,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group _qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY; _qGrouping = grouping; _qSpacing = spacing; _qOffset = offset; _queuedChangesSegId = segId; + DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId); return; // queued changes are applied immediately after effect function returns } _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); + if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector } void WS2812FX::setUpSegmentFromQueuedChanges() { @@ -1609,10 +1534,6 @@ void WS2812FX::setUpSegmentFromQueuedChanges() { _queuedChangesSegId = 255; } -void WS2812FX::restartRuntime() { - for (segment &seg : _segments) seg.markForReset(); -} - void WS2812FX::resetSegments() { _segments.clear(); // destructs all Segment as part of clearing #ifndef WLED_DISABLE_2D @@ -1729,7 +1650,7 @@ void WS2812FX::fixInvalidSegments() { bool WS2812FX::checkSegmentAlignment() { bool aligned = false; for (segment &seg : _segments) { - for (uint8_t b = 0; bgetStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; } @@ -1752,17 +1673,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) { } void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { - if (i2 >= i) - { - for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); - } else - { - for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col); - } -} - -void WS2812FX::setTransitionMode(bool t) { - for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); + if (i2 < i) std::swap(i,i2); + for (unsigned x = i; x <= i2; x++) setPixelColor(x, col); } #ifdef WLED_DEBUG @@ -1793,7 +1705,7 @@ void WS2812FX::loadCustomPalettes() { if (readObjectFromFile(fileName, nullptr, &pDoc)) { JsonArray pal = pDoc[F("palette")]; - if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries) + if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries) if (pal[0].is() && pal[1].is()) { // we have an array of index & hex strings size_t palSize = MIN(pal.size(), 36); @@ -1802,7 +1714,7 @@ void WS2812FX::loadCustomPalettes() { uint8_t rgbw[] = {0,0,0,0}; tcp[ j ] = (uint8_t) pal[ i ].as(); // index colorFromHexString(rgbw, pal[i+1].as()); // will catch non-string entires - for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component + for (size_t c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3])); } } else { @@ -1810,13 +1722,15 @@ void WS2812FX::loadCustomPalettes() { palSize -= palSize % 4; // make sure size is multiple of 4 for (size_t i=0; i()<256; i+=4) { tcp[ i ] = (uint8_t) pal[ i ].as(); // index - tcp[i+1] = (uint8_t) pal[i+1].as(); // R - tcp[i+2] = (uint8_t) pal[i+2].as(); // G - tcp[i+3] = (uint8_t) pal[i+3].as(); // B + tcp[i+1] = gamma8((uint8_t) pal[i+1].as()); // R + tcp[i+2] = gamma8((uint8_t) pal[i+2].as()); // G + tcp[i+3] = gamma8((uint8_t) pal[i+3].as()); // B DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); } } customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); + } else { + DEBUG_PRINTLN(F("Wrong palette format.")); } } } else { @@ -1832,7 +1746,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { char fileName[32]; strcpy_P(fileName, PSTR("/ledmap")); if (n) sprintf(fileName +7, "%d", n); - strcat(fileName, ".json"); + strcat_P(fileName, PSTR(".json")); bool isFile = WLED_FS.exists(fileName); if (!isFile) { @@ -1866,7 +1780,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (!map.isNull() && map.size()) { // not an empty map customMappingSize = map.size(); customMappingTable = new uint16_t[customMappingSize]; - for (uint16_t i=0; i> 4; - for (uint8_t i = 0; i < _count; i++) { + for (unsigned i = 0; i < _count; i++) { if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { return _mappings[i].colorOrder | (swapW << 4); } @@ -118,7 +118,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) _iType = PolyBus::getI(bc.type, _pins, nr); if (_iType == I_NONE) return; if (bc.doubleBuffer && !allocData(bc.count * (Bus::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count - _buffering = bc.doubleBuffer; + //_buffering = bc.doubleBuffer; uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz); @@ -128,7 +128,7 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com) void BusDigital::show() { if (!_valid) return; - if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop + if (_data) { // use _buffering this causes ~20% FPS drop size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); for (size_t i=0; i<_len; i++) { size_t offset = i*channels; @@ -153,7 +153,7 @@ void BusDigital::show() { #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, !_data); // faster if buffer consistency is not important (use !_buffering this causes 20% FPS drop) } bool BusDigital::canShow() { @@ -173,14 +173,14 @@ void BusDigital::setBrightness(uint8_t b) { Bus::setBrightness(b); PolyBus::setBrightness(_busPtr, _iType, b); - if (_buffering) return; + if (_data) return; // use _buffering this causes ~20% FPS drop // must update/repaint every LED in the NeoPixelBus buffer to the new brightness // the only case where repainting is unnecessary is when all pixels are set after the brightness change but before the next show // (which we can't rely on) uint16_t hwLen = _len; if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus - for (uint_fast16_t i = 0; i < hwLen; i++) { + for (unsigned i = 0; i < hwLen; i++) { // use 0 as color order, actual order does not matter here as we just update the channel values as-is uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0),prevBri); PolyBus::setPixelColor(_busPtr, _iType, i, c, 0); @@ -200,7 +200,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { if (!_valid) return; if (Bus::hasWhite(_type)) c = autoWhiteCalc(c); if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT - if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop + if (_data) { // use _buffering this causes ~20% FPS drop size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix*channels; if (Bus::hasRGB(_type)) { @@ -228,9 +228,9 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { } // returns original color if global buffering is enabled, else returns lossly restored color from bus -uint32_t BusDigital::getPixelColor(uint16_t pix) { +uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) { if (!_valid) return 0; - if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop + if (_data) { // use _buffering this causes ~20% FPS drop size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type); size_t offset = pix*channels; uint32_t c; @@ -261,7 +261,7 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) { uint8_t BusDigital::getPins(uint8_t* pinArray) { uint8_t numPins = IS_2PIN(_type) ? 2 : 1; - for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i]; + for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; return numPins; } @@ -305,7 +305,7 @@ BusPwm::BusPwm(BusConfig &bc) } #endif - for (uint8_t i = 0; i < numPins; i++) { + for (unsigned i = 0; i < numPins; i++) { uint8_t currentPin = bc.pins[i]; if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { deallocatePins(); return; @@ -384,7 +384,7 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) { void BusPwm::show() { if (!_valid) return; uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { + for (unsigned i = 0; i < numPins; i++) { uint8_t scaled = (_data[i] * _bri) / 255; if (_reversed) scaled = 255 - scaled; #ifdef ESP8266 @@ -398,7 +398,7 @@ void BusPwm::show() { uint8_t BusPwm::getPins(uint8_t* pinArray) { if (!_valid) return 0; uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { + for (unsigned i = 0; i < numPins; i++) { pinArray[i] = _pins[i]; } return numPins; @@ -406,7 +406,7 @@ uint8_t BusPwm::getPins(uint8_t* pinArray) { void BusPwm::deallocatePins() { uint8_t numPins = NUM_PWM_PINS(_type); - for (uint8_t i = 0; i < numPins; i++) { + for (unsigned i = 0; i < numPins; i++) { pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); if (!pinManager.isPinOk(_pins[i])) continue; #ifdef ESP8266 @@ -512,7 +512,7 @@ void BusNetwork::show() { } uint8_t BusNetwork::getPins(uint8_t* pinArray) { - for (uint8_t i = 0; i < 4; i++) { + for (unsigned i = 0; i < 4; i++) { pinArray[i] = _client[i]; } return 4; @@ -566,24 +566,24 @@ void BusManager::removeAll() { DEBUG_PRINTLN(F("Removing all.")); //prevents crashes due to deleting busses while in use. while (!canAllShow()) yield(); - for (uint8_t i = 0; i < numBusses; i++) delete busses[i]; + for (unsigned i = 0; i < numBusses; i++) delete busses[i]; numBusses = 0; } void BusManager::show() { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { busses[i]->show(); } } void BusManager::setStatusPixel(uint32_t c) { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { busses[i]->setStatusPixel(c); } } void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { Bus* b = busses[i]; uint16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; @@ -592,7 +592,7 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) { } void BusManager::setBrightness(uint8_t b) { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { busses[i]->setBrightness(b); } } @@ -607,7 +607,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { } uint32_t BusManager::getPixelColor(uint16_t pix) { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { Bus* b = busses[i]; uint16_t bstart = b->getStart(); if (pix < bstart || pix >= bstart + b->getLength()) continue; @@ -617,7 +617,7 @@ uint32_t BusManager::getPixelColor(uint16_t pix) { } bool BusManager::canAllShow() { - for (uint8_t i = 0; i < numBusses; i++) { + for (unsigned i = 0; i < numBusses; i++) { if (!busses[i]->canShow()) return false; } return true; @@ -631,7 +631,7 @@ Bus* BusManager::getBus(uint8_t busNr) { //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) uint16_t BusManager::getTotalLength() { uint16_t len = 0; - for (uint8_t i=0; igetLength(); + for (unsigned i=0; igetLength(); return len; } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 4249c880..149cdcfb 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -222,7 +222,7 @@ class BusDigital : public Bus { uint16_t _frequencykHz; void * _busPtr; const ColorOrderMap &_colorOrderMap; - bool _buffering; // temporary until we figure out why comparison "_data != nullptr" causes severe FPS drop + //bool _buffering; // temporary until we figure out why comparison "_data" causes severe FPS drop inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) { if (restoreBri < 255) { diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 4234d9b0..89ac9aab 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -35,7 +35,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(simplifiedUI, id[F("sui")]); #endif - JsonObject nw_ins_0 = doc["nw"]["ins"][0]; + JsonObject nw = doc["nw"]; +#ifndef WLED_DISABLE_ESPNOW + CJSON(enableESPNow, nw[F("espnow")]); + getStringFromJson(linked_remote, nw[F("linked_remote")], 13); + linked_remote[12] = '\0'; +#endif + + JsonObject nw_ins_0 = nw["ins"][0]; getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33); //int nw_ins_0_pskl = nw_ins_0[F("pskl")]; //The WiFi PSK is normally not contained in the regular file for security reasons. @@ -216,7 +223,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0)) { // not an ADC analog pin - DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s); + DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]); + DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s); + DEBUG_PRINTLN(F(" is not an analog pin!")); btnPin[s] = -1; pinManager.deallocatePin(pin,PinOwner::Button); } @@ -357,6 +366,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject light_tr = light["tr"]; CJSON(fadeTransition, light_tr["mode"]); + CJSON(modeBlending, light_tr["fx"]); int tdd = light_tr["dur"] | -1; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; CJSON(strip.paletteFade, light_tr["pal"]); @@ -382,6 +392,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(udpPort, if_sync[F("port0")]); // 21324 CJSON(udpPort2, if_sync[F("port1")]); // 65506 +#ifndef WLED_DISABLE_ESPNOW + CJSON(useESPNowSync, if_sync[F("espnow")]); +#endif + JsonObject if_sync_recv = if_sync["recv"]; CJSON(receiveNotificationBrightness, if_sync_recv["bri"]); CJSON(receiveNotificationColor, if_sync_recv["col"]); @@ -389,17 +403,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(receiveGroups, if_sync_recv["grp"]); CJSON(receiveSegmentOptions, if_sync_recv["seg"]); CJSON(receiveSegmentBounds, if_sync_recv["sb"]); - //! following line might be a problem if called after boot - receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions); JsonObject if_sync_send = if_sync["send"]; - prev = notifyDirectDefault; - CJSON(notifyDirectDefault, if_sync_send[F("dir")]); - if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault; + CJSON(sendNotifications, if_sync_send["en"]); + sendNotificationsRT = sendNotifications; + CJSON(notifyDirect, if_sync_send[F("dir")]); CJSON(notifyButton, if_sync_send["btn"]); CJSON(notifyAlexa, if_sync_send["va"]); CJSON(notifyHue, if_sync_send["hue"]); - CJSON(notifyMacro, if_sync_send["macro"]); +// CJSON(notifyMacro, if_sync_send["macro"]); CJSON(syncGroups, if_sync_send["grp"]); if (if_sync_send[F("twice")]) udpNumRetries = 1; // import setting from 0.13 and earlier CJSON(udpNumRetries, if_sync_send["ret"]); @@ -409,7 +421,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]); JsonObject if_live = interfaces["live"]; - CJSON(receiveDirect, if_live["en"]); + CJSON(receiveDirect, if_live["en"]); // UDP/Hyperion realtime CJSON(useMainSegmentOnly, if_live[F("mso")]); CJSON(e131Port, if_live["port"]); // 5568 if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation @@ -453,13 +465,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(retainMqttMsg, if_mqtt[F("rtn")]); #endif -#ifndef WLED_DISABLE_ESPNOW - JsonObject remote = doc["remote"]; - CJSON(enable_espnow_remote, remote[F("remote_enabled")]); - getStringFromJson(linked_remote, remote[F("linked_remote")], 13); -#endif - - #ifndef WLED_DISABLE_HUESYNC JsonObject if_hue = interfaces["hue"]; CJSON(huePollingEnabled, if_hue["en"]); @@ -647,6 +652,10 @@ void serializeConfig() { #endif JsonObject nw = doc.createNestedObject("nw"); +#ifndef WLED_DISABLE_ESPNOW + nw[F("espnow")] = enableESPNow; + nw[F("linked_remote")] = linked_remote; +#endif JsonArray nw_ins = nw.createNestedArray("ins"); @@ -827,6 +836,7 @@ void serializeConfig() { JsonObject light_tr = light.createNestedObject("tr"); light_tr["mode"] = fadeTransition; + light_tr["fx"] = modeBlending; light_tr["dur"] = transitionDelayDefault / 100; light_tr["pal"] = strip.paletteFade; light_tr[F("rpc")] = randomPaletteChangeTime; @@ -848,6 +858,10 @@ void serializeConfig() { if_sync[F("port0")] = udpPort; if_sync[F("port1")] = udpPort2; +#ifndef WLED_DISABLE_ESPNOW + if_sync[F("espnow")] = useESPNowSync; +#endif + JsonObject if_sync_recv = if_sync.createNestedObject("recv"); if_sync_recv["bri"] = receiveNotificationBrightness; if_sync_recv["col"] = receiveNotificationColor; @@ -857,11 +871,12 @@ void serializeConfig() { if_sync_recv["sb"] = receiveSegmentBounds; JsonObject if_sync_send = if_sync.createNestedObject("send"); + if_sync_send["en"] = sendNotifications; if_sync_send[F("dir")] = notifyDirect; if_sync_send["btn"] = notifyButton; if_sync_send["va"] = notifyAlexa; if_sync_send["hue"] = notifyHue; - if_sync_send["macro"] = notifyMacro; +// if_sync_send["macro"] = notifyMacro; if_sync_send["grp"] = syncGroups; if_sync_send["ret"] = udpNumRetries; @@ -870,7 +885,7 @@ void serializeConfig() { if_nodes[F("bcast")] = nodeBroadcastEnabled; JsonObject if_live = interfaces.createNestedObject("live"); - if_live["en"] = receiveDirect; + if_live["en"] = receiveDirect; // UDP/Hyperion realtime if_live[F("mso")] = useMainSegmentOnly; if_live["port"] = e131Port; if_live[F("mc")] = e131Multicast; @@ -888,6 +903,7 @@ void serializeConfig() { if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("offset")] = arlsOffset; +#ifndef WLED_DISABLE_ALEXA JsonObject if_va = interfaces.createNestedObject("va"); if_va[F("alexa")] = alexaEnabled; @@ -896,6 +912,7 @@ void serializeConfig() { if_va_macros.add(macroAlexaOff); if_va["p"] = alexaNumPresets; +#endif #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); @@ -912,13 +929,6 @@ void serializeConfig() { if_mqtt_topics[F("group")] = mqttGroupTopic; #endif -#ifndef WLED_DISABLE_ESPNOW - JsonObject remote = doc.createNestedObject(F("remote")); - remote[F("remote_enabled")] = enable_espnow_remote; - remote[F("linked_remote")] = linked_remote; -#endif - - #ifndef WLED_DISABLE_HUESYNC JsonObject if_hue = interfaces.createNestedObject("hue"); if_hue["en"] = huePollingEnabled; @@ -1033,7 +1043,7 @@ bool deserializeConfigSec() { JsonObject ap = doc["ap"]; getStringFromJson(apPass, ap["psk"] , 65); - JsonObject interfaces = doc["if"]; + [[maybe_unused]] JsonObject interfaces = doc["if"]; #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces["mqtt"]; @@ -1072,7 +1082,7 @@ void serializeConfigSec() { JsonObject ap = doc.createNestedObject("ap"); ap["psk"] = apPass; - JsonObject interfaces = doc.createNestedObject("if"); + [[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if"); #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["psk"] = mqttPass; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 8c4baabb..21c27d65 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -35,23 +35,59 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) * color add function that preserves ratio * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule */ -uint32_t color_add(uint32_t c1, uint32_t c2) +uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) { - uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); - uint16_t max = r; - if (g > max) max = g; - if (b > max) max = b; - if (w > max) max = w; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + if (fast) { + uint8_t r = R(c1); + uint8_t g = G(c1); + uint8_t b = B(c1); + uint8_t w = W(c1); + r = qadd8(r, R(c2)); + g = qadd8(g, G(c2)); + b = qadd8(b, B(c2)); + w = qadd8(w, W(c2)); + return RGBW32(r,g,b,w); + } else { + uint32_t r = R(c1) + R(c2); + uint32_t g = G(c1) + G(c2); + uint32_t b = B(c1) + B(c2); + uint32_t w = W(c1) + W(c2); + uint16_t max = r; + if (g > max) max = g; + if (b > max) max = b; + if (w > max) max = w; + if (max < 256) return RGBW32(r, g, b, w); + else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + } +} + +/* + * fades color toward black + * if using "video" method the resulting color will never become black unless it is already black + */ +uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) +{ + uint8_t r = R(c1); + uint8_t g = G(c1); + uint8_t b = B(c1); + uint8_t w = W(c1); + if (video) { + r = scale8_video(r, amount); + g = scale8_video(g, amount); + b = scale8_video(b, amount); + w = scale8_video(w, amount); + } else { + r = scale8(r, amount); + g = scale8(g, amount); + b = scale8(b, amount); + w = scale8(w, amount); + } + return RGBW32(r, g, b, w); } void setRandomColor(byte* rgb) { - lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); + lastRandomIndex = get_random_wheel_index(lastRandomIndex); colorHStoRGB(lastRandomIndex*256,255,rgb); } diff --git a/wled00/const.h b/wled00/const.h index 6ee83451..5930445a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -166,7 +166,7 @@ #define CALL_MODE_NO_NOTIFY 5 #define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_HUE 7 -#define CALL_MODE_PRESET_CYCLE 8 +#define CALL_MODE_PRESET_CYCLE 8 //no longer used #define CALL_MODE_BLYNK 9 //no longer used #define CALL_MODE_ALEXA 10 #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only @@ -270,6 +270,10 @@ #define COL_ORDER_GBR 5 #define COL_ORDER_MAX 5 +//ESP-NOW +#define ESP_NOW_STATE_UNINIT 0 +#define ESP_NOW_STATE_ON 1 +#define ESP_NOW_STATE_ERROR 2 //Button type #define BTN_TYPE_NONE 0 @@ -313,10 +317,9 @@ #define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment #define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed #define SEG_OPTION_RESET 5 //Segment runtime requires reset -#define SEG_OPTION_TRANSITIONAL 6 -#define SEG_OPTION_REVERSED_Y 7 -#define SEG_OPTION_MIRROR_Y 8 -#define SEG_OPTION_TRANSPOSED 9 +#define SEG_OPTION_REVERSED_Y 6 +#define SEG_OPTION_MIRROR_Y 7 +#define SEG_OPTION_TRANSPOSED 8 //Segment differs return byte #define SEG_DIFFERS_BRI 0x01 // opacity @@ -345,6 +348,7 @@ #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist #define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist +#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist #define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured #define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) diff --git a/wled00/data/index.css b/wled00/data/index.css index 1b34285d..a8092c76 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -20,8 +20,8 @@ --c-g: #2c1; --c-l: #48a; --c-y: #a90; - --t-b: 0.5; - --c-o: rgba(34, 34, 34, 0.9); + --t-b: .5; + --c-o: rgba(34, 34, 34, .9); --c-tb : rgba(34, 34, 34, var(--t-b)); --c-tba: rgba(102, 102, 102, var(--t-b)); --c-tbh: rgba(51, 51, 51, var(--t-b)); @@ -33,7 +33,8 @@ --bbp: 9px 0 7px 0; --bhd: none; --sgp: "block"; - --bmt: 0px; + --bmt: 0; + --sti: 42px; } html { @@ -88,7 +89,7 @@ a, a:visited { } button { - outline: none; + outline: 0; cursor: pointer; } @@ -219,7 +220,7 @@ button { .pop-c span { padding: 2px 6px; } - + .search-icon { position: absolute; top: 8px; @@ -238,7 +239,7 @@ button { .flr { color: var(--c-f); transform: rotate(0deg); - transition: transform 0.3s; + transition: transform .3s; position: absolute; top: 0; right: 0; @@ -258,13 +259,13 @@ button { #liveview { height: 4px; width: 100%; - border: 0px; + border: 0; } #liveview2D { height: 90%; width: 90%; - border: 0px; + border: 0; position: absolute; top: 50%; left: 50%; @@ -287,8 +288,8 @@ button { .tab button { background-color: transparent; float: left; - border: none; - transition: color 0.3s, background-color 0.3s; + border: 0; + transition: color .3s, background-color .3s; font-size: 17px; color: var(--c-c); min-width: 44px; @@ -301,7 +302,7 @@ button { .bot button { padding: var(--bbp); - width:25%; + width: 25%; margin: 0; } @@ -336,8 +337,9 @@ button { width: 100%; width: calc(100%/var(--n)); box-sizing: border-box; - border: 0px; - overflow: auto; + border: 0; + overflow-y: auto; + overflow-x: hidden; height: 100%; overscroll-behavior: none; padding: 0 4px; @@ -388,8 +390,8 @@ button { align-items: center; justify-content: center; z-index: 11; - opacity: 0.95; - transition: 0.7s; + opacity: .95; + transition: .7s; pointer-events: none; } @@ -399,7 +401,7 @@ button { position: sticky !important; top: 0; z-index: 2; - margin: 0 auto auto; + margin: 0 auto auto; } .staybot { @@ -456,65 +458,55 @@ button { padding: 4px 2px; position: relative; opacity: 1; - transition: opacity 0.5s linear, height 0.5s, transform 0.5s; + transition: opacity .5s linear, height .25s, transform .25s; } .filter { z-index: 1; - overflow: hidden; + /*overflow: visible;*/ + border-radius: 0 0 16px 16px; + max-width: 220px; + height: 54px; + line-height: 1.5; + padding-bottom: 8px; } -/* Tooltip text */ -.slider .tooltiptext, .option .tooltiptext { +/* New tooltip */ +.tooltip { + position: absolute; + opacity: 0; visibility: hidden; + transition: opacity .25s ease, visibility .25s ease; background-color: var(--c-5); - /*border: 2px solid var(--c-2);*/ box-shadow: 4px 4px 10px 4px var(--c-1); color: var(--c-f); text-align: center; - padding: 4px 8px; + padding: 8px 16px; border-radius: 6px; - - /* Position the tooltip text */ - width: 160px; - position: absolute; z-index: 1; - bottom: 80%; - left: 50%; - margin-left: -92px; - - /* Ensure tooltip goes away when mouse leaves control */ pointer-events: none; +} - /* Fade in tooltip */ - opacity: 0; - transition: opacity 0.75s; -} -.option .tooltiptext { - bottom: 120%; -} -/* Tooltip arrow */ -.slider .tooltiptext::after, .option .tooltiptext::after { + .tooltip::after { content: ""; position: absolute; - top: 100%; - left: 50%; - margin-left: -5px; - border-width: 5px; - border-style: solid; + border: 8px solid; border-color: var(--c-5) transparent transparent transparent; -} -/* Show the tooltip text when you mouse over the tooltip container */ -.slider:hover .tooltiptext, .option .check:hover .tooltiptext { - visibility: visible; + top: 100%; + left: calc(50% - 8px); + z-index: 0; + } + +.tooltip.visible { opacity: 1; + visibility: visible; } .fade { visibility: hidden; /* hide it */ opacity: 0; /* make it transparent */ transform: scaleY(0); /* shrink content */ - height: 0px; /* force other elements to move */ + height: 0; /* force other elements to move */ padding: 0; /* remove empty space */ } @@ -542,24 +534,24 @@ button { #toast.show { opacity: 1; - animation: fadein 0.5s, fadein 0.5s 2.5s reverse; + animation: fadein .5s, fadein .5s 2.5s reverse; } #toast.error { opacity: 1; background-color: #b21; - animation: fadein 0.5s; + animation: fadein .5s; } .modal { - position:fixed; - left: 0px; - bottom: 0px; - right: 0px; + position: fixed; + left: 0; + bottom: 0; + right: 0; top: calc(var(--th) - 1px); background-color: var(--c-o); transform: translateY(100%); - transition: transform 0.4s; + transition: transform .4s; padding: 8px; font-size: 20px; overflow: auto; @@ -641,7 +633,7 @@ button { } #heart { - transition: color 0.9s; + transition: color .9s; font-size: 16px; color: #f00; } @@ -720,7 +712,7 @@ input[type=range] { } input[type=range]:focus { - outline: none; + outline: 0; } input[type=range]::-webkit-slider-runnable-track { width: 100%; @@ -743,7 +735,7 @@ input[type=range]::-moz-range-track { background-color: rgba(0, 0, 0, 0); } input[type=range]::-moz-range-thumb { - border: 0px solid rgba(0, 0, 0, 0); + border: 0 solid rgba(0, 0, 0, 0); height: 16px; width: 16px; border-radius: 50%; @@ -770,7 +762,7 @@ input[type=range]::-moz-range-thumb { } #briwrap { - min-width: 267px; + min-width: 300px; float: right; margin-top: var(--bmt); } @@ -789,11 +781,11 @@ input[type=range]::-moz-range-thumb { color: var(--c-d); cursor: pointer; border-radius: 25px; - transition-duration: 0.3s; + transition-duration: .3s; -webkit-backface-visibility: hidden; - -webkit-transform:translate3d(0,0,0); + -webkit-transform: translate3d(0,0,0); backface-visibility: hidden; - transform:translate3d(0,0,0); + transform: translate3d(0,0,0); overflow: hidden; text-overflow: ellipsis; border: 1px solid var(--c-3); @@ -893,13 +885,13 @@ select { cursor: pointer; border: 0 solid var(--c-2); border-radius: 20px; - transition-duration: 0.5s; + transition-duration: .5s; -webkit-backface-visibility: hidden; - -webkit-transform:translate3d(0,0,0); - -webkit-appearance: none; - -moz-appearance: none; + -webkit-transform: translate3d(0,0,0); + -webkit-appearance: none; + -moz-appearance: none; backface-visibility: hidden; - transform:translate3d(0,0,0); + transform: translate3d(0,0,0); text-overflow: ellipsis; } #tt { @@ -917,15 +909,15 @@ div.sel-p { position: relative; } div.sel-p:after { - content: ""; - position: absolute; - right: 10px; - top: 22px; - width: 0; - height: 0; - border-left: 8px solid transparent; - border-right: 8px solid transparent; - border-top: 8px solid var(--c-f); + content: ""; + position: absolute; + right: 10px; + top: 22px; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid var(--c-f); } select.sel-ple { text-align: center; @@ -942,13 +934,13 @@ input[type=number], input[type=text] { background: var(--c-3); color: var(--c-f); - border: 0px solid var(--c-2); + border: 0 solid var(--c-2); border-radius: 10px; padding: 8px; /*margin: 6px 6px 6px 0;*/ font-size: 19px; - transition: background-color 0.2s; - outline: none; + transition: background-color .2s; + outline: 0; -webkit-appearance: textfield; -moz-appearance: textfield; appearance: textfield; @@ -988,7 +980,7 @@ textarea { height: 90px; border-radius: 5px; border: 2px solid var(--c-5); - outline: none; + outline: 0; resize: none; font-size: 19px; padding: 5px; @@ -1041,7 +1033,7 @@ textarea { top: 1px; } .plname { - top:0; + top: 0; } /* preset id number */ @@ -1121,8 +1113,8 @@ textarea { } .revchkl { - padding: 4px 0px 0px 35px; - margin-bottom: 0px; + padding: 4px 0 0 35px; + margin-bottom: 0; margin-top: 8px; } @@ -1218,9 +1210,9 @@ TD .checkmark, TD .radiomark { .seg, .pres { background-color: var(--c-2); /*color: var(--c-f);*/ /* seems to affect only the Add segment button, which should be same color as reset segments */ - border: 0px solid var(--c-f); + border: 0 solid var(--c-f); text-align: left; - transition: background-color 0.5s; + transition: background-color .5s; border-radius: 21px; } @@ -1237,14 +1229,18 @@ TD .checkmark, TD .radiomark { /* checkmark labels */ .filter .fchkl, .option .ochkl { display: inline-block; - min-width: 0.7em; - padding: 1px 4px 4px 32px; + min-width: .7em; + padding: 1px 4px 1px 32px; text-align: left; line-height: 24px; vertical-align: middle; -webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */ filter: grayscale(100%); } +.filter .fchkl { + margin: 0 4px; + min-width: 20px; +} .lbl-l { font-size: 13px; @@ -1263,15 +1259,15 @@ TD .checkmark, TD .radiomark { /* list wrapper */ .list { position: relative; - transition: background-color 0.5s; - margin: auto auto 10px; + transition: background-color .5s; + margin: auto auto 10px; line-height: 24px; } /* list item */ .lstI { align-items: center; - cursor: pointer; + cursor: pointer; background-color: var(--c-2); overflow: hidden; position: -webkit-sticky; @@ -1293,7 +1289,7 @@ TD .checkmark, TD .radiomark { #segcont .seg:hover:not([class*="expanded"]), .lstI:hover:not([class*="expanded"]) { - background: var(--c-5); + background: var(--c-5); } .selected .checkmark, @@ -1313,7 +1309,7 @@ TD .checkmark, TD .radiomark { .lstI.sticky, .lstI.selected { z-index: 1; - box-shadow: 0px 0px 10px 4px var(--c-1); + box-shadow: 0 0 10px 4px var(--c-1); } #pcont .selected:not([class*="expanded"]) { @@ -1321,20 +1317,14 @@ TD .checkmark, TD .radiomark { top: 42px; } -#fxlist .lstI.selected { - top: 84px; -} - -#fxlist .lstI.sticky { - top: 42px; -} - +#fxlist .lstI.selected, #pallist .lstI.selected { - top: 84px; + top: calc(var(--sti) + 42px); } +#fxlist .lstI.sticky, #pallist .lstI.sticky { - top: 42px; + top: var(--sti); } /* list item content */ @@ -1370,8 +1360,8 @@ TD .checkmark, TD .radiomark { display: block; width: 100%; box-sizing: border-box; - padding: 8px 40px 8px 44px; - margin: 5px auto 0; + padding: 8px 40px 8px 44px; + margin: 5px auto 0; text-align: left; border-radius: 21px; background: var(--c-2); @@ -1457,7 +1447,7 @@ TD .checkmark, TD .radiomark { } ::-webkit-scrollbar-thumb { background: var(--c-sb); - opacity: 0.2; + opacity: .2; border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { @@ -1483,7 +1473,7 @@ TD .checkmark, TD .radiomark { @media all and (max-width: 335px) { .sliderbubble { - display: none; + display: none; } } diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 0cf48d6e..8221822e 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -48,7 +48,7 @@ }); setTimeout(()=>{h.appendChild(l)},100); }); - setTimeout(()=>{h.appendChild(l)},100); + setTimeout(()=>{h.appendChild(l)},200); @@ -72,8 +72,8 @@

Brightness

-
- +
+
@@ -88,98 +88,91 @@
-
-
+
+
- Hue
-
+
- Saturation
-
+
- Value/Brightness
-
+
- Kelvin/Temperature
-
-
+
- Red channel
-
+
- Green channel
-
+
- Blue channel
- -
+
- White channel
- -
+
- White balance
-
-
-
-
-
-

-
-
-
-
-
R
+
+
+
+
+
+

+
+
+
+
+
R
- - - + + +

- +
+
+ + + +

Color palette

@@ -189,7 +182,7 @@
-
-
- - -
@@ -209,13 +198,39 @@

Effect mode

- + - + +
+ + + + + + +
-
-
- - - - - - -
-
+
- Effect speed
-
+
- Effect intensity
-
+
- Custom 1
-
+
- Custom 2
-
+
- Custom 3
-
+
diff --git a/wled00/data/index.js b/wled00/data/index.js index c356efa8..016fdd5f 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -1,6 +1,6 @@ //page js var loc = false, locip, locproto = "http:"; -var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true; +var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false/*, syncTglRecv = true*/; var hasWhite = false, hasRGB = false, hasCCT = false; var nlDur = 60, nlTar = 0; var nlMode = false; @@ -24,19 +24,18 @@ var lastinfo = {}; var isM = false, mw = 0, mh=0; var ws, cpick, ranges, wsRpt=0; var cfg = { - theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, + theme:{base:"dark", bg:{url:"", rnd: false, rndGrayscale: false, rndBlur: false}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, - labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, - css:true, hdays:false, fxdef:true} + labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, + css:true, hdays:false, fxdef:true, on:0, off:0, idsort: false} }; var hol = [ [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day [2025,3,20,2,"https://aircoookie.github.io/easter.png"], - [2023,3,9,2,"https://aircoookie.github.io/easter.png"], [2024,2,31,2,"https://aircoookie.github.io/easter.png"], - [0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July - [0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year + [0,6,4,1,"https://images.alphacoders.com/516/516792.jpg"], // 4th of July + [0,0,1,1,"https://images.alphacoders.com/119/1198800.jpg"] // new year ]; function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} @@ -74,6 +73,7 @@ function setCSL(cs) function applyCfg() { cTheme(cfg.theme.base === "light"); + gId("Colors").style.paddingTop = cfg.comp.colors.picker ? "0" : "28px"; var bg = cfg.theme.color.bg; if (bg) sCol('--c-1', bg); var l = cfg.comp.labels; @@ -109,6 +109,7 @@ function tglLabels() function tglRgb() { cfg.comp.colors.rgb = !cfg.comp.colors.rgb; + cfg.comp.colors.picker = !cfg.comp.colors.picker; applyCfg(); } @@ -217,13 +218,11 @@ function onLoad() // detect reverse proxy and/or HTTPS let pathn = l.pathname; let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/"); - //if (paths[0]==="sliders") paths.shift(); - //while (paths[0]==="") paths.shift(); locproto = l.protocol; locip = l.hostname + (l.port ? ":" + l.port : ""); if (paths.length > 0 && paths[0]!=="") { loc = true; - locip += "/" + paths[0]; + locip += "/" + paths.join('/'); } else if (locproto==="https:") { loc = true; } @@ -231,6 +230,7 @@ function onLoad() var sett = localStorage.getItem('wledUiCfg'); if (sett) cfg = mergeDeep(cfg, JSON.parse(sett)); + tooltip(); resetPUtil(); if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true); @@ -255,7 +255,6 @@ function onLoad() }); } else loadBg(cfg.theme.bg.url); - if (cfg.comp.css) loadSkinCSS('skinCss'); selectSlot(0); updateTablinks(0); @@ -265,14 +264,17 @@ function onLoad() loadPalettes(()=>{ // fill effect extra data array loadFXData(()=>{ - // load and populate effects - loadFX(()=>{ - setTimeout(()=>{ // ESP8266 can't handle quick requests - loadPalettesData(()=>{ - requestJson();// will load presets and create WS - }); - },100); - }); + setTimeout(()=>{ // ESP8266 can't handle quick requests + // load and populate effects + loadFX(()=>{ + setTimeout(()=>{ // ESP8266 can't handle quick requests + loadPalettesData(()=>{ + requestJson();// will load presets and create WS + if (cfg.comp.css) setTimeout(()=>{loadSkinCSS('skinCss')},100); + }); + },100); + }); + },100); }); }); resetUtil(); @@ -280,11 +282,10 @@ function onLoad() d.addEventListener("visibilitychange", handleVisibilityChange, false); //size(); gId("cv").style.opacity=0; - var sls = d.querySelectorAll('input[type="range"]'); - for (var sl of sls) { + d.querySelectorAll('input[type="range"]').forEach((sl)=>{ sl.addEventListener('touchstart', toggleBubble); sl.addEventListener('touchend', toggleBubble); - } + }); } function updateTablinks(tabI) @@ -426,18 +427,30 @@ function presetError(empty) if (hasBackup) { cn += `

`; if (empty) - cn += `However, there is backup preset data of a previous installation available.
- (Saving a preset will hide this and overwrite the backup)`; + cn += `However, there is backup preset data of a previous installation available.
(Saving a preset will hide this and overwrite the backup)`; else cn += `Here is a backup of the last known good state:`; - cn += `
- `; + cn += `
`; + cn += `
`; } cn += `
`; gId('pcont').innerHTML = cn; if (hasBackup) gId('bck').value = bckstr; } +function restore(txt) { + var req = new XMLHttpRequest(); + req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)}); + req.addEventListener('error', function(e){showToast(e.stack,true);}); + req.open("POST", getURL("/upload")); + var formData = new FormData(); + var b = new Blob([txt], {type: "application/json"}); + formData.append("data", b, '/presets.json'); + req.send(formData); + setTimeout(loadPresets, 2000); + return false; +} + function loadPresets(callback = null) { // 1st boot (because there is a callback) @@ -566,8 +579,7 @@ function populatePresets(fromls) if (!pJson) {setTimeout(loadPresets,250); return;} delete pJson["0"]; var cn = ""; - var arr = Object.entries(pJson); - arr.sort(cmpP); + var arr = Object.entries(pJson).sort(cmpP); pQL = []; var is = []; pNum = 0; @@ -613,7 +625,7 @@ function parseInfo(i) { if (loc) name = "(L) " + name; d.title = name; ledCount = i.leds.count; - syncTglRecv = i.str; + //syncTglRecv = i.str; maxSeg = i.leds.maxseg; pmt = i.fs.pmt; gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none"; @@ -622,12 +634,12 @@ function parseInfo(i) { mh = i.leds.matrix ? i.leds.matrix.h : 0; isM = mw>0 && mh>0; if (!isM) { - gId("filter0D").classList.remove('hide'); - gId("filter1D").classList.add('hide'); + //gId("filter0D").classList.remove('hide'); + //gId("filter1D").classList.add('hide'); gId("filter2D").classList.add('hide'); } else { - gId("filter0D").classList.add('hide'); - gId("filter1D").classList.remove('hide'); + //gId("filter0D").classList.add('hide'); + //gId("filter1D").classList.remove('hide'); gId("filter2D").classList.remove('hide'); } // if (i.noaudio) { @@ -672,8 +684,6 @@ function populateInfo(i) } } var vcn = "Kuuhaku"; - if (i.ver.startsWith("0.14.")) vcn = "Hoshi"; -// if (i.ver.includes("-bl")) vcn = "Supāku"; if (i.cn) vcn = i.cn; cn += `v${i.ver} "${vcn}"

@@ -694,7 +704,7 @@ ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
`; gId('kv').innerHTML = cn; // update all sliders in Info - for (let sd of (gId('kv').getElementsByClassName('sliderdisplay')||[])) { + for (let sd of (d.querySelectorAll('#kv .sliderdisplay')||[])) { let s = sd.previousElementSibling; if (s) updateTrail(s); } @@ -908,7 +918,7 @@ function populatePalettes() for (let pa of lJson) { html += generateListItemHtml( 'palette', - pa[0], + pa[0], pa[1], 'setPalette', `
` @@ -916,8 +926,9 @@ function populatePalettes() } gId('pallist').innerHTML=html; // append custom palettes (when loading for the 1st time) - if (!isEmpty(lastinfo) && lastinfo.cpalcount) { - for (let j = 0; j0) gId("rmPal").classList.remove("hide"); + else gId("rmPal").classList.add("hide"); } function redrawPalPrev() @@ -1074,7 +1087,7 @@ function updateTrail(e) { if (e==null) return; let sd = e.parentNode.getElementsByClassName('sliderdisplay')[0]; - if (sd && getComputedStyle(sd).getPropertyValue("--bg") !== "none") { + if (sd && getComputedStyle(sd).getPropertyValue("--bg").trim() !== "none") { // trim() for Safari var max = e.hasAttribute('max') ? e.attributes.max.value : 255; var perc = Math.round(e.value * 100 / max); if (perc < 50) perc += 2; @@ -1194,6 +1207,7 @@ function updateUI() gId('buttonPower').className = (isOn) ? 'active':''; gId('buttonNl').className = (nlA) ? 'active':''; gId('buttonSync').className = (syncSend) ? 'active':''; + gId('pxmb').style.display = (isM) ? "inline-block" : "none"; updateSelectedFx(); updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes @@ -1209,7 +1223,7 @@ function updateUI() if (hasRGB) { updateTrail(gId('sliderR')); updateTrail(gId('sliderG')); - updateTrail(gId('sliderB')); + updateTrail(gId('sliderB')); } if (hasWhite) updateTrail(gId('sliderW')); @@ -1271,18 +1285,19 @@ function updateSelectedFx() selectedEffect.classList.add('selected'); setEffectParameters(selectedFx); // hide non-0D effects if segment only has 1 pixel (0D) - var fxs = parent.querySelectorAll('.lstI'); - for (const fx of fxs) { - if (!fx.dataset.opt) continue; - let opts = fx.dataset.opt.split(";"); - if (fx.dataset.id>0) { - if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects) - else { - if ((segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) || (!isM && opts[3] && ((opts[3].indexOf("2")>=0 && opts[3].indexOf("1")<0)))) fx.classList.add('hide'); - else fx.classList.remove('hide'); + parent.querySelectorAll('.lstI').forEach((fx)=>{ + let ds = fx.dataset; + if (ds.opt) { + let opts = ds.opt.split(";"); + if (ds.id>0) { + if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects) + else { + if ((segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) || (!isM && opts[3] && ((opts[3].indexOf("2")>=0 && opts[3].indexOf("1")<0)))) fx.classList.add('hide'); + else fx.classList.remove('hide'); + } } } - } + }); // hide 2D mapping and/or sound simulation options var selectedName = selectedEffect.querySelector(".lstIname").innerText; var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`); @@ -1302,7 +1317,7 @@ function displayRover(i,s) function cmpP(a, b) { - if (!a[1].n) return (a[0] > b[0]); + if (cfg.comp.idsort || !a[1].n) return (parseInt(a[0]) > parseInt(b[0])); // sort playlists first, followed by presets with characters and last presets with special 1st character const c = a[1].n.charCodeAt(0); const d = b[1].n.charCodeAt(0); @@ -1404,7 +1419,7 @@ function readState(s,command=false) if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");}); - var cd = gId('csl').children; + var cd = gId('csl').querySelectorAll("button"); for (let e = cd.length-1; e >= 0; e--) { cd[e].dataset.r = i.col[e][0]; cd[e].dataset.g = i.col[e][1]; @@ -1485,46 +1500,47 @@ function setEffectParameters(idx) var paOnOff = (effectPars.length<3 || effectPars[2]=='')?[]:effectPars[2].split(","); // set html slider items on/off - let nSliders = 5; - for (let i=0; ii && slOnOff[i] != "")) { - if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i]; - else if (i==0) label.innerHTML = "Effect speed"; - else if (i==1) label.innerHTML = "Effect intensity"; - else label.innerHTML = "Custom" + (i-1); - slider.classList.remove('hide'); - } else { - slider.classList.add('hide'); - } - } - if (slOnOff.length>5) { // up to 3 checkboxes + let sliders = d.querySelectorAll("#sliders .sliderwrap"); + sliders.forEach((slider, i)=>{ + let text = slider.getAttribute("tooltip"); + if ((!controlDefined && i<((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i]!="")) { + if (slOnOff.length>i && slOnOff[i]!="!") text = slOnOff[i]; + slider.setAttribute("tooltip", text); + slider.parentElement.classList.remove('hide'); + } else + slider.parentElement.classList.add('hide'); + }); + + if (slOnOff.length > 5) { // up to 3 checkboxes gId('fxopt').classList.remove('fade'); - for (let i = 0; i<3; i++) { + let checks = d.querySelectorAll("#sliders .ochkl"); + checks.forEach((check, i)=>{ + let text = check.getAttribute("tooltip"); if (5+i5+i && slOnOff[5+i]!="!") text = slOnOff[5+i]; + check.setAttribute("tooltip", text); + check.classList.remove('hide'); } else - gId('opt'+i).classList.add('hide'); - } - } else { - gId('fxopt').classList.add('fade'); - } + check.classList.add('hide'); + }); + } else gId('fxopt').classList.add('fade'); // set the bottom position of selected effect (sticky) as the top of sliders div - setInterval(()=>{ + function setSelectedEffectPosition() { let top = parseInt(getComputedStyle(gId("sliders")).height); top += 5; let sel = d.querySelector('#fxlist .selected'); if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setFX()) - },750); + } + + setSelectedEffectPosition(); + setInterval(setSelectedEffectPosition,750); // set html color items on/off var cslLabel = ''; var sep = ''; var cslCnt = 0, oCsel = csel; - for (let i=0; i{ var btn = gId("csl" + i); // if no controlDefined or coOnOff has a value if (coOnOff.length>i && coOnOff[i] != "") { @@ -1554,12 +1570,16 @@ function setEffectParameters(idx) btn.dataset.hide = 1; btn.innerHTML = `${i+1}`; // name hidden buttons 1..3 for * palettes } - } + }); gId("cslLabel").innerHTML = cslLabel; + if (cslLabel!=="") gId("cslLabel").classList.remove("hide"); + else gId("cslLabel").classList.add("hide"); // set palette on/off var palw = gId("palw"); // wrapper var pall = gId("pall"); // label + var icon = ' '; + var text = 'Color palette'; // if not controlDefined or palette has a value if (hasRGB && ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0])))) { palw.style.display = "inline-block"; @@ -1569,13 +1589,13 @@ function setEffectParameters(idx) var v = Math.max(0,Math.min(255,parseInt(paOnOff[0].substr(dPos+1)))); paOnOff[0] = paOnOff[0].substring(0,dPos); } - if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0]; - else pall.innerHTML = ' Color palette'; + if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0]; } else { // disable palette list - pall.innerHTML = ' Color palette not used'; + text += ' not used'; palw.style.display = "none"; } + pall.innerHTML = icon + text; // not all color selectors shown, hide palettes created from color selectors // NOTE: this will disallow user to select "* Color ..." palettes which may be undesirable in some cases or for some users //for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) { @@ -1666,6 +1686,8 @@ function togglePower() obj.seg = []; obj.seg[0] = {"id": lastinfo.liveseg, "frz": false}; } + if (cfg.comp.on >0 && isOn) obj = {"ps": cfg.comp.on }; // don't use setPreset() + if (cfg.comp.off>0 && !isOn) obj = {"ps": cfg.comp.off}; // don't use setPreset() requestJson(obj); } @@ -1688,7 +1710,7 @@ function toggleSync() if (syncSend) showToast('Other lights in the network will now sync to this one.'); else showToast('This light and other lights in the network will no longer sync.'); var obj = {"udpn": {"send": syncSend}}; - if (syncTglRecv) obj.udpn.recv = syncSend; + //if (syncTglRecv) obj.udpn.recv = syncSend; requestJson(obj); } @@ -1700,7 +1722,7 @@ function toggleLiveview() let wsOn = ws && ws.readyState === WebSocket.OPEN; var lvID = "liveview"; - if (isM && wsOn) { + if (isM && wsOn) { lvID += "2D"; if (isLv) gId('klv2D').innerHTML = ``; gId('mlv2D').style.transform = (isLv) ? "translateY(0px)":"translateY(100%)"; @@ -1793,12 +1815,12 @@ function makePlSel(el, incPl=false) { var plSelContent = ""; delete pJson["0"]; // remove filler preset - var arr = Object.entries(pJson); - for (var a of arr) { + Object.entries(pJson).sort(cmpP).forEach((a)=>{ var n = a[1].n ? a[1].n : "Preset " + a[0]; - if (!incPl && a[1].playlist && a[1].playlist.ps) continue; // remove playlists, sub-playlists not yet supported - plSelContent += `` - } + if (cfg.comp.idsort) n = a[0] + ' ' + n; + if (!(!incPl && a[1].playlist && a[1].playlist.ps)) // skip playlists, sub-playlists not yet supported + plSelContent += ``; + }); return plSelContent; } @@ -1807,21 +1829,19 @@ function refreshPlE(p) var plEDiv = gId(`ple${p}`); if (!plEDiv) return; var content = "
Playlist entries
"; - for (var i = 0; i < plJson[p].ps.length; i++) { - content += makePlEntry(p,i); - } + plJson[p].ps.forEach((e,i)=>{content += makePlEntry(p,i);}); + content += `
`; plEDiv.innerHTML = content; var dels = plEDiv.getElementsByClassName("btn-pl-del"); if (dels.length < 2) dels[0].style.display = "none"; - var sels = gId(`seg${p+100}`).getElementsByClassName("sel"); - for (var i of sels) { + d.querySelectorAll(`#seg${p+100} .sel`).forEach((i)=>{ if (i.dataset.val) { if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val; else plJson[p].ps[i.dataset.index] = parseInt(i.value); } - } + }); } // p: preset ID, i: ps index @@ -1887,7 +1907,7 @@ function makeP(i,pl) end: 0 }; var rep = plJson[i].repeat ? plJson[i].repeat : 0; - content = + content = `