diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index 384e2e2ba..9d8337154 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -4,7 +4,7 @@ #include #include -#ifndef ESP32 +#ifndef ARDUINO_ARCH_ESP32 #error This audio reactive usermod does not support the ESP8266. #endif @@ -25,38 +25,46 @@ // #define FFT_SAMPLING_LOG // FFT result debugging // #define SR_DEBUG // generic SR DEBUG messages - #ifdef SR_DEBUG - #define DEBUGSR_PRINT(x) Serial.print(x) - #define DEBUGSR_PRINTLN(x) Serial.println(x) - #define DEBUGSR_PRINTF(x...) Serial.printf(x) + #define DEBUGSR_PRINT(x) DEBUGOUT.print(x) + #define DEBUGSR_PRINTLN(x) DEBUGOUT.println(x) + #define DEBUGSR_PRINTF(x...) DEBUGOUT.printf(x) #else #define DEBUGSR_PRINT(x) #define DEBUGSR_PRINTLN(x) #define DEBUGSR_PRINTF(x...) #endif +#if defined(MIC_LOGGER) || defined(FFT_SAMPLING_LOG) + #define PLOT_PRINT(x) DEBUGOUT.print(x) + #define PLOT_PRINTLN(x) DEBUGOUT.println(x) + #define PLOT_PRINTF(x...) DEBUGOUT.printf(x) +#else + #define PLOT_PRINT(x) + #define PLOT_PRINTLN(x) + #define PLOT_PRINTF(x...) +#endif +// use audio source class (ESP32 specific) #include "audio_source.h" - -constexpr i2s_port_t I2S_PORT = I2S_NUM_0; -constexpr int BLOCK_SIZE = 128; -constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms -//constexpr SRate_t SAMPLE_RATE = 16000; // 16kHz - use if FFTtask takes more than 20ms. Physical sample time -> 32ms -//constexpr SRate_t SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms -//constexpr SRate_t SAMPLE_RATE = 10240; // Base sample rate in Hz - previous default. Physical sample time -> 50ms - -#define FFT_MIN_CYCLE 21 // minimum time before FFT task is repeated. Use with 22Khz sampling -//#define FFT_MIN_CYCLE 30 // Use with 16Khz sampling -//#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated. Use with 20Khz sampling -//#define FFT_MIN_CYCLE 46 // minimum time before FFT task is repeated. Use with 10Khz sampling +constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !) +constexpr int BLOCK_SIZE = 128; // I2S buffer size (samples) // globals static uint8_t inputLevel = 128; // UI slider value -static uint8_t soundSquelch = 10; // squelch value for volume reactive routines (config value) -static uint8_t sampleGain = 60; // sample gain (config value) -static uint8_t soundAgc = 0; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) +#ifndef SR_SQUELCH + uint8_t soundSquelch = 10; // squelch value for volume reactive routines (config value) +#else + uint8_t soundSquelch = SR_SQUELCH; // squelch value for volume reactive routines (config value) +#endif +#ifndef SR_GAIN + uint8_t sampleGain = 60; // sample gain (config value) +#else + uint8_t sampleGain = SR_GAIN; // sample gain (config value) +#endif +static uint8_t soundAgc = 1; // Automagic gain control: 0 - none, 1 - normal, 2 - vivid, 3 - lazy (config value) static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1 - receive (config value) +static bool udpSyncConnected = false; // UDP connection status -> true if connected to multicast group // user settable parameters for limitSoundDynamics() static bool limiterOn = true; // bool: enable / disable dynamics limiter @@ -86,11 +94,13 @@ const float agcSampleSmooth[AGC_NUM_PRESETS] = { 1/12.f, 1/6.f, 1/16.f}; // static AudioSource *audioSource = nullptr; static volatile bool disableSoundProcessing = false; // if true, sound processing (FFT, filters, AGC) will be suspended. "volatile" as its shared between tasks. +static bool useBandPassFilter = false; // if true, enables a bandpass filter 80Hz-16Khz to remove noise. Applies before FFT. // audioreactive variables shared with FFT task static float micDataReal = 0.0f; // MicIn data with full 24bit resolution - lowest 8bit after decimal point static float multAgc = 1.0f; // sample * multAgc = sampleAgc. Our AGC multiplier static float sampleAvg = 0.0f; // Smoothed Average sample - sampleAvg < 1 means "quiet" (simple noise gate) +static float sampleAgc = 0.0f; // Smoothed AGC sample // peak detection static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay() @@ -106,6 +116,62 @@ static void autoResetPeak(void); // peak auto-reset function // Begin FFT Code // //////////////////// +// some prototypes, to ensure consistent interfaces +static float mapf(float x, float in_min, float in_max, float out_min, float out_max); // map function for float +static float fftAddAvg(int from, int to); // average of several FFT result bins +void FFTcode(void * parameter); // audio processing task: read samples, run FFT, fill GEQ channels from FFT results +static void runMicFilter(uint16_t numSamples, float *sampleBuffer); // pre-filtering of raw samples (band-pass) +static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels); // post-processing and post-amp of GEQ channels + +#define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !! + +static TaskHandle_t FFT_Task = nullptr; + +// Table of multiplication factors so that we can even out the frequency response. +static float fftResultPink[NUM_GEQ_CHANNELS] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }; + +// globals and FFT Output variables shared with animations +static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency +static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency +static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects +#if defined(WLED_DEBUG) || defined(SR_DEBUG) +static uint64_t fftTime = 0; +static uint64_t sampleTime = 0; +#endif + +// FFT Task variables (filtering and post-processing) +static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. +static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) +#ifdef SR_DEBUG +static float fftResultMax[NUM_GEQ_CHANNELS] = {0.0f}; // A table used for testing to determine how our post-processing is working. +#endif + +// audio source parameters and constant +constexpr SRate_t SAMPLE_RATE = 22050; // Base sample rate in Hz - 22Khz is a standard rate. Physical sample time -> 23ms +//constexpr SRate_t SAMPLE_RATE = 16000; // 16kHz - use if FFTtask takes more than 20ms. Physical sample time -> 32ms +//constexpr SRate_t SAMPLE_RATE = 20480; // Base sample rate in Hz - 20Khz is experimental. Physical sample time -> 25ms +//constexpr SRate_t SAMPLE_RATE = 10240; // Base sample rate in Hz - previous default. Physical sample time -> 50ms +#define FFT_MIN_CYCLE 21 // minimum time before FFT task is repeated. Use with 22Khz sampling +//#define FFT_MIN_CYCLE 30 // Use with 16Khz sampling +//#define FFT_MIN_CYCLE 23 // minimum time before FFT task is repeated. Use with 20Khz sampling +//#define FFT_MIN_CYCLE 46 // minimum time before FFT task is repeated. Use with 10Khz sampling + +// FFT Constants +constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2 +constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information. +// the following are observed values, supported by a bit of "educated guessing" +//#define FFT_DOWNSCALE 0.65f // 20kHz - downscaling factor for FFT results - "Flat-Top" window @20Khz, old freq channels +#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels +#define LOG_256 5.54517744f // log(256) + +// These are the input and output vectors. Input vectors receive computed results from FFT. +static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins +static float vImag[samplesFFT] = {0.0f}; // imaginary parts +#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT +static float windowWeighingFactors[samplesFFT] = {0.0f}; +#endif + +// Create FFT object #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups @@ -116,58 +182,20 @@ static void autoResetPeak(void); // peak auto-reset function #endif #include -// FFT Output variables shared with animations -#define NUM_GEQ_CHANNELS 16 // number of frequency channels. Don't change !! -static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency -static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency -static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects - -// FFT Constants -constexpr uint16_t samplesFFT = 512; // Samples in an FFT batch - This value MUST ALWAYS be a power of 2 -constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT results - only the "lower half" contains useful information. - -// These are the input and output vectors. Input vectors receive computed results from FFT. -static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins -static float vImag[samplesFFT] = {0.0f}; // imaginary parts - -// the following are observed values, supported by a bit of "educated guessing" -//#define FFT_DOWNSCALE 0.65f // 20kHz - downscaling factor for FFT results - "Flat-Top" window @20Khz, old freq channels -#define FFT_DOWNSCALE 0.46f // downscaling factor for FFT results - for "Flat-Top" window @22Khz, new freq channels -#define LOG_256 5.54517744 - -#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT -static float windowWeighingFactors[samplesFFT] = {0.0f}; -#endif - -// Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. -static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; -static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON) -#ifdef SR_DEBUG -static float fftResultMax[NUM_GEQ_CHANNELS] = {0.0f}; // A table used for testing to determine how our post-processing is working. -#endif - -#if defined(WLED_DEBUG) || defined(SR_DEBUG) -static uint64_t fftTime = 0; -static uint64_t sampleTime = 0; -#endif - -// Table of multiplication factors so that we can even out the frequency response. -static float fftResultPink[NUM_GEQ_CHANNELS] = { 1.70f, 1.71f, 1.73f, 1.78f, 1.68f, 1.56f, 1.55f, 1.63f, 1.79f, 1.62f, 1.80f, 2.06f, 2.47f, 3.35f, 6.83f, 9.55f }; - -// Create FFT object #ifdef UM_AUDIOREACTIVE_USE_NEW_FFT static ArduinoFFT FFT = ArduinoFFT( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); #else static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); #endif -static TaskHandle_t FFT_Task = nullptr; +// Helper functions // float version of map() static 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; } +// compute average of several FFT resut bins static float fftAddAvg(int from, int to) { float result = 0.0f; for (int i = from; i <= to; i++) { @@ -176,7 +204,9 @@ static float fftAddAvg(int from, int to) { return result / float(to - from + 1); } +// // FFT main task +// void FFTcode(void * parameter) { DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); @@ -213,6 +243,10 @@ void FFTcode(void * parameter) xLastWakeTime = xTaskGetTickCount(); // update "last unblocked time" for vTaskDelay + // band pass filter - can reduce noise floor by a factor of 50 + // downside: frequencies below 100Hz will be ignored + if (useBandPassFilter) runMicFilter(samplesFFT, vReal); + // find highest sample in the batch float maxSample = 0.0f; // max sample from FFT batch for (int i=0; i < samplesFFT; i++) { @@ -229,7 +263,7 @@ void FFTcode(void * parameter) #ifdef SR_DEBUG if (true) { // this allows measure FFT runtimes, as it disables the "only when needed" optimization #else - if (sampleAvg > 0.5f) { // noise gate open means that FFT results will be used. Don't run FFT if results are not needed. + if (sampleAvg > 0.25f) { // noise gate open means that FFT results will be used. Don't run FFT if results are not needed. #endif // run FFT (takes 3-5ms on ESP32, ~12ms on ESP32-S2) @@ -273,7 +307,7 @@ void FFTcode(void * parameter) } // for() // mapping of FFT result bins to frequency channels - if (sampleAvg > 0.5f) { // noise gate open + if (fabsf(sampleAvg) > 0.5f) { // noise gate open #if 0 /* This FFT post processing is a DIY endeavour. What we really need is someone with sound engineering expertise to do a great job here AND most importantly, that the animations look GREAT as a result. * @@ -303,10 +337,22 @@ void FFTcode(void * parameter) #else /* new mapping, optimized for 22050 Hz by softhack007 */ // bins frequency range - fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass - fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass - fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass - fftCalc[ 3] = fftAddAvg(5,7); // 2 216 - 301 bass + midrange + if (useBandPassFilter) { + // skip frequencies below 100hz + fftCalc[ 0] = 0.8f * fftAddAvg(3,4); + fftCalc[ 1] = 0.9f * fftAddAvg(4,5); + fftCalc[ 2] = fftAddAvg(5,6); + fftCalc[ 3] = fftAddAvg(6,7); + // don't use the last bins from 206 to 255. + fftCalc[15] = fftAddAvg(165,205) * 0.75f; // 40 7106 - 8828 high -- with some damping + } else { + fftCalc[ 0] = fftAddAvg(1,2); // 1 43 - 86 sub-bass + fftCalc[ 1] = fftAddAvg(2,3); // 1 86 - 129 bass + fftCalc[ 2] = fftAddAvg(3,5); // 2 129 - 216 bass + fftCalc[ 3] = fftAddAvg(5,7); // 2 216 - 301 bass + midrange + // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise) + fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping + } fftCalc[ 4] = fftAddAvg(7,10); // 3 301 - 430 midrange fftCalc[ 5] = fftAddAvg(10,13); // 3 430 - 560 midrange fftCalc[ 6] = fftAddAvg(13,19); // 5 560 - 818 midrange @@ -318,8 +364,6 @@ void FFTcode(void * parameter) fftCalc[12] = fftAddAvg(70,86); // 16 3015 - 3704 high mid fftCalc[13] = fftAddAvg(86,104); // 18 3704 - 4479 high mid fftCalc[14] = fftAddAvg(104,165) * 0.88f; // 61 4479 - 7106 high mid + high -- with slight damping - fftCalc[15] = fftAddAvg(165,215) * 0.70f; // 50 7106 - 9259 high -- with some damping - // don't use the last bins from 216 to 255. They are usually contaminated by aliasing (aka noise) #endif } else { // noise gate closed - just decay old values for (int i=0; i < NUM_GEQ_CHANNELS; i++) { @@ -329,9 +373,67 @@ void FFTcode(void * parameter) } // post-processing of frequency channels (pink noise adjustment, AGC, smooting, scaling) - for (int i=0; i < NUM_GEQ_CHANNELS; i++) { + postProcessFFTResults((fabsf(sampleAvg) > 0.25f)? true : false , NUM_GEQ_CHANNELS); - if (sampleAvg > 0.5f) { // noise gate open +#if defined(WLED_DEBUG) || defined(SR_DEBUG) + if (haveDoneFFT && (start < esp_timer_get_time())) { // filter out overflows + uint64_t fftTimeInMillis = ((esp_timer_get_time() - start) +5ULL) / 10ULL; // "+5" to ensure proper rounding + fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth + } +#endif + // run peak detection + autoResetPeak(); + detectSamplePeak(); + + #if !defined(I2S_GRAB_ADC1_COMPLETELY) + if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_I2SAdc)) // the "delay trick" does not help for analog ADC + #endif + vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers + + } // for(;;)ever +} // FFTcode() task end + + +/////////////////////////// +// Pre / Postprocessing // +/////////////////////////// + +static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // pre-filtering of raw samples (band-pass) +{ + // low frequency cutoff parameter - see https://dsp.stackexchange.com/questions/40462/exponential-moving-average-cut-off-frequency + //constexpr float alpha = 0.04f; // 150Hz + //constexpr float alpha = 0.03f; // 110Hz + constexpr float alpha = 0.0225f; // 80hz + //constexpr float alpha = 0.01693f;// 60hz + // high frequency cutoff parameter + //constexpr float beta1 = 0.75f; // 11Khz + //constexpr float beta1 = 0.82f; // 15Khz + //constexpr float beta1 = 0.8285f; // 18Khz + constexpr float beta1 = 0.85f; // 20Khz + + constexpr float beta2 = (1.0f - beta1) / 2.0; + static float last_vals[2] = { 0.0f }; // FIR high freq cutoff filter + static float lowfilt = 0.0f; // IIR low frequency cutoff filter + + for (int i=0; i < numSamples; i++) { + // FIR lowpass, to remove high frequency noise + float highFilteredSample; + if (i < (numSamples-1)) highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*sampleBuffer[i+1]; // smooth out spikes + else highFilteredSample = beta1*sampleBuffer[i] + beta2*last_vals[0] + beta2*last_vals[1]; // spcial handling for last sample in array + last_vals[1] = last_vals[0]; + last_vals[0] = sampleBuffer[i]; + sampleBuffer[i] = highFilteredSample; + // IIR highpass, to remove low frequency noise + lowfilt += alpha * (sampleBuffer[i] - lowfilt); + sampleBuffer[i] = sampleBuffer[i] - lowfilt; + } +} + +static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // post-processing and post-amp of GEQ channels +{ + for (int i=0; i < numberOfChannels; i++) { + + if (noiseGateOpen) { // noise gate open // Adjustment for frequency curves. fftCalc[i] *= fftResultPink[i]; if (FFTScalingMode > 0) fftCalc[i] *= FFT_DOWNSCALE; // adjustment related to FFT windowing function @@ -401,36 +503,23 @@ void FFTcode(void * parameter) } fftResult[i] = constrain((int)currentResult, 0, 255); } - -#if defined(WLED_DEBUG) || defined(SR_DEBUG) - if (haveDoneFFT && (start < esp_timer_get_time())) { // filter out overflows - uint64_t fftTimeInMillis = ((esp_timer_get_time() - start) +5ULL) / 10ULL; // "+5" to ensure proper rounding - fftTime = (fftTimeInMillis*3 + fftTime*7)/10; // smooth - } -#endif - // run peak detection - autoResetPeak(); - detectSamplePeak(); - - #if !defined(I2S_GRAB_ADC1_COMPLETELY) - if ((audioSource == nullptr) || (audioSource->getType() != AudioSource::Type_I2SAdc)) // the "delay trick" does not help for analog ADC - #endif - vTaskDelayUntil( &xLastWakeTime, xFrequency); // release CPU, and let I2S fill its buffers - - } // for(;;)ever -} // FFTcode() task end - - +} //////////////////// // Peak detection // //////////////////// // peak detection is called from FFT task when vReal[] contains valid FFT results static void detectSamplePeak(void) { + bool havePeak = false; + // Poor man's beat detection by seeing if sample > Average + some value. + // This goes through ALL of the 255 bins - but ignores stupid settings + // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync. if ((sampleAvg > 1) && (maxVol > 0) && (binNum > 1) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) { - // This goes through ALL of the 255 bins - but ignores stupid settings - // Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync. + havePeak = true; + } + + if (havePeak) { samplePeak = true; timeOfPeak = millis(); udpSamplePeak = true; @@ -459,10 +548,11 @@ class AudioReactive : public Usermod { #else int8_t audioPin = AUDIOPIN; #endif - #ifndef DMTYPE // I2S mic type + #ifndef SR_DMTYPE // I2S mic type uint8_t dmType = 1; // 0=none/disabled/analog; 1=generic I2S + #define SR_DMTYPE 1 // default type = I2S #else - uint8_t dmType = DMTYPE; + uint8_t dmType = SR_DMTYPE; #endif #ifndef I2S_SDPIN // aka DOUT int8_t i2ssdPin = 32; @@ -526,7 +616,6 @@ class AudioReactive : public Usermod { // variables for UDP sound sync WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!) - bool udpSyncConnected = false;// UDP connection status -> true if connected to multicast group unsigned long lastTime = 0; // last time of running UDP Microphone Sync const uint16_t delayMs = 10; // I don't want to sample too often and overload WLED uint16_t audioSyncPort= 11988;// default port for UDP sound sync @@ -538,15 +627,14 @@ class AudioReactive : public Usermod { // variables used by getSample() and agcAvg() int16_t micIn = 0; // Current sample starts with negative values and large values, which is why it's 16 bit signed double sampleMax = 0.0; // Max sample over a few seconds. Needed for AGC controler. - float micLev = 0.0f; // Used to convert returned value to have '0' as minimum. A leveller + double micLev = 0.0; // Used to convert returned value to have '0' as minimum. A leveller float expAdjF = 0.0f; // Used for exponential filter. float sampleReal = 0.0f; // "sampleRaw" as float, to provide bits that are lost otherwise (before amplification by sampleGain or inputLevel). Needed for AGC. int16_t sampleRaw = 0; // Current sample. Must only be updated ONCE!!! (amplified mic value by sampleGain and inputLevel) int16_t rawSampleAgc = 0; // not smoothed AGC sample - float sampleAgc = 0.0f; // Smoothed AGC sample // variables used in effects - float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample + float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample int16_t volumeRaw = 0; // either sampleRaw or rawSampleAgc depending on soundAgc float my_magnitude =0.0f; // FFT_Magnitude, scaled by multAgc @@ -576,28 +664,28 @@ class AudioReactive : public Usermod { if (disableSoundProcessing && (!udpSyncConnected || ((audioSyncEnabled & 0x02) == 0))) return; // no audio availeable #ifdef MIC_LOGGER // Debugging functions for audio input and sound processing. Comment out the values you want to see - Serial.print("micReal:"); Serial.print(micDataReal); Serial.print("\t"); - Serial.print("volumeSmth:"); Serial.print(volumeSmth); Serial.print("\t"); - //Serial.print("volumeRaw:"); Serial.print(volumeRaw); Serial.print("\t"); - //Serial.print("DC_Level:"); Serial.print(micLev); Serial.print("\t"); - //Serial.print("sampleAgc:"); Serial.print(sampleAgc); Serial.print("\t"); - //Serial.print("sampleAvg:"); Serial.print(sampleAvg); Serial.print("\t"); - //Serial.print("sampleReal:"); Serial.print(sampleReal); Serial.print("\t"); - //Serial.print("micIn:"); Serial.print(micIn); Serial.print("\t"); - //Serial.print("sample:"); Serial.print(sample); Serial.print("\t"); - //Serial.print("sampleMax:"); Serial.print(sampleMax); Serial.print("\t"); - //Serial.print("samplePeak:"); Serial.print((samplePeak!=0) ? 128:0); Serial.print("\t"); - //Serial.print("multAgc:"); Serial.print(multAgc, 4); Serial.print("\t"); - Serial.println(); + PLOT_PRINT("micReal:"); PLOT_PRINT(micDataReal); PLOT_PRINT("\t"); + PLOT_PRINT("volumeSmth:"); PLOT_PRINT(volumeSmth); PLOT_PRINT("\t"); + //PLOT_PRINT("volumeRaw:"); PLOT_PRINT(volumeRaw); PLOT_PRINT("\t"); + PLOT_PRINT("DC_Level:"); PLOT_PRINT(micLev); PLOT_PRINT("\t"); + //PLOT_PRINT("sampleAgc:"); PLOT_PRINT(sampleAgc); PLOT_PRINT("\t"); + //PLOT_PRINT("sampleAvg:"); PLOT_PRINT(sampleAvg); PLOT_PRINT("\t"); + //PLOT_PRINT("sampleReal:"); PLOT_PRINT(sampleReal); PLOT_PRINT("\t"); + //PLOT_PRINT("micIn:"); PLOT_PRINT(micIn); PLOT_PRINT("\t"); + //PLOT_PRINT("sample:"); PLOT_PRINT(sample); PLOT_PRINT("\t"); + //PLOT_PRINT("sampleMax:"); PLOT_PRINT(sampleMax); PLOT_PRINT("\t"); + //PLOT_PRINT("samplePeak:"); PLOT_PRINT((samplePeak!=0) ? 128:0); PLOT_PRINT("\t"); + //PLOT_PRINT("multAgc:"); PLOT_PRINT(multAgc, 4); PLOT_PRINT("\t"); + PLOT_PRINTLN(); #endif #ifdef FFT_SAMPLING_LOG #if 0 for(int i=0; i 0.5f)) { sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering + // another simple way to detect samplePeak + if ((binNum < 10) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) { + samplePeak = true; + timeOfPeak = millis(); + udpSamplePeak = true; + } } else { if ((multAgc*sampleMax > agcZoneStop[AGC_preset]) && (soundAgc > 0)) sampleMax += 0.5f * (sampleReal - sampleMax); // over AGC Zone - get back quickly @@ -1015,11 +1110,14 @@ class AudioReactive : public Usermod { } // Reset I2S peripheral for good measure - i2s_driver_uninstall(I2S_NUM_0); + i2s_driver_uninstall(I2S_NUM_0); // E (696) I2S: i2s_driver_uninstall(2006): I2S port 0 has not installed #if !defined(CONFIG_IDF_TARGET_ESP32C3) + delay(100); periph_module_reset(PERIPH_I2S0_MODULE); // not possible on -C3 #endif delay(100); // Give that poor microphone some time to setup. + + useBandPassFilter = false; switch (dmType) { #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) // stub cases for not-yet-supported I2S modes on other ESP32 chips @@ -1048,14 +1146,15 @@ class AudioReactive : public Usermod { break; case 4: DEBUGSR_PRINT(F("AR: Generic I2S Microphone with Master Clock - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); - audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/24.0f); delay(100); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin); break; #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) case 5: - DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_MIC_CHANNEL_TEXT)); - audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE); + DEBUGSR_PRINT(F("AR: I2S PDM Microphone - ")); DEBUGSR_PRINTLN(F(I2S_PDM_MIC_CHANNEL_TEXT)); + audioSource = new I2SSource(SAMPLE_RATE, BLOCK_SIZE, 1.0f/4.0f); + useBandPassFilter = true; // this reduces the noise floor on SPM1423 from 5% Vpp (~380) down to 0.05% Vpp (~5) delay(100); if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin); break; @@ -1079,7 +1178,11 @@ class AudioReactive : public Usermod { if (enabled) disableSoundProcessing = false; // all good - enable audio processing if((!audioSource) || (!audioSource->isInitialized())) { // audio source failed to initialize. Still stay "enabled", as there might be input arriving via UDP Sound Sync + #ifdef WLED_DEBUG + DEBUG_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings.")); + #else DEBUGSR_PRINTLN(F("AR: Failed to initialize sound input driver. Please check input PIN settings.")); + #endif disableSoundProcessing = true; } @@ -1353,10 +1456,11 @@ class AudioReactive : public Usermod { if (enabled) { // Input Level Slider if (disableSoundProcessing == false) { // only show slider when audio processing is running - if (soundAgc > 0) + if (soundAgc > 0) { infoArr = user.createNestedArray(F("GEQ Input Level")); // if AGC is on, this slider only affects fftResult[] frequencies - else + } else { infoArr = user.createNestedArray(F("Audio Input Level")); + } uiDomString = F("
= FFT_MIN_CYCLE) // FFT time over budget -> I2S buffer will overflow infoArr.add("! ms"); else if ((fftTime/80 + sampleTime/80) >= FFT_MIN_CYCLE) // FFT time >75% of budget -> risk of instability @@ -1541,8 +1645,10 @@ class AudioReactive : public Usermod { JsonObject top = root.createNestedObject(FPSTR(_name)); top[FPSTR(_enabled)] = enabled; + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); amic["pin"] = audioPin; + #endif JsonObject dmic = top.createNestedObject(FPSTR(_digitalmic)); dmic[F("type")] = dmType; @@ -1595,9 +1701,20 @@ class AudioReactive : public Usermod { configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled); + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); + #else + audioPin = -1; // MCU does not support analog mic + #endif configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType); + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) + if (dmType == 0) dmType = SR_DMTYPE; // MCU does not support analog + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + if (dmType == 5) dmType = SR_DMTYPE; // MCU does not support PDM + #endif + #endif + configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][0], i2ssdPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][1], i2swsPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["pin"][2], i2sckPin); diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h index c76b2bb07..16bbbb658 100644 --- a/usermods/audioreactive/audio_source.h +++ b/usermods/audioreactive/audio_source.h @@ -23,11 +23,15 @@ // see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents // and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes -#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) +#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265) // there are two things in these MCUs that could lead to problems with audio processing: // * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x) // * single core, so FFT task might slow down other things like LED updates + #if !defined(SOC_I2S_NUM) || (SOC_I2S_NUM < 1) + #error This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2. + #else #warning This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2. + #endif #endif /* ToDo: remove. ES7243 is controlled via compiler defines @@ -76,11 +80,15 @@ #ifdef I2S_USE_RIGHT_CHANNEL #define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT #define I2S_MIC_CHANNEL_TEXT "right channel only (work-around swapped channel bug in IDF 4.4)." +#define I2S_PDM_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT +#define I2S_PDM_MIC_CHANNEL_TEXT "right channel only" #else //#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ALL_LEFT //#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_RIGHT_LEFT #define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT #define I2S_MIC_CHANNEL_TEXT "left channel only (work-around swapped channel bug in IDF 4.4)." +#define I2S_PDM_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT +#define I2S_PDM_MIC_CHANNEL_TEXT "left channel only." #endif #else @@ -92,6 +100,9 @@ #define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT #define I2S_MIC_CHANNEL_TEXT "left channel only." #endif +#define I2S_PDM_MIC_CHANNEL I2S_MIC_CHANNEL +#define I2S_PDM_MIC_CHANNEL_TEXT I2S_MIC_CHANNEL_TEXT + #endif @@ -138,15 +149,17 @@ class AudioSource { virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing // Private constructor, to make sure it is not callable except from derived classes - AudioSource(SRate_t sampleRate, int blockSize) : + AudioSource(SRate_t sampleRate, int blockSize, float sampleScale) : _sampleRate(sampleRate), _blockSize(blockSize), - _initialized(false) + _initialized(false), + _sampleScale(sampleScale) {}; SRate_t _sampleRate; // Microphone sampling rate int _blockSize; // I2S block size bool _initialized; // Gets set to true if initialization is successful + float _sampleScale; // pre-scaling factor for I2S samples }; /* Basic I2S microphone source @@ -154,8 +167,8 @@ class AudioSource { */ class I2SSource : public AudioSource { public: - I2SSource(SRate_t sampleRate, int blockSize) : - AudioSource(sampleRate, blockSize) { + I2SSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) : + AudioSource(sampleRate, blockSize, sampleScale) { _config = { .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), .sample_rate = _sampleRate, @@ -195,18 +208,51 @@ class I2SSource : public AudioSource { return; } } else { + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) + #if !defined(SOC_I2S_SUPPORTS_PDM_RX) + #warning this MCU does not support PDM microphones + #endif + #endif #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) // This is an I2S PDM microphone, these microphones only use a clock and - // data line, to make it simpler to debug, use the WS pin as CLK and SD - // pin as DATA + // data line, to make it simpler to debug, use the WS pin as CLK and SD pin as DATA + // example from espressif: https://github.com/espressif/esp-idf/blob/release/v4.4/examples/peripherals/i2s/i2s_audio_recorder_sdcard/main/i2s_recorder_main.c + + // note to self: PDM has known bugs on S3, and does not work on C3 + // * S3: PDM sample rate only at 50% of expected rate: https://github.com/espressif/esp-idf/issues/9893 + // * S3: I2S PDM has very low amplitude: https://github.com/espressif/esp-idf/issues/8660 + // * C3: does not support PDM to PCM input. SoC would allow PDM RX, but there is no hardware to directly convert to PCM so it will not work. https://github.com/espressif/esp-idf/issues/8796 + _config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided. PDM is not supported on ESP32-S2. PDM RX not supported on ESP32-C3 + _config.channel_format =I2S_PDM_MIC_CHANNEL; // seems that PDM mono mode always uses left channel. + _config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality #endif } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) + if (mclkPin != I2S_PIN_NO_CHANGE) { + _config.use_apll = true; // experimental - use aPLL clock source to improve sampling quality, and to avoid glitches. + // //_config.fixed_mclk = 512 * _sampleRate; + // //_config.fixed_mclk = 256 * _sampleRate; + } + + #if !defined(SOC_I2S_SUPPORTS_APLL) + #warning this MCU does not have an APLL high accuracy clock for audio + // S3: not supported; S2: supported; C3: not supported + _config.use_apll = false; // APLL not supported on this MCU + #endif + #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) + if (ESP.getChipRevision() == 0) _config.use_apll = false; // APLL is broken on ESP32 revision 0 + #endif +#endif + // Reserve the master clock pin if provided _mclkPin = mclkPin; if (mclkPin != I2S_PIN_NO_CHANGE) { - if(!pinManager.allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) return; + if(!pinManager.allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) { + DEBUGSR_PRINTF("\nAR: Failed to allocate I2S pin: MCLK=%d\n", mclkPin); + return; + } else _routeMclk(mclkPin); } @@ -220,15 +266,25 @@ class I2SSource : public AudioSource { .data_in_num = i2ssdPin }; + //DEBUGSR_PRINTF("[AR] I2S: SD=%d, WS=%d, SCK=%d, MCLK=%d\n", i2ssdPin, i2swsPin, i2sckPin, mclkPin); + esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr); if (err != ESP_OK) { - DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err); + DEBUGSR_PRINTF("AR: Failed to install i2s driver: %d\n", err); return; } + DEBUGSR_PRINTF("AR: I2S#0 driver %s aPLL; fixed_mclk=%d.\n", _config.use_apll? "uses":"without", _config.fixed_mclk); + DEBUGSR_PRINTF("AR: %d bits, Sample scaling factor = %6.4f\n", _config.bits_per_sample, _sampleScale); + if (_config.mode & I2S_MODE_PDM) { + DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in PDM MASTER mode.")); + } else { + DEBUGSR_PRINTLN(F("AR: I2S#0 driver installed in MASTER mode.")); + } + err = i2s_set_pin(I2S_NUM_0, &_pinConfig); if (err != ESP_OK) { - DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err); + DEBUGSR_PRINTF("AR: Failed to set i2s pin config: %d\n", err); i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver return; } @@ -236,7 +292,7 @@ class I2SSource : public AudioSource { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) err = i2s_set_clk(I2S_NUM_0, _sampleRate, I2S_SAMPLE_RESOLUTION, I2S_CHANNEL_MONO); // set bit clocks. Also takes care of MCLK routing if needed. if (err != ESP_OK) { - DEBUGSR_PRINTF("Failed to configure i2s clocks: %d\n", err); + DEBUGSR_PRINTF("AR: Failed to configure i2s clocks: %d\n", err); i2s_driver_uninstall(I2S_NUM_0); // uninstall already-installed driver return; } @@ -288,6 +344,7 @@ class I2SSource : public AudioSource { currSample = (float) newSamples[i]; // 16bit input -> use as-is #endif buffer[i] = currSample; + buffer[i] *= _sampleScale; // scale samples } } } @@ -328,18 +385,25 @@ class ES7243 : public I2SSource { private: // I2C initialization functions for ES7243 void _es7243I2cBegin() { - Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U); + bool i2c_initialized = Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U); + if (i2c_initialized == false) { + DEBUGSR_PRINTLN(F("AR: ES7243 failed to initialize I2C bus driver.")); + } } void _es7243I2cWrite(uint8_t reg, uint8_t val) { #ifndef ES7243_ADDR Wire.beginTransmission(0x13); + #define ES7243_ADDR 0x13 // default address #else Wire.beginTransmission(ES7243_ADDR); #endif Wire.write((uint8_t)reg); Wire.write((uint8_t)val); - Wire.endTransmission(); + uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK + if (i2cErr != 0) { + DEBUGSR_PRINTF("AR: ES7243 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, ES7243_ADDR, reg, val); + } } void _es7243InitAdc() { @@ -353,15 +417,28 @@ class ES7243 : public I2SSource { } public: - ES7243(SRate_t sampleRate, int blockSize) : - I2SSource(sampleRate, blockSize) { + ES7243(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) : + I2SSource(sampleRate, blockSize, sampleScale) { _config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; }; void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) { + // check that pins are valid + if ((sdaPin < 0) || (sclPin < 0)) { + DEBUGSR_PRINTF("\nAR: invalid ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); + return; + } + + if ((i2sckPin < 0) || (mclkPin < 0)) { + DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin); + return; + } + // Reserve SDA and SCL pins of the I2C interface - if (!pinManager.allocatePin(sdaPin, true, PinOwner::HW_I2C) || - !pinManager.allocatePin(sclPin, true, PinOwner::HW_I2C)) { + PinManagerPinType es7243Pins[2] = { { sdaPin, true }, { sclPin, true } }; + if (!pinManager.allocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C)) { + pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C); + DEBUGSR_PRINTF("\nAR: Failed to allocate ES7243 I2C pins: SDA=%d, SCL=%d\n", sdaPin, sclPin); return; } @@ -375,8 +452,8 @@ public: void deinitialize() { // Release SDA and SCL pins of the I2C interface - pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::HW_I2C); - pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::HW_I2C); + PinManagerPinType es7243Pins[2] = { { pin_ES7243_SDA, true }, { pin_ES7243_SCL, true } }; + pinManager.deallocateMultiplePins(es7243Pins, 2, PinOwner::HW_I2C); I2SSource::deinitialize(); } @@ -385,6 +462,13 @@ public: int8_t pin_ES7243_SCL; }; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0) +#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC) + #warning this MCU does not support analog sound input +#endif +#endif + #if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3) // ADC over I2S is only availeable in "classic" ESP32 @@ -395,8 +479,8 @@ public: */ class I2SAdcSource : public I2SSource { public: - I2SAdcSource(SRate_t sampleRate, int blockSize) : - I2SSource(sampleRate, blockSize) { + I2SAdcSource(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) : + I2SSource(sampleRate, blockSize, sampleScale) { _config = { .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN), .sample_rate = _sampleRate, @@ -430,7 +514,7 @@ class I2SAdcSource : public I2SSource { // Determine Analog channel. Only Channels on ADC1 are supported int8_t channel = digitalPinToAnalogChannel(_audioPin); if (channel > 9) { - DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", _audioPin); + DEBUGSR_PRINTF("Incompatible GPIO used for analog audio input: %d\n", _audioPin); return; } else { adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel)); @@ -465,11 +549,12 @@ class I2SAdcSource : public I2SSource { //return; } #else - err = i2s_adc_disable(I2S_NUM_0); - //err = i2s_stop(I2S_NUM_0); - if (err != ESP_OK) { - DEBUGSR_PRINTF("Failed to initially disable i2s adc: %d\n", err); - } + // bugfix: do not disable ADC initially - its already disabled after driver install. + //err = i2s_adc_disable(I2S_NUM_0); + // //err = i2s_stop(I2S_NUM_0); + //if (err != ESP_OK) { + // DEBUGSR_PRINTF("Failed to initially disable i2s adc: %d\n", err); + //} #endif _initialized = true; @@ -585,8 +670,8 @@ class I2SAdcSource : public I2SSource { // a user recommended this: Try to set .communication_format to I2S_COMM_FORMAT_STAND_I2S and call i2s_set_clk() after i2s_set_pin(). class SPH0654 : public I2SSource { public: - SPH0654(SRate_t sampleRate, int blockSize) : - I2SSource(sampleRate, blockSize) + SPH0654(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f) : + I2SSource(sampleRate, blockSize, sampleScale) {} void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) { diff --git a/usermods/audioreactive/readme.md b/usermods/audioreactive/readme.md index f14c168dc..f11e219e7 100644 --- a/usermods/audioreactive/readme.md +++ b/usermods/audioreactive/readme.md @@ -1,36 +1,73 @@ # Audioreactive usermod This usermod allows controlling LEDs using audio input. Audio input can be either microphone or analog-in (AUX) using appropriate adapter. -Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...). +Supported microphones range from cheap analog (MAX4466, MAX9814, ...) to high quality digital (INMP441, ICS-43434, ...) and dgital Line-In. The usermod does audio processing and provides data structure that specially written effect can use. The usermod **does not** provide effects or draws anything to LED strip/matrix. +## Additional Documentation +This usermod is an evolution of [SR-WLED](https://github.com/atuline/WLED), and a lot of documentation and information can be found in the [SR-WLED wiki](https://github.com/atuline/WLED/wiki): +* [getting started with audio](https://github.com/atuline/WLED/wiki/First-Time-Setup#sound) +* [Sound settings](https://github.com/atuline/WLED/wiki/Sound-Settings) - similar to options on the usemod settings page in WLED. +* [Digital Audio](https://github.com/atuline/WLED/wiki/Digital-Microphone-Hookup) +* [Analog Audio](https://github.com/atuline/WLED/wiki/Analog-Audio-Input-Options) +* [UDP Sound sync](https://github.com/atuline/WLED/wiki/UDP-Sound-Sync) + + +## Supported MCUs +This audioreactive usermod works best on "classic ESP32" (dual core), and on ESP32-S3 which also has dual core and hardware floating point support. + +It will compile succesfully for ESP32-S2 and ESP32-C3, however might not work well, as other WLED functions will become slow. Audio processing requires a lot of computing power, which can be problematic on smaller MCUs like -S2 and -C3. + +Analog audio is only possible on "classic" ESP32, but not on other MCUs like ESP32-S3. + +Currently ESP8266 is not supported, due to low speed and small RAM of this chip. +There are however plans to create a lightweight audioreactive for the 8266, with reduced features. ## Installation -Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment as well as `arduinoFFT` to your `lib_deps`. +### using customised _arduinoFFT_ library for use with this usermod +Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment `build_flags`, as well as `https://github.com/blazoncek/arduinoFFT.git` to your `lib_deps`. If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed. Customised _arduinoFFT_ library for use with this usermod can be found at https://github.com/blazoncek/arduinoFFT.git +### using latest (develop) _arduinoFFT_ library +Alternatively, you can use the latest arduinoFFT development version. +ArduinoFFT `develop` library is slightly more accurate, and slighly faster than our customised library, however also needs additional 2kB RAM. + +* `build_flags` = `-D USERMOD_AUDIOREACTIVE` `-D UM_AUDIOREACTIVE_USE_NEW_FFT` +* `lib_deps`= `https://github.com/kosme/arduinoFFT#develop @ 1.9.2` + ## Configuration All parameters are runtime configurable though some may require hard boot after change (I2S microphone or selected GPIOs). -If you want to define default GPIOs during compile time use the following (default values in parentheses): +If you want to define default GPIOs during compile time use the following addtional build_flags (default values in parentheses): -- `DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S, 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S -- `AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36) -- `I2S_SDPIN=x` : GPIO for SD pin on digital mcrophone (32) -- `I2S_WSPIN=x` : GPIO for WS pin on digital mcrophone (15) -- `I2S_CKPIN=x` : GPIO for SCK pin on digital mcrophone (14) -- `ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1) -- `ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1) -- `MCLK_PIN=x` : GPIO for master clock pin on digital mcrophone (-1) +- `-D SR_DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S (default), 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S +- `-D AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36) +- `-D I2S_SDPIN=x` : GPIO for SD pin on digital microphone (32) +- `-D I2S_WSPIN=x` : GPIO for WS pin on digital microphone (15) +- `-D I2S_CKPIN=x` : GPIO for SCK pin on digital microphone (14) +- `-D MCLK_PIN=x` : GPIO for master clock pin on digital Line-In boards (-1) +- `-D ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1) +- `-D ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1) **NOTE** Due to the fact that usermod uses I2S peripherial for analog audio sampling, use of analog *buttons* (i.e. potentiometers) is disabled while running this usermod with analog microphone. +### Advanced Compile-Time Options +You can use the following additional flags in your `build_flags` +* `-D SR_SQUELCH=x` : Default "squelch" setting (10) +* `-D SR_GAIN=x` : Default "gain" setting (60) +* `-D I2S_USE_RIGHT_CHANNEL`: Use RIGHT instead of LEFT channel (not recommended unless you strictly need this). +* `-D I2S_USE_16BIT_SAMPLES`: Use 16bit instead of 32bit for internal sample buffers. Reduces sampling quality, but frees some RAM ressources (not recommended unless you absolutely need this). +* `-D I2S_GRAB_ADC1_COMPLETELY`: Experimental: continously sample analog ADC microphone. Only effective on ESP32. WARNING this _will_ cause conflicts(lock-up) with any analogRead() call. +* `-D MIC_LOGGER` : (debugging) Logs samples from the microphone to serial USB. Use with serial plotter (Arduino IDE) +* `-D SR_DEBUG` : (debugging) Additional error diagnostics and debug info on serial USB. + ## Release notes -2022-06 Ported from [soundreactive](https://github.com/atuline/WLED) by @blazoncek (AKA Blaz Kristan) +* 2022-06 Ported from [soundreactive WLED](https://github.com/atuline/WLED) - by @blazoncek (AKA Blaz Kristan) and the [SR-WLED team](https://github.com/atuline/WLED/wiki#sound-reactive-wled-fork-team). +* 2022-11 Updated to align with "[MoonModules/WLED](https://amg.wled.me)" audioreactive usermod - by @softhack007 (AKA Frank Möhle). diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dab05581c..ad8225fdd 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -120,7 +120,7 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint16_t mode_blink(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), false, true); } -static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;1d"; +static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!"; /* @@ -129,7 +129,7 @@ static const char _data_FX_MODE_BLINK[] PROGMEM = "Blink@!,Duty cycle;!,!;!;1d"; uint16_t mode_blink_rainbow(void) { return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), false, false); } -static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!;1d"; +static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequency,Blink duration;!,!;!"; /* @@ -138,7 +138,7 @@ static const char _data_FX_MODE_BLINK_RAINBOW[] PROGMEM = "Blink Rainbow@Frequen uint16_t mode_strobe(void) { return blink(SEGCOLOR(0), SEGCOLOR(1), true, true); } -static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;1d"; +static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!"; /* @@ -147,7 +147,7 @@ static const char _data_FX_MODE_STROBE[] PROGMEM = "Strobe@!;!,!;!;1d"; uint16_t mode_strobe_rainbow(void) { return blink(SEGMENT.color_wheel(SEGENV.call & 0xFF), SEGCOLOR(1), true, false); } -static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!;1d"; +static const char _data_FX_MODE_STROBE_RAINBOW[] PROGMEM = "Strobe Rainbow@!;,!;!"; /* @@ -213,7 +213,7 @@ uint16_t color_wipe(bool rev, bool useRandomColors) { uint16_t mode_color_wipe(void) { return color_wipe(false, false); } -static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!;!;1d"; +static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!;!"; /* @@ -222,7 +222,7 @@ static const char _data_FX_MODE_COLOR_WIPE[] PROGMEM = "Wipe@!,!;!,!;!;1d"; uint16_t mode_color_sweep(void) { return color_wipe(true, false); } -static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!;!;1d"; +static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!;!"; /* @@ -232,7 +232,7 @@ static const char _data_FX_MODE_COLOR_SWEEP[] PROGMEM = "Sweep@!,!;!,!;!;1d"; uint16_t mode_color_wipe_random(void) { return color_wipe(false, true); } -static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!;1,2,3;!;1d"; +static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!;;!"; /* @@ -241,11 +241,11 @@ static const char _data_FX_MODE_COLOR_WIPE_RANDOM[] PROGMEM = "Wipe Random@!;1,2 uint16_t mode_color_sweep_random(void) { return color_wipe(true, true); } -static const char _data_FX_MODE_COLOR_SWEEP_RANDOM[] PROGMEM = "Sweep Random"; +static const char _data_FX_MODE_COLOR_SWEEP_RANDOM[] PROGMEM = "Sweep Random@!;;!"; /* - * Lights all LEDs in one random color up. Then switches them + * Lights all LEDs up in one random color. Then switches them * to the next random color. */ uint16_t mode_random_color(void) { @@ -274,7 +274,7 @@ uint16_t mode_random_color(void) { SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade)); return FRAMETIME; } -static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;1,2,3;!;1d"; +static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!"; /* @@ -317,7 +317,7 @@ uint16_t dynamic(boolean smooth=false) { uint16_t mode_dynamic(void) { return dynamic(false); } -static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!;1,2,3;!;1d"; +static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!;;!"; /* @@ -326,7 +326,7 @@ static const char _data_FX_MODE_DYNAMIC[] PROGMEM = "Dynamic@!,!;1,2,3;!;1d"; uint16_t mode_dynamic_smooth(void) { return dynamic(true); } -static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth"; +static const char _data_FX_MODE_DYNAMIC_SMOOTH[] PROGMEM = "Dynamic Smooth@!,!;;!"; /* @@ -348,7 +348,7 @@ uint16_t mode_breath(void) { return FRAMETIME; } -static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;1d"; +static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!"; /* @@ -364,7 +364,7 @@ uint16_t mode_fade(void) { return FRAMETIME; } -static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!;1d"; +static const char _data_FX_MODE_FADE[] PROGMEM = "Fade@!;!,!;!"; /* @@ -404,7 +404,7 @@ uint16_t scan(bool dual) uint16_t mode_scan(void) { return scan(false); } -static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!;1d"; +static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!"; /* @@ -413,7 +413,7 @@ static const char _data_FX_MODE_SCAN[] PROGMEM = "Scan@!,# of dots;!,!,!;!;1d"; uint16_t mode_dual_scan(void) { return scan(true); } -static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots;!,!,!;!;1d"; +static const char _data_FX_MODE_DUAL_SCAN[] PROGMEM = "Scan Dual@!,# of dots;!,!,!;!"; /* @@ -431,7 +431,7 @@ uint16_t mode_rainbow(void) { return FRAMETIME; } -static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;1,2,3;!;1d"; +static const char _data_FX_MODE_RAINBOW[] PROGMEM = "Colorloop@!,Saturation;;!"; /* @@ -449,7 +449,7 @@ uint16_t mode_rainbow_cycle(void) { return FRAMETIME; } -static const char _data_FX_MODE_RAINBOW_CYCLE[] PROGMEM = "Rainbow@!,Size;1,2,3;!;1d"; +static const char _data_FX_MODE_RAINBOW_CYCLE[] PROGMEM = "Rainbow@!,Size;;!"; /* @@ -488,7 +488,7 @@ uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) { uint16_t mode_theater_chase(void) { return running(SEGCOLOR(0), SEGCOLOR(1), true); } -static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!,!;!;1d"; +static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!,!;!"; /* @@ -498,7 +498,7 @@ static const char _data_FX_MODE_THEATER_CHASE[] PROGMEM = "Theater@!,Gap size;!, uint16_t mode_theater_chase_rainbow(void) { return running(SEGMENT.color_wheel(SEGENV.step), SEGCOLOR(1), true); } -static const char _data_FX_MODE_THEATER_CHASE_RAINBOW[] PROGMEM = "Theater Rainbow@!,Gap size;1,2,3;!;1d"; +static const char _data_FX_MODE_THEATER_CHASE_RAINBOW[] PROGMEM = "Theater Rainbow@!,Gap size;,!;!"; /* @@ -541,7 +541,7 @@ uint16_t running_base(bool saw, bool dual=false) { uint16_t mode_running_dual(void) { return running_base(false, true); } -static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual"; +static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual@!,Wave width;L,!,R;!"; /* @@ -550,7 +550,7 @@ static const char _data_FX_MODE_RUNNING_DUAL[] PROGMEM = "Running Dual"; uint16_t mode_running_lights(void) { return running_base(false); } -static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width;!,!;!;1d"; +static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width;!,!;!"; /* @@ -559,7 +559,7 @@ static const char _data_FX_MODE_RUNNING_LIGHTS[] PROGMEM = "Running@!,Wave width uint16_t mode_saw(void) { return running_base(true); } -static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!;1d"; +static const char _data_FX_MODE_SAW[] PROGMEM = "Saw@!,Width;!,!;!"; /* @@ -596,7 +596,7 @@ uint16_t mode_twinkle(void) { return FRAMETIME; } -static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!;!,!;!;m12=0,1d"; //pixels +static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0"; //pixels /* @@ -642,7 +642,7 @@ uint16_t dissolve(uint32_t color) { uint16_t mode_dissolve(void) { return dissolve(SEGCOLOR(0)); } -static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed;!,!;!;1d"; +static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Dissolve speed;!,!;!"; /* @@ -651,7 +651,7 @@ static const char _data_FX_MODE_DISSOLVE[] PROGMEM = "Dissolve@Repeat speed,Diss uint16_t mode_dissolve_random(void) { return dissolve(SEGMENT.color_wheel(random8())); } -static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!;!;1d"; +static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat speed,Dissolve speed;,!;!"; /* @@ -673,7 +673,7 @@ uint16_t mode_sparkle(void) { SEGMENT.setPixelColor(SEGENV.aux0, SEGCOLOR(0)); return FRAMETIME; } -static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!;!,!;!;m12=0,1d"; +static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!;!,!;!;;m12=0"; /* @@ -694,7 +694,7 @@ uint16_t mode_flash_sparkle(void) { } return FRAMETIME; } -static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!;Bg,Fx;!;m12=0,1d"; +static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!;Bg,Fx;!;;m12=0"; /* @@ -717,7 +717,7 @@ uint16_t mode_hyper_sparkle(void) { } return FRAMETIME; } -static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!;Bg,Fx;!;m12=0,1d"; +static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!;Bg,Fx;!;;m12=0"; /* @@ -747,7 +747,7 @@ uint16_t mode_multi_strobe(void) { return FRAMETIME; } -static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!;1d"; +static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!"; /* @@ -799,7 +799,7 @@ uint16_t mode_android(void) { return 3 + ((8 * (uint32_t)(255 - SEGMENT.speed)) / SEGLEN); } -static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;m12=1,1d"; //vertical +static const char _data_FX_MODE_ANDROID[] PROGMEM = "Android@!,Width;!,!;!;;m12=1"; //vertical /* @@ -880,7 +880,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett uint16_t mode_chase_color(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), true); } -static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!;1d"; +static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!"; /* @@ -889,7 +889,7 @@ static const char _data_FX_MODE_CHASE_COLOR[] PROGMEM = "Chase@!,Width;!,!,!;!;1 uint16_t mode_chase_random(void) { return chase(SEGCOLOR(1), (SEGCOLOR(2)) ? SEGCOLOR(2) : SEGCOLOR(0), SEGCOLOR(0), false); } -static const char _data_FX_MODE_CHASE_RANDOM[] PROGMEM = "Chase Random@!,Width;!,,!;!;1d"; +static const char _data_FX_MODE_CHASE_RANDOM[] PROGMEM = "Chase Random@!,Width;!,,!;!"; /* @@ -903,7 +903,7 @@ uint16_t mode_chase_rainbow(void) { return chase(color, SEGCOLOR(0), SEGCOLOR(1), false); } -static const char _data_FX_MODE_CHASE_RAINBOW[] PROGMEM = "Chase Rainbow@!,Width;!,!;;1d"; +static const char _data_FX_MODE_CHASE_RAINBOW[] PROGMEM = "Chase Rainbow@!,Width;!,!;!"; /* @@ -917,7 +917,7 @@ uint16_t mode_chase_rainbow_white(void) { return chase(SEGCOLOR(0), color2, color3, false); } -static const char _data_FX_MODE_CHASE_RAINBOW_WHITE[] PROGMEM = "Rainbow Runner@!,Size;Bg;;1d"; +static const char _data_FX_MODE_CHASE_RAINBOW_WHITE[] PROGMEM = "Rainbow Runner@!,Size;Bg;!"; /* @@ -962,7 +962,7 @@ uint16_t mode_colorful(void) { return FRAMETIME; } -static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!;1d"; +static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2,3;!"; /* @@ -993,7 +993,7 @@ uint16_t mode_traffic_light(void) { return FRAMETIME; } -static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!;,!;!;1d"; +static const char _data_FX_MODE_TRAFFIC_LIGHT[] PROGMEM = "Traffic Light@!,US style;,!;!"; /* @@ -1023,7 +1023,7 @@ uint16_t mode_chase_flash(void) { } return delay; } -static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx,!;!;1d"; +static const char _data_FX_MODE_CHASE_FLASH[] PROGMEM = "Chase Flash@!;Bg,Fx;!"; /* @@ -1058,7 +1058,7 @@ uint16_t mode_chase_flash_random(void) { } return delay; } -static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@!;,Fx;0;1d"; +static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@!;!,!;!"; /* @@ -1067,7 +1067,7 @@ static const char _data_FX_MODE_CHASE_FLASH_RANDOM[] PROGMEM = "Chase Flash Rnd@ uint16_t mode_running_color(void) { return running(SEGCOLOR(0), SEGCOLOR(1)); } -static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!;!;1d"; +static const char _data_FX_MODE_RUNNING_COLOR[] PROGMEM = "Chase 2@!,Width;!,!;!"; /* @@ -1104,7 +1104,7 @@ uint16_t mode_running_random(void) { SEGENV.aux1 = it; return FRAMETIME; } -static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream"; +static const char _data_FX_MODE_RUNNING_RANDOM[] PROGMEM = "Stream@!,Zone size;;!"; uint16_t larson_scanner(bool dual) { @@ -1146,7 +1146,7 @@ uint16_t larson_scanner(bool dual) { uint16_t mode_larson_scanner(void){ return larson_scanner(false); } -static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Fade rate;!,!;!;m12=0,1d"; +static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Fade rate;!,!;!;;m12=0"; /* @@ -1156,7 +1156,7 @@ static const char _data_FX_MODE_LARSON_SCANNER[] PROGMEM = "Scanner@!,Fade rate; uint16_t mode_dual_larson_scanner(void){ return larson_scanner(true); } -static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,Fade rate;!,!;!;m12=0,1d"; +static const char _data_FX_MODE_DUAL_LARSON_SCANNER[] PROGMEM = "Scanner Dual@!,Fade rate;!,!,!;!;;m12=0"; /* @@ -1183,7 +1183,7 @@ uint16_t mode_comet(void) { return FRAMETIME; } -static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!,!;!;1d"; +static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!"; /* @@ -1221,7 +1221,7 @@ uint16_t mode_fireworks() { } return FRAMETIME; } -static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;!;ix=192,pal=11,1d,2d"; +static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!;!;12;ix=192,pal=11"; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h @@ -1256,7 +1256,7 @@ uint16_t mode_rain() } return mode_fireworks(); } -static const char _data_FX_MODE_RAIN[] PROGMEM = "Rain@!,Spawning rate;!,!;;ix=128,pal=0,1d,2d"; +static const char _data_FX_MODE_RAIN[] PROGMEM = "Rain@!,Spawning rate;!,!;!;12;ix=128,pal=0"; /* @@ -1285,7 +1285,7 @@ uint16_t mode_fire_flicker(void) { SEGENV.step = it; return FRAMETIME; } -static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!;1d"; +static const char _data_FX_MODE_FIRE_FLICKER[] PROGMEM = "Fire Flicker@!,!;!;!"; /* @@ -1323,7 +1323,7 @@ uint16_t gradient_base(bool loading) { uint16_t mode_gradient(void) { return gradient_base(false); } -static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!;!;ix=16,1d"; +static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!;!;;ix=16"; /* @@ -1332,7 +1332,7 @@ static const char _data_FX_MODE_GRADIENT[] PROGMEM = "Gradient@!,Spread;!,!;!;ix uint16_t mode_loading(void) { return gradient_base(true); } -static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;ix=16,1d"; +static const char _data_FX_MODE_LOADING[] PROGMEM = "Loading@!,Fade;!,!;!;;ix=16"; //American Police Light with all LEDs Red and Blue @@ -1371,7 +1371,7 @@ uint16_t mode_two_dots() return police_base(SEGCOLOR(0), color2); } -static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size;1,2,Bg;!;1d"; +static const char _data_FX_MODE_TWO_DOTS[] PROGMEM = "Two Dots@!,Dot size;1,2,Bg;!"; /* @@ -1458,7 +1458,7 @@ uint16_t mode_fairy() { } return FRAMETIME; } -static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy"; +static const char _data_FX_MODE_FAIRY[] PROGMEM = "Fairy@!,# of flashers;!,!;!"; /* @@ -1508,7 +1508,7 @@ uint16_t mode_fairytwinkle() { } return FRAMETIME; } -static const char _data_FX_MODE_FAIRYTWINKLE[] PROGMEM = "Fairy Twinkle@;;;m12=0,1d"; //pixels +static const char _data_FX_MODE_FAIRYTWINKLE[] PROGMEM = "Fairytwinkle@!,!;!,!;!;;m12=0"; //pixels /* @@ -1539,7 +1539,7 @@ uint16_t tricolor_chase(uint32_t color1, uint32_t color2) { uint16_t mode_tricolor_chase(void) { return tricolor_chase(SEGCOLOR(2), SEGCOLOR(0)); } -static const char _data_FX_MODE_TRICOLOR_CHASE[] PROGMEM = "Chase 3@!,Size;1,2,3;0;1d"; +static const char _data_FX_MODE_TRICOLOR_CHASE[] PROGMEM = "Chase 3@!,Size;1,2,3;!"; /* @@ -1580,7 +1580,7 @@ uint16_t mode_icu(void) { return SPEED_FORMULA_L; } -static const char _data_FX_MODE_ICU[] PROGMEM = "ICU"; +static const char _data_FX_MODE_ICU[] PROGMEM = "ICU@!,!;!,!;!"; /* @@ -1621,7 +1621,7 @@ uint16_t mode_tricolor_wipe(void) return FRAMETIME; } -static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;0;1d"; +static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!"; /* @@ -1666,7 +1666,7 @@ uint16_t mode_tricolor_fade(void) return FRAMETIME; } -static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade"; +static const char _data_FX_MODE_TRICOLOR_FADE[] PROGMEM = "Tri Fade@!;1,2,3;!"; /* @@ -1741,7 +1741,7 @@ uint16_t mode_random_chase(void) random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } -static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2"; +static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2@!;;"; //7 bytes @@ -1848,7 +1848,7 @@ uint16_t mode_lightning(void) } return FRAMETIME; } -static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning"; +static const char _data_FX_MODE_LIGHTNING[] PROGMEM = "Lightning@!,!;!,!;!"; // Pride2015 @@ -1894,7 +1894,7 @@ uint16_t mode_pride_2015(void) SEGENV.aux0 = sHue16; return FRAMETIME; } -static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;;1d"; +static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;"; //eight colored dots, weaving in and out of sync with each other @@ -1911,7 +1911,7 @@ uint16_t mode_juggle(void){ } return FRAMETIME; } -static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;!,!;!;sx=16,ix=240,1d"; +static const char _data_FX_MODE_JUGGLE[] PROGMEM = "Juggle@!,Trail;;!;;sx=16,ix=240"; uint16_t mode_palette() @@ -1931,7 +1931,7 @@ uint16_t mode_palette() } return FRAMETIME; } -static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;1,2,3;!;1d"; +static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Cycle speed;;!"; // WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active @@ -2016,7 +2016,7 @@ uint16_t mode_fire_2012() return FRAMETIME; } -static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate;1,2,3;!;sx=120,ix=64,m12=1,1d"; //bars +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate;;!;1;sx=120,ix=64,m12=1"; // bars // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb @@ -2071,7 +2071,7 @@ uint16_t mode_colorwaves() SEGENV.aux0 = sHue16; return FRAMETIME; } -static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!"; // colored stripes pulsing at a defined Beats-Per-Minute (BPM) @@ -2087,7 +2087,7 @@ uint16_t mode_bpm() } return FRAMETIME; } -static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;1,2,3;!;sx=64,1d"; +static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64"; uint16_t mode_fillnoise8() @@ -2104,7 +2104,7 @@ uint16_t mode_fillnoise8() return FRAMETIME; } -static const char _data_FX_MODE_FILLNOISE8[] PROGMEM = "Fill Noise@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_FILLNOISE8[] PROGMEM = "Fill Noise@!;!;!"; uint16_t mode_noise16_1() @@ -2129,7 +2129,7 @@ uint16_t mode_noise16_1() return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_NOISE16_1[] PROGMEM = "Noise 1@!;!;!"; uint16_t mode_noise16_2() @@ -2151,7 +2151,7 @@ uint16_t mode_noise16_2() return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_NOISE16_2[] PROGMEM = "Noise 2@!;!;!"; uint16_t mode_noise16_3() @@ -2176,7 +2176,7 @@ uint16_t mode_noise16_3() return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!"; //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino @@ -2192,7 +2192,7 @@ uint16_t mode_noise16_4() } return FRAMETIME; } -static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!,!;!,!,!;!;1d"; +static const char _data_FX_MODE_NOISE16_4[] PROGMEM = "Noise 4@!;!;!"; //based on https://gist.github.com/kriegsman/5408ecd397744ba0393e @@ -2248,7 +2248,7 @@ uint16_t mode_colortwinkle() } return FRAMETIME_FIXED; } -static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade speed,Spawn speed;1,2,3;!;m12=0,1d"; //pixels +static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade speed,Spawn speed;;!;;m12=0"; //pixels //Calm effect, like a lake at night @@ -2269,7 +2269,7 @@ uint16_t mode_lake() { } return FRAMETIME; } -static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;1,2,3;!;1d"; +static const char _data_FX_MODE_LAKE[] PROGMEM = "Lake@!;Fx;!"; // meteor effect @@ -2306,7 +2306,7 @@ uint16_t mode_meteor() { return FRAMETIME; } -static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!;1d"; +static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!;!"; // smooth meteor effect @@ -2345,7 +2345,7 @@ uint16_t mode_meteor_smooth() { SEGENV.step += SEGMENT.speed +1; return FRAMETIME; } -static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!;!;1d"; +static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!;!"; //Railway Crossing / Christmas Fairy lights @@ -2377,7 +2377,7 @@ uint16_t mode_railway() SEGENV.step += FRAMETIME; return FRAMETIME; } -static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway"; +static const char _data_FX_MODE_RAILWAY[] PROGMEM = "Railway@!,Smoothness;;!"; //Water ripple @@ -2472,13 +2472,13 @@ uint16_t ripple_base(bool rainbow) uint16_t mode_ripple(void) { return ripple_base(false); } -static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple"; +static const char _data_FX_MODE_RIPPLE[] PROGMEM = "Ripple@!,Wave #;,!;!"; uint16_t mode_ripple_rainbow(void) { return ripple_base(true); } -static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow"; +static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!"; // TwinkleFOX by Mark Kriegsman: https://gist.github.com/kriegsman/756ea6dcae8e30845b5a @@ -2615,14 +2615,14 @@ uint16_t mode_twinklefox() { return twinklefox_base(false); } -static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox"; +static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate;;!"; uint16_t mode_twinklecat() { return twinklefox_base(true); } -static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat"; +static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate;;!"; //inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes @@ -2684,7 +2684,7 @@ uint16_t mode_halloween_eyes() return FRAMETIME; } -static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time;!,!;!;1d,2d"; +static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time;!,!;!;12"; //Speed slider sets amount of LEDs lit, intensity sets unlit @@ -2706,7 +2706,7 @@ uint16_t mode_static_pattern() return FRAMETIME; } -static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,Bg;!;pal=0,1d"; +static const char _data_FX_MODE_STATIC_PATTERN[] PROGMEM = "Solid Pattern@Fg size,Bg size;Fg,!;!;;pal=0"; uint16_t mode_tri_static_pattern() @@ -2732,7 +2732,7 @@ uint16_t mode_tri_static_pattern() return FRAMETIME; } -static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tri@,Size;1,2,3;!;1d,pal=0"; +static const char _data_FX_MODE_TRI_STATIC_PATTERN[] PROGMEM = "Solid Pattern Tri@,Size;1,2,3;;;pal=0"; uint16_t spots_base(uint16_t threshold) @@ -2767,7 +2767,7 @@ uint16_t mode_spots() { return spots_base((255 - SEGMENT.speed) << 8); } -static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width;!,!;!;1d"; +static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@,Width;!,!;!"; //Intensity slider sets number of "lights", LEDs per light fade in and out @@ -2778,7 +2778,7 @@ uint16_t mode_spots_fade() uint16_t tr = (t >> 1) + (t >> 2); return spots_base(tr); } -static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width;!,!;!;1d"; +static const char _data_FX_MODE_SPOTS_FADE[] PROGMEM = "Spots Fade@Spread,Width;!,!;!"; //each needs 12 bytes @@ -2857,7 +2857,7 @@ uint16_t mode_bouncing_balls(void) { return FRAMETIME; } -static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls;!,!,!;!;m12=1,1d"; //bar +static const char _data_FX_MODE_BOUNCINGBALLS[] PROGMEM = "Bouncing Balls@Gravity,# of balls;!,!,!;!;1;m12=1"; //bar /* @@ -2900,19 +2900,19 @@ uint16_t sinelon_base(bool dual, bool rainbow=false) { uint16_t mode_sinelon(void) { return sinelon_base(false); } -static const char _data_FX_MODE_SINELON[] PROGMEM = "Sinelon"; +static const char _data_FX_MODE_SINELON[] PROGMEM = "Sinelon@!,Trail;!,!,!;!"; uint16_t mode_sinelon_dual(void) { return sinelon_base(true); } -static const char _data_FX_MODE_SINELON_DUAL[] PROGMEM = "Sinelon Dual"; +static const char _data_FX_MODE_SINELON_DUAL[] PROGMEM = "Sinelon Dual@!,Trail;!,!,!;!"; uint16_t mode_sinelon_rainbow(void) { return sinelon_base(false, true); } -static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow"; +static const char _data_FX_MODE_SINELON_RAINBOW[] PROGMEM = "Sinelon Rainbow@!,Trail;,,!;!"; //Rainbow with glitter, inspired by https://gist.github.com/kriegsman/062e10f7f07ba8518af6 @@ -2927,7 +2927,7 @@ uint16_t mode_glitter() return FRAMETIME; } -static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@,!;!,!,!;!;m12=0,1d"; //pixels +static const char _data_FX_MODE_GLITTER[] PROGMEM = "Glitter@!,!;;!;;m12=0"; //pixels //each needs 19 bytes @@ -3000,7 +3000,7 @@ uint16_t mode_popcorn(void) { return FRAMETIME; } -static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!;!,!,!;!;m12=1,1d"; //bar +static const char _data_FX_MODE_POPCORN[] PROGMEM = "Popcorn@!,!;!,!,!;!;;m12=1"; //bar //values close to 100 produce 5Hz flicker, which looks very candle-y @@ -3087,14 +3087,14 @@ uint16_t mode_candle() { return candle(false); } -static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@Flicker rate,Flicker intensity;!,!;!;sx=96,ix=224,pal=0,1d"; +static const char _data_FX_MODE_CANDLE[] PROGMEM = "Candle@!,!;!,!;!;1;sx=96,ix=224,pal=0"; uint16_t mode_candle_multi() { return candle(true); } -static const char _data_FX_MODE_CANDLE_MULTI[] PROGMEM = "Candle Multi@Flicker rate,Flicker intensity;!,!;!;sx=96,ix=224,pal=0,1d"; +static const char _data_FX_MODE_CANDLE_MULTI[] PROGMEM = "Candle Multi@!,!;!,!;!;;sx=96,ix=224,pal=0"; /* @@ -3227,7 +3227,7 @@ uint16_t mode_starburst(void) { return FRAMETIME; } #undef STARBURST_MAX_FRAG -static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments;,!;!;pal=11,m12=0,1d"; +static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chance,Fragments;,!;!;;pal=11,m12=0"; /* @@ -3363,7 +3363,7 @@ uint16_t mode_exploding_fireworks(void) return FRAMETIME; } #undef MAX_SPARKS -static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!=11;ix=128,1d,2d"; +static const char _data_FX_MODE_EXPLODING_FIREWORKS[] PROGMEM = "Fireworks 1D@Gravity,Firing side;!,!;!;12;pal=11,ix=128"; /* @@ -3449,7 +3449,7 @@ uint16_t mode_drip(void) return FRAMETIME; } -static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips;!,!;!;m12=1,1d"; //bar +static const char _data_FX_MODE_DRIP[] PROGMEM = "Drip@Gravity,# of drips;!,!;!;;m12=1"; //bar /* @@ -3538,7 +3538,7 @@ uint16_t mode_tetrix(void) { return FRAMETIME; } -static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width;!,!;!;sx=0,ix=0,pal=11,m12=1,1d"; +static const char _data_FX_MODE_TETRIX[] PROGMEM = "Tetrix@!,Width;!,!;!;;sx=0,ix=0,pal=11,m12=1"; /* @@ -3564,7 +3564,7 @@ uint16_t mode_plasma(void) { return FRAMETIME; } -static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase;1,2,3;!;1d"; +static const char _data_FX_MODE_PLASMA[] PROGMEM = "Plasma@Phase,!;!;!"; /* @@ -3617,12 +3617,12 @@ uint16_t mode_percent(void) { return FRAMETIME; } -static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!;1d"; +static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!"; /* * Modulates the brightness similar to a heartbeat - * tries to draw an ECG aproximation on a 2D matrix + * (unimplemented?) tries to draw an ECG aproximation on a 2D matrix */ uint16_t mode_heartbeat(void) { uint8_t bpm = 40 + (SEGMENT.speed >> 3); @@ -3650,7 +3650,7 @@ uint16_t mode_heartbeat(void) { return FRAMETIME; } -static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;m12=1,1d"; +static const char _data_FX_MODE_HEARTBEAT[] PROGMEM = "Heartbeat@!,!;!,!;!;;m12=1"; // "Pacifica" @@ -3767,7 +3767,7 @@ uint16_t mode_pacifica() strip.now = nowOld; return FRAMETIME; } -static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica"; +static const char _data_FX_MODE_PACIFICA[] PROGMEM = "Pacifica@!,Angle;;!;;pal=51"; //Solid colour background with glitter @@ -3782,7 +3782,7 @@ uint16_t mode_solid_glitter() return FRAMETIME; } -static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;!;0;m12=0,1d"; +static const char _data_FX_MODE_SOLID_GLITTER[] PROGMEM = "Solid Glitter@,!;!;;;m12=0"; /* @@ -3835,7 +3835,7 @@ uint16_t mode_sunrise() { return FRAMETIME; } -static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min];;!;sx=60,1d"; +static const char _data_FX_MODE_SUNRISE[] PROGMEM = "Sunrise@Time [min],Width;;!;;sx=60"; /* @@ -3870,13 +3870,13 @@ uint16_t phased_base(uint8_t moder) { // We're making sine wave uint16_t mode_phased(void) { return phased_base(0); } -static const char _data_FX_MODE_PHASED[] PROGMEM = "Phased"; +static const char _data_FX_MODE_PHASED[] PROGMEM = "Phased@!,!;!,!;!"; uint16_t mode_phased_noise(void) { return phased_base(1); } -static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise"; +static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!"; uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. @@ -3891,7 +3891,7 @@ uint16_t mode_twinkleup(void) { // A very short twinkle routine return FRAMETIME; } -static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;m12=0,1d"; +static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0"; // Peaceful noise that's slow and with gradually changing palettes. Does not support WLED palettes or default colours or controls. @@ -3930,7 +3930,7 @@ uint16_t mode_noisepal(void) { // Slow noise return FRAMETIME; } -static const char _data_FX_MODE_NOISEPAL[] PROGMEM = "Noise Pal"; +static const char _data_FX_MODE_NOISEPAL[] PROGMEM = "Noise Pal@!,Scale;;!"; // Sine waves that have controllable phase change speed, frequency and cutoff. By Andrew Tuline. @@ -3990,7 +3990,7 @@ uint16_t mode_flow(void) return FRAMETIME; } -static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,!;!,!,!;!;m12=1,1d"; //vertical +static const char _data_FX_MODE_FLOW[] PROGMEM = "Flow@!,Zones;;!;;m12=1"; //vertical /* @@ -4016,7 +4016,7 @@ uint16_t mode_chunchun(void) } return FRAMETIME; } -static const char _data_FX_MODE_CHUNCHUN[] PROGMEM = "Chunchun@!,Gap size;!,!;!;1d"; +static const char _data_FX_MODE_CHUNCHUN[] PROGMEM = "Chunchun@!,Gap size;!,!;!"; //13 bytes @@ -4163,7 +4163,7 @@ uint16_t mode_dancing_shadows(void) return FRAMETIME; } -static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!,!,!;!;1d"; +static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!;!"; /* @@ -4184,7 +4184,7 @@ uint16_t mode_washing_machine(void) { return FRAMETIME; } -static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine"; +static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!;;!"; /* @@ -4212,7 +4212,7 @@ uint16_t mode_blends(void) { return FRAMETIME; } -static const char _data_FX_MODE_BLENDS[] PROGMEM = "Blends@Shift speed,Blend speed;1,2,3;!;1d"; +static const char _data_FX_MODE_BLENDS[] PROGMEM = "Blends@Shift speed,Blend speed;;!"; /* @@ -4341,7 +4341,7 @@ uint16_t mode_tv_simulator(void) { return FRAMETIME; } -static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator"; +static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;"; /* @@ -4504,7 +4504,7 @@ uint16_t mode_aurora(void) { return FRAMETIME; } -static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;sx=24,pal=50,1d"; +static const char _data_FX_MODE_AURORA[] PROGMEM = "Aurora@!,!;1,2,3;!;;sx=24,pal=50"; // WLED-SR effects @@ -4524,7 +4524,7 @@ uint16_t mode_perlinmove(void) { return FRAMETIME; } // mode_perlinmove() -static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,fade rate;!,!;!;1d"; +static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,Fade rate;!,!;!"; ///////////////////////// @@ -4542,7 +4542,7 @@ uint16_t mode_wavesins(void) { return FRAMETIME; } // mode_waveins() -static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness variation,Starting color,Range of colors,Color variation;!;!;1d"; +static const char _data_FX_MODE_WAVESINS[] PROGMEM = "Wavesins@!,Brightness variation,Starting color,Range of colors,Color variation;!;!"; ////////////////////////////// @@ -4565,7 +4565,7 @@ uint16_t mode_FlowStripe(void) { return FRAMETIME; } // mode_FlowStripe() -static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;;1d"; +static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Effect speed;;"; #ifndef WLED_DISABLE_2D @@ -4609,7 +4609,7 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma return FRAMETIME; } // mode_2DBlackHole() -static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2d"; +static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2"; //////////////////////////// @@ -4661,7 +4661,7 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so return FRAMETIME; } // mode_2DColoredBursts() -static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines;;!;2d"; +static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Speed,# of lines;;!;2"; ///////////////////// @@ -4688,7 +4688,7 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa return FRAMETIME; } // mode_2Ddna() -static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;1,2,3;!;2d"; +static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2"; ///////////////////////// @@ -4731,7 +4731,7 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma return FRAMETIME; } // mode_2DDNASpiral() -static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency;;!;2d"; +static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed,Y frequency;;!;2"; ///////////////////////// @@ -4762,7 +4762,7 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli return FRAMETIME; } // mode_2DDrift() -static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount;;!;2d"; +static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur amount;;!;2"; ////////////////////////// @@ -4797,7 +4797,7 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline return FRAMETIME; } // mode_2Dfirenoise() -static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;;2d"; +static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y scale;;!;2"; ////////////////////////////// @@ -4824,7 +4824,7 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so return FRAMETIME; } // mode_2DFrizzles() -static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur;;!;2d"; +static const char _data_FX_MODE_2DFRIZZLES[] PROGMEM = "Frizzles@X frequency,Y frequency,Blur;;!;2"; /////////////////////////////////////////// @@ -4928,7 +4928,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https: return FRAMETIME_FIXED * (128-(SEGMENT.speed>>1)); // update only when appropriate time passes (in 42 FPS slots) } // mode_2Dgameoflife() -static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2d"; +static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2"; ///////////////////////// @@ -4949,7 +4949,7 @@ uint16_t mode_2DHiphotic() { // By: ldirko https://edit return FRAMETIME; } // mode_2DHiphotic() -static const char _data_FX_MODE_2DHIPHOTIC[] PROGMEM = "Hiphotic@X scale,Y scale,,,Speed;;!;2d"; +static const char _data_FX_MODE_2DHIPHOTIC[] PROGMEM = "Hiphotic@X scale,Y scale,,,Speed;!;!;2"; ///////////////////////// @@ -5066,7 +5066,7 @@ uint16_t mode_2DJulia(void) { // An animated Julia set return FRAMETIME; } // mode_2DJulia() -static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size;;!;ix=24,c1=128,c2=128,c3=16,2d"; +static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per pixel,X center,Y center,Area size;!;!;2;ix=24,c1=128,c2=128,c3=16"; ////////////////////////////// @@ -5093,7 +5093,7 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline return FRAMETIME; } // mode_2DLissajous() -static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!,!,!;!;2d"; +static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate;!;!;2"; /////////////////////// @@ -5156,7 +5156,7 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. return FRAMETIME; } // mode_2Dmatrix() -static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@Falling speed,Spawning rate,Trail,,,Custom color;Spawn,Trail;;pal=0,2d"; +static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Trail,,,Custom color;Spawn,Trail;;2"; ///////////////////////// @@ -5215,7 +5215,7 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have return FRAMETIME; } // mode_2Dmetaballs() -static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@Speed;!,!,!;!;2d"; +static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2"; ////////////////////// @@ -5238,7 +5238,7 @@ uint16_t mode_2Dnoise(void) { // By Andrew Tuline return FRAMETIME; } // mode_2Dnoise() -static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@Speed,Scale;!,!,!;!;2d"; +static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2"; ////////////////////////////// @@ -5281,7 +5281,7 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito return FRAMETIME; } // mode_2DPlasmaball() -static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fade,Blur;!,!,!;!;2d"; +static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fade,Blur;;!;2"; //////////////////////////////// @@ -5336,7 +5336,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https return FRAMETIME; } // mode_2DPolarLights() -static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@Speed,Scale;;;2d"; +static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;;;2"; ///////////////////////// @@ -5364,7 +5364,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi return FRAMETIME; } // mode_2DPulser() -static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@Speed,Blur;;!;2d"; +static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2"; ///////////////////////// @@ -5386,15 +5386,15 @@ uint16_t mode_2DSindots(void) { // By: ldirko http byte t1 = millis() / (257 - SEGMENT.speed); // 20; byte t2 = sin8(t1) / 4 * 2; for (int i = 0; i < 13; i++) { - byte x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! - byte y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! + byte x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15! + byte y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15! SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND)); } SEGMENT.blur(SEGMENT.custom2>>3); return FRAMETIME; } // mode_2DSindots() -static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@Speed,Dot distance,Fade rate,Blur;;!;2d"; +static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fade rate,Blur;;!;2"; ////////////////////////////// @@ -5436,7 +5436,7 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g return FRAMETIME; } // mode_2Dsquaredswirl() -static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Blur;;!;2d"; +static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Blur;;!;2"; ////////////////////////////// @@ -5487,7 +5487,7 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi return FRAMETIME; } // mode_2DSunradiation() -static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Variance,Brightness;;;2d"; +static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Variance,Brightness;;;2"; ///////////////////////// @@ -5519,7 +5519,7 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so return FRAMETIME; } // mode_2DTartan() -static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale;;!;2d"; +static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale;;!;2"; ///////////////////////// @@ -5565,7 +5565,7 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht return FRAMETIME; } -static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;!,!,!;!;2d"; +static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2"; ///////////////////////// @@ -5639,7 +5639,7 @@ uint16_t mode_2Dcrazybees(void) { } return FRAMETIME; } -static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2d"; +static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; ///////////////////////// @@ -5729,7 +5729,7 @@ uint16_t mode_2Dghostrider(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,Blur;!,!,!;!;2d"; +static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,Blur;;!;2"; //////////////////////////// @@ -5830,7 +5830,7 @@ uint16_t mode_2Dfloatingblobs(void) { return FRAMETIME; } #undef MAX_BLOBS -static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!,!;!;c1=8,2d"; +static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur;!;!;2;c1=8"; //////////////////////////// @@ -5890,7 +5890,7 @@ uint16_t mode_2Dscrollingtext(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size;!,!;!;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0,2d"; +static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size;!,!;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0"; //////////////////////////// @@ -5922,7 +5922,7 @@ uint16_t mode_2Ddriftrose(void) { return FRAMETIME; } -static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;2d"; +static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;2"; #endif // WLED_DISABLE_2D @@ -6048,7 +6048,7 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli return FRAMETIME; } // mode_ripplepeak() -static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Max # of ripples,Select bin,Volume (min);!,!;!;c2=0,m12=0,si=0,1d,vo"; // Pixel, Beatsin +static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Max # of ripples,Select bin,Volume (min);!,!;!;1v;c2=0,m12=0,si=0"; // Pixel, Beatsin #ifndef WLED_DISABLE_2D @@ -6096,7 +6096,7 @@ uint16_t mode_2DSwirl(void) { return FRAMETIME; } // mode_2DSwirl() -static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,Bg Swirl;!;ix=64si=0,2d,vo"; // Beatsin +static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,Bg Swirl;!;2v;ix=64,si=0"; // Beatsin // TODO: color 1 unused? ///////////////////////// @@ -6142,7 +6142,7 @@ uint16_t mode_2DWaverly(void) { return FRAMETIME; } // mode_2DWaverly() -static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity;;!;ix=64,si=0,2d,vo"; // Beatsin +static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sensitivity;;!;2v;ix=64,si=0"; // Beatsin #endif // WLED_DISABLE_2D @@ -6202,7 +6202,7 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline. return FRAMETIME; } // mode_gravcenter() -static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;,!;!;ix=128,m12=2,si=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_GRAVCENTER[] PROGMEM = "Gravcenter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin /////////////////////// @@ -6253,7 +6253,7 @@ uint16_t mode_gravcentric(void) { // Gravcentric. By Andrew return FRAMETIME; } // mode_gravcentric() -static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!;!;ix=128,m12=3,si=0,1d,vo"; // Corner, Beatsin +static const char _data_FX_MODE_GRAVCENTRIC[] PROGMEM = "Gravcentric@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=3,si=0"; // Corner, Beatsin /////////////////////// @@ -6299,7 +6299,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline. return FRAMETIME; } // mode_gravimeter() -static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;ix=128,m12=2,si=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,Sensitivity;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin ////////////////////// @@ -6322,7 +6322,7 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. return FRAMETIME; } // mode_juggles() -static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;,!;!;m12=0,si=0,1d,vo"; // Pixels, Beatsin +static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6354,7 +6354,7 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. return FRAMETIME; } // mode_matripix() -static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;!;ix=64,m12=2,si=1,1d,vo"; //,rev=1,mi=1,rY=1,mY=1 Circle, WeWillRockYou, reverseX +static const char _data_FX_MODE_MATRIPIX[] PROGMEM = "Matripix@!,Brightness;!,!;!;1v;ix=64,m12=2,si=1"; //,rev=1,mi=1,rY=1,mY=1 Circle, WeWillRockYou, reverseX ////////////////////// @@ -6389,7 +6389,7 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. return FRAMETIME; } // mode_midnoise() -static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Maximum length;,!;!;ix=128,m12=1,si=0,1d,vo"; // Bar, Beatsin +static const char _data_FX_MODE_MIDNOISE[] PROGMEM = "Midnoise@Fade rate,Max. length;!,!;!;1v;ix=128,m12=1,si=0"; // Bar, Beatsin ////////////////////// @@ -6422,7 +6422,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. return FRAMETIME; } // mode_noisefire() -static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;m12=2,si=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;1v;m12=2,si=0"; // Circle, Beatsin /////////////////////// @@ -6457,7 +6457,7 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. return FRAMETIME; } // mode_noisemeter() -static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Width;!,!;!;ix=128,m12=2,si=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_NOISEMETER[] PROGMEM = "Noisemeter@Fade rate,Width;!,!;!;1v;ix=128,m12=2,si=0"; // Circle, Beatsin ////////////////////// @@ -6491,7 +6491,7 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline. return FRAMETIME; } // mode_pixelwave() -static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;ix=64,m12=2,si=0,1d,vo"; // Circle, Beatsin +static const char _data_FX_MODE_PIXELWAVE[] PROGMEM = "Pixelwave@!,Sensitivity;!,!;!;1v;ix=64,m12=2,si=0"; // Circle, Beatsin ////////////////////// @@ -6532,7 +6532,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. return FRAMETIME; } // mode_plasmoid() -static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;sx=128,ix=128,m12=0,si=0,1d,vo"; // Pixels, Beatsin +static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;1v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin /////////////////////// @@ -6576,7 +6576,7 @@ uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline. return FRAMETIME; } // mode_puddlepeak() -static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Puddle size,Select bin,Volume (min);!,!;!;c2=0,m12=0,si=0,1d,vo"; // Pixels, Beatsin +static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Puddle size,Select bin,Volume (min);!,!;!;1v;c2=0,m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6607,7 +6607,7 @@ uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline. return FRAMETIME; } // mode_puddles() -static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle size;!,!;!;m12=0,si=0,1d,vo"; // Pixels, Beatsin +static const char _data_FX_MODE_PUDDLES[] PROGMEM = "Puddles@Fade rate,Puddle size;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6635,7 +6635,7 @@ uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline. return FRAMETIME; } // mode_pixels() -static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels;,!;!;m12=0,si=0,1d,vo"; // Pixels, Beatsin +static const char _data_FX_MODE_PIXELS[] PROGMEM = "Pixels@Fade rate,# of pixels;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin /////////////////////////////// @@ -6676,7 +6676,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. return FRAMETIME; } // mode_blurz() -static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur amount;!,Color mix;!;m12=0,si=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color mix;!;1f;m12=0,si=0"; // Pixels, Beatsin ///////////////////////// @@ -6711,7 +6711,7 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil return FRAMETIME; } // mode_DJLight() -static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;m12=2,si=0,1d,fr"; // Circle, Beatsin +static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,si=0"; // Circle, Beatsin //////////////////// @@ -6747,7 +6747,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. return FRAMETIME; } // mode_freqmap() -static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;,!;!;m12=0,si=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting color;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin /////////////////////// @@ -6802,7 +6802,7 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch return FRAMETIME; } // mode_freqmatrix() -static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;m12=3,si=0,1d,fr"; // Corner, Beatsin +static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Time delay,Sound effect,Low bin,High bin,Sensivity;;;1f;m12=3,si=0"; // Corner, Beatsin ////////////////////// @@ -6837,7 +6837,7 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline. return FRAMETIME; } // mode_freqpixels() -static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting colour and # of pixels;;;m12=0,si=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Starting color and # of pixels;;;1f;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6906,7 +6906,7 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun return FRAMETIME; } // mode_freqwave() -static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;m12=2,si=0,1d,fr"; // Circle, Beatsin +static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Time delay,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin /////////////////////// @@ -6958,7 +6958,7 @@ uint16_t mode_gravfreq(void) { // Gravfreq. By Andrew Tuline. return FRAMETIME; } // mode_gravfreq() -static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;,!;!;ix=128,m12=0,si=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sensivity;!,!;!;1f;ix=128,m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -6986,7 +6986,7 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli return FRAMETIME; } // mode_noisemove() -static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;,!;!;m12=0,si=0,1d,fr"; // Pixels, Beatsin +static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin ////////////////////// @@ -7026,7 +7026,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac return FRAMETIME; } // mode_rocktaves() -static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;,!;!;m12=1,si=0,1d,fr"; // Bar, Beatsin +static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1,si=0"; // Bar, Beatsin /////////////////////// @@ -7078,7 +7078,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin return FRAMETIME; } // mode_waterfall() -static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;c2=0,m12=2,si=0,1d,fr"; // Circles, Beatsin +static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;1f;c2=0,m12=2,si=0"; // Circles, Beatsin #ifndef WLED_DISABLE_2D @@ -7138,7 +7138,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. return FRAMETIME; } // mode_2DGEQ() -static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,,Color bars;!,,Peak Color;!;c1=255,c2=64,pal=11,si=0,2d,fr"; // Beatsin +static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# of bands,,,Color bars;!,,Peaks;!;2f;c1=255,c2=64,pal=11,si=0"; // Beatsin ///////////////////////// @@ -7196,7 +7196,7 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil return FRAMETIME; } // mode_2DFunkyPlank -static const char _data_FX_MODE_2DFUNKYPLANK[] PROGMEM = "Funky Plank@Scroll speed,,# of bands;;;si=0,2d,fr"; // Beatsin +static const char _data_FX_MODE_2DFUNKYPLANK[] PROGMEM = "Funky Plank@Scroll speed,,# of bands;;;2f;si=0"; // Beatsin ///////////////////////// @@ -7299,7 +7299,7 @@ uint16_t mode_2DAkemi(void) { return FRAMETIME; } // mode_2DAkemi -static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;si=0,2d,fr"; //beatsin +static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette;2f;si=0"; //beatsin #endif // WLED_DISABLE_2D diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index fb60298d7..d6d94fbd9 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -192,6 +192,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { // read multiple button configuration JsonObject btn_obj = hw["btn"]; + int pull = -1; // trick for inverted setting + CJSON(pull, btn_obj[F("pull")]); + if (pull>=0) disablePullUp = pull; JsonArray hw_btn_ins = btn_obj[F("ins")]; if (!hw_btn_ins.isNull()) { uint8_t s = 0; @@ -200,11 +203,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { int8_t pin = btn["pin"][0] | -1; if (pin > -1 && pinManager.allocatePin(pin, false, PinOwner::Button)) { btnPin[s] = pin; - #ifdef ESP32 - pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP); - #else - pinMode(btnPin[s], INPUT_PULLUP); - #endif + if (disablePullUp) { + pinMode(btnPin[s], INPUT); + } else { + #ifdef ESP32 + pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP); + #else + pinMode(btnPin[s], INPUT_PULLUP); + #endif + } } else { btnPin[s] = -1; } @@ -737,6 +744,7 @@ void serializeConfig() { // button(s) JsonObject hw_btn = hw.createNestedObject("btn"); hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used) + hw_btn[F("pull")] = !disablePullUp; JsonArray hw_btn_ins = hw_btn.createNestedArray("ins"); // configuration for all buttons diff --git a/wled00/data/index.js b/wled00/data/index.js index 6e2a38770..93881c08f 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -522,7 +522,7 @@ function loadFXData(callback = null) fxdata = json||[]; // add default value for Solid fxdata.shift() - fxdata.unshift(";!;0"); + fxdata.unshift(";!;"); }) .catch((e)=>{ fxdata = []; @@ -847,17 +847,18 @@ function populateEffects() let fd = ""; if (ef.name.indexOf("RSVD") < 0) { if (Array.isArray(fxdata) && fxdata.length>id) { - if (fxdata[id].length==0) fd = ";;!;1d" + if (fxdata[id].length==0) fd = ";;!;1" else fd = fxdata[id]; let eP = (fd == '')?[]:fd.split(";"); // effect parameters let p = (eP.length<3 || eP[2]==='')?[]:eP[2].split(","); // palette data if (p.length>0 && (p[0] !== "" && !isNumeric(p[0]))) nm += "🎨"; // effects using palette - let m = (eP.length<4 || eP[3]==='')?[]:eP[3].split(","); // metadata - if (m.length>0) for (let r of m) { - if (r.substring(0,2)=="1d") nm += "⋮"; // 1D effects - if (r.substring(0,2)=="2d") nm += "▦"; // 2D effects - if (r.substring(0,2)=="vo") nm += "♪"; // volume effects - if (r.substring(0,2)=="fr") nm += "♫"; // frequency effects + let m = (eP.length<4 || eP[3]==='')?'1':eP[3]; // flags + if (id == 0) m = ''; // solid has no flags + if (m.length>0) { + if (m.includes('1')) nm += "⋮"; // 1D effects + if (m.includes('2')) nm += "▦"; // 2D effects + if (m.includes('v')) nm += "♪"; // volume effects + if (m.includes('f')) nm += "♫"; // frequency effects } } html += generateListItemHtml('fx',id,nm,'setFX','',fd); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 6f1dc59b2..befcc9a6b 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -617,6 +617,7 @@ Length:
+ Disable internal pull-up/down:
Touch threshold:
IR GPIO: