From 12a94c50b8d5de411171e6738649bd451663d6e0 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 16 Jun 2022 16:10:38 +0200 Subject: [PATCH] Various fixes. Added support for no audio to some effects. --- usermods/audioreactive/audio_reactive.h | 134 ++--- wled00/FX.cpp | 641 +++++++++++++----------- wled00/FX.h | 6 +- 3 files changed, 426 insertions(+), 355 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index e07cb2f8..dc7a62af 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -149,7 +149,6 @@ float fftAdd(int from, int to) { // FFT main code void FFTcode(void * parameter) { - DEBUGSR_PRINT("FFT running on core: "); DEBUGSR_PRINTLN(xPortGetCoreID()); #ifdef MAJORPEAK_SUPPRESS_NOISE static double xtemp[24] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; @@ -169,16 +168,15 @@ void FFTcode(void * parameter) { //micDataSm = ((micData * 3) + micData)/4; const int halfSamplesFFT = samplesFFT / 2; // samplesFFT divided by 2 - float maxSample1 = 0.0; // max sample from first half of FFT batch - float maxSample2 = 0.0; // max sample from second half of FFT batch - for (int i=0; i < samplesFFT; i++) - { + float maxSample1 = 0.0f; // max sample from first half of FFT batch + float maxSample2 = 0.0f; // max sample from second half of FFT batch + for (int i=0; i < samplesFFT; i++) { // set imaginary parts to 0 vImag[i] = 0; // pick our our current mic sample - we take the max value from all samples that go into FFT if ((vReal[i] <= (INT16_MAX - 1024)) && (vReal[i] >= (INT16_MIN + 1024))) //skip extreme values - normally these are artefacts { - if (i <= halfSamplesFFT) { + if (i < halfSamplesFFT) { if (fabsf((float)vReal[i]) > maxSample1) maxSample1 = fabsf((float)vReal[i]); } else { if (fabsf((float)vReal[i]) > maxSample2) maxSample2 = fabsf((float)vReal[i]); @@ -299,18 +297,15 @@ void FFTcode(void * parameter) { for (int i=0; i < 16; i++) { // Noise supression of fftCalc bins using soundSquelch adjustment for different input types. - fftCalc[i] = (fftCalc[i] - (float)soundSquelch * (float)linearNoise[i] / 4.0f <= 0.0f) ? 0 : fftCalc[i]; - + fftCalc[i] = (fftCalc[i] < ((float)soundSquelch * (float)linearNoise[i] / 4.0f)) ? 0 : fftCalc[i]; // Adjustment for frequency curves. fftCalc[i] *= fftResultPink[i]; - // Manual linear adjustment of gain using sampleGain adjustment for different input types. - fftCalc[i] *= soundAgc ? multAgc : (float)sampleGain/40.0f * inputLevel/128 + (float)fftCalc[i]/16.0f; //with inputLevel adjustment + fftCalc[i] *= soundAgc ? multAgc : ((float)sampleGain/40.0f * (float)inputLevel/128.0f + 1.0f/16.0f); //with inputLevel adjustment // Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely. - // fftResult[i] = (int)fftCalc[i]; - fftResult[i] = constrain((int)fftCalc[i], 0, 254); // question: why do we constrain values to 8bit here ??? - fftAvg[i] = (float)fftResult[i]*0.05f + (1.0f - 0.05f)*fftAvg[i]; // why no just 0.95f*fftAvg[i]? + fftResult[i] = constrain((int)fftCalc[i], 0, 254); + fftAvg[i] = (float)fftResult[i]*0.05f + 0.95f*fftAvg[i]; } // release second sample to volume reactive effects. @@ -318,13 +313,14 @@ void FFTcode(void * parameter) { micDataSm = (uint16_t)maxSample2; micDataReal = maxSample2; +#ifdef SR_DEBUG // Looking for fftResultMax for each bin using Pink Noise -// for (int i=0; i<16; i++) { -// fftResultMax[i] = ((fftResultMax[i] * 63.0) + fftResult[i]) / 64.0; -// Serial.print(fftResultMax[i]*fftResultPink[i]); Serial.print("\t"); -// } -// Serial.println(" "); - +// for (int i=0; i<16; i++) { +// fftResultMax[i] = ((fftResultMax[i] * 63.0) + fftResult[i]) / 64.0; +// Serial.print(fftResultMax[i]*fftResultPink[i]); Serial.print("\t"); +// } +// Serial.println(); +#endif } // for(;;) } // FFTcode() @@ -390,6 +386,8 @@ class AudioReactive : public Usermod { WiFiUDP fftUdp; // set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer) + bool enabled = true; + bool initDone = false; const uint16_t delayMs = 10; // I don't want to sample too often and overload WLED uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger @@ -739,48 +737,50 @@ class AudioReactive : public Usermod { */ void setup() { - // usermod exchangeable data - // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers - um_data = new um_data_t; - um_data->u_size = 18; - um_data->u_type = new um_types_t[um_data->u_size]; - um_data->u_data = new void*[um_data->u_size]; - um_data->u_data[0] = &maxVol; // assigned in effect function!!! - um_data->u_type[0] = UMT_BYTE; - um_data->u_data[1] = fftResult; //*used - um_data->u_type[1] = UMT_BYTE_ARR; - um_data->u_data[2] = &sample; //*used (for debugging) - um_data->u_type[2] = UMT_INT16; - um_data->u_data[3] = &rawSampleAgc; //*used - um_data->u_type[3] = UMT_INT16; - um_data->u_data[4] = &samplePeak; //*used - um_data->u_type[4] = UMT_BYTE; - um_data->u_data[5] = &binNum; // assigned in effect function!!! - um_data->u_type[5] = UMT_BYTE; - um_data->u_data[6] = &FFT_MajorPeak; //*used - um_data->u_type[6] = UMT_DOUBLE; - um_data->u_data[7] = &FFT_Magnitude; //*used - um_data->u_type[7] = UMT_DOUBLE; - um_data->u_data[8] = &sampleAvg; //*used - um_data->u_type[8] = UMT_FLOAT; - um_data->u_data[9] = &soundAgc; //*used - um_data->u_type[9] = UMT_BYTE; - um_data->u_data[10] = &sampleAgc; //*used (can be calculated as: sampleReal * multAgc) - um_data->u_type[10] = UMT_FLOAT; - um_data->u_data[11] = &multAgc; //*used (for debugging) - um_data->u_type[11] = UMT_FLOAT; - um_data->u_data[12] = &sampleReal; //*used (for debugging) - um_data->u_type[12] = UMT_FLOAT; - um_data->u_data[13] = &sampleGain; //*used (for debugging & Binmap) - um_data->u_type[13] = UMT_FLOAT; - um_data->u_data[14] = myVals; //*used (only once, Pixels) - um_data->u_type[14] = UMT_UINT16_ARR; - um_data->u_data[15] = &soundSquelch; //*used (only once, Binmap) - um_data->u_type[15] = UMT_BYTE; - um_data->u_data[16] = fftBin; //*used (only once, Binmap) - um_data->u_type[16] = UMT_FLOAT_ARR; - um_data->u_data[17] = &inputLevel; // assigned in effect function!!! - um_data->u_type[17] = UMT_BYTE; + if (!initDone) { + // usermod exchangeable data + // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers + um_data = new um_data_t; + um_data->u_size = 18; + um_data->u_type = new um_types_t[um_data->u_size]; + um_data->u_data = new void*[um_data->u_size]; + um_data->u_data[0] = &maxVol; // assigned in effect function!!! + um_data->u_type[0] = UMT_BYTE; + um_data->u_data[1] = fftResult; //*used + um_data->u_type[1] = UMT_BYTE_ARR; + um_data->u_data[2] = &sample; //*used (for debugging) + um_data->u_type[2] = UMT_INT16; + um_data->u_data[3] = &rawSampleAgc; //*used + um_data->u_type[3] = UMT_INT16; + um_data->u_data[4] = &samplePeak; //*used + um_data->u_type[4] = UMT_BYTE; + um_data->u_data[5] = &binNum; // assigned in effect function!!! + um_data->u_type[5] = UMT_BYTE; + um_data->u_data[6] = &FFT_MajorPeak; //*used + um_data->u_type[6] = UMT_DOUBLE; + um_data->u_data[7] = &FFT_Magnitude; //*used + um_data->u_type[7] = UMT_DOUBLE; + um_data->u_data[8] = &sampleAvg; //*used + um_data->u_type[8] = UMT_FLOAT; + um_data->u_data[9] = &soundAgc; //*used + um_data->u_type[9] = UMT_BYTE; + um_data->u_data[10] = &sampleAgc; //*used (can be calculated as: sampleReal * multAgc) + um_data->u_type[10] = UMT_FLOAT; + um_data->u_data[11] = &multAgc; //*used (for debugging) + um_data->u_type[11] = UMT_FLOAT; + um_data->u_data[12] = &sampleReal; //*used (for debugging) + um_data->u_type[12] = UMT_FLOAT; + um_data->u_data[13] = &sampleGain; //*used (for debugging & Binmap) + um_data->u_type[13] = UMT_FLOAT; + um_data->u_data[14] = myVals; //*used (only once, Pixels) + um_data->u_type[14] = UMT_UINT16_ARR; + um_data->u_data[15] = &soundSquelch; //*used (only once, Binmap) + um_data->u_type[15] = UMT_BYTE; + um_data->u_data[16] = fftBin; //*used (only once, Binmap) + um_data->u_type[16] = UMT_FLOAT_ARR; + um_data->u_data[17] = &inputLevel; // assigned in effect function!!! + um_data->u_type[17] = UMT_BYTE; + } // Reset I2S peripheral for good measure i2s_driver_uninstall(I2S_NUM_0); @@ -833,7 +833,7 @@ class AudioReactive : public Usermod { //sampling_period_us = round(1000000*(1.0/SAMPLE_RATE)); - onUpdateBegin(false); // create FFT task + if (enabled) onUpdateBegin(false); // create FFT task /* // Define the FFT Task and lock it to core 0 xTaskCreatePinnedToCore( @@ -845,6 +845,7 @@ class AudioReactive : public Usermod { &FFT_Task, // Task handle 0); // Core where the task should run */ + initDone = true; } @@ -1002,7 +1003,7 @@ class AudioReactive : public Usermod { bool getUMData(um_data_t **data) { - if (!data) return false; // no pointer provided by caller -> exit + if (!data || !enabled) return false; // no pointer provided by caller or not enabled -> exit *data = um_data; return true; } @@ -1107,6 +1108,7 @@ class AudioReactive : public Usermod { void addToConfig(JsonObject& root) { JsonObject top = root.createNestedObject(FPSTR(_name)); + top[F("enabled")] = enabled; JsonObject amic = top.createNestedObject(FPSTR(_analogmic)); amic["pin"] = audioPin; @@ -1154,6 +1156,12 @@ class AudioReactive : public Usermod { bool configComplete = !top.isNull(); + bool prevEnabled = enabled; + configComplete &= getJsonValue(top[F("enabled")], enabled); + if (initDone && prevEnabled != enabled) { + onUpdateBegin(!enabled); // create or remove FFT task + } + configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin); configComplete &= getJsonValue(top[FPSTR(_digitalmic)]["type"], dmType); diff --git a/wled00/FX.cpp b/wled00/FX.cpp index db0339ef..19c73055 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5537,110 +5537,6 @@ uint16_t WS2812FX::mode_2Dtartan(void) { // By: Elliott Kember https:/ static const char *_data_FX_MODE_2DTARTAN PROGMEM = "2D Tartan@X scale,Y scale;;!"; -///////////////////////// -// 2D Akemi // -///////////////////////// -static uint8_t akemi[] PROGMEM = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,2,2,3,3,3,3,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,2,3,3,0,0,0,0,0,0,3,3,2,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,2,3,0,0,0,6,5,5,4,0,0,0,3,2,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,2,3,0,0,6,6,5,5,5,5,4,4,0,0,3,2,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0, - 0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0, - 0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0, - 0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,9,0,0,0,0, - 0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0, - 0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0, - 0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,7,7,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, - 1,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,2, - 0,2,2,2,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,2,2,2,0, - 0,0,0,3,2,0,0,0,6,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,2,2,0,0,0, - 0,0,0,3,2,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,2,3,0,0,0, - 0,0,0,0,3,2,0,0,0,0,3,3,0,3,3,0,0,3,3,0,3,3,0,0,0,0,2,2,0,0,0,0, - 0,0,0,0,3,2,0,0,0,0,3,2,0,3,2,0,0,3,2,0,3,2,0,0,0,0,2,3,0,0,0,0, - 0,0,0,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,2,3,0,0,0,0,0, - 0,0,0,0,0,3,2,2,2,2,0,0,0,3,2,0,0,3,2,0,0,0,3,2,2,2,3,0,0,0,0,0, - 0,0,0,0,0,0,3,3,3,0,0,0,0,3,2,0,0,3,2,0,0,0,0,3,3,3,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -uint16_t WS2812FX::mode_2DAkemi(void) { - if (!isMatrix) return mode_static(); // not a 2D set-up - - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); - - uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; - counter = counter >> 8; - - const float lightFactor = 0.15f; - const float normalFactor = 0.4f; - float base = 0.0f; - uint8_t *fftResult = nullptr; - - um_data_t *um_data; - if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - fftResult = (uint8_t*)um_data->u_data[1]; - base = fftResult[0]/255.0f; - } - - //draw and color Akemi - for (uint16_t y=0; y < rows; y++) for (uint16_t x=0; x < cols; x++) { - CRGB color; - CRGB soundColor = ORANGE; - CRGB faceColor = color_wheel(counter); - CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; //default warmish white 0xABA8FF; //0xFF52e5;// - uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols] - switch (ak) { - case 0: color = BLACK; break; - case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B - case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888 - case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868 - case 6: faceColor.r *= lightFactor; faceColor.g *= lightFactor; faceColor.b *= lightFactor; color=faceColor; break; //light face 0x31AAFF - case 5: faceColor.r *= normalFactor; faceColor.g *= normalFactor; faceColor.b *= normalFactor; color=faceColor; break; //normal face 0x0094FF - case 4: color = faceColor; break; //dark face 0x007DC6 - case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white - case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break; - default: color = BLACK; - } - - if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high - setPixelColorXY(x, 0, BLACK); - setPixelColorXY(x, y+1, color); - } else - setPixelColorXY(x, y, color); - } - - //add geq left and right - if (um_data && fftResult) { - for (uint16_t x=0; x < cols/8; x++) { - uint16_t band = x * cols/8; - uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); - CRGB color = color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); - - for (uint16_t y=0; y < barHeight; y++) { - setPixelColorXY(x, rows/2-y, color); - setPixelColorXY(cols-1-x, rows/2-y, color); - } - } - } - - return FRAMETIME; -} // mode_2DAkemi -static const char *_data_FX_MODE_2DAKEMI PROGMEM = "2D Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette"; - - ///////////////////////// // 2D spaceships // ///////////////////////// @@ -6048,7 +5944,193 @@ static const char *_data_FX_MODE_DRIFT_ROSE PROGMEM = "2D Drift Rose@Fade,Blur;; /////////////////////////////////////////////////////////////////////////////// -//************************* audio routines ********************************** +/******************** audio enhanced routines ************************/ +/////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////// +// * Ripple Peak // +///////////////////////////////// +uint16_t WS2812FX::mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline. + // This currently has no controls. + #define maxsteps 16 // Case statement wouldn't allow a variable. + + uint16_t maxRipples = 16; + uint16_t dataSize = sizeof(Ripple) * maxRipples; + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + Ripple* ripples = reinterpret_cast(SEGENV.data); + + uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment + uint8_t samplePeak = 0; // actually a bool + double FFT_MajorPeak = 0.0; + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + FFT_MajorPeak = *(double*)um_data->u_data[8]; + binNum = (uint8_t*)um_data->u_data[5]; + maxVol = (uint8_t*)um_data->u_data[0]; + samplePeak = *(uint8_t*)um_data->u_data[4]; + } else { + // add support for no audio data + uint32_t ms = millis(); + samplePeak = random8() > 250; + FFT_MajorPeak = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + } + + if (SEGENV.call == 0) SEGENV.aux0 = 255; + + *binNum = SEGMENT.custom2; // Select a bin. + *maxVol = SEGMENT.custom3/2; // Our volume comparator. + + fade_out(240); // Lower frame rate means less effective fading than FastLED + fade_out(240); + + for (uint16_t i = 0; i < SEGMENT.intensity/16; i++) { // Limit the number of ripples. + if (samplePeak) ripples[i].state = 255; + + switch (ripples[i].state) { + case 254: // Inactive mode + break; + + case 255: // Initialize ripple variables. + ripples[i].pos = random16(SEGLEN); + #ifdef ESP32 + ripples[i].color = (int)(log10f(FFT_MajorPeak)*128); + #else + ripples[i].color = random8(); + #endif + ripples[i].state = 0; + break; + + case 0: + setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); + ripples[i].state++; + break; + + case maxsteps: // At the end of the ripples. 254 is an inactive mode. + ripples[i].state = 254; + break; + + default: // Middle of the ripples. + setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); + ripples[i].state++; // Next step. + break; + } // switch step + } // for i + + return FRAMETIME; +} // mode_ripplepeak() +static const char *_data_FX_MODE_RIPPLEPEAK PROGMEM = " ♪ Ripple Peak@Fade rate,Max # of ripples,,Select bin,Volume (minimum);!,!;!"; + + +///////////////////////// +// * 2D Swirl // +///////////////////////// +// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline +uint16_t WS2812FX::mode_2DSwirl(void) { + if (!isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint16_t dataSize = sizeof(CRGB) * SEGMENT.width() * SEGMENT.height(); // using width*height prevents reallocation if mirroring is enabled + + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + CRGB *leds = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) fill_solid(leds, CRGB::Black); + + const uint8_t borderWidth = 2; + + blur2d(leds, SEGMENT.custom1); + + uint8_t i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); + uint8_t j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); + uint8_t ni = (cols - 1) - i; + uint8_t nj = (cols - 1) - j; + uint16_t ms = millis(); + + uint8_t soundAgc = 0; + int16_t rawSampleAgc = 0, sample; + float sampleAvg = 0.0f; + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + soundAgc = *(uint8_t*)um_data->u_data[9]; + rawSampleAgc = *(int16_t*)um_data->u_data[3]; + sample = *(int16_t*)um_data->u_data[2]; + sampleAvg = *(float*)um_data->u_data[8]; + } else { + // add support for no audio data + sample = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + sample = map(sample, 50, 190, 0, 224); + sampleAvg = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here + } + + int tmpSound = (soundAgc) ? rawSampleAgc : sample; + + leds[XY( i, j)] += ColorFromPalette(currentPalette, (ms / 11 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 11, 200, 255); + leds[XY( j, i)] += ColorFromPalette(currentPalette, (ms / 13 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 13, 200, 255); + leds[XY(ni, nj)] += ColorFromPalette(currentPalette, (ms / 17 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 17, 200, 255); + leds[XY(nj, ni)] += ColorFromPalette(currentPalette, (ms / 29 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 29, 200, 255); + leds[XY( i, nj)] += ColorFromPalette(currentPalette, (ms / 37 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 37, 200, 255); + leds[XY(ni, j)] += ColorFromPalette(currentPalette, (ms / 41 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 41, 200, 255); + + setPixels(leds); + return FRAMETIME; +} // mode_2DSwirl() +static const char *_data_FX_MODE_2DSWIRL PROGMEM = " ♪ 2D Swirl@!,Sensitivity=64,Blur;,Bg Swirl;!"; + + +///////////////////////// +// * 2D Waverly // +///////////////////////// +// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline +uint16_t WS2812FX::mode_2DWaverly(void) { + if (!isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint16_t dataSize = sizeof(CRGB) * SEGMENT.width() * SEGMENT.height(); // using width*height prevents reallocation if mirroring is enabled + + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + CRGB *leds = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) { + fill_solid(leds, CRGB::Black); + } + + um_data_t *um_data; + uint8_t soundAgc = 0; + float sampleAgc = 0.0f, sampleAvg = 0.0f; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + soundAgc = *(uint8_t*)um_data->u_data[9]; + sampleAgc = *(float*)um_data->u_data[10]; + sampleAvg = *(float*)um_data->u_data[8]; + } + + fadeToBlackBy(leds, SEGMENT.speed); + + long t = millis() / 2; + for (uint16_t i = 0; i < cols; i++) { + uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; + // use audio if available + if (um_data) { + thisVal /= 32; // reduce intensity of inoise8() + thisVal *= (soundAgc) ? sampleAgc : sampleAvg; + } + uint16_t thisMax = map(thisVal, 0, 512, 0, rows); + + for (uint16_t j = 0; j < thisMax; j++) { + leds[XY(i, j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); + leds[XY((cols - 1) - i, (rows - 1) - j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); + } + } + blur2d(leds, 16); + + setPixels(leds); + return FRAMETIME; +} // mode_2DWaverly() +static const char *_data_FX_MODE_2DWAVERLY PROGMEM = " ♪ 2D Waverly@Amplification,Sensitivity=64;;!"; // float version of map() @@ -6062,7 +6144,6 @@ typedef struct Gravity { int gravityCounter; } gravity; - /////////////////////// // * GRAVCENTER // /////////////////////// @@ -6079,6 +6160,11 @@ uint16_t WS2812FX::mode_gravcenter(void) { // Gravcenter. By Andr //sampleAgc = *(float*)um_data->u_data[10]; //sampleAvg = *(float*)um_data->u_data[8]; tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8]; + } else { + // add support for no audio data + uint32_t ms = millis(); + tmpSound = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + //tmpSound = map(sample, 50, 190, 0, 255); } fade_out(240); @@ -6128,6 +6214,11 @@ uint16_t WS2812FX::mode_gravcentric(void) { // Gravcentric. //sampleAgc = *(float*)um_data->u_data[10]; //sampleAvg = *(float*)um_data->u_data[8]; tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8]; + } else { + // add support for no audio data + uint32_t ms = millis(); + tmpSound = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + //tmpSound = map(sample, 50, 190, 0, 255); } fade_out(240); @@ -6179,6 +6270,11 @@ uint16_t WS2812FX::mode_gravimeter(void) { // Gravmeter. By Andre //sampleAgc = *(float*)um_data->u_data[10]; //sampleAvg = *(float*)um_data->u_data[8]; tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8]; + } else { + // add support for no audio data + uint32_t ms = millis(); + tmpSound = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + //tmpSound = map(sample, 50, 190, 0, 255); } fade_out(240); @@ -6289,6 +6385,11 @@ uint16_t WS2812FX::mode_juggles(void) { // Juggles. By Andrew float sampleAgc = 0.0f; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { sampleAgc = *(float*)um_data->u_data[10]; + } else { + // add support for no audio data + uint32_t ms = millis(); + sampleAgc = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + //sampleAgc = map(sample, 50, 190, 0, 255); } fade_out(224); @@ -6309,11 +6410,16 @@ static const char *_data_FX_MODE_JUGGLES PROGMEM = " ♪ Juggles@!,# of balls;,! uint16_t WS2812FX::mode_matripix(void) { // Matripix. By Andrew Tuline. um_data_t *um_data; uint8_t soundAgc = 0; - int16_t rawSampleAgc = 0, sample = inoise8(23455,4234); // I have no idea what that does + int16_t rawSampleAgc = 0, sample; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { soundAgc = *(uint8_t*)um_data->u_data[9]; rawSampleAgc = *(int16_t*)um_data->u_data[3]; sample = *(int16_t*)um_data->u_data[2]; + } else { + // add support for no audio data + uint32_t ms = millis(); + sample = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + sample = map(sample, 50, 190, 0, 255); } if (SEGENV.call == 0) fill(BLACK); @@ -6346,6 +6452,11 @@ uint16_t WS2812FX::mode_midnoise(void) { // Midnoise. By Andrew soundAgc = *(uint8_t*)um_data->u_data[9]; sampleAgc = *(float*)um_data->u_data[10]; sampleAvg = *(float*)um_data->u_data[8]; + } else { + // add support for no audio data + uint32_t ms = millis(); + sampleAvg = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here } fade_out(SEGMENT.speed); @@ -6390,6 +6501,9 @@ uint16_t WS2812FX::mode_noisefire(void) { // Noisefire. By Andre sampleAvg = *(float*)um_data->u_data[8]; } else { // add support for no audio data + uint32_t ms = millis(); + sampleAvg = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here } for (uint16_t i = 0; i < SEGLEN; i++) { @@ -6414,8 +6528,8 @@ uint16_t WS2812FX::mode_noisemeter(void) { // Noisemeter. By Andr um_data_t *um_data; uint8_t soundAgc = 0; - int16_t rawSampleAgc = 0, sample = inoise8(23455,4234); // I have no idea what that does - float sampleAgc = 0.0f, sampleAvg = 0.0f; + int16_t rawSampleAgc = 0, sample; + float sampleAgc = 0.0f, sampleAvg; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { soundAgc = *(uint8_t*)um_data->u_data[9]; sampleAgc = *(float*)um_data->u_data[10]; @@ -6424,6 +6538,11 @@ uint16_t WS2812FX::mode_noisemeter(void) { // Noisemeter. By Andr sample = *(int16_t*)um_data->u_data[2]; } else { // add support for no audio data + uint32_t ms = millis(); + sample = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + sample = map(sample, 50, 190, 0, 255); + sampleAvg = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here } uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255); @@ -6482,13 +6601,16 @@ uint16_t WS2812FX::mode_pixelwave(void) { // Pixelwave. By Andre um_data_t *um_data; uint8_t soundAgc = 0; - int16_t rawSampleAgc = 0, sample = inoise8(23455,4234); // I have no idea what that does + int16_t rawSampleAgc = 0, sample; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { soundAgc = *(uint8_t*)um_data->u_data[9]; rawSampleAgc = *(int16_t*)um_data->u_data[3]; sample = *(int16_t*)um_data->u_data[2]; } else { // add support for no audio data + uint32_t ms = millis(); + sample = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + sample = map(sample, 50, 190, 0, 255); } uint8_t secondHand = micros()/(256-SEGMENT.speed)/500+1 % 16; @@ -6524,13 +6646,16 @@ uint16_t WS2812FX::mode_plasmoid(void) { // Plasmoid. By Andrew um_data_t *um_data; uint8_t soundAgc = 0; - float sampleAgc = 0.0f, sampleAvg = 0.0f; + float sampleAgc = 0.0f, sampleAvg; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { soundAgc = *(uint8_t*)um_data->u_data[9]; sampleAgc = *(float*)um_data->u_data[10]; sampleAvg = *(float*)um_data->u_data[8]; } else { // add support for no audio data + uint32_t ms = millis(); + sampleAvg = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here } fade_out(64); @@ -6565,7 +6690,7 @@ uint16_t WS2812FX::mode_puddlepeak(void) { // Puddlepeak. By Andr uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 255); uint16_t pos = random(SEGLEN); // Set a random starting position. - uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment + uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux0); // just in case assignment uint8_t samplePeak = 0; float sampleAgc = 0.0f; um_data_t *um_data; @@ -6576,6 +6701,10 @@ uint16_t WS2812FX::mode_puddlepeak(void) { // Puddlepeak. By Andr samplePeak = *(uint8_t*)um_data->u_data[4]; } else { // add support for no audio data + uint32_t ms = millis(); + samplePeak = random8() > 250; + sampleAgc = inoise8(beatsin8(90, 0, 200)*15 + (ms>>10), ms>>3); + //sampleAgc = mapf(sampleAvg, 0, 255, 0, 255); // help me out here } *binNum = SEGMENT.custom2; // Select a bin. @@ -6608,7 +6737,7 @@ uint16_t WS2812FX::mode_puddles(void) { // Puddles. By Andrew fade_out(fadeVal); uint8_t soundAgc = 0; - int16_t rawSampleAgc = 0, sample = inoise8(23455,4234); // I have no idea what that does + int16_t rawSampleAgc = 0, sample; um_data_t *um_data; if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { soundAgc = *(uint8_t*)um_data->u_data[9]; @@ -6616,6 +6745,9 @@ uint16_t WS2812FX::mode_puddles(void) { // Puddles. By Andrew sample = *(int16_t*)um_data->u_data[2]; } else { // add support for no audio data + uint32_t ms = millis(); + sample = inoise8(beatsin8(120, 10, 30)*10 + (ms>>14), ms>>3); + sample = map(sample, 50, 190, 0, 255); } uint16_t tmpSound = (soundAgc) ? rawSampleAgc : sample; @@ -6634,182 +6766,9 @@ uint16_t WS2812FX::mode_puddles(void) { // Puddles. By Andrew static const char *_data_FX_MODE_PUDDLES PROGMEM = " ♪ Puddles@Fade rate,Puddle size;!,!;!"; -///////////////////////////////// -// * Ripple Peak // -///////////////////////////////// -uint16_t WS2812FX::mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline. - // This currently has no controls. - #define maxsteps 16 // Case statement wouldn't allow a variable. - - uint16_t maxRipples = 16; - uint16_t dataSize = sizeof(Ripple) * maxRipples; - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - Ripple* ripples = reinterpret_cast(SEGENV.data); - - uint8_t *binNum = (uint8_t*)&SEGENV.aux1, *maxVol = (uint8_t*)(&SEGENV.aux1+1); // just in case assignment - uint8_t samplePeak = 0; - double FFT_MajorPeak = 0.0; - um_data_t *um_data; - if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - FFT_MajorPeak = *(double*)um_data->u_data[8]; - binNum = (uint8_t*)um_data->u_data[5]; - maxVol = (uint8_t*)um_data->u_data[0]; - samplePeak = *(uint8_t*)um_data->u_data[4]; - } else { - // add support for no audio data - } - - if (SEGENV.call == 0) SEGENV.aux0 = 255; - - *binNum = SEGMENT.custom2; // Select a bin. - *maxVol = SEGMENT.custom3/2; // Our volume comparator. - - fade_out(240); // Lower frame rate means less effective fading than FastLED - fade_out(240); - - for (uint16_t i = 0; i < SEGMENT.intensity/16; i++) { // Limit the number of ripples. - if (samplePeak) ripples[i].state = 255; - - switch (ripples[i].state) { - case 254: // Inactive mode - break; - - case 255: // Initialize ripple variables. - ripples[i].pos = random16(SEGLEN); - #ifdef ESP32 - ripples[i].color = (int)(log10f(FFT_MajorPeak)*128); - #else - ripples[i].color = random8(); - #endif - ripples[i].state = 0; - break; - - case 0: - setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0)); - ripples[i].state++; - break; - - case maxsteps: // At the end of the ripples. 254 is an inactive mode. - ripples[i].state = 254; - break; - - default: // Middle of the ripples. - setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); - setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2)); - ripples[i].state++; // Next step. - break; - } // switch step - } // for i - - return FRAMETIME; -} // mode_ripplepeak() -static const char *_data_FX_MODE_RIPPLEPEAK PROGMEM = " ♪ Ripple Peak@Fade rate,Max # of ripples,,Select bin,Volume (minimum);!,!;!"; - - -///////////////////////// -// * 2D Swirl // -///////////////////////// -// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline -uint16_t WS2812FX::mode_2DSwirl(void) { - if (!isMatrix) return mode_static(); // not a 2D set-up - - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = sizeof(CRGB) * SEGMENT.width() * SEGMENT.height(); // using width*height prevents reallocation if mirroring is enabled - - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - CRGB *leds = reinterpret_cast(SEGENV.data); - - if (SEGENV.call == 0) fill_solid(leds, CRGB::Black); - - const uint8_t borderWidth = 2; - - blur2d( leds, SEGMENT.custom1); - - uint8_t i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth); - uint8_t j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth); - uint8_t ni = (cols - 1) - i; - uint8_t nj = (cols - 1) - j; - uint16_t ms = millis(); - - uint8_t soundAgc = 0; - int16_t rawSampleAgc = 0, sample = inoise8(23455,4234); // I have no idea what that does - float sampleAvg = 0.0f; - um_data_t *um_data; - if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - soundAgc = *(uint8_t*)um_data->u_data[9]; - rawSampleAgc = *(int16_t*)um_data->u_data[3]; - sample = *(int16_t*)um_data->u_data[2]; - sampleAvg = *(float*)um_data->u_data[8]; - } else { - // add support for no audio data - } - - int tmpSound = (soundAgc) ? rawSampleAgc : sample; - - leds[XY( i, j)] += ColorFromPalette(currentPalette, (ms / 11 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 11, 200, 255); - leds[XY( j, i)] += ColorFromPalette(currentPalette, (ms / 13 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 13, 200, 255); - leds[XY(ni, nj)] += ColorFromPalette(currentPalette, (ms / 17 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 17, 200, 255); - leds[XY(nj, ni)] += ColorFromPalette(currentPalette, (ms / 29 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 29, 200, 255); - leds[XY( i, nj)] += ColorFromPalette(currentPalette, (ms / 37 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 37, 200, 255); - leds[XY(ni, j)] += ColorFromPalette(currentPalette, (ms / 41 + sampleAvg*4), tmpSound * SEGMENT.intensity / 64, LINEARBLEND); //CHSV( ms / 41, 200, 255); - - setPixels(leds); - return FRAMETIME; -} // mode_2DSwirl() -static const char *_data_FX_MODE_2DSWIRL PROGMEM = " ♪ 2D Swirl@!,Sensitivity=64,Blur;,Bg Swirl;!"; - - -///////////////////////// -// * 2D Waverly // -///////////////////////// -// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline -uint16_t WS2812FX::mode_2DWaverly(void) { - if (!isMatrix) return mode_static(); // not a 2D set-up - - const uint16_t cols = SEGMENT.virtualWidth(); - const uint16_t rows = SEGMENT.virtualHeight(); - const uint16_t dataSize = sizeof(CRGB) * SEGMENT.width() * SEGMENT.height(); // using width*height prevents reallocation if mirroring is enabled - - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - CRGB *leds = reinterpret_cast(SEGENV.data); - - if (SEGENV.call == 0) { - fill_solid(leds, CRGB::Black); - } - - um_data_t *um_data; - uint8_t soundAgc = 0; - float sampleAgc = 0.0f, sampleAvg = 0.0f; - if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { - soundAgc = *(uint8_t*)um_data->u_data[9]; - sampleAgc = *(float*)um_data->u_data[10]; - sampleAvg = *(float*)um_data->u_data[8]; - } - - fadeToBlackBy(leds, SEGMENT.speed); - - long t = millis() / 2; - for (uint16_t i = 0; i < cols; i++) { - uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; - // use audio if available - if (um_data) { - thisVal /= 32; // reduce intensity of inoise8() - thisVal *= (soundAgc) ? sampleAgc : sampleAvg; - } - uint16_t thisMax = map(thisVal, 0, 512, 0, rows); - - for (uint16_t j = 0; j < thisMax; j++) { - leds[XY(i, j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); - leds[XY((cols - 1) - i, (rows - 1) - j)] += ColorFromPalette(currentPalette, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND); - } - } - blur2d(leds, 16); - - setPixels(leds); - return FRAMETIME; -} // mode_2DWaverly() -static const char *_data_FX_MODE_2DWAVERLY PROGMEM = " ♪ 2D Waverly@Amplification,Sensitivity=64;;!"; +/////////////////////////////////////////////////////////////////////////////// +/******************** audio only routines ************************/ +/////////////////////////////////////////////////////////////////////////////// /////////////////////////////// @@ -7517,6 +7476,110 @@ uint16_t WS2812FX::mode_2DFunkyPlank(void) { // Written by ??? Adap static const char *_data_FX_MODE_2DFUNKYPLANK PROGMEM = " ♫ 2D Funky Plank@Scroll speed,,# of bands;;"; +///////////////////////// +// 2D Akemi // +///////////////////////// +static uint8_t akemi[] PROGMEM = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,2,2,3,3,3,3,3,3,2,2,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,2,3,3,0,0,0,0,0,0,3,3,2,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,2,3,0,0,0,6,5,5,4,0,0,0,3,2,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,0,6,6,5,5,5,5,4,4,0,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,2,3,0,6,5,5,5,5,5,5,5,5,5,5,4,0,3,2,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,9,0,0,0,0, + 0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0, + 0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0, + 0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,7,7,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,2, + 0,2,2,2,0,0,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,2,2,2,0, + 0,0,0,3,2,0,0,0,6,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0,0,2,2,0,0,0, + 0,0,0,3,2,0,0,0,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,2,3,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,3,0,3,3,0,0,3,3,0,3,3,0,0,0,0,2,2,0,0,0,0, + 0,0,0,0,3,2,0,0,0,0,3,2,0,3,2,0,0,3,2,0,3,2,0,0,0,0,2,3,0,0,0,0, + 0,0,0,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,3,2,0,0,2,3,0,0,0,0,0, + 0,0,0,0,0,3,2,2,2,2,0,0,0,3,2,0,0,3,2,0,0,0,3,2,2,2,3,0,0,0,0,0, + 0,0,0,0,0,0,3,3,3,0,0,0,0,3,2,0,0,3,2,0,0,0,0,3,3,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +uint16_t WS2812FX::mode_2DAkemi(void) { + if (!isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + uint16_t counter = (now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; + counter = counter >> 8; + + const float lightFactor = 0.15f; + const float normalFactor = 0.4f; + float base = 0.0f; + uint8_t *fftResult = nullptr; + + um_data_t *um_data; + if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { + fftResult = (uint8_t*)um_data->u_data[1]; + base = fftResult[0]/255.0f; + } + + //draw and color Akemi + for (uint16_t y=0; y < rows; y++) for (uint16_t x=0; x < cols; x++) { + CRGB color; + CRGB soundColor = ORANGE; + CRGB faceColor = color_wheel(counter); + CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; //default warmish white 0xABA8FF; //0xFF52e5;// + uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols] + switch (ak) { + case 0: color = BLACK; break; + case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B + case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888 + case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868 + case 6: faceColor.r *= lightFactor; faceColor.g *= lightFactor; faceColor.b *= lightFactor; color=faceColor; break; //light face 0x31AAFF + case 5: faceColor.r *= normalFactor; faceColor.g *= normalFactor; faceColor.b *= normalFactor; color=faceColor; break; //normal face 0x0094FF + case 4: color = faceColor; break; //dark face 0x007DC6 + case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white + case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break; + default: color = BLACK; + } + + if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high + setPixelColorXY(x, 0, BLACK); + setPixelColorXY(x, y+1, color); + } else + setPixelColorXY(x, y, color); + } + + //add geq left and right + if (um_data && fftResult) { + for (uint16_t x=0; x < cols/8; x++) { + uint16_t band = x * cols/8; + uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); + CRGB color = color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); + + for (uint16_t y=0; y < barHeight; y++) { + setPixelColorXY(x, rows/2-y, color); + setPixelColorXY(cols-1-x, rows/2-y, color); + } + } + } + + return FRAMETIME; +} // mode_2DAkemi +static const char *_data_FX_MODE_2DAKEMI PROGMEM = "2D Akemi@Color speed,Dance;Head palette,Arms & Legs,Eyes & Mouth;Face palette"; + + ////////////////////////////////////////////////////////////////////////////////////////// // mode data static const char *_data_RESERVED PROGMEM = "Reserved"; diff --git a/wled00/FX.h b/wled00/FX.h index d6ff9160..619498e6 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -265,7 +265,7 @@ #define FX_MODE_PUDDLEPEAK 144 #define FX_MODE_NOISEMOVE 145 #define FX_MODE_2DNOISE 146 // non audio -#define FX_MODE_PERLINMOVE 147 // should be moved to 124 +#define FX_MODE_PERLINMOVE 147 // should be moved to 53 #define FX_MODE_RIPPLEPEAK 148 #define FX_MODE_2DFIRENOISE 149 // non audio #define FX_MODE_2DSQUAREDSWIRL 150 // non audio @@ -297,12 +297,12 @@ #define FX_MODE_2DLISSAJOUS 176 // non audio #define FX_MODE_2DFRIZZLES 177 // non audio #define FX_MODE_2DPLASMABALL 178 // non audio -#define FX_MODE_FLOWSTRIPE 179 // should be moved to 125 +#define FX_MODE_FLOWSTRIPE 179 // should be moved to 114 #define FX_MODE_2DHIPHOTIC 180 // non audio #define FX_MODE_2DSINDOTS 181 // non audio #define FX_MODE_2DDNASPIRAL 182 // non audio #define FX_MODE_2DBLACKHOLE 183 // non audio -#define FX_MODE_WAVESINS 184 // should be moved to 126 +#define FX_MODE_WAVESINS 184 // should be moved to 48 #define FX_MODE_ROCKTAVES 185 #define FX_MODE_2DAKEMI 186 //#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects