2021-10-11 12:13:34 +00:00
# include "wled.h"
# include "fcn_declare.h"
# include "const.h"
2021-11-23 19:20:19 +00:00
//helper to get int value at a position in string
int getNumVal ( const String * req , uint16_t pos )
{
return req - > substring ( pos + 3 ) . toInt ( ) ;
}
//helper to get int value with in/decrementing support via ~ syntax
void parseNumber ( const char * str , byte * val , byte minv , byte maxv )
{
if ( str = = nullptr | | str [ 0 ] = = ' \0 ' ) return ;
2024-12-20 18:12:29 +00:00
if ( str [ 0 ] = = ' r ' ) { * val = hw_random8 ( minv , maxv ? maxv : 255 ) ; return ; } // maxv for random cannot be 0
2022-03-04 20:49:07 +00:00
bool wrap = false ;
if ( str [ 0 ] = = ' w ' & & strlen ( str ) > 1 ) { str + + ; wrap = true ; }
2021-11-23 19:20:19 +00:00
if ( str [ 0 ] = = ' ~ ' ) {
int out = atoi ( str + 1 ) ;
2022-06-03 16:38:46 +00:00
if ( out = = 0 ) {
2021-11-23 19:20:19 +00:00
if ( str [ 1 ] = = ' 0 ' ) return ;
2022-06-03 16:38:46 +00:00
if ( str [ 1 ] = = ' - ' ) {
2021-11-23 19:20:19 +00:00
* val = ( int ) ( * val - 1 ) < ( int ) minv ? maxv : min ( ( int ) maxv , ( * val - 1 ) ) ; //-1, wrap around
} else {
* val = ( int ) ( * val + 1 ) > ( int ) maxv ? minv : max ( ( int ) minv , ( * val + 1 ) ) ; //+1, wrap around
}
} else {
2022-03-04 20:49:07 +00:00
if ( wrap & & * val = = maxv & & out > 0 ) out = minv ;
else if ( wrap & & * val = = minv & & out < 0 ) out = maxv ;
2023-01-06 08:24:29 +00:00
else {
2022-03-04 20:49:07 +00:00
out + = * val ;
if ( out > maxv ) out = maxv ;
if ( out < minv ) out = minv ;
}
2021-11-23 19:20:19 +00:00
* val = out ;
}
2022-06-03 16:38:46 +00:00
return ;
} else if ( minv = = maxv & & minv = = 0 ) { // limits "unset" i.e. both 0
2021-11-23 19:20:19 +00:00
byte p1 = atoi ( str ) ;
2022-06-03 16:38:46 +00:00
const char * str2 = strchr ( str , ' ~ ' ) ; // min/max range (for preset cycle, e.g. "1~5~")
2021-11-23 19:20:19 +00:00
if ( str2 ) {
2022-06-03 16:38:46 +00:00
byte p2 = atoi ( + + str2 ) ; // skip ~
if ( p2 > 0 ) {
while ( isdigit ( * ( + + str2 ) ) ) ; // skip digits
parseNumber ( str2 , val , p1 , p2 ) ;
return ;
}
2021-11-23 19:20:19 +00:00
}
}
2022-06-03 16:38:46 +00:00
* val = atoi ( str ) ;
2021-11-23 19:20:19 +00:00
}
2024-12-05 18:30:14 +00:00
//getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
2021-11-23 19:20:19 +00:00
bool getVal ( JsonVariant elem , byte * val , byte vmin , byte vmax ) {
if ( elem . is < int > ( ) ) {
2021-12-03 20:09:53 +00:00
if ( elem < 0 ) return false ; //ignore e.g. {"ps":-1}
2021-11-23 19:20:19 +00:00
* val = elem ;
return true ;
} else if ( elem . is < const char * > ( ) ) {
const char * str = elem ;
2024-12-05 18:30:14 +00:00
size_t len = strnlen ( str , 14 ) ;
if ( len = = 0 | | len > 12 ) return false ;
// fix for #3605 & #4346
// ignore vmin and vmax and use as specified in API
if ( len > 3 & & ( strchr ( str , ' r ' ) | | strchr ( str , ' ~ ' ) ! = strrchr ( str , ' ~ ' ) ) ) vmax = vmin = 0 ; // we have "X~Y(r|~[w][-][Z])" form
// end fix
2021-11-23 19:20:19 +00:00
parseNumber ( str , val , vmin , vmax ) ;
return true ;
}
return false ; //key does not exist
}
2025-01-09 12:29:06 +00:00
bool getBoolVal ( const JsonVariant & elem , bool dflt ) {
2023-11-06 19:08:45 +00:00
if ( elem . is < const char * > ( ) & & elem . as < const char * > ( ) [ 0 ] = = ' t ' ) {
return ! dflt ;
} else {
return elem | dflt ;
}
}
2022-06-03 20:22:18 +00:00
bool updateVal ( const char * req , const char * key , byte * val , byte minv , byte maxv )
2021-11-23 19:20:19 +00:00
{
2022-06-03 20:22:18 +00:00
const char * v = strstr ( req , key ) ;
if ( v ) v + = strlen ( key ) ;
else return false ;
parseNumber ( v , val , minv , maxv ) ;
2021-11-23 19:20:19 +00:00
return true ;
}
2024-09-18 23:19:40 +00:00
static size_t printSetFormInput ( Print & settingsScript , const char * key , const char * selector , int value ) {
return settingsScript . printf_P ( PSTR ( " d.Sf.%s.%s=%d; " ) , key , selector , value ) ;
2021-10-11 12:13:34 +00:00
}
2024-09-18 23:19:40 +00:00
size_t printSetFormCheckbox ( Print & settingsScript , const char * key , int val ) {
return printSetFormInput ( settingsScript , key , PSTR ( " checked " ) , val ) ;
2021-10-11 12:13:34 +00:00
}
2024-09-18 23:19:40 +00:00
size_t printSetFormValue ( Print & settingsScript , const char * key , int val ) {
return printSetFormInput ( settingsScript , key , PSTR ( " value " ) , val ) ;
2024-09-10 00:00:23 +00:00
}
2024-09-18 23:19:40 +00:00
size_t printSetFormIndex ( Print & settingsScript , const char * key , int index ) {
return printSetFormInput ( settingsScript , key , PSTR ( " selectedIndex " ) , index ) ;
2021-10-11 12:13:34 +00:00
}
2024-09-18 23:19:40 +00:00
size_t printSetFormValue ( Print & settingsScript , const char * key , const char * val ) {
return settingsScript . printf_P ( PSTR ( " d.Sf.%s.value= \" %s \" ; " ) , key , val ) ;
2024-09-10 00:00:23 +00:00
}
2021-10-11 12:13:34 +00:00
2024-09-18 23:19:40 +00:00
size_t printSetClassElementHTML ( Print & settingsScript , const char * key , const int index , const char * val ) {
return settingsScript . printf_P ( PSTR ( " d.getElementsByClassName( \" %s \" )[%d].innerHTML= \" %s \" ; " ) , key , index , val ) ;
2021-10-11 12:13:34 +00:00
}
2024-09-10 00:00:23 +00:00
2021-10-11 12:13:34 +00:00
void prepareHostname ( char * hostname )
{
2024-02-09 21:15:29 +00:00
sprintf_P ( hostname , PSTR ( " wled-%*s " ) , 6 , escapedMac . c_str ( ) + 6 ) ;
2021-10-11 12:13:34 +00:00
const char * pC = serverDescription ;
2024-07-09 19:50:27 +00:00
unsigned pos = 5 ; // keep "wled-"
2021-10-11 12:13:34 +00:00
while ( * pC & & pos < 24 ) { // while !null and not over length
if ( isalnum ( * pC ) ) { // if the current char is alpha-numeric append it to the hostname
hostname [ pos ] = * pC ;
pos + + ;
} else if ( * pC = = ' ' | | * pC = = ' _ ' | | * pC = = ' - ' | | * pC = = ' + ' | | * pC = = ' ! ' | | * pC = = ' ? ' | | * pC = = ' * ' ) {
hostname [ pos ] = ' - ' ;
pos + + ;
}
2022-10-10 14:46:23 +00:00
// else do nothing - no leading hyphens and do not include hyphens for all other characters.
pC + + ;
}
//last character must not be hyphen
2022-10-13 12:25:01 +00:00
if ( pos > 5 ) {
while ( pos > 4 & & hostname [ pos - 1 ] = = ' - ' ) pos - - ;
hostname [ pos ] = ' \0 ' ; // terminate string (leave at least "wled")
2022-10-10 14:46:23 +00:00
}
2021-10-11 12:13:34 +00:00
}
bool isAsterisksOnly ( const char * str , byte maxLen )
{
2024-07-11 19:22:58 +00:00
for ( unsigned i = 0 ; i < maxLen ; i + + ) {
2021-10-11 12:13:34 +00:00
if ( str [ i ] = = 0 ) break ;
if ( str [ i ] ! = ' * ' ) return false ;
}
//at this point the password contains asterisks only
return ( str [ 0 ] ! = 0 ) ; //false on empty string
}
2021-11-12 22:33:10 +00:00
2025-02-15 19:07:41 +00:00
//threading/network callback details: https://github.com/wled-dev/WLED/pull/2336#discussion_r762276994
2025-01-07 19:22:52 +00:00
bool requestJSONBufferLock ( uint8_t moduleID )
2021-11-12 22:33:10 +00:00
{
2023-12-21 20:30:17 +00:00
if ( pDoc = = nullptr ) {
DEBUG_PRINTLN ( F ( " ERROR: JSON buffer not allocated! " ) ) ;
return false ;
}
2021-11-12 22:33:10 +00:00
2024-08-04 18:02:05 +00:00
# if defined(ARDUINO_ARCH_ESP32)
// Use a recursive mutex type in case our task is the one holding the JSON buffer.
// This can happen during large JSON web transactions. In this case, we continue immediately
// and then will return out below if the lock is still held.
if ( xSemaphoreTakeRecursive ( jsonBufferLockMutex , 250 ) = = pdFALSE ) return false ; // timed out waiting
# elif defined(ARDUINO_ARCH_ESP8266)
// If we're in system context, delay() won't return control to the user context, so there's
// no point in waiting.
if ( can_yield ( ) ) {
unsigned long now = millis ( ) ;
while ( jsonBufferLock & & ( millis ( ) - now < 250 ) ) delay ( 1 ) ; // wait for fraction for buffer lock
}
# else
# error Unsupported task framework - fix requestJSONBufferLock
# endif
// If the lock is still held - by us, or by another task
2024-02-17 10:30:29 +00:00
if ( jsonBufferLock ) {
2025-01-07 19:22:52 +00:00
DEBUG_PRINTF_P ( PSTR ( " ERROR: Locking JSON buffer (%d) failed! (still locked by %d) \n " ) , moduleID , jsonBufferLock ) ;
2024-08-04 18:02:05 +00:00
# ifdef ARDUINO_ARCH_ESP32
xSemaphoreGiveRecursive ( jsonBufferLockMutex ) ;
# endif
return false ;
2021-11-14 15:56:34 +00:00
}
2021-11-12 22:33:10 +00:00
2025-01-07 19:22:52 +00:00
jsonBufferLock = moduleID ? moduleID : 255 ;
2024-08-04 19:08:46 +00:00
DEBUG_PRINTF_P ( PSTR ( " JSON buffer locked. (%d) \n " ) , jsonBufferLock ) ;
2023-12-21 20:30:17 +00:00
pDoc - > clear ( ) ;
2021-11-12 22:33:10 +00:00
return true ;
}
void releaseJSONBufferLock ( )
{
2024-08-04 19:08:46 +00:00
DEBUG_PRINTF_P ( PSTR ( " JSON buffer released. (%d) \n " ) , jsonBufferLock ) ;
2021-11-14 15:56:34 +00:00
jsonBufferLock = 0 ;
2024-08-04 18:02:05 +00:00
# ifdef ARDUINO_ARCH_ESP32
xSemaphoreGiveRecursive ( jsonBufferLockMutex ) ;
# endif
2021-11-12 22:33:10 +00:00
}
2022-01-31 19:35:11 +00:00
// extracts effect mode (or palette) name from names serialized string
2024-02-06 13:47:20 +00:00
// caller must provide large enough buffer for name (including SR extensions)!
2022-01-31 19:35:11 +00:00
uint8_t extractModeName ( uint8_t mode , const char * src , char * dest , uint8_t maxLen )
{
2022-05-24 11:45:35 +00:00
if ( src = = JSON_mode_names | | src = = nullptr ) {
2022-06-26 21:01:22 +00:00
if ( mode < strip . getModeCount ( ) ) {
2022-05-21 16:37:09 +00:00
char lineBuffer [ 256 ] ;
2022-05-21 22:41:45 +00:00
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
2023-08-17 15:10:29 +00:00
strncpy_P ( lineBuffer , strip . getModeData ( mode ) , sizeof ( lineBuffer ) / sizeof ( char ) - 1 ) ;
lineBuffer [ sizeof ( lineBuffer ) / sizeof ( char ) - 1 ] = ' \0 ' ; // terminate string
2022-07-10 20:23:25 +00:00
size_t len = strlen ( lineBuffer ) ;
size_t j = 0 ;
for ( ; j < maxLen & & j < len ; j + + ) {
if ( lineBuffer [ j ] = = ' \0 ' | | lineBuffer [ j ] = = ' @ ' ) break ;
dest [ j ] = lineBuffer [ j ] ;
2022-05-21 16:37:09 +00:00
}
2022-07-10 20:23:25 +00:00
dest [ j ] = 0 ; // terminate string
2022-05-21 16:37:09 +00:00
return strlen ( dest ) ;
} else return 0 ;
}
2024-03-19 21:04:24 +00:00
if ( src = = JSON_palette_names & & mode > ( GRADIENT_PALETTE_COUNT + 13 ) ) {
snprintf_P ( dest , maxLen , PSTR ( " ~ Custom %d ~ " ) , 255 - mode ) ;
2023-08-28 15:58:30 +00:00
dest [ maxLen - 1 ] = ' \0 ' ;
return strlen ( dest ) ;
}
2024-07-09 19:50:27 +00:00
unsigned qComma = 0 ;
2022-01-31 19:35:11 +00:00
bool insideQuotes = false ;
2024-07-09 19:50:27 +00:00
unsigned printedChars = 0 ;
2022-01-31 19:35:11 +00:00
char singleJsonSymbol ;
size_t len = strlen_P ( src ) ;
// Find the mode name in JSON
for ( size_t i = 0 ; i < len ; i + + ) {
singleJsonSymbol = pgm_read_byte_near ( src + i ) ;
if ( singleJsonSymbol = = ' \0 ' ) break ;
if ( singleJsonSymbol = = ' @ ' & & insideQuotes & & qComma = = mode ) break ; //stop when SR extension encountered
switch ( singleJsonSymbol ) {
case ' " ' :
insideQuotes = ! insideQuotes ;
break ;
case ' [ ' :
case ' ] ' :
break ;
case ' , ' :
if ( ! insideQuotes ) qComma + + ;
default :
if ( ! insideQuotes | | ( qComma ! = mode ) ) break ;
dest [ printedChars + + ] = singleJsonSymbol ;
}
if ( ( qComma > mode ) | | ( printedChars > = maxLen ) ) break ;
}
dest [ printedChars ] = ' \0 ' ;
return strlen ( dest ) ;
}
2022-05-20 12:48:40 +00:00
2022-05-24 11:45:35 +00:00
// extracts effect slider data (1st group after @)
2022-07-23 20:00:19 +00:00
uint8_t extractModeSlider ( uint8_t mode , uint8_t slider , char * dest , uint8_t maxLen , uint8_t * var )
2022-05-24 11:45:35 +00:00
{
dest [ 0 ] = ' \0 ' ; // start by clearing buffer
2022-06-26 21:01:22 +00:00
if ( mode < strip . getModeCount ( ) ) {
2022-08-03 19:36:47 +00:00
String lineBuffer = FPSTR ( strip . getModeData ( mode ) ) ;
2022-05-24 11:45:35 +00:00
if ( lineBuffer . length ( ) > 0 ) {
2025-01-07 19:18:42 +00:00
int start = lineBuffer . indexOf ( ' @ ' ) ; // String::indexOf() returns an int, not an unsigned; -1 means "not found"
int stop = lineBuffer . indexOf ( ' ; ' , start ) ;
2022-05-24 11:45:35 +00:00
if ( start > 0 & & stop > 0 ) {
2022-07-23 20:00:19 +00:00
String names = lineBuffer . substring ( start , stop ) ; // include @
2025-01-07 19:18:42 +00:00
int nameBegin = 1 , nameEnd , nameDefault ;
2022-07-23 20:00:19 +00:00
if ( slider < 10 ) {
for ( size_t i = 0 ; i < = slider ; i + + ) {
const char * tmpstr ;
dest [ 0 ] = ' \0 ' ; //clear dest buffer
2025-01-07 19:18:42 +00:00
if ( nameBegin < = 0 ) break ; // there are no more names
2022-07-23 20:00:19 +00:00
nameEnd = names . indexOf ( ' , ' , nameBegin ) ;
if ( i = = slider ) {
nameDefault = names . indexOf ( ' = ' , nameBegin ) ; // find default value
if ( nameDefault > 0 & & var & & ( ( nameEnd > 0 & & nameDefault < nameEnd ) | | nameEnd < 0 ) ) {
* var = ( uint8_t ) atoi ( names . substring ( nameDefault + 1 ) . c_str ( ) ) ;
}
if ( names . charAt ( nameBegin ) = = ' ! ' ) {
switch ( slider ) {
case 0 : tmpstr = PSTR ( " FX Speed " ) ; break ;
case 1 : tmpstr = PSTR ( " FX Intensity " ) ; break ;
case 2 : tmpstr = PSTR ( " FX Custom 1 " ) ; break ;
case 3 : tmpstr = PSTR ( " FX Custom 2 " ) ; break ;
case 4 : tmpstr = PSTR ( " FX Custom 3 " ) ; break ;
default : tmpstr = PSTR ( " FX Custom " ) ; break ;
}
2022-08-03 20:27:45 +00:00
strncpy_P ( dest , tmpstr , maxLen ) ; // copy the name into buffer (replacing previous)
dest [ maxLen - 1 ] = ' \0 ' ;
2022-07-23 20:00:19 +00:00
} else {
if ( nameEnd < 0 ) tmpstr = names . substring ( nameBegin ) . c_str ( ) ; // did not find ",", last name?
else tmpstr = names . substring ( nameBegin , nameEnd ) . c_str ( ) ;
2022-08-03 20:27:45 +00:00
strlcpy ( dest , tmpstr , maxLen ) ; // copy the name into buffer (replacing previous)
2022-07-23 20:00:19 +00:00
}
}
nameBegin = nameEnd + 1 ; // next name (if "," is not found it will be 0)
} // next slider
} else if ( slider = = 255 ) {
// palette
strlcpy ( dest , " pal " , maxLen ) ;
names = lineBuffer . substring ( stop + 1 ) ; // stop has index of color slot names
nameBegin = names . indexOf ( ' ; ' ) ; // look for palette
if ( nameBegin > = 0 ) {
2022-07-23 20:38:35 +00:00
nameEnd = names . indexOf ( ' ; ' , nameBegin + 1 ) ;
2022-07-23 20:00:19 +00:00
if ( ! isdigit ( names [ nameBegin + 1 ] ) ) nameBegin = names . indexOf ( ' = ' , nameBegin + 1 ) ; // look for default value
2022-07-23 20:38:35 +00:00
if ( nameEnd > = 0 & & nameBegin > nameEnd ) nameBegin = - 1 ;
2022-07-23 20:00:19 +00:00
if ( nameBegin > = 0 & & var ) {
* var = ( uint8_t ) atoi ( names . substring ( nameBegin + 1 ) . c_str ( ) ) ;
2022-05-24 11:45:35 +00:00
}
}
2022-07-23 20:00:19 +00:00
}
2022-05-24 11:45:35 +00:00
// we have slider name (including default value) in the dest buffer
for ( size_t i = 0 ; i < strlen ( dest ) ; i + + ) if ( dest [ i ] = = ' = ' ) { dest [ i ] = ' \0 ' ; break ; } // truncate default value
2023-01-06 08:24:29 +00:00
2022-05-24 11:45:35 +00:00
} else {
// defaults to just speed and intensity since there is no slider data
switch ( slider ) {
case 0 : strncpy_P ( dest , PSTR ( " FX Speed " ) , maxLen ) ; break ;
case 1 : strncpy_P ( dest , PSTR ( " FX Intensity " ) , maxLen ) ; break ;
}
2022-07-23 20:00:19 +00:00
dest [ maxLen ] = ' \0 ' ; // strncpy does not necessarily null terminate string
2022-05-24 11:45:35 +00:00
}
}
return strlen ( dest ) ;
}
return 0 ;
}
2023-12-01 23:48:49 +00:00
// extracts mode parameter defaults from last section of mode data (e.g. "Juggle@!,Trail;!,!,;!;012;sx=16,ix=240")
2022-07-23 20:00:19 +00:00
int16_t extractModeDefaults ( uint8_t mode , const char * segVar )
{
if ( mode < strip . getModeCount ( ) ) {
2023-08-17 14:24:16 +00:00
char lineBuffer [ 256 ] ;
2023-08-17 15:10:29 +00:00
strncpy_P ( lineBuffer , strip . getModeData ( mode ) , sizeof ( lineBuffer ) / sizeof ( char ) - 1 ) ;
lineBuffer [ sizeof ( lineBuffer ) / sizeof ( char ) - 1 ] = ' \0 ' ; // terminate string
2022-08-31 02:31:56 +00:00
if ( lineBuffer [ 0 ] ! = 0 ) {
2022-08-31 00:26:26 +00:00
char * startPtr = strrchr ( lineBuffer , ' ; ' ) ; // last ";" in FX data
if ( ! startPtr ) return - 1 ;
char * stopPtr = strstr ( startPtr , segVar ) ;
if ( ! stopPtr ) return - 1 ;
2022-07-23 20:00:19 +00:00
2022-08-31 02:31:56 +00:00
stopPtr + = strlen ( segVar ) + 1 ; // skip "="
2022-08-31 00:26:26 +00:00
return atoi ( stopPtr ) ;
2022-07-23 20:00:19 +00:00
}
}
return - 1 ;
}
2023-06-14 09:53:39 +00:00
void checkSettingsPIN ( const char * pin ) {
if ( ! pin ) return ;
if ( ! correctPIN & & millis ( ) - lastEditTime < PIN_RETRY_COOLDOWN ) return ; // guard against PIN brute force
bool correctBefore = correctPIN ;
correctPIN = ( strlen ( settingsPIN ) = = 0 | | strncmp ( settingsPIN , pin , 4 ) = = 0 ) ;
if ( correctBefore ! = correctPIN ) createEditHandler ( correctPIN ) ;
lastEditTime = millis ( ) ;
}
2022-05-20 12:48:40 +00:00
uint16_t crc16 ( const unsigned char * data_p , size_t length ) {
uint8_t x ;
uint16_t crc = 0xFFFF ;
if ( ! length ) return 0x1D0F ;
while ( length - - ) {
x = crc > > 8 ^ * data_p + + ;
x ^ = x > > 4 ;
crc = ( crc < < 8 ) ^ ( ( uint16_t ) ( x < < 12 ) ) ^ ( ( uint16_t ) ( x < < 5 ) ) ^ ( ( uint16_t ) x ) ;
}
return crc ;
2022-07-25 19:31:50 +00:00
}
Added integer based `sin()/cos()` functions, changed all trig functions to wled_math
- `sin16_t() / cos16_t()` are faster and more accurate than fastled versions
- `sin_approx() / cos_approx()` are float wrappers for `sin16_t() / cos16_t()` and are accurate enough to replace `sinf()/cosf()`
- `atan2()` is used only in octopus to calculate center offset, new approximated version saves flash
- `tan(), atan(), asin(), acos(), floor(), fmod()` are used only for sunrise/sunset calculation, using wled_math version saves flash
- `beatsinx()` replacements are to make use of new `sin16_t()/sin8_t()` functions to reduce flash size
- Extensively tested surnise/sunset calculation: deviation is 1min. max
- Tested some of the relevant FX and found no visual difference: Julia, 2D Drift, Drift Rose, Ghost rider, Rotozoomer, Palette, Arc 1D expansion
- total flash savings: 7.4k
2024-10-05 10:29:31 +00:00
// fastled beatsin: 1:1 replacements to remove the use of fastled sin16()
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin88_t ( accum88 beats_per_minute_88 , uint16_t lowest , uint16_t highest , uint32_t timebase , uint16_t phase_offset )
{
uint16_t beat = beat88 ( beats_per_minute_88 , timebase ) ;
uint16_t beatsin ( sin16_t ( beat + phase_offset ) + 32768 ) ;
uint16_t rangewidth = highest - lowest ;
uint16_t scaledbeat = scale16 ( beatsin , rangewidth ) ;
uint16_t result = lowest + scaledbeat ;
return result ;
}
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin16_t ( accum88 beats_per_minute , uint16_t lowest , uint16_t highest , uint32_t timebase , uint16_t phase_offset )
{
uint16_t beat = beat16 ( beats_per_minute , timebase ) ;
uint16_t beatsin = ( sin16_t ( beat + phase_offset ) + 32768 ) ;
uint16_t rangewidth = highest - lowest ;
uint16_t scaledbeat = scale16 ( beatsin , rangewidth ) ;
uint16_t result = lowest + scaledbeat ;
return result ;
}
// Generates an 8-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint8_t beatsin8_t ( accum88 beats_per_minute , uint8_t lowest , uint8_t highest , uint32_t timebase , uint8_t phase_offset )
{
uint8_t beat = beat8 ( beats_per_minute , timebase ) ;
uint8_t beatsin = sin8_t ( beat + phase_offset ) ;
uint8_t rangewidth = highest - lowest ;
uint8_t scaledbeat = scale8 ( beatsin , rangewidth ) ;
uint8_t result = lowest + scaledbeat ;
return result ;
}
2022-07-25 19:31:50 +00:00
///////////////////////////////////////////////////////////////////////////////
// Begin simulateSound (to enable audio enhanced effects to display something)
///////////////////////////////////////////////////////////////////////////////
// Currently 4 types defined, to be fine tuned and new types added
2023-05-29 19:06:10 +00:00
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
2022-07-25 19:31:50 +00:00
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0 ,
2023-09-10 16:52:14 +00:00
UMS_WeWillRockYou ,
UMS_10_13 ,
UMS_14_3
2022-07-25 19:31:50 +00:00
} um_soundSimulations_t ;
2023-01-06 08:24:29 +00:00
um_data_t * simulateSound ( uint8_t simulationId )
2022-07-25 19:31:50 +00:00
{
static uint8_t samplePeak ;
static float FFT_MajorPeak ;
static uint8_t maxVol ;
static uint8_t binNum ;
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
static float volumeSmth ;
static uint16_t volumeRaw ;
static float my_magnitude ;
2022-07-25 19:31:50 +00:00
//arrays
uint8_t * fftResult ;
static um_data_t * um_data = nullptr ;
if ( ! um_data ) {
//claim storage for arrays
fftResult = ( uint8_t * ) malloc ( sizeof ( uint8_t ) * 16 ) ;
// initialize um_data pointer structure
// NOTE!!!
// This may change as AudioReactive usermod may change
um_data = new um_data_t ;
2022-07-29 13:50:09 +00:00
um_data - > u_size = 8 ;
2022-07-25 19:31:50 +00:00
um_data - > u_type = new um_types_t [ um_data - > u_size ] ;
um_data - > u_data = new void * [ um_data - > u_size ] ;
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
um_data - > u_data [ 0 ] = & volumeSmth ;
um_data - > u_data [ 1 ] = & volumeRaw ;
2023-01-06 08:24:29 +00:00
um_data - > u_data [ 2 ] = fftResult ;
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
um_data - > u_data [ 3 ] = & samplePeak ;
um_data - > u_data [ 4 ] = & FFT_MajorPeak ;
um_data - > u_data [ 5 ] = & my_magnitude ;
um_data - > u_data [ 6 ] = & maxVol ;
um_data - > u_data [ 7 ] = & binNum ;
2022-07-25 19:31:50 +00:00
} else {
// get arrays from um_data
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
fftResult = ( uint8_t * ) um_data - > u_data [ 2 ] ;
2022-07-25 19:31:50 +00:00
}
uint32_t ms = millis ( ) ;
switch ( simulationId ) {
default :
case UMS_BeatSin :
for ( int i = 0 ; i < 16 ; i + + )
Added integer based `sin()/cos()` functions, changed all trig functions to wled_math
- `sin16_t() / cos16_t()` are faster and more accurate than fastled versions
- `sin_approx() / cos_approx()` are float wrappers for `sin16_t() / cos16_t()` and are accurate enough to replace `sinf()/cosf()`
- `atan2()` is used only in octopus to calculate center offset, new approximated version saves flash
- `tan(), atan(), asin(), acos(), floor(), fmod()` are used only for sunrise/sunset calculation, using wled_math version saves flash
- `beatsinx()` replacements are to make use of new `sin16_t()/sin8_t()` functions to reduce flash size
- Extensively tested surnise/sunset calculation: deviation is 1min. max
- Tested some of the relevant FX and found no visual difference: Julia, 2D Drift, Drift Rose, Ghost rider, Rotozoomer, Palette, Arc 1D expansion
- total flash savings: 7.4k
2024-10-05 10:29:31 +00:00
fftResult [ i ] = beatsin8_t ( 120 / ( i + 1 ) , 0 , 255 ) ;
// fftResult[i] = (beatsin8_t(120, 0, 255) + (256/16 * i)) % 256;
2025-01-07 19:22:52 +00:00
volumeSmth = fftResult [ 8 ] ;
2022-07-25 19:31:50 +00:00
break ;
case UMS_WeWillRockYou :
if ( ms % 2000 < 200 ) {
2024-12-20 18:12:29 +00:00
volumeSmth = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
for ( int i = 0 ; i < 5 ; i + + )
2024-12-20 18:12:29 +00:00
fftResult [ i ] = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
}
else if ( ms % 2000 < 400 ) {
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
volumeSmth = 0 ;
2022-07-25 19:31:50 +00:00
for ( int i = 0 ; i < 16 ; i + + )
fftResult [ i ] = 0 ;
}
else if ( ms % 2000 < 600 ) {
2024-12-20 18:12:29 +00:00
volumeSmth = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
for ( int i = 5 ; i < 11 ; i + + )
2024-12-20 18:12:29 +00:00
fftResult [ i ] = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
}
else if ( ms % 2000 < 800 ) {
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
volumeSmth = 0 ;
2022-07-25 19:31:50 +00:00
for ( int i = 0 ; i < 16 ; i + + )
fftResult [ i ] = 0 ;
}
else if ( ms % 2000 < 1000 ) {
2024-12-20 18:12:29 +00:00
volumeSmth = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
for ( int i = 11 ; i < 16 ; i + + )
2024-12-20 18:12:29 +00:00
fftResult [ i ] = hw_random8 ( ) ;
2022-07-25 19:31:50 +00:00
}
else {
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
volumeSmth = 0 ;
2022-07-25 19:31:50 +00:00
for ( int i = 0 ; i < 16 ; i + + )
fftResult [ i ] = 0 ;
}
break ;
2023-09-10 16:52:14 +00:00
case UMS_10_13 :
2023-05-29 19:06:10 +00:00
for ( int i = 0 ; i < 16 ; i + + )
2025-03-03 05:57:16 +00:00
fftResult [ i ] = perlin8 ( beatsin8_t ( 90 / ( i + 1 ) , 0 , 200 ) * 15 + ( ms > > 10 ) , ms > > 3 ) ;
2025-01-07 19:22:52 +00:00
volumeSmth = fftResult [ 8 ] ;
2023-05-29 19:06:10 +00:00
break ;
case UMS_14_3 :
for ( int i = 0 ; i < 16 ; i + + )
2025-03-03 05:57:16 +00:00
fftResult [ i ] = perlin8 ( beatsin8_t ( 120 / ( i + 1 ) , 10 , 30 ) * 10 + ( ms > > 14 ) , ms > > 3 ) ;
2023-05-29 19:06:10 +00:00
volumeSmth = fftResult [ 8 ] ;
2023-09-10 16:52:14 +00:00
break ;
2022-07-25 19:31:50 +00:00
}
2024-12-20 18:12:29 +00:00
samplePeak = hw_random8 ( ) > 250 ;
2023-07-03 13:07:14 +00:00
FFT_MajorPeak = 21 + ( volumeSmth * volumeSmth ) / 8.0f ; // walk thru full range of 21hz...8200hz
maxVol = 31 ; // this gets feedback fro UI
2022-07-25 19:31:50 +00:00
binNum = 8 ; // this gets feedback fro UI
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
volumeRaw = volumeSmth ;
2023-09-09 18:48:17 +00:00
my_magnitude = 10000.0f / 8.0f ; //no idea if 10000 is a good value for FFT_Magnitude ???
Refactor um_data variables for audio reactive
- change sample to sampleRaw
- add volumeSmth, volumeRaw, my_magnitude and calculate in agcAvg
- remove sampleAvg, soundAgc, sampleAgc, sampleRaw, rawSampleAgc, FFT_Magnitude, multAgc, sampleReal, sampleGain, (myVals), soundSquelch from um_data interface
- refactor all effects using above variables
2022-07-29 13:24:04 +00:00
if ( volumeSmth < 1 ) my_magnitude = 0.001f ; // noise gate closed - mute
2022-07-25 19:31:50 +00:00
return um_data ;
}
2022-08-10 18:53:11 +00:00
2024-03-05 15:27:28 +00:00
static const char s_ledmap_tmpl [ ] PROGMEM = " ledmap%d.json " ;
2023-02-14 16:11:58 +00:00
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
2022-08-10 18:53:11 +00:00
void enumerateLedmaps ( ) {
2024-11-09 10:33:10 +00:00
StaticJsonDocument < 64 > filter ;
filter [ " n " ] = true ;
2022-08-10 18:53:11 +00:00
ledMaps = 1 ;
2023-02-14 16:11:58 +00:00
for ( size_t i = 1 ; i < WLED_MAX_LEDMAPS ; i + + ) {
2024-03-05 15:27:28 +00:00
char fileName [ 33 ] = " / " ;
sprintf_P ( fileName + 1 , s_ledmap_tmpl , i ) ;
2022-08-10 18:53:11 +00:00
bool isFile = WLED_FS . exists ( fileName ) ;
2023-02-14 16:11:58 +00:00
# ifndef ESP8266
if ( ledmapNames [ i - 1 ] ) { //clear old name
2025-01-09 12:29:06 +00:00
free ( ledmapNames [ i - 1 ] ) ;
2023-02-14 16:11:58 +00:00
ledmapNames [ i - 1 ] = nullptr ;
}
# endif
if ( isFile ) {
ledMaps | = 1 < < i ;
# ifndef ESP8266
if ( requestJSONBufferLock ( 21 ) ) {
2024-11-09 10:33:10 +00:00
if ( readObjectFromFile ( fileName , nullptr , pDoc , & filter ) ) {
2023-02-14 16:11:58 +00:00
size_t len = 0 ;
2023-12-21 20:30:17 +00:00
JsonObject root = pDoc - > as < JsonObject > ( ) ;
if ( ! root [ " n " ] . isNull ( ) ) {
2023-02-14 16:11:58 +00:00
// name field exists
2023-12-21 20:30:17 +00:00
const char * name = root [ " n " ] . as < const char * > ( ) ;
2023-02-14 16:11:58 +00:00
if ( name ! = nullptr ) len = strlen ( name ) ;
if ( len > 0 & & len < 33 ) {
2025-01-09 12:29:06 +00:00
ledmapNames [ i - 1 ] = static_cast < char * > ( malloc ( len + 1 ) ) ;
2023-02-14 16:11:58 +00:00
if ( ledmapNames [ i - 1 ] ) strlcpy ( ledmapNames [ i - 1 ] , name , 33 ) ;
}
}
if ( ! ledmapNames [ i - 1 ] ) {
char tmp [ 33 ] ;
2024-03-05 15:27:28 +00:00
snprintf_P ( tmp , 32 , s_ledmap_tmpl , i ) ;
2023-02-14 16:11:58 +00:00
len = strlen ( tmp ) ;
2025-01-09 12:29:06 +00:00
ledmapNames [ i - 1 ] = static_cast < char * > ( malloc ( len + 1 ) ) ;
2023-02-14 16:11:58 +00:00
if ( ledmapNames [ i - 1 ] ) strlcpy ( ledmapNames [ i - 1 ] , tmp , 33 ) ;
}
}
releaseJSONBufferLock ( ) ;
}
# endif
}
2022-08-10 18:53:11 +00:00
}
2023-08-28 15:58:30 +00:00
}
2023-09-10 16:52:14 +00:00
/*
* Returns a new , random color wheel index with a minimum distance of 42 from pos .
*/
uint8_t get_random_wheel_index ( uint8_t pos ) {
uint8_t r = 0 , x = 0 , y = 0 , d = 0 ;
while ( d < 42 ) {
2024-12-20 18:12:29 +00:00
r = hw_random8 ( ) ;
2023-09-10 16:52:14 +00:00
x = abs ( pos - r ) ;
y = 255 - x ;
d = MIN ( x , y ) ;
}
return r ;
}
2024-04-03 16:38:06 +00:00
2024-09-15 09:04:02 +00:00
// float version of map()
float mapf ( float x , float in_min , float in_max , float out_min , float out_max ) {
return ( x - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min ;
}
2024-09-15 09:05:16 +00:00
2024-04-03 16:38:06 +00:00
uint32_t hashInt ( uint32_t s ) {
// borrowed from https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
s = ( ( s > > 16 ) ^ s ) * 0x45d9f3b ;
s = ( ( s > > 16 ) ^ s ) * 0x45d9f3b ;
return ( s > > 16 ) ^ s ;
}
2025-01-14 21:39:20 +00:00
2024-12-20 18:12:29 +00:00
// 32 bit random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h)
uint32_t hw_random ( uint32_t upperlimit ) {
uint32_t rnd = hw_random ( ) ;
uint64_t scaled = uint64_t ( rnd ) * uint64_t ( upperlimit ) ;
return scaled > > 32 ;
}
int32_t hw_random ( int32_t lowerlimit , int32_t upperlimit ) {
if ( lowerlimit > = upperlimit ) {
return lowerlimit ;
}
uint32_t diff = upperlimit - lowerlimit ;
return hw_random ( diff ) + lowerlimit ;
2025-01-14 21:39:20 +00:00
}
2025-03-03 05:57:16 +00:00
/*
* Fixed point integer based Perlin noise functions by @ dedehai
* Note : optimized for speed and to mimic fastled inoise functions , not for accuracy or best randomness
*/
// hash based gradient (speed is key)
static inline __attribute__ ( ( always_inline ) ) uint32_t perlinHash ( uint32_t x ) {
//x ^= x >> 15; //11?
//x *= 0x85ebca6b;
//x ^= x >> 7;
//x *= 0xc2b2ae35;
//x ^= x >> 17;
//return x;
//version from above, does not look too good
//x = ((x >> 16) ^ x) * 0x45d9f3b;
//x = ((x >> 16) ^ x) * 0x45d9f3b;
//return (x >> 16) ^ x;
//found this online at https://github.com/skeeto/hash-prospector
// allegedly even better than the above murmur hash
x ^ = x > > 16 ;
x * = 0x7feb352d ;
x ^ = x > > 15 ;
x * = 0x846ca68b ;
x ^ = x > > 16 ;
return x ;
}
//int8_t slopes[8] = {-4,-3,-2,-1,1,2,3,4};
static inline __attribute__ ( ( always_inline ) ) int32_t cornergradient ( uint32_t h ) {
int grad = ( h & 0x0F ) - 8 ; // +7 to -8
// int grad = slopes[h & 0x7]; // +1 or -1 (better mimics fastled but much slower, can be optimized by passing an array pointer or making the array global
//int grad = (h & 0x07) - 4; // +3 to -4
//int grad = (h & 0x03) - 2; // +1 to -2
//int grad = (h & 0x07) * (h & 0x10 ? -1 : 1); // symmetrical, much (!) slower
//return slopes[h & 0x7]; // lookup table is also very slow...
return grad ;
}
// Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster!
static inline __attribute__ ( ( always_inline ) ) int32_t gradient1D ( uint32_t x0 , int32_t dx ) {
int32_t grad = cornergradient ( perlinHash ( x0 ) ) ;
return ( grad * dx ) > > 1 ;
}
static inline __attribute__ ( ( always_inline ) ) int32_t gradient2D ( uint32_t x0 , int32_t dx , uint32_t y0 , int32_t dy ) {
uint32_t hashx = perlinHash ( x0 ) ;
//uint32_t hashy = perlinHash(hashx ^ y0);
uint32_t hashy = perlinHash ( y0 + 1013904223UL ) ;
int32_t gradx = cornergradient ( hashx ) ;
int32_t grady = cornergradient ( hashy ) ;
return ( gradx * dx + grady * dy ) > > 2 ;
}
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 ) {
//uint32_t hashx = perlinHash(x0);
//uint32_t hashy = perlinHash(hashx ^ y0);
//uint32_t hashz = perlinHash(hashy ^ z0);
uint32_t hashx = perlinHash ( x0 ) ;
uint32_t hashy = perlinHash ( y0 + 1013904223UL ) ;
uint32_t hashz = perlinHash ( z0 + 1664525UL ) ;
int32_t gradx = cornergradient ( hashx ) ;
int32_t grady = cornergradient ( hashy ) ;
int32_t gradz = cornergradient ( hashz ) ;
return ( gradx * dx + grady * dy + gradz * dz ) > > 4 ;
}
// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point
static uint32_t smoothstep ( const uint32_t t ) {
uint32_t t_squared = ( t * t ) > > 16 ;
uint32_t factor = ( 3 < < 16 ) - ( ( t < < 1 ) ) ;
return ( t_squared * factor ) > > 17 ; // scale for best resolution without overflow
}
// simple linear interpolation for fixed-point values, scaled for perlin noise use
static inline int32_t lerpPerlin ( int32_t a , int32_t b , int32_t t ) {
return a + ( ( ( b - a ) * t ) > > 15 ) ;
}
// 1D Perlin noise function that returns a value in range of approximately -32768 to +32768
int32_t perlin1D_raw ( uint32_t x ) {
// integer and fractional part coordinates
int32_t x0 = x > > 16 ;
int32_t x1 = x0 + 1 ;
int32_t dx0 = x & 0xFFFF ;
int32_t dx1 = dx0 - 0xFFFF ;
// gradient values for the two corners
int32_t g0 = gradient1D ( x0 , dx0 ) ;
int32_t g1 = gradient1D ( x1 , dx1 ) ;
// interpolate and smooth function
int32_t t = smoothstep ( dx0 ) ;
int32_t noise = lerpPerlin ( g0 , g1 , t ) ;
return noise ;
}
// 2D Perlin noise function that returns a value in range of approximately -32768 to +32768
int32_t perlin2D_raw ( uint32_t x , uint32_t y ) {
int32_t x0 = x > > 16 ;
int32_t y0 = y > > 16 ;
int32_t x1 = x0 + 1 ;
int32_t y1 = y0 + 1 ;
int32_t dx0 = x & 0xFFFF ;
int32_t dy0 = y & 0xFFFF ;
int32_t dx1 = dx0 - 0xFFFF ;
int32_t dy1 = dy0 - 0xFFFF ;
int32_t g00 = gradient2D ( x0 , dx0 , y0 , dy0 ) ;
int32_t g10 = gradient2D ( x1 , dx1 , y0 , dy0 ) ;
int32_t g01 = gradient2D ( x0 , dx0 , y1 , dy1 ) ;
int32_t g11 = gradient2D ( x1 , dx1 , y1 , dy1 ) ;
uint32_t tx = smoothstep ( dx0 ) ;
uint32_t ty = smoothstep ( dy0 ) ;
int32_t nx0 = lerpPerlin ( g00 , g10 , tx ) ;
int32_t nx1 = lerpPerlin ( g01 , g11 , tx ) ;
int32_t noise = lerpPerlin ( nx0 , nx1 , ty ) ;
return noise ;
}
// 2D Perlin noise function that returns a value in range of approximately -40000 to +40000
int32_t perlin3D_raw ( uint32_t x , uint32_t y , uint32_t z ) {
int32_t x0 = x > > 16 ;
int32_t y0 = y > > 16 ;
int32_t z0 = z > > 16 ;
int32_t x1 = x0 + 1 ;
int32_t y1 = y0 + 1 ;
int32_t z1 = z0 + 1 ;
int32_t dx0 = x & 0xFFFF ;
int32_t dy0 = y & 0xFFFF ;
int32_t dz0 = z & 0xFFFF ;
int32_t dx1 = dx0 - 0xFFFF ;
int32_t dy1 = dy0 - 0xFFFF ;
int32_t dz1 = dz0 - 0xFFFF ;
int32_t g000 = gradient3D ( x0 , dx0 , y0 , dy0 , z0 , dz0 ) ;
int32_t g001 = gradient3D ( x0 , dx0 , y0 , dy0 , z1 , dz1 ) ;
int32_t g010 = gradient3D ( x0 , dx0 , y1 , dy1 , z0 , dz0 ) ;
int32_t g011 = gradient3D ( x0 , dx0 , y1 , dy1 , z1 , dz1 ) ;
int32_t g100 = gradient3D ( x1 , dx1 , y0 , dy0 , z0 , dz0 ) ;
int32_t g101 = gradient3D ( x1 , dx1 , y0 , dy0 , z1 , dz1 ) ;
int32_t g110 = gradient3D ( x1 , dx1 , y1 , dy1 , z0 , dz0 ) ;
int32_t g111 = gradient3D ( x1 , dx1 , y1 , dy1 , z1 , dz1 ) ;
uint32_t tx = smoothstep ( dx0 ) ;
uint32_t ty = smoothstep ( dy0 ) ;
uint32_t tz = smoothstep ( dz0 ) ;
int32_t nx0 = lerpPerlin ( g000 , g100 , tx ) ;
int32_t nx1 = lerpPerlin ( g010 , g110 , tx ) ;
int32_t nx2 = lerpPerlin ( g001 , g101 , tx ) ;
int32_t nx3 = lerpPerlin ( g011 , g111 , tx ) ;
int32_t ny0 = lerpPerlin ( nx0 , nx1 , ty ) ;
int32_t ny1 = lerpPerlin ( nx2 , nx3 , ty ) ;
int32_t noise = lerpPerlin ( ny0 , ny1 , tz ) ;
return noise ;
}
// scaling functions for fastled replacement
uint8_t perlin8 ( uint16_t x ) {
return ( perlin1D_raw ( uint32_t ( x ) < < 8 ) > > 8 ) + 0x7F ;
}
uint8_t perlin8 ( uint16_t x , uint16_t y ) {
return uint8_t ( ( perlin2D_raw ( uint32_t ( x ) < < 8 , uint32_t ( y ) < < 8 ) > > 8 ) + 0x7F ) ;
}
uint8_t perlin8 ( uint16_t x , uint16_t y , uint16_t z ) {
return ( ( perlin3D_raw ( uint32_t ( x ) < < 8 , uint32_t ( y ) < < 8 , uint32_t ( z ) < < 8 ) * 85 ) > > 15 ) + 0x7F ;
}
uint16_t perlin16 ( uint32_t x ) {
return perlin1D_raw ( x ) + 0x7FFF ;
}
uint16_t perlin16 ( uint32_t x , uint32_t y ) {
return perlin2D_raw ( x , y ) + 0x7FFF ;
}
uint16_t perlin16 ( uint32_t x , uint32_t y , uint32_t z ) {
return ( ( perlin3D_raw ( x , y , z ) * 70 ) > > 6 ) + 0x7FFF ;
}