Merge branch 'mdev' into audio-palette-updates

This commit is contained in:
Will Tatam
2023-04-14 18:28:43 +01:00
142 changed files with 15093 additions and 14352 deletions

View File

@@ -81,7 +81,7 @@ uint16_t Segment::maxHeight = 1;
// copy constructor
Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy(this, &orig, sizeof(Segment));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
name = nullptr;
data = nullptr;
_dataLen = 0;
@@ -97,7 +97,7 @@ Segment::Segment(const Segment &orig) {
// move constructor
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy(this, &orig, sizeof(Segment));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
@@ -116,7 +116,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (leds && !Segment::_globalLeds) free(leds);
deallocateData();
// copy source
memcpy(this, &orig, sizeof(Segment));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
// erase pointers to allocated data
name = nullptr;
data = nullptr;
@@ -141,7 +141,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
deallocateData(); // free old runtime data
if (_t) delete _t;
if (leds && !Segment::_globalLeds) free(leds);
memcpy(this, &orig, sizeof(Segment));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
@@ -188,7 +188,8 @@ void Segment::deallocateData() {
void Segment::resetIfRequired() {
if (reset) {
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
//if (_t) { delete _t; _t = nullptr; transitional = false; }
if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false; // setOption(SEG_OPTION_RESET, false);
}
@@ -230,14 +231,34 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
case FX_MODE_SUNRISE : pal = 35; break; // heat palette
case FX_MODE_FLOW : pal = 6; break; // party
case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
}
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
uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) {
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)));
_lastPaletteChange = millis();
timeSinceLastChange = 0;
}
//WLEDMM: smooth transitions of palettes instead of every 5 sec with short transition
for (int i=0; i< 16; i++) {
targetPalette[i].r = prevRandomPalette[i].r*(5000-timeSinceLastChange)/5000 + randomPalette[i].r*timeSinceLastChange/5000;
targetPalette[i].g = prevRandomPalette[i].g*(5000-timeSinceLastChange)/5000 + randomPalette[i].g*timeSinceLastChange/5000;
targetPalette[i].b = prevRandomPalette[i].b*(5000-timeSinceLastChange)/5000 + randomPalette[i].b*timeSinceLastChange/5000;
}
break;}
case 74: {//periodically replace palette with a random one. Transition palette change in 500ms
uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
prevRandomPalette = randomPalette;
randomPalette = CRGBPalette16(
CHSV(random8(), random8(160, 255), random8(128, 255)),
@@ -401,8 +422,8 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
markForReset();
return;
}
if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2);
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
startY = 0;
stopY = 1;
#ifndef WLED_DISABLE_2D
@@ -423,6 +444,10 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot >= NUM_COLORS || c == colors[slot]) return false;
if (!_isRGB && !_hasW) {
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
}
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast
@@ -556,12 +581,12 @@ class JMapC {
char previousSegmentName[50] = "";
~JMapC() {
Serial.println("~JMapC");
USER_PRINTLN("~JMapC");
deletejVectorMap();
}
void deletejVectorMap() {
if (jVectorMap.size() > 0) {
Serial.println("delete jVectorMap");
USER_PRINTLN("delete jVectorMap");
for (size_t i=0; i<jVectorMap.size(); i++)
delete jVectorMap[i].array;
jVectorMap.clear();
@@ -605,11 +630,11 @@ class JMapC {
else if (SEGMENT.name != nullptr && strcmp(SEGMENT.name, previousSegmentName) != 0) {
uint32_t dataSize = 0;
deletejVectorMap();
Serial.print("New "); Serial.println(SEGMENT.name);
USER_PRINT("New "); USER_PRINTLN(SEGMENT.name);
char jMapFileName[50];
strcpy(jMapFileName, "/");
strcat(jMapFileName, SEGMENT.name);
strcat(jMapFileName, ".jmap");
strcat(jMapFileName, ".json");
File jMapFile;
jMapFile = WLED_FS.open(jMapFileName, "r");
@@ -620,11 +645,11 @@ class JMapC {
jMapFile.find("[");
do {
DeserializationError err = deserializeJson(docChunk, jMapFile);
// serializeJson(docChunk, Serial); Serial.println();
// Serial.printf("docChunk %u / %u%% (%u %u %u) %u\n", (unsigned int)docChunk.memoryUsage(), 100 * docChunk.memoryUsage() / docChunk.capacity(), (unsigned int)docChunk.size(), docChunk.overflowed(), (unsigned int)docChunk.nesting(), jMapFile.size());
// serializeJson(docChunk, Serial); USER_PRINTLN();
// USER_PRINTF("docChunk %u / %u%% (%u %u %u) %u\n", (unsigned int)docChunk.memoryUsage(), 100 * docChunk.memoryUsage() / docChunk.capacity(), (unsigned int)docChunk.size(), docChunk.overflowed(), (unsigned int)docChunk.nesting(), jMapFile.size());
if (err)
{
Serial.printf("deserializeJson() of parseTree failed with code %s\n", err.c_str());
USER_PRINTF("deserializeJson() of parseTree failed with code %s\n", err.c_str());
delete[] SEGMENT.name; SEGMENT.name = nullptr; //need to clear the name as otherwise continuously loaded
return;
}
@@ -665,10 +690,10 @@ class JMapC {
scale = MIN(SEGMENT.virtualWidth() / maxWidth, SEGMENT.virtualHeight() / maxHeight);
dataSize += sizeof(jVectorMap);
Serial.print("dataSize ");
Serial.print(dataSize);
Serial.print(" scale ");
Serial.println(scale);
USER_PRINT("dataSize ");
USER_PRINT(dataSize);
USER_PRINT(" scale ");
USER_PRINTLN(scale);
strcpy(previousSegmentName, SEGMENT.name);
}
} //updatejMapDoc
@@ -677,7 +702,7 @@ class JMapC {
//WLEDMM jMap
void Segment::createjMap() {
if (!jMap) {
Serial.println("createjMap");
USER_PRINTLN("createjMap");
jMap = new JMapC();
}
}
@@ -686,7 +711,7 @@ void Segment::createjMap() {
void Segment::deletejMap() {
//Should be called from ~Segment but causes crash (and ~Segment is called quite often...)
if (jMap) {
Serial.println("deletejMap");
USER_PRINTLN("deletejMap");
delete (JMapC *)jMap; jMap = nullptr;
}
}
@@ -758,7 +783,9 @@ void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH,
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionaly
{
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
i &= 0xFFFF;
if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
@@ -847,12 +874,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
}
return;
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
int x = 0, y = 0;
if (virtualHeight()>1) y = i;
if (virtualWidth() >1) x = i;
setPixelColorXY(x, y, col);
return;
if (start < Segment::maxWidth*Segment::maxHeight) {
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
int x = 0, y = 0;
if (virtualHeight()>1) y = i;
if (virtualWidth() >1) x = i;
setPixelColorXY(x, y, col);
return;
}
}
#endif
@@ -931,7 +960,9 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i)
{
#ifndef WLED_DISABLE_2D
int vStrip = i>>16;
#endif
i &= 0xFFFF;
#ifndef WLED_DISABLE_2D
@@ -1018,29 +1049,46 @@ uint8_t Segment::differs(Segment& b) const {
}
void Segment::refreshLightCapabilities() {
uint8_t capabilities = 0x01;
uint8_t capabilities = 0;
uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0;
if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
uint16_t index = x + Segment::maxWidth * y;
if (index < strip.customMappingSize) index = strip.customMappingTable[index]; // convert logical address to physical
if (index < 0xFFFFU) {
if (segStartIdx > index) segStartIdx = index;
if (segStopIdx < index) segStopIdx = index;
}
if (segStartIdx == segStopIdx) segStopIdx++; // we only have 1 pixel segment
}
} else {
// we are on the strip located after the matrix
segStartIdx = start;
segStopIdx = stop;
}
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
if (bus->getStart() >= stop) continue;
if (bus->getStart() + bus->getLength() <= start) continue;
if (bus->getStart() >= segStopIdx) continue;
if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
uint8_t type = bus->getType();
if (type == TYPE_ONOFF || type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel
if (!cctFromRgb) {
switch (type) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT
}
//uint8_t type = bus->getType();
if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
if (bus->hasWhite()) {
uint8_t aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
// if auto white calculation from RGB is active (Accurate/Brighter), force RGB controls even if there are no RGB busses
if (!whiteSlider) capabilities |= SEG_CAPABILITY_RGB;
// if auto white calculation from RGB is disabled/optional (None/Dual), allow white channel adjustments
if ( whiteSlider) capabilities |= SEG_CAPABILITY_W;
}
if (correctWB && !(type == TYPE_ANALOG_1CH || type == TYPE_ONOFF)) capabilities |= 0x04; //white balance correction (uses CCT slider)
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB)
}
_capabilities = capabilities;
}
@@ -1164,7 +1212,7 @@ void Segment::blur(uint8_t blur_amount)
* The colours are a transition r -> g -> b -> back to r
* Inspired by the Adafruit examples.
*/
uint32_t Segment::color_wheel(uint8_t pos) { // TODO
uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos;
if(pos < 85) {
@@ -1268,6 +1316,65 @@ uint8_t * Segment::getAudioPalette(int pal) {
// WS2812FX class implementation
///////////////////////////////////////////////////////////////////////////////
//WLEDMM from util.cpp
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
void WS2812FX::enumerateLedmaps() {
ledMaps = 1;
for (int i=1; i<10; i++) {
char fileName[33];
snprintf_P(fileName, sizeof(fileName), PSTR("/ledmap%d.json"), i);
bool isFile = WLED_FS.exists(fileName);
#ifndef ESP8266
if (ledmapNames[i-1]) { //clear old name
delete[] ledmapNames[i-1];
ledmapNames[i-1] = nullptr;
}
#endif
if (isFile) {
ledMaps |= 1 << i;
#ifndef ESP8266
if (requestJSONBufferLock(21)) {
if (readObjectFromFile(fileName, nullptr, &doc)) {
size_t len = 0;
if (!doc["n"].isNull()) {
// name field exists
const char *name = doc["n"].as<const char*>();
if (name != nullptr) len = strlen(name);
if (len > 0 && len < 33) {
ledmapNames[i-1] = new char[len+1];
if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33);
}
}
if (!ledmapNames[i-1]) {
char tmp[33];
snprintf_P(tmp, 32, PSTR("ledmap%d.json"), i);
len = strlen(tmp);
ledmapNames[i-1] = new char[len+1];
if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33);
}
}
releaseJSONBufferLock();
}
#endif
}
}
//WLEDMM add segment names to be used as ledmap names
uint8_t segment_index = 0;
for (segment &seg : _segments) {
if (seg.name != nullptr && strcmp(seg.name, "") != 0) {
char fileName[33];
snprintf_P(fileName, sizeof(fileName), PSTR("/lm%s.json"), seg.name);
bool isFile = WLED_FS.exists(fileName);
if (isFile) ledMaps |= 1 << (10+segment_index);
}
segment_index++;
}
}
//do not call this method from system context (network callback)
void WS2812FX::finalizeInit(void)
{
@@ -1308,7 +1415,7 @@ void WS2812FX::finalizeInit(void)
if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
//RGBW mode is enabled if at least one of the strips is RGBW
_hasWhiteChannel |= bus->isRgbw();
_hasWhiteChannel |= bus->hasWhite();
//refresh is required to remain off if at least one of the strips requires the refresh.
_isOffRefreshRequired |= bus->isOffRefreshRequired();
uint16_t busEnd = bus->getStart() + bus->getLength();
@@ -1322,10 +1429,11 @@ void WS2812FX::finalizeInit(void)
#endif
}
if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up
if (isMatrix) setUpMatrix();
else {
Segment::maxWidth = _length;
Segment::maxHeight = 1;
}
}
//initialize leds array. TBD: realloc if nr of leds change
if (Segment::_globalLeds) {
@@ -1334,17 +1442,20 @@ void WS2812FX::finalizeInit(void)
Segment::_globalLeds = nullptr;
}
if (useLedsArray) {
size_t arrSize = sizeof(CRGB) * getLengthTotal();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length);
Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
else
#endif
Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length);
memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length);
Segment::_globalLeds = (CRGB*) malloc(arrSize);
memset(Segment::_globalLeds, 0, arrSize);
}
//segments are created in makeAutoSegments();
DEBUG_PRINTLN(F("Loading custom palettes"));
loadCustomPalettes(); // (re)load all custom palettes
DEBUG_PRINTLN(F("Loading custom ledmaps"));
deserializeMap(); // (re)load default ledmap
}
@@ -1405,15 +1516,15 @@ void WS2812FX::service() {
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
{
if (i >= _length) return;
if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return;
busses.setPixelColor(i, col);
}
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
if (i >= _length) return 0;
if (i < customMappingSize) i = customMappingTable[i];
if (i >= _length) return 0;
return busses.getPixelColor(i);
}
@@ -1475,7 +1586,7 @@ void WS2812FX::estimateCurrentAndLimitBri() {
}
}
if (bus->isRgbw()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3;
busPowerSum = busPowerSum >> 2; //same as /= 4
}
@@ -1626,6 +1737,12 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
return c;
}
uint16_t WS2812FX::getLengthTotal(void) {
uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
return len;
}
uint16_t WS2812FX::getLengthPhysical(void) {
uint16_t len = 0;
for (size_t b = 0; b < busses.getNumBusses(); b++) {
@@ -1643,12 +1760,7 @@ bool WS2812FX::hasRGBWBus(void) {
for (size_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
switch (bus->getType()) {
case TYPE_SK6812_RGBW:
case TYPE_TM1814:
case TYPE_ANALOG_4CH:
return true;
}
if (bus->hasRGB() && bus->hasWhite()) return true;
}
return false;
}
@@ -1673,7 +1785,6 @@ void WS2812FX::purgeSegments(bool force) {
if (_segments.size() <= 1) return;
for (size_t i = _segments.size()-1; i > 0; i--)
if (_segments[i].stop == 0 || force) {
DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i);
deleted++;
_segments.erase(_segments.begin() + i);
}
@@ -1696,43 +1807,58 @@ 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
segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length);
#else
segment seg = Segment(0, _length);
#endif
_segments.push_back(seg);
_mainSegment = 0;
void WS2812FX::resetSegments(bool boundsOnly) { //WLEDMM add boundsonly
if (!boundsOnly) {
_segments.clear(); // destructs all Segment as part of clearing
#ifndef WLED_DISABLE_2D
segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length);
#else
segment seg = Segment(0, _length);
#endif
_segments.push_back(seg);
_mainSegment = 0;
} else { //WLEDMM boundsonly
for (segment &seg : _segments) {
#ifndef WLED_DISABLE_2D
seg.start = 0;
seg.stop = Segment::maxWidth;
seg.startY = 0;
seg.stopY = Segment::maxHeight;
#else
seg.start = 0;
seg.stop = _length;
#endif
}
}
}
void WS2812FX::makeAutoSegments(bool forceReset) {
if (isMatrix) {
#ifndef WLED_DISABLE_2D
// only create 1 2D segment
if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment
else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId();
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
_mainSegment = i;
}
#endif
} else if (autoSegments) { //make one segment per bus
if (autoSegments) { //make one segment per bus
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
uint8_t s = 0;
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
size_t s = 0;
#ifndef WLED_DISABLE_2D
// 2D segment is the 1st one using entire matrix
if (isMatrix) {
segStarts[0] = 0;
segStops[0] = Segment::maxWidth*Segment::maxHeight;
s++;
}
#endif
for (size_t i = s; i < busses.getNumBusses(); i++) {
Bus* b = busses.getBus(i);
segStarts[s] = b->getStart();
segStops[s] = segStarts[s] + b->getLength();
#ifndef WLED_DISABLE_2D
if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
#endif
//check for overlap with previous segments
for (size_t j = 0; j < s; j++) {
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
@@ -1744,23 +1870,40 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
}
s++;
}
_segments.clear();
for (size_t i = 0; i < s; i++) {
Segment seg = Segment(segStarts[i], segStops[i]);
seg.selected = true;
_segments.push_back(seg);
_segments.reserve(s); // prevent reallocations
// there is always at least one segment (but we need to differentiate between 1D and 2D)
#ifndef WLED_DISABLE_2D
if (isMatrix)
_segments.push_back(Segment(0, Segment::maxWidth, 0, Segment::maxHeight));
else
#endif
_segments.push_back(Segment(segStarts[0], segStops[0]));
for (size_t i = 1; i < s; i++) {
_segments.push_back(Segment(segStarts[i], segStops[i]));
}
_mainSegment = 0;
} else {
if (forceReset || getSegmentsNum() == 0) resetSegments();
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced
else if (getActiveSegmentsNum() == 1) {
size_t i = getLastActiveSegmentId();
#ifndef WLED_DISABLE_2D
_segments[i].start = 0;
_segments[i].stop = Segment::maxWidth;
_segments[i].startY = 0;
_segments[i].stopY = Segment::maxHeight;
_segments[i].grouping = 1;
_segments[i].spacing = 0;
#else
_segments[i].start = 0;
_segments[i].stop = _length;
_mainSegment = 0;
#endif
}
}
_mainSegment = 0;
fixInvalidSegments();
}
@@ -1768,14 +1911,30 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
void WS2812FX::fixInvalidSegments() {
//make sure no segment is longer than total (sanity check)
for (size_t i = getSegmentsNum()-1; i > 0; i--) {
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > _length) _segments[i].stop = _length;
// this is always called as the last step after finalizeInit(), update covered bus types
_segments[i].refreshLightCapabilities();
if (isMatrix) {
#ifndef WLED_DISABLE_2D
if (_segments[i].start >= Segment::maxWidth * Segment::maxHeight) {
// 1D segment at the end of matrix
if (_segments[i].start >= _length || _segments[i].startY > 0 || _segments[i].stopY > 1) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > _length) _segments[i].stop = _length;
continue;
}
if (_segments[i].start >= Segment::maxWidth || _segments[i].startY >= Segment::maxHeight) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > Segment::maxWidth) _segments[i].stop = Segment::maxWidth;
if (_segments[i].stopY > Segment::maxHeight) _segments[i].stopY = Segment::maxHeight;
#endif
} else {
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
if (_segments[i].stop > _length) _segments[i].stop = _length;
}
}
// this is always called as the last step after finalizeInit(), update covered bus types
for (segment &seg : _segments)
seg.refreshLightCapabilities();
}
//true if all segments align with a bus, or if a segment covers the total length
//irrelevant in 2D set-up
bool WS2812FX::checkSegmentAlignment() {
bool aligned = false;
for (segment &seg : _segments) {
@@ -1792,8 +1951,7 @@ bool WS2812FX::checkSegmentAlignment() {
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
uint8_t WS2812FX::setPixelSegment(uint8_t n)
{
uint8_t WS2812FX::setPixelSegment(uint8_t n) {
uint8_t prevSegId = _segment_index;
if (n < _segments.size()) {
_segment_index = n;
@@ -1802,8 +1960,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
return prevSegId;
}
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
{
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);
@@ -1813,26 +1970,24 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
}
}
void WS2812FX::setTransitionMode(bool t)
{
void WS2812FX::setTransitionMode(bool t) {
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0);
}
#ifdef WLED_DEBUG
void WS2812FX::printSize()
{
void WS2812FX::printSize() {
size_t size = 0;
for (const Segment &seg : _segments) size += seg.getSize();
DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size);
DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB));
size = getLengthTotal();
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
}
#endif
void WS2812FX::loadCustomPalettes()
{
void WS2812FX::loadCustomPalettes() {
byte tcp[72]; //support gradient palettes with up to 18 entries
CRGBPalette16 targetPalette;
customPalettes.clear(); // start fresh
@@ -1880,85 +2035,103 @@ void WS2812FX::loadCustomPalettes()
}
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
void WS2812FX::deserializeMap(uint8_t n) {
// WLEDMM: also supports isMatrix
bool WS2812FX::deserializeMap(uint8_t n) {
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json");
bool isFile = WLED_FS.exists(fileName);
//WLEDMM: als support segment name ledmaps
bool isFile = false;;
if (n<10) {
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n); //WLEDMM: trick to not include 0 in ledmap.json
strcat(fileName, ".json");
isFile = WLED_FS.exists(fileName);
} else { //WLEDM add segment name as ledmap.name
uint8_t segment_index = 0;
for (segment &seg : _segments) {
if (n == 10 + segment_index && !isFile && seg.name != nullptr) {
sprintf_P(fileName, PSTR("/%s.json"), seg.name);
isFile = WLED_FS.exists(fileName);
}
if (isFile) break;
segment_index++;
}
}
if (!isFile) {
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
if (!n && customMappingTable != nullptr) {
//WLEDMM: if isMatrix then not erase but back to matrix default
if (isMatrix)
setUpMatrix(true);
else {
customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
}
//WLEDM: doubt this is necessary as return false causes setupMatrix to deal with this
if (!isMatrix && !n && customMappingTable != nullptr) {
customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
loadedLedmap = 0; //WLEDMM
}
return;
return false;
}
if (!requestJSONBufferLock(7)) return;
DEBUG_PRINT(F("Reading LED map from "));
DEBUG_PRINTLN(fileName);
if (!requestJSONBufferLock(7)) return false;
if (!readObjectFromFile(fileName, nullptr, &doc)) {
releaseJSONBufferLock();
return; //if file does not exist just exit
return false; //if file does not exist just exit
}
USER_PRINT(F("Reading LED map from ")); //WLEDMM use USER_PRINT
USER_PRINTLN(fileName);
// erase old custom ledmap
if (customMappingTable != nullptr) {
customMappingSize = 0;
delete[] customMappingTable;
customMappingTable = nullptr;
loadedLedmap = 0;
}
JsonArray map = doc[F("map")];
if (!map.isNull() && map.size()) { // not an empty map
//WLEDMM: if isMatrix then customMap size is whole matrix
#ifndef WLED_DISABLE_2D
if (isMatrix)
customMappingSize = Segment::maxWidth * Segment::maxHeight;
else
#endif
customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<MIN(map.size(),customMappingSize); i++) {
customMappingTable[i] = (uint16_t) map[i];
//WLEDMM: support ledmap file properties width and height
if (doc[F("width")]>0 && doc[F("height")]>0) {
Segment::maxWidth = doc[F("width")];;
Segment::maxHeight = doc[F("height")];;
resetSegments(true); //WLEDMM not makeAutoSegments() as we only want to change bounds
}
setUpMatrix(false); //WLEDMM: apply logical to physical mapping after the ledmap
customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++)
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
loadedLedmap = n;
#ifdef WLED_DEBUG
DEBUG_PRINTF("Custom ledmap: %d\n", loadedLedmap);
for (uint16_t i=0; i<customMappingSize; i++) {
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]);
}
DEBUG_PRINTLN();
#endif
}
else
setUpMatrix(true); //WLEDMM: if no map then back to matrix default
releaseJSONBufferLock();
return true;
}
WS2812FX* WS2812FX::instance = nullptr;
//Bus static member definition, would belong in bus_manager.cpp
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0;
uint8_t Bus::_gAWM = 255;
const char JSON_mode_names[] PROGMEM = R"=====(["FX names moved"])=====";
//WLEDMM netmindz ar palette add Audio responsive
const char JSON_palette_names[] PROGMEM = R"=====([
"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",
"Default","* Random Smooth ☾","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",
"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash",
"Pastel","Sunset 2","Beach","Vintage","Departure","Landscape","Beech","Sherbet","Hult","Hult 64",
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
"Candy2","Audio Responsive Ratio","Audio Responsive Hue","Audio Responsive Ramp"
"Candy2","Audio Responsive Ratio","Audio Responsive Hue","Audio Responsive Ramp","* Random Cycle"
])=====";