From 3c57e2e2b98d1eefbb020d840a8bfd17fd3c4a8d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Fri, 19 Aug 2022 14:36:47 +0200 Subject: [PATCH] AR: special gain for GEO, some bugfixes andparameter tinkering - new feature: "Input Level" (info page) can be used as global "GEQ gain" - only when AGC is ON (was already possible when AGC=off) - some parameter tweaking in FFT function - hidden feature: FFT decay is slower when setting a high "dynamics Limiter Fall time" (steps: <1000, <2000, <3000, >3000) - FFT_MajorPeak default 1.0f (as log(0.0) is invalid) - FX.cppp: ensure that fftResult[] is always used inside array bounds --- usermods/audioreactive/audio_reactive.h | 62 +++++++++++++++++-------- wled00/FX.cpp | 6 ++- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index d33fd218..b2b66b08 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -113,7 +113,7 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample 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. -static float FFT_MajorPeak = 0.0f; +static float FFT_MajorPeak = 1.0f; static float FFT_Magnitude = 0.0f; // These are the input and output vectors. Input vectors receive computed results from FFT. @@ -250,9 +250,10 @@ void FFTcode(void * parameter) #else FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant #endif + FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 10240.0f); for (int i = 0; i < samplesFFT_2; i++) { // Values for bins 0 and 1 are WAY too large. Might as well start at 3. - float t = fabs(vReal[i]); // just to be sure - values in fft bins should be positive any way + float t = fabsf(vReal[i]); // just to be sure - values in fft bins should be positive any way fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max. } // for() @@ -288,7 +289,8 @@ void FFTcode(void * parameter) } else { // noise gate closed for (int i=0; i < 16; i++) { - fftCalc[i] *= 0.82f; // decay to zero + //fftCalc[i] *= 0.82f; // decay to zero + fftCalc[i] *= 0.85f; // decay to zero if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f; } } @@ -307,9 +309,12 @@ void FFTcode(void * parameter) // smooth results - rise fast, fall slower if(fftCalc[i] > fftAvg[i]) // rise fast fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i] - else // fall slow - fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // will need approx 5 cycles (150ms) for converging against fftCalc[i] - + else { // fall slow + if (decayTime < 1000) fftAvg[i] = fftCalc[i]*0.22f + 0.78f*fftAvg[i]; // approx 5 cycles (225ms) for falling to zero + else if (decayTime < 2000) fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // default - approx 9 cycles (225ms) for falling to zero + else if (decayTime < 3000) fftAvg[i] = fftCalc[i]*0.14f + 0.86f*fftAvg[i]; // approx 14 cycles (350ms) for falling to zero + else fftAvg[i] = fftCalc[i]*0.1f + 0.9f*fftAvg[i]; // approx 20 cycles (500ms) for falling to zero + } // constrain internal vars - just to be sure fftCalc[i] = constrain(fftCalc[i], 0.0f, 1023.0f); fftAvg[i] = constrain(fftAvg[i], 0.0f, 1023.0f); @@ -344,6 +349,11 @@ void FFTcode(void * parameter) } // Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely. + if (soundAgc > 0) { // apply extra "GEQ Gain" if set by user + float post_gain = (float)inputLevel/128.0f; + if (post_gain < 1.0f) post_gain = ((post_gain -1.0f) * 0.8f) +1.0f; + currentResult *= post_gain; + } fftResult[i] = constrain((int)currentResult, 0, 255); } @@ -849,7 +859,7 @@ class AudioReactive : public Usermod { my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f); FFT_Magnitude = my_magnitude; - FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 0.0f); + FFT_MajorPeak = fmaxf(receivedPacket->FFT_MajorPeak, 1.0f); //DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet"); haveFreshData = true; } @@ -990,7 +1000,7 @@ class AudioReactive : public Usermod { return; } // We cannot wait indefinitely before processing audio data - if (strip.isUpdating() && (millis() - lastUMRun < 1)) return; // be nice, but not too nice + if (strip.isUpdating() && (millis() - lastUMRun < 2)) return; // be nice, but not too nice // suspend local sound processing when "real time mode" is active (E131, UDP, ADALIGHT, ARTNET) if ( (realtimeOverride == REALTIME_OVERRIDE_NONE) // please odd other orrides here if needed @@ -1059,6 +1069,9 @@ class AudioReactive : public Usermod { limitSampleDynamics(); // optional - makes volumeSmth very smooth and fluent +#if 0 + /* currently this is _not_ working. Code relies on "musical note" symbol as second char of the effect name */ + #error I told you its not working right now // update WebServer UI uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment if (lastMode != knownMode) { // only execute if mode changes @@ -1099,6 +1112,7 @@ class AudioReactive : public Usermod { last_user_inputLevel = new_user_inputLevel; } } +#endif } @@ -1162,13 +1176,14 @@ class AudioReactive : public Usermod { volumeRaw = 0; volumeSmth = 0; sampleAgc = 0; sampleAvg = 0; sampleRaw = 0; rawSampleAgc = 0; - my_magnitude = 0; FFT_Magnitude = 0; FFT_MajorPeak = 0; + my_magnitude = 0; FFT_Magnitude = 0; FFT_MajorPeak = 1; multAgc = 1; // reset FFT data memset(fftCalc, 0, sizeof(fftCalc)); memset(fftAvg, 0, sizeof(fftAvg)); memset(fftResult, 0, sizeof(fftResult)); for(int i=(init?0:1); i<16; i+=2) fftResult[i] = 16; // make a tiny pattern + inputLevel = 128; // resset level slider to default if (init && FFT_Task) { vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash @@ -1241,15 +1256,22 @@ class AudioReactive : public Usermod { infoArr.add(uiDomString); if (enabled) { - infoArr = user.createNestedArray(F("Input level")); - uiDomString = F("
"); // - infoArr.add(uiDomString); + // Input Level Slider + if (disableSoundProcessing == false) { // only show slider when audio processing is running + if (soundAgc > 0) + infoArr = user.createNestedArray(F("GEQ Input Level")); // if AGC is on, this slider only affects fftResult[] frequencies + else + infoArr = user.createNestedArray(F("Audio Input Level")); + uiDomString = F("
"); // + infoArr.add(uiDomString); + } + //else infoArr.add("
 
"); // no processing - add empty line // current Audio input infoArr = user.createNestedArray(F("Audio Source")); @@ -1262,7 +1284,7 @@ class AudioReactive : public Usermod { else infoArr.add(" - idle"); } else { - infoArr.add(" - no network"); + infoArr.add(" - no connection"); } } else { // Analog or I2S digital input @@ -1319,6 +1341,8 @@ class AudioReactive : public Usermod { } } else infoArr.add("off"); + if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" (unconnected)"); + //if (!udpSyncConnected) infoArr.add(" (unconnected)"); #ifdef WLED_DEBUG infoArr = user.createNestedArray(F("Sampling time")); diff --git a/wled00/FX.cpp b/wled00/FX.cpp index b5a9ab79..2ba78c89 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7093,6 +7093,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. for (int x=0; x < cols; x++) { uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); + band = constrain(band, 0, 15); uint16_t colorIndex = band * 17; uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here if (barHeight > previousBarHeight[x]) previousBarHeight[x] = barHeight; //drive the peak up @@ -7153,8 +7154,8 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil // display values of int b = 0; for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) { - int hue = fftResult[band]; - int v = map(fftResult[band], 0, 255, 10, 255); + int hue = fftResult[band % 16]; + int v = map(fftResult[band % 16], 0, 255, 10, 255); for (int w = 0; w < barWidth; w++) { int xpos = (barWidth * b) + w; SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v)); @@ -7269,6 +7270,7 @@ uint16_t mode_2DAkemi(void) { if (um_data && fftResult) { for (int x=0; x < cols/8; x++) { uint16_t band = x * cols/8; + band = constrain(band, 0, 15); uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);