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
This commit is contained in:
Frank 2022-08-19 14:36:47 +02:00
parent 753ae51dd5
commit 3c57e2e2b9
2 changed files with 47 additions and 21 deletions

View File

@ -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 = 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. 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; static float FFT_Magnitude = 0.0f;
// These are the input and output vectors. Input vectors receive computed results from FFT. // These are the input and output vectors. Input vectors receive computed results from FFT.
@ -250,9 +250,10 @@ void FFTcode(void * parameter)
#else #else
FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant FFT.MajorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
#endif #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. 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. fftBin[i] = t / 16.0f; // Reduce magnitude. Want end result to be linear and ~4096 max.
} // for() } // for()
@ -288,7 +289,8 @@ void FFTcode(void * parameter)
} else { // noise gate closed } else { // noise gate closed
for (int i=0; i < 16; i++) { 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; if (fftCalc[i] < 4.0f) fftCalc[i] = 0.0f;
} }
} }
@ -307,9 +309,12 @@ void FFTcode(void * parameter)
// smooth results - rise fast, fall slower // smooth results - rise fast, fall slower
if(fftCalc[i] > fftAvg[i]) // rise fast 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] fftAvg[i] = fftCalc[i] *0.75f + 0.25f*fftAvg[i]; // will need approx 2 cycles (50ms) for converging against fftCalc[i]
else // fall slow else { // fall slow
fftAvg[i] = fftCalc[i]*0.17f + 0.83f*fftAvg[i]; // will need approx 5 cycles (150ms) for converging against fftCalc[i] 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 // constrain internal vars - just to be sure
fftCalc[i] = constrain(fftCalc[i], 0.0f, 1023.0f); fftCalc[i] = constrain(fftCalc[i], 0.0f, 1023.0f);
fftAvg[i] = constrain(fftAvg[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. // 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); fftResult[i] = constrain((int)currentResult, 0, 255);
} }
@ -849,7 +859,7 @@ class AudioReactive : public Usermod {
my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f); my_magnitude = fmaxf(receivedPacket->FFT_Magnitude, 0.0f);
FFT_Magnitude = my_magnitude; 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"); //DEBUGSR_PRINTLN("Finished parsing UDP Sync Packet");
haveFreshData = true; haveFreshData = true;
} }
@ -990,7 +1000,7 @@ class AudioReactive : public Usermod {
return; return;
} }
// We cannot wait indefinitely before processing audio data // 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) // 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 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 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 // update WebServer UI
uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment uint8_t knownMode = strip.getFirstSelectedSeg().mode; // 1st selected segment is more appropriate than main segment
if (lastMode != knownMode) { // only execute if mode changes if (lastMode != knownMode) { // only execute if mode changes
@ -1099,6 +1112,7 @@ class AudioReactive : public Usermod {
last_user_inputLevel = new_user_inputLevel; last_user_inputLevel = new_user_inputLevel;
} }
} }
#endif
} }
@ -1162,13 +1176,14 @@ class AudioReactive : public Usermod {
volumeRaw = 0; volumeSmth = 0; volumeRaw = 0; volumeSmth = 0;
sampleAgc = 0; sampleAvg = 0; sampleAgc = 0; sampleAvg = 0;
sampleRaw = 0; rawSampleAgc = 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; multAgc = 1;
// reset FFT data // reset FFT data
memset(fftCalc, 0, sizeof(fftCalc)); memset(fftCalc, 0, sizeof(fftCalc));
memset(fftAvg, 0, sizeof(fftAvg)); memset(fftAvg, 0, sizeof(fftAvg));
memset(fftResult, 0, sizeof(fftResult)); memset(fftResult, 0, sizeof(fftResult));
for(int i=(init?0:1); i<16; i+=2) fftResult[i] = 16; // make a tiny pattern 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) { if (init && FFT_Task) {
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
@ -1241,15 +1256,22 @@ class AudioReactive : public Usermod {
infoArr.add(uiDomString); infoArr.add(uiDomString);
if (enabled) { if (enabled) {
infoArr = user.createNestedArray(F("Input level")); // Input Level Slider
uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({"); if (disableSoundProcessing == false) { // only show slider when audio processing is running
uiDomString += FPSTR(_name); if (soundAgc > 0)
uiDomString += F(":{"); infoArr = user.createNestedArray(F("GEQ Input Level")); // if AGC is on, this slider only affects fftResult[] frequencies
uiDomString += FPSTR(_inputLvl); else
uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value="); infoArr = user.createNestedArray(F("Audio Input Level"));
uiDomString += inputLevel; uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({");
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output> uiDomString += FPSTR(_name);
infoArr.add(uiDomString); uiDomString += F(":{");
uiDomString += FPSTR(_inputLvl);
uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value=");
uiDomString += inputLevel;
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
infoArr.add(uiDomString);
}
//else infoArr.add("<br/> <div>&nbsp;</div>"); // no processing - add empty line
// current Audio input // current Audio input
infoArr = user.createNestedArray(F("Audio Source")); infoArr = user.createNestedArray(F("Audio Source"));
@ -1262,7 +1284,7 @@ class AudioReactive : public Usermod {
else else
infoArr.add(" - idle"); infoArr.add(" - idle");
} else { } else {
infoArr.add(" - no network"); infoArr.add(" - no connection");
} }
} else { } else {
// Analog or I2S digital input // Analog or I2S digital input
@ -1319,6 +1341,8 @@ class AudioReactive : public Usermod {
} }
} else } else
infoArr.add("off"); infoArr.add("off");
if (audioSyncEnabled && !udpSyncConnected) infoArr.add(" <i>(unconnected)</i>");
//if (!udpSyncConnected) infoArr.add(" <i>(unconnected)</i>");
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
infoArr = user.createNestedArray(F("Sampling time")); infoArr = user.createNestedArray(F("Sampling time"));

View File

@ -7093,6 +7093,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
for (int x=0; x < cols; x++) { for (int x=0; x < cols; x++) {
uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1); uint8_t band = map(x, 0, cols-1, 0, NUM_BANDS - 1);
band = constrain(band, 0, 15);
uint16_t colorIndex = band * 17; uint16_t colorIndex = band * 17;
uint16_t barHeight = map(fftResult[band], 0, 255, 0, rows); // do not subtract -1 from rows here 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 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 // display values of
int b = 0; int b = 0;
for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) { for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
int hue = fftResult[band]; int hue = fftResult[band % 16];
int v = map(fftResult[band], 0, 255, 10, 255); int v = map(fftResult[band % 16], 0, 255, 10, 255);
for (int w = 0; w < barWidth; w++) { for (int w = 0; w < barWidth; w++) {
int xpos = (barWidth * b) + w; int xpos = (barWidth * b) + w;
SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v)); SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
@ -7269,6 +7270,7 @@ uint16_t mode_2DAkemi(void) {
if (um_data && fftResult) { if (um_data && fftResult) {
for (int x=0; x < cols/8; x++) { for (int x=0; x < cols/8; x++) {
uint16_t band = x * cols/8; uint16_t band = x * cols/8;
band = constrain(band, 0, 15);
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); 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); CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);