| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "wled.h" | ||
| 2 | #include "fcn_declare.h" | ||
| 3 | #include "const.h" | ||
| 4 | |||
| 5 | |||
| 6 | //helper to get int value at a position in string | ||
| 7 | ✗ | int getNumVal(const String* req, uint16_t pos) | |
| 8 | { | ||
| 9 | ✗ | return std::stoi(req->substr(pos+3)); | |
| 10 | } | ||
| 11 | |||
| 12 | |||
| 13 | //helper to get int value with in/decrementing support via ~ syntax | ||
| 14 | ✗ | void parseNumber(const char* str, byte* val, byte minv, byte maxv) | |
| 15 | { | ||
| 16 | ✗ | if (str == nullptr || str[0] == '\0') return; | |
| 17 | ✗ | if (str[0] == 'r') {*val = hw_random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0 | |
| 18 | ✗ | bool wrap = false; | |
| 19 | ✗ | if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;} | |
| 20 | ✗ | if (str[0] == '~') { | |
| 21 | ✗ | int out = atoi(str +1); | |
| 22 | ✗ | if (out == 0) { | |
| 23 | ✗ | if (str[1] == '0') return; | |
| 24 | ✗ | if (str[1] == '-') { | |
| 25 | ✗ | *val = (int)(*val -1) < (int)minv ? maxv : min((int)maxv,(*val -1)); //-1, wrap around | |
| 26 | } else { | ||
| 27 | ✗ | *val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1)); //+1, wrap around | |
| 28 | } | ||
| 29 | } else { | ||
| 30 | ✗ | if (wrap && *val == maxv && out > 0) out = minv; | |
| 31 | ✗ | else if (wrap && *val == minv && out < 0) out = maxv; | |
| 32 | else { | ||
| 33 | ✗ | out += *val; | |
| 34 | ✗ | if (out > maxv) out = maxv; | |
| 35 | ✗ | if (out < minv) out = minv; | |
| 36 | } | ||
| 37 | ✗ | *val = out; | |
| 38 | } | ||
| 39 | ✗ | return; | |
| 40 | ✗ | } else if (minv == maxv && minv == 0) { // limits "unset" i.e. both 0 | |
| 41 | ✗ | byte p1 = atoi(str); | |
| 42 | ✗ | const char* str2 = strchr(str,'~'); // min/max range (for preset cycle, e.g. "1~5~") | |
| 43 | ✗ | if (str2) { | |
| 44 | ✗ | byte p2 = atoi(++str2); // skip ~ | |
| 45 | ✗ | if (p2 > 0) { | |
| 46 | ✗ | while (isdigit(*(++str2))); // skip digits | |
| 47 | ✗ | parseNumber(str2, val, p1, p2); | |
| 48 | ✗ | return; | |
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | ✗ | *val = atoi(str); | |
| 53 | } | ||
| 54 | #ifndef WLED_DISABLE_FPP | ||
| 55 | //getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form) | ||
| 56 | bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) { | ||
| 57 | if (elem.is<int>()) { | ||
| 58 | if (elem < 0) return false; //ignore e.g. {"ps":-1} | ||
| 59 | *val = elem; | ||
| 60 | return true; | ||
| 61 | } else if (elem.is<const char*>()) { | ||
| 62 | const char* str = elem; | ||
| 63 | size_t len = strnlen(str, 14); | ||
| 64 | if (len == 0 || len > 12) return false; | ||
| 65 | // fix for #3605 & #4346 | ||
| 66 | // ignore vmin and vmax and use as specified in API | ||
| 67 | if (len > 3 && (strchr(str,'r') || strchr(str,'~') != strrchr(str,'~'))) vmax = vmin = 0; // we have "X~Y(r|~[w][-][Z])" form | ||
| 68 | // end fix | ||
| 69 | parseNumber(str, val, vmin, vmax); | ||
| 70 | return true; | ||
| 71 | } | ||
| 72 | return false; //key does not exist | ||
| 73 | } | ||
| 74 | |||
| 75 | |||
| 76 | bool getBoolVal(const JsonVariant &elem, bool dflt) { | ||
| 77 | if (elem.is<const char*>() && elem.as<const char*>()[0] == 't') { | ||
| 78 | return !dflt; | ||
| 79 | } else { | ||
| 80 | return elem | dflt; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv) | ||
| 85 | { | ||
| 86 | const char *v = strstr(req, key); | ||
| 87 | if (v) v += strlen(key); | ||
| 88 | else return false; | ||
| 89 | parseNumber(v, val, minv, maxv); | ||
| 90 | return true; | ||
| 91 | } | ||
| 92 | |||
| 93 | static size_t printSetFormInput(Print& settingsScript, const char* key, const char* selector, int value) { | ||
| 94 | return settingsScript.printf_P(PSTR("d.Sf.%s.%s=%d;"), key, selector, value); | ||
| 95 | } | ||
| 96 | |||
| 97 | size_t printSetFormCheckbox(Print& settingsScript, const char* key, int val) { | ||
| 98 | return printSetFormInput(settingsScript, key, PSTR("checked"), val); | ||
| 99 | } | ||
| 100 | size_t printSetFormValue(Print& settingsScript, const char* key, int val) { | ||
| 101 | return printSetFormInput(settingsScript, key, PSTR("value"), val); | ||
| 102 | } | ||
| 103 | size_t printSetFormIndex(Print& settingsScript, const char* key, int index) { | ||
| 104 | return printSetFormInput(settingsScript, key, PSTR("selectedIndex"), index); | ||
| 105 | } | ||
| 106 | |||
| 107 | size_t printSetFormValue(Print& settingsScript, const char* key, const char* val) { | ||
| 108 | return settingsScript.printf_P(PSTR("d.Sf.%s.value=\"%s\";"),key,val); | ||
| 109 | } | ||
| 110 | |||
| 111 | size_t printSetClassElementHTML(Print& settingsScript, const char* key, const int index, const char* val) { | ||
| 112 | return settingsScript.printf_P(PSTR("d.getElementsByClassName(\"%s\")[%d].innerHTML=\"%s\";"), key, index, val); | ||
| 113 | } | ||
| 114 | |||
| 115 | |||
| 116 | |||
| 117 | void prepareHostname(char* hostname) | ||
| 118 | { | ||
| 119 | sprintf_P(hostname, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6); | ||
| 120 | const char *pC = serverDescription; | ||
| 121 | unsigned pos = 5; // keep "wled-" | ||
| 122 | while (*pC && pos < 24) { // while !null and not over length | ||
| 123 | if (isalnum(*pC)) { // if the current char is alpha-numeric append it to the hostname | ||
| 124 | hostname[pos] = *pC; | ||
| 125 | pos++; | ||
| 126 | } else if (*pC == ' ' || *pC == '_' || *pC == '-' || *pC == '+' || *pC == '!' || *pC == '?' || *pC == '*') { | ||
| 127 | hostname[pos] = '-'; | ||
| 128 | pos++; | ||
| 129 | } | ||
| 130 | // else do nothing - no leading hyphens and do not include hyphens for all other characters. | ||
| 131 | pC++; | ||
| 132 | } | ||
| 133 | //last character must not be hyphen | ||
| 134 | if (pos > 5) { | ||
| 135 | while (pos > 4 && hostname[pos -1] == '-') pos--; | ||
| 136 | hostname[pos] = '\0'; // terminate string (leave at least "wled") | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | |||
| 141 | bool isAsterisksOnly(const char* str, byte maxLen) | ||
| 142 | { | ||
| 143 | for (unsigned i = 0; i < maxLen; i++) { | ||
| 144 | if (str[i] == 0) break; | ||
| 145 | if (str[i] != '*') return false; | ||
| 146 | } | ||
| 147 | //at this point the password contains asterisks only | ||
| 148 | return (str[0] != 0); //false on empty string | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | //threading/network callback details: https://github.com/wled-dev/WLED/pull/2336#discussion_r762276994 | ||
| 153 | bool requestJSONBufferLock(uint8_t moduleID) | ||
| 154 | { | ||
| 155 | if (pDoc == nullptr) { | ||
| 156 | DEBUG_PRINTLN(F("ERROR: JSON buffer not allocated!")); | ||
| 157 | return false; | ||
| 158 | } | ||
| 159 | |||
| 160 | #if defined(ARDUINO_ARCH_ESP32) | ||
| 161 | // Use a recursive mutex type in case our task is the one holding the JSON buffer. | ||
| 162 | // This can happen during large JSON web transactions. In this case, we continue immediately | ||
| 163 | // and then will return out below if the lock is still held. | ||
| 164 | if (xSemaphoreTakeRecursive(jsonBufferLockMutex, 250) == pdFALSE) return false; // timed out waiting | ||
| 165 | #elif defined(ARDUINO_ARCH_ESP8266) | ||
| 166 | // If we're in system context, delay() won't return control to the user context, so there's | ||
| 167 | // no point in waiting. | ||
| 168 | if (can_yield()) { | ||
| 169 | unsigned long now = millis(); | ||
| 170 | while (jsonBufferLock && (millis()-now < 250)) delay(1); // wait for fraction for buffer lock | ||
| 171 | } | ||
| 172 | #else | ||
| 173 | #error Unsupported task framework - fix requestJSONBufferLock | ||
| 174 | #endif | ||
| 175 | // If the lock is still held - by us, or by another task | ||
| 176 | if (jsonBufferLock) { | ||
| 177 | DEBUG_PRINTF_P(PSTR("ERROR: Locking JSON buffer (%d) failed! (still locked by %d)\n"), moduleID, jsonBufferLock); | ||
| 178 | #ifdef ARDUINO_ARCH_ESP32 | ||
| 179 | xSemaphoreGiveRecursive(jsonBufferLockMutex); | ||
| 180 | #endif | ||
| 181 | return false; | ||
| 182 | } | ||
| 183 | |||
| 184 | jsonBufferLock = moduleID ? moduleID : 255; | ||
| 185 | DEBUG_PRINTF_P(PSTR("JSON buffer locked. (%d)\n"), jsonBufferLock); | ||
| 186 | pDoc->clear(); | ||
| 187 | return true; | ||
| 188 | } | ||
| 189 | |||
| 190 | |||
| 191 | void releaseJSONBufferLock() | ||
| 192 | { | ||
| 193 | DEBUG_PRINTF_P(PSTR("JSON buffer released. (%d)\n"), jsonBufferLock); | ||
| 194 | jsonBufferLock = 0; | ||
| 195 | #ifdef ARDUINO_ARCH_ESP32 | ||
| 196 | xSemaphoreGiveRecursive(jsonBufferLockMutex); | ||
| 197 | #endif | ||
| 198 | } | ||
| 199 | #else | ||
| 200 | 3542 | void releaseJSONBufferLock() {} | |
| 201 | #endif | ||
| 202 | |||
| 203 | // extracts effect mode (or palette) name from names serialized string | ||
| 204 | // caller must provide large enough buffer for name (including SR extensions)! | ||
| 205 | ✗ | uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen) | |
| 206 | { | ||
| 207 | ✗ | if (src == JSON_mode_names || src == nullptr) { | |
| 208 | ✗ | if (mode < strip().getModeCount()) { | |
| 209 | char lineBuffer[256]; | ||
| 210 | //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); | ||
| 211 | ✗ | strncpy_P(lineBuffer, strip().getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); | |
| 212 | ✗ | lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string | |
| 213 | ✗ | size_t len = strlen(lineBuffer); | |
| 214 | ✗ | size_t j = 0; | |
| 215 | ✗ | for (; j < maxLen && j < len; j++) { | |
| 216 | ✗ | if (lineBuffer[j] == '\0' || lineBuffer[j] == '@') break; | |
| 217 | ✗ | dest[j] = lineBuffer[j]; | |
| 218 | } | ||
| 219 | ✗ | dest[j] = 0; // terminate string | |
| 220 | ✗ | return strlen(dest); | |
| 221 | ✗ | } else return 0; | |
| 222 | } | ||
| 223 | |||
| 224 | ✗ | if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) { | |
| 225 | ✗ | snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode); | |
| 226 | ✗ | dest[maxLen-1] = '\0'; | |
| 227 | ✗ | return strlen(dest); | |
| 228 | } | ||
| 229 | |||
| 230 | ✗ | unsigned qComma = 0; | |
| 231 | ✗ | bool insideQuotes = false; | |
| 232 | ✗ | unsigned printedChars = 0; | |
| 233 | char singleJsonSymbol; | ||
| 234 | ✗ | size_t len = strlen_P(src); | |
| 235 | |||
| 236 | // Find the mode name in JSON | ||
| 237 | ✗ | for (size_t i = 0; i < len; i++) { | |
| 238 | ✗ | singleJsonSymbol = pgm_read_byte_near((uint8_t*)(src + i)); | |
| 239 | ✗ | if (singleJsonSymbol == '\0') break; | |
| 240 | ✗ | if (singleJsonSymbol == '@' && insideQuotes && qComma == mode) break; //stop when SR extension encountered | |
| 241 | ✗ | switch (singleJsonSymbol) { | |
| 242 | ✗ | case '"': | |
| 243 | ✗ | insideQuotes = !insideQuotes; | |
| 244 | ✗ | break; | |
| 245 | ✗ | case '[': | |
| 246 | case ']': | ||
| 247 | ✗ | break; | |
| 248 | ✗ | case ',': | |
| 249 | ✗ | if (!insideQuotes) qComma++; | |
| 250 | default: | ||
| 251 | ✗ | if (!insideQuotes || (qComma != mode)) break; | |
| 252 | ✗ | dest[printedChars++] = singleJsonSymbol; | |
| 253 | } | ||
| 254 | ✗ | if ((qComma > mode) || (printedChars >= maxLen)) break; | |
| 255 | } | ||
| 256 | ✗ | dest[printedChars] = '\0'; | |
| 257 | ✗ | return strlen(dest); | |
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | // extracts effect slider data (1st group after @) | ||
| 262 | ✗ | uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var) | |
| 263 | { | ||
| 264 | ✗ | dest[0] = '\0'; // start by clearing buffer | |
| 265 | |||
| 266 | ✗ | if (mode < strip().getModeCount()) { | |
| 267 | ✗ | String lineBuffer = strip().getModeData(mode); | |
| 268 | ✗ | if (lineBuffer.length() > 0) { | |
| 269 | ✗ | int start = lineBuffer.find('@'); // String::find() returns an int, not an unsigned; -1 means "not found" | |
| 270 | ✗ | int stop = lineBuffer.find(';', start); | |
| 271 | ✗ | if (start>0 && stop>0) { | |
| 272 | ✗ | String names = lineBuffer.substr(start, stop); // include @ | |
| 273 | ✗ | int nameBegin = 1, nameEnd, nameDefault; | |
| 274 | ✗ | if (slider < 10) { | |
| 275 | ✗ | for (size_t i=0; i<=slider; i++) { | |
| 276 | const char *tmpstr; | ||
| 277 | ✗ | dest[0] = '\0'; //clear dest buffer | |
| 278 | ✗ | if (nameBegin <= 0) break; // there are no more names | |
| 279 | ✗ | nameEnd = names.find(',', nameBegin); | |
| 280 | ✗ | if (i == slider) { | |
| 281 | ✗ | nameDefault = names.find('=', nameBegin); // find default value | |
| 282 | ✗ | if (nameDefault > 0 && var && ((nameEnd>0 && nameDefault<nameEnd) || nameEnd<0)) { | |
| 283 | ✗ | *var = (uint8_t)atoi(names.substr(nameDefault+1).c_str()); | |
| 284 | } | ||
| 285 | ✗ | if (names[nameBegin] == '!') { | |
| 286 | ✗ | switch (slider) { | |
| 287 | ✗ | case 0: tmpstr = PSTR("FX Speed"); break; | |
| 288 | ✗ | case 1: tmpstr = PSTR("FX Intensity"); break; | |
| 289 | ✗ | case 2: tmpstr = PSTR("FX Custom 1"); break; | |
| 290 | ✗ | case 3: tmpstr = PSTR("FX Custom 2"); break; | |
| 291 | ✗ | case 4: tmpstr = PSTR("FX Custom 3"); break; | |
| 292 | ✗ | default: tmpstr = PSTR("FX Custom"); break; | |
| 293 | } | ||
| 294 | ✗ | strncpy_P(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous) | |
| 295 | ✗ | dest[maxLen-1] = '\0'; | |
| 296 | } else { | ||
| 297 | ✗ | if (nameEnd<0) tmpstr = names.substr(nameBegin).c_str(); // did not find ",", last name? | |
| 298 | ✗ | else tmpstr = names.substr(nameBegin, nameEnd).c_str(); | |
| 299 | ✗ | strncpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous) | |
| 300 | } | ||
| 301 | } | ||
| 302 | ✗ | nameBegin = nameEnd+1; // next name (if "," is not found it will be 0) | |
| 303 | } // next slider | ||
| 304 | ✗ | } else if (slider == 255) { | |
| 305 | // palette | ||
| 306 | ✗ | strncpy(dest, "pal", maxLen); | |
| 307 | ✗ | names = lineBuffer.substr(stop+1); // stop has index of color slot names | |
| 308 | ✗ | nameBegin = names.find(';'); // look for palette | |
| 309 | ✗ | if (nameBegin >= 0) { | |
| 310 | ✗ | nameEnd = names.find(';', nameBegin+1); | |
| 311 | ✗ | if (!isdigit(names[nameBegin+1])) nameBegin = names.find('=', nameBegin+1); // look for default value | |
| 312 | ✗ | if (nameEnd >= 0 && nameBegin > nameEnd) nameBegin = -1; | |
| 313 | ✗ | if (nameBegin >= 0 && var) { | |
| 314 | ✗ | *var = (uint8_t)atoi(names.substr(nameBegin+1).c_str()); | |
| 315 | } | ||
| 316 | } | ||
| 317 | } | ||
| 318 | // we have slider name (including default value) in the dest buffer | ||
| 319 | ✗ | for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value | |
| 320 | |||
| 321 | ✗ | } else { | |
| 322 | // defaults to just speed and intensity since there is no slider data | ||
| 323 | ✗ | switch (slider) { | |
| 324 | ✗ | case 0: strncpy_P(dest, PSTR("FX Speed"), maxLen); break; | |
| 325 | ✗ | case 1: strncpy_P(dest, PSTR("FX Intensity"), maxLen); break; | |
| 326 | } | ||
| 327 | ✗ | dest[maxLen] = '\0'; // strncpy does not necessarily null terminate string | |
| 328 | } | ||
| 329 | } | ||
| 330 | ✗ | return strlen(dest); | |
| 331 | ✗ | } | |
| 332 | ✗ | return 0; | |
| 333 | } | ||
| 334 | |||
| 335 | |||
| 336 | // extracts mode parameter defaults from last section of mode data (e.g. "Juggle@!,Trail;!,!,;!;012;sx=16,ix=240") | ||
| 337 | 2109422 | int16_t extractModeDefaults(uint8_t mode, const char *segVar) | |
| 338 | { | ||
| 339 |
1/2✓ Branch 0 taken 2109422 times.
✗ Branch 1 not taken.
|
2109422 | if (mode < strip().getModeCount()) { |
| 340 | char lineBuffer[256]; | ||
| 341 |
1/2✓ Branch 0 taken 2109422 times.
✗ Branch 1 not taken.
|
2109422 | strncpy_P(lineBuffer, strip().getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1); |
| 342 | 2109422 | lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string | |
| 343 |
1/2✓ Branch 0 taken 2109422 times.
✗ Branch 1 not taken.
|
2109422 | if (lineBuffer[0] != 0) { |
| 344 | 2109422 | char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data | |
| 345 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2109342 times.
|
4218764 | if (!startPtr) return -1; |
| 346 | |||
| 347 | 2109342 | char* stopPtr = strstr(startPtr, segVar); | |
| 348 |
2/2✓ Branch 0 taken 1492197 times.
✓ Branch 1 taken 617145 times.
|
2109342 | if (!stopPtr) return -1; |
| 349 | |||
| 350 | 617145 | stopPtr += strlen(segVar) +1; // skip "=" | |
| 351 | 617145 | return atoi(stopPtr); | |
| 352 | } | ||
| 353 | } | ||
| 354 | ✗ | return -1; | |
| 355 | } | ||
| 356 | |||
| 357 | |||
| 358 | ✗ | void checkSettingsPIN(const char* pin) { | |
| 359 | ✗ | if (!pin) return; | |
| 360 | ✗ | if (!correctPIN && millis() - lastEditTime < PIN_RETRY_COOLDOWN) return; // guard against PIN brute force | |
| 361 | ✗ | bool correctBefore = correctPIN; | |
| 362 | ✗ | correctPIN = (strlen(settingsPIN) == 0 || strncmp(settingsPIN, pin, 4) == 0); | |
| 363 | //if (correctBefore != correctPIN) createEditHandler(correctPIN); | ||
| 364 | ✗ | lastEditTime = millis(); | |
| 365 | } | ||
| 366 | |||
| 367 | |||
| 368 | 5 | uint16_t crc16(const unsigned char* data_p, size_t length) { | |
| 369 | uint8_t x; | ||
| 370 | 5 | uint16_t crc = 0xFFFF; | |
| 371 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (!length) return 0x1D0F; |
| 372 |
2/2✓ Branch 0 taken 6207 times.
✓ Branch 1 taken 5 times.
|
6212 | while (length--) { |
| 373 | 6207 | x = crc >> 8 ^ *data_p++; | |
| 374 | 6207 | x ^= x>>4; | |
| 375 | 6207 | crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x); | |
| 376 | } | ||
| 377 | 5 | return crc; | |
| 378 | } | ||
| 379 | |||
| 380 | // fastled beatsin: 1:1 replacements to remove the use of fastled sin16() | ||
| 381 | // Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. | ||
| 382 | 529 | uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) | |
| 383 | { | ||
| 384 | 529 | uint16_t beat = beat88( beats_per_minute_88, timebase); | |
| 385 | 529 | uint16_t beatsin (sin16_t( beat + phase_offset) + 32768); | |
| 386 | 529 | uint16_t rangewidth = highest - lowest; | |
| 387 | 529 | uint16_t scaledbeat = scale16( beatsin, rangewidth); | |
| 388 | 529 | uint16_t result = lowest + scaledbeat; | |
| 389 | 529 | return result; | |
| 390 | } | ||
| 391 | |||
| 392 | // Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. | ||
| 393 | 22045 | uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) | |
| 394 | { | ||
| 395 | 22045 | uint16_t beat = beat16( beats_per_minute, timebase); | |
| 396 | 22045 | uint16_t beatsin = (sin16_t( beat + phase_offset) + 32768); | |
| 397 | 22045 | uint16_t rangewidth = highest - lowest; | |
| 398 | 22045 | uint16_t scaledbeat = scale16( beatsin, rangewidth); | |
| 399 | 22045 | uint16_t result = lowest + scaledbeat; | |
| 400 | 22045 | return result; | |
| 401 | } | ||
| 402 | |||
| 403 | // Generates an 8-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. | ||
| 404 | 74197 | uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset) | |
| 405 | { | ||
| 406 | 74197 | uint8_t beat = beat8( beats_per_minute, timebase); | |
| 407 | 74197 | uint8_t beatsin = sin8_t( beat + phase_offset); | |
| 408 | 74197 | uint8_t rangewidth = highest - lowest; | |
| 409 | 74197 | uint8_t scaledbeat = scale8( beatsin, rangewidth); | |
| 410 | 74197 | uint8_t result = lowest + scaledbeat; | |
| 411 | 74197 | return result; | |
| 412 | } | ||
| 413 | |||
| 414 | #ifndef WLED_DISABLE_FPP | ||
| 415 | /////////////////////////////////////////////////////////////////////////////// | ||
| 416 | // Begin simulateSound (to enable audio enhanced effects to display something) | ||
| 417 | /////////////////////////////////////////////////////////////////////////////// | ||
| 418 | // Currently 4 types defined, to be fine tuned and new types added | ||
| 419 | // (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type) | ||
| 420 | typedef enum UM_SoundSimulations { | ||
| 421 | UMS_BeatSin = 0, | ||
| 422 | UMS_WeWillRockYou, | ||
| 423 | UMS_10_13, | ||
| 424 | UMS_14_3 | ||
| 425 | } um_soundSimulations_t; | ||
| 426 | |||
| 427 | um_data_t* simulateSound(uint8_t simulationId) | ||
| 428 | { | ||
| 429 | static uint8_t samplePeak; | ||
| 430 | static float FFT_MajorPeak; | ||
| 431 | static uint8_t maxVol; | ||
| 432 | static uint8_t binNum; | ||
| 433 | |||
| 434 | static float volumeSmth; | ||
| 435 | static uint16_t volumeRaw; | ||
| 436 | static float my_magnitude; | ||
| 437 | |||
| 438 | //arrays | ||
| 439 | uint8_t *fftResult; | ||
| 440 | |||
| 441 | static um_data_t* um_data = nullptr; | ||
| 442 | |||
| 443 | if (!um_data) { | ||
| 444 | //claim storage for arrays | ||
| 445 | fftResult = (uint8_t *)malloc(sizeof(uint8_t) * 16); | ||
| 446 | |||
| 447 | // initialize um_data pointer structure | ||
| 448 | // NOTE!!! | ||
| 449 | // This may change as AudioReactive usermod may change | ||
| 450 | um_data = new um_data_t; | ||
| 451 | um_data->u_size = 8; | ||
| 452 | um_data->u_type = new um_types_t[um_data->u_size]; | ||
| 453 | um_data->u_data = new void*[um_data->u_size]; | ||
| 454 | um_data->u_data[0] = &volumeSmth; | ||
| 455 | um_data->u_data[1] = &volumeRaw; | ||
| 456 | um_data->u_data[2] = fftResult; | ||
| 457 | um_data->u_data[3] = &samplePeak; | ||
| 458 | um_data->u_data[4] = &FFT_MajorPeak; | ||
| 459 | um_data->u_data[5] = &my_magnitude; | ||
| 460 | um_data->u_data[6] = &maxVol; | ||
| 461 | um_data->u_data[7] = &binNum; | ||
| 462 | } else { | ||
| 463 | // get arrays from um_data | ||
| 464 | fftResult = (uint8_t*)um_data->u_data[2]; | ||
| 465 | } | ||
| 466 | |||
| 467 | uint32_t ms = millis(); | ||
| 468 | |||
| 469 | switch (simulationId) { | ||
| 470 | default: | ||
| 471 | case UMS_BeatSin: | ||
| 472 | for (int i = 0; i<16; i++) | ||
| 473 | fftResult[i] = beatsin8_t(120 / (i+1), 0, 255); | ||
| 474 | // fftResult[i] = (beatsin8_t(120, 0, 255) + (256/16 * i)) % 256; | ||
| 475 | volumeSmth = fftResult[8]; | ||
| 476 | break; | ||
| 477 | case UMS_WeWillRockYou: | ||
| 478 | if (ms%2000 < 200) { | ||
| 479 | volumeSmth = hw_random8(); | ||
| 480 | for (int i = 0; i<5; i++) | ||
| 481 | fftResult[i] = hw_random8(); | ||
| 482 | } | ||
| 483 | else if (ms%2000 < 400) { | ||
| 484 | volumeSmth = 0; | ||
| 485 | for (int i = 0; i<16; i++) | ||
| 486 | fftResult[i] = 0; | ||
| 487 | } | ||
| 488 | else if (ms%2000 < 600) { | ||
| 489 | volumeSmth = hw_random8(); | ||
| 490 | for (int i = 5; i<11; i++) | ||
| 491 | fftResult[i] = hw_random8(); | ||
| 492 | } | ||
| 493 | else if (ms%2000 < 800) { | ||
| 494 | volumeSmth = 0; | ||
| 495 | for (int i = 0; i<16; i++) | ||
| 496 | fftResult[i] = 0; | ||
| 497 | } | ||
| 498 | else if (ms%2000 < 1000) { | ||
| 499 | volumeSmth = hw_random8(); | ||
| 500 | for (int i = 11; i<16; i++) | ||
| 501 | fftResult[i] = hw_random8(); | ||
| 502 | } | ||
| 503 | else { | ||
| 504 | volumeSmth = 0; | ||
| 505 | for (int i = 0; i<16; i++) | ||
| 506 | fftResult[i] = 0; | ||
| 507 | } | ||
| 508 | break; | ||
| 509 | case UMS_10_13: | ||
| 510 | for (int i = 0; i<16; i++) | ||
| 511 | fftResult[i] = perlin8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); | ||
| 512 | volumeSmth = fftResult[8]; | ||
| 513 | break; | ||
| 514 | case UMS_14_3: | ||
| 515 | for (int i = 0; i<16; i++) | ||
| 516 | fftResult[i] = perlin8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); | ||
| 517 | volumeSmth = fftResult[8]; | ||
| 518 | break; | ||
| 519 | } | ||
| 520 | |||
| 521 | samplePeak = hw_random8() > 250; | ||
| 522 | FFT_MajorPeak = 21 + (volumeSmth*volumeSmth) / 8.0f; // walk thru full range of 21hz...8200hz | ||
| 523 | maxVol = 31; // this gets feedback fro UI | ||
| 524 | binNum = 8; // this gets feedback fro UI | ||
| 525 | volumeRaw = volumeSmth; | ||
| 526 | my_magnitude = 10000.0f / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ??? | ||
| 527 | if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute | ||
| 528 | |||
| 529 | return um_data; | ||
| 530 | } | ||
| 531 | #endif | ||
| 532 | |||
| 533 | static const char s_ledmap_tmpl[] PROGMEM = "ledmap%d.json"; | ||
| 534 | // enumerate all ledmapX.json files on FS and extract ledmap names if existing | ||
| 535 | 3542 | void enumerateLedmaps() { | |
| 536 | #ifndef WLED_DISABLE_FPP | ||
| 537 | |||
| 538 | StaticJsonDocument<64> filter; | ||
| 539 | filter["n"] = true; | ||
| 540 | ledMaps = 1; | ||
| 541 | for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) { | ||
| 542 | char fileName[33] = "/"; | ||
| 543 | sprintf_P(fileName+1, s_ledmap_tmpl, i); | ||
| 544 | bool isFile = WLED_FS.exists(fileName); | ||
| 545 | |||
| 546 | #ifndef ESP8266 | ||
| 547 | if (ledmapNames[i-1]) { //clear old name | ||
| 548 | free(ledmapNames[i-1]); | ||
| 549 | ledmapNames[i-1] = nullptr; | ||
| 550 | } | ||
| 551 | #endif | ||
| 552 | |||
| 553 | if (isFile) { | ||
| 554 | ledMaps |= 1 << i; | ||
| 555 | |||
| 556 | #ifndef ESP8266 | ||
| 557 | if (requestJSONBufferLock(21)) { | ||
| 558 | if (readObjectFromFile(fileName, nullptr, pDoc, &filter)) { | ||
| 559 | size_t len = 0; | ||
| 560 | JsonObject root = pDoc->as<JsonObject>(); | ||
| 561 | if (!root["n"].isNull()) { | ||
| 562 | // name field exists | ||
| 563 | const char *name = root["n"].as<const char*>(); | ||
| 564 | if (name != nullptr) len = strlen(name); | ||
| 565 | if (len > 0 && len < 33) { | ||
| 566 | ledmapNames[i-1] = static_cast<char*>(malloc(len+1)); | ||
| 567 | if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33); | ||
| 568 | } | ||
| 569 | } | ||
| 570 | if (!ledmapNames[i-1]) { | ||
| 571 | char tmp[33]; | ||
| 572 | snprintf_P(tmp, 32, s_ledmap_tmpl, i); | ||
| 573 | len = strlen(tmp); | ||
| 574 | ledmapNames[i-1] = static_cast<char*>(malloc(len+1)); | ||
| 575 | if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | releaseJSONBufferLock(); | ||
| 579 | } | ||
| 580 | #endif | ||
| 581 | } | ||
| 582 | |||
| 583 | } | ||
| 584 | #endif | ||
| 585 | 3542 | } | |
| 586 | |||
| 587 | /* | ||
| 588 | * Returns a new, random color wheel index with a minimum distance of 42 from pos. | ||
| 589 | */ | ||
| 590 | 25 | uint8_t get_random_wheel_index(uint8_t pos) { | |
| 591 | 25 | uint8_t r = 0, x = 0, y = 0, d = 0; | |
| 592 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 25 times.
|
61 | while (d < 42) { |
| 593 | 36 | r = hw_random8(); | |
| 594 | 36 | x = abs(pos - r); | |
| 595 | 36 | y = 255 - x; | |
| 596 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 6 times.
|
36 | d = MIN(x, y); |
| 597 | } | ||
| 598 | 25 | return r; | |
| 599 | } | ||
| 600 | |||
| 601 | // float version of map() | ||
| 602 | 174 | float mapf(float x, float in_min, float in_max, float out_min, float out_max) { | |
| 603 | 174 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; | |
| 604 | } | ||
| 605 | |||
| 606 | ✗ | uint32_t hashInt(uint32_t s) { | |
| 607 | // borrowed from https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key | ||
| 608 | ✗ | s = ((s >> 16) ^ s) * 0x45d9f3b; | |
| 609 | ✗ | s = ((s >> 16) ^ s) * 0x45d9f3b; | |
| 610 | ✗ | return (s >> 16) ^ s; | |
| 611 | } | ||
| 612 | |||
| 613 | // 32 bit random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h) | ||
| 614 | 1151 | uint32_t hw_random(uint32_t upperlimit) { | |
| 615 | 1151 | uint32_t rnd = hw_random(); | |
| 616 | 1151 | uint64_t scaled = uint64_t(rnd) * uint64_t(upperlimit); | |
| 617 | 1151 | return scaled >> 32; | |
| 618 | } | ||
| 619 | |||
| 620 | ✗ | int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { | |
| 621 | ✗ | if(lowerlimit >= upperlimit) { | |
| 622 | ✗ | return lowerlimit; | |
| 623 | } | ||
| 624 | ✗ | uint32_t diff = upperlimit - lowerlimit; | |
| 625 | ✗ | return hw_random(diff) + lowerlimit; | |
| 626 | } | ||
| 627 | |||
| 628 | /* | ||
| 629 | * Fixed point integer based Perlin noise functions by @dedehai | ||
| 630 | * Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness | ||
| 631 | */ | ||
| 632 | #define PERLIN_SHIFT 1 | ||
| 633 | |||
| 634 | // calculate gradient for corner from hash value | ||
| 635 | static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) { | ||
| 636 | // using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate, also changes range and needs proper adustment) | ||
| 637 | // return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 | ||
| 638 | // return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 | ||
| 639 | // return (h & 0x07) - 4; // use PERLIN_SHIFT 2 | ||
| 640 | 2160926 | return (h & 0x03) - 2; // use PERLIN_SHIFT 1 -> closest to original fastled version | |
| 641 | } | ||
| 642 | |||
| 643 | // Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! | ||
| 644 | static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) { | ||
| 645 | 15062 | uint32_t h = x0 * 0x27D4EB2D; | |
| 646 | 15062 | h ^= h >> 15; | |
| 647 | 15062 | h *= 0x92C3412B; | |
| 648 | 15062 | h ^= h >> 13; | |
| 649 | 15062 | h ^= h >> 7; | |
| 650 | 15062 | return (hashToGradient(h) * dx) >> PERLIN_SHIFT; | |
| 651 | } | ||
| 652 | |||
| 653 | static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) { | ||
| 654 | 243540 | uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D); | |
| 655 | 243540 | h ^= h >> 15; | |
| 656 | 243540 | h *= 0x92C3412B; | |
| 657 | 243540 | h ^= h >> 13; | |
| 658 | 487080 | return (hashToGradient(h) * dx + hashToGradient(h>>PERLIN_SHIFT) * dy) >> (1 + PERLIN_SHIFT); | |
| 659 | } | ||
| 660 | |||
| 661 | static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { | ||
| 662 | // fast and good entropy hash from corner coordinates | ||
| 663 | 552928 | uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9); | |
| 664 | 552928 | h ^= h >> 15; | |
| 665 | 552928 | h *= 0x92C3412B; | |
| 666 | 552928 | h ^= h >> 13; | |
| 667 | 1658784 | return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3 | |
| 668 | } | ||
| 669 | |||
| 670 | // fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point, scaled to avoid overflows | ||
| 671 | 336649 | static uint32_t smoothstep(const uint32_t t) { | |
| 672 | 336649 | uint32_t t_squared = (t * t) >> 16; | |
| 673 | 336649 | uint32_t factor = (3 << 16) - ((t << 1)); | |
| 674 | 336649 | return (t_squared * factor) >> 18; // scale to avoid overflows and give best resolution | |
| 675 | } | ||
| 676 | |||
| 677 | // simple linear interpolation for fixed-point values, scaled for perlin noise use | ||
| 678 | 673998 | static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { | |
| 679 | 673998 | return a + (((b - a) * t) >> 14); // match scaling with smoothstep to yield 16.16bit values | |
| 680 | } | ||
| 681 | |||
| 682 | // 1D Perlin noise function that returns a value in range of -24691 to 24689 | ||
| 683 | 7531 | int32_t perlin1D_raw(uint32_t x, bool is16bit) { | |
| 684 | // integer and fractional part coordinates | ||
| 685 | 7531 | int32_t x0 = x >> 16; | |
| 686 | 7531 | int32_t x1 = x0 + 1; | |
| 687 |
1/2✓ Branch 0 taken 7531 times.
✗ Branch 1 not taken.
|
7531 | if(is16bit) x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF |
| 688 | |||
| 689 | 7531 | int32_t dx0 = x & 0xFFFF; | |
| 690 | 7531 | int32_t dx1 = dx0 - 0x10000; | |
| 691 | // gradient values for the two corners | ||
| 692 | 7531 | int32_t g0 = gradient1D(x0, dx0); | |
| 693 | 7531 | int32_t g1 = gradient1D(x1, dx1); | |
| 694 | // interpolate and smooth function | ||
| 695 | 7531 | int32_t tx = smoothstep(dx0); | |
| 696 | 7531 | int32_t noise = lerpPerlin(g0, g1, tx); | |
| 697 | 7531 | return noise; | |
| 698 | } | ||
| 699 | |||
| 700 | // 2D Perlin noise function that returns a value in range of -20633 to 20629 | ||
| 701 | 60885 | int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) { | |
| 702 | 60885 | int32_t x0 = x >> 16; | |
| 703 | 60885 | int32_t y0 = y >> 16; | |
| 704 | 60885 | int32_t x1 = x0 + 1; | |
| 705 | 60885 | int32_t y1 = y0 + 1; | |
| 706 | |||
| 707 |
2/2✓ Branch 0 taken 25164 times.
✓ Branch 1 taken 35721 times.
|
60885 | if(is16bit) { |
| 708 | 25164 | x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF | |
| 709 | 25164 | y1 = y1 & 0xFF; | |
| 710 | } | ||
| 711 | |||
| 712 | 60885 | int32_t dx0 = x & 0xFFFF; | |
| 713 | 60885 | int32_t dy0 = y & 0xFFFF; | |
| 714 | 60885 | int32_t dx1 = dx0 - 0x10000; | |
| 715 | 60885 | int32_t dy1 = dy0 - 0x10000; | |
| 716 | |||
| 717 | 60885 | int32_t g00 = gradient2D(x0, dx0, y0, dy0); | |
| 718 | 60885 | int32_t g10 = gradient2D(x1, dx1, y0, dy0); | |
| 719 | 60885 | int32_t g01 = gradient2D(x0, dx0, y1, dy1); | |
| 720 | 60885 | int32_t g11 = gradient2D(x1, dx1, y1, dy1); | |
| 721 | |||
| 722 | 60885 | uint32_t tx = smoothstep(dx0); | |
| 723 | 60885 | uint32_t ty = smoothstep(dy0); | |
| 724 | |||
| 725 | 60885 | int32_t nx0 = lerpPerlin(g00, g10, tx); | |
| 726 | 60885 | int32_t nx1 = lerpPerlin(g01, g11, tx); | |
| 727 | |||
| 728 | 60885 | int32_t noise = lerpPerlin(nx0, nx1, ty); | |
| 729 | 60885 | return noise; | |
| 730 | } | ||
| 731 | |||
| 732 | // 3D Perlin noise function that returns a value in range of -16788 to 16381 | ||
| 733 | 69116 | int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { | |
| 734 | 69116 | int32_t x0 = x >> 16; | |
| 735 | 69116 | int32_t y0 = y >> 16; | |
| 736 | 69116 | int32_t z0 = z >> 16; | |
| 737 | 69116 | int32_t x1 = x0 + 1; | |
| 738 | 69116 | int32_t y1 = y0 + 1; | |
| 739 | 69116 | int32_t z1 = z0 + 1; | |
| 740 | |||
| 741 |
2/2✓ Branch 0 taken 39822 times.
✓ Branch 1 taken 29294 times.
|
69116 | if(is16bit) { |
| 742 | 39822 | x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF | |
| 743 | 39822 | y1 = y1 & 0xFF; | |
| 744 | 39822 | z1 = z1 & 0xFF; | |
| 745 | } | ||
| 746 | |||
| 747 | 69116 | int32_t dx0 = x & 0xFFFF; | |
| 748 | 69116 | int32_t dy0 = y & 0xFFFF; | |
| 749 | 69116 | int32_t dz0 = z & 0xFFFF; | |
| 750 | 69116 | int32_t dx1 = dx0 - 0x10000; | |
| 751 | 69116 | int32_t dy1 = dy0 - 0x10000; | |
| 752 | 69116 | int32_t dz1 = dz0 - 0x10000; | |
| 753 | |||
| 754 | 69116 | int32_t g000 = gradient3D(x0, dx0, y0, dy0, z0, dz0); | |
| 755 | 69116 | int32_t g001 = gradient3D(x0, dx0, y0, dy0, z1, dz1); | |
| 756 | 69116 | int32_t g010 = gradient3D(x0, dx0, y1, dy1, z0, dz0); | |
| 757 | 69116 | int32_t g011 = gradient3D(x0, dx0, y1, dy1, z1, dz1); | |
| 758 | 69116 | int32_t g100 = gradient3D(x1, dx1, y0, dy0, z0, dz0); | |
| 759 | 69116 | int32_t g101 = gradient3D(x1, dx1, y0, dy0, z1, dz1); | |
| 760 | 69116 | int32_t g110 = gradient3D(x1, dx1, y1, dy1, z0, dz0); | |
| 761 | 69116 | int32_t g111 = gradient3D(x1, dx1, y1, dy1, z1, dz1); | |
| 762 | |||
| 763 | 69116 | uint32_t tx = smoothstep(dx0); | |
| 764 | 69116 | uint32_t ty = smoothstep(dy0); | |
| 765 | 69116 | uint32_t tz = smoothstep(dz0); | |
| 766 | |||
| 767 | 69116 | int32_t nx0 = lerpPerlin(g000, g100, tx); | |
| 768 | 69116 | int32_t nx1 = lerpPerlin(g010, g110, tx); | |
| 769 | 69116 | int32_t nx2 = lerpPerlin(g001, g101, tx); | |
| 770 | 69116 | int32_t nx3 = lerpPerlin(g011, g111, tx); | |
| 771 | 69116 | int32_t ny0 = lerpPerlin(nx0, nx1, ty); | |
| 772 | 69116 | int32_t ny1 = lerpPerlin(nx2, nx3, ty); | |
| 773 | |||
| 774 | 69116 | int32_t noise = lerpPerlin(ny0, ny1, tz); | |
| 775 | 69116 | return noise; | |
| 776 | } | ||
| 777 | |||
| 778 | // scaling functions for fastled replacement | ||
| 779 | ✗ | uint16_t perlin16(uint32_t x) { | |
| 780 | ✗ | return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range: about 4838 to 60766) | |
| 781 | } | ||
| 782 | |||
| 783 | 35721 | uint16_t perlin16(uint32_t x, uint32_t y) { | |
| 784 | 35721 | return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (fastled range: about 1748 to 63697) | |
| 785 | } | ||
| 786 | |||
| 787 | 29294 | uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { | |
| 788 | 29294 | return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (fastled range: about 4766 to 60840) | |
| 789 | } | ||
| 790 | |||
| 791 | 7531 | uint8_t perlin8(uint16_t x) { | |
| 792 | 7531 | return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; //scale to 16 bit, offset, then scale to 8bit | |
| 793 | } | ||
| 794 | |||
| 795 | 25164 | uint8_t perlin8(uint16_t x, uint16_t y) { | |
| 796 | 25164 | return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; //scale to 16 bit, offset, then scale to 8bit | |
| 797 | } | ||
| 798 | |||
| 799 | 39822 | uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { | |
| 800 | 39822 | return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit | |
| 801 | } | ||
| 802 |