From d92a93f1d5674a3b079286f39448c47fd260ccc2 Mon Sep 17 00:00:00 2001
From: Frank <91616163+softhack007@users.noreply.github.com>
Date: Wed, 17 Aug 2022 13:40:54 +0200
Subject: [PATCH] AR: added dynamics limiter usermod cfg options
- On/Off controls the complete feature
- Rise Time and Fall Time are the minimum times (in milliseconds) for "volume" to go from 0% to 80% and back.
- when "On" we also use some filtering to smooth FFTResults[]. Rise and Fall Times do not affect Frequency reactive effects otherwise.
---
usermods/audioreactive/audio_reactive.h | 44 +++++++++++++++++--------
1 file changed, 30 insertions(+), 14 deletions(-)
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 67f0d862..c7c7e5f8 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -27,9 +27,6 @@
// #define NO_MIC_LOGGER // exclude MIC_LOGGER from SR_DEBUG
// hackers corner
-#if !defined(SOUND_DYNAMICS_LIMITER) && !defined(NO_SOUND_DYNAMICS_LIMITER)
-#define SOUND_DYNAMICS_LIMITER // experimental: define to enable a dynamics limiter that avoids "sudden flashes" at onsets. Makes some effects look more "smooth and fluent"
-#endif
#ifdef SR_DEBUG
#define DEBUGSR_PRINT(x) Serial.print(x)
@@ -64,8 +61,9 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - non
static uint8_t audioSyncEnabled = 0; // bit field: bit 0 - send, bit 1 - receive (config value)
// user settable parameters for limitSoundDynamics()
-static int attackTime = 80; // int: attack time in milliseconds. Default 0.1sec
-static int decayTime = 1400; // int: decay time in milliseconds. Default 1.4sec
+static bool limiterOn = true; // bool: enable / disable dynamics limiter
+static uint16_t attackTime = 80; // int: attack time in milliseconds. Default 0.08sec
+static uint16_t decayTime = 1400; // int: decay time in milliseconds. Default 1.40sec
//
// AGC presets
@@ -289,11 +287,10 @@ void FFTcode(void * parameter)
//fftAvg[i] = fftCalc[i]*0.05f + 0.95f*fftAvg[i]; // will need approx 10 cycles (250ms) for converging against fftCalc[i]
// Now, let's dump it all into fftResult. Need to do this, otherwise other routines might grab fftResult values prematurely.
-#if !defined(SOUND_DYNAMICS_LIMITER)
- fftResult[i] = constrain((int)fftCalc[i], 0, 254);
-#else
- fftResult[i] = constrain((int)fftAvg[i], 0, 254);
-#endif
+ if(limiterOn == true)
+ fftResult[i] = constrain((int)fftAvg[i], 0, 254);
+ else
+ fftResult[i] = constrain((int)fftCalc[i], 0, 254);
}
#ifdef WLED_DEBUG
@@ -695,11 +692,12 @@ class AudioReactive : public Usermod {
*/
// effects: Gravimeter, Gravcenter, Gravcentric, Noisefire, Plasmoid, Freqpixels, Freqwave, Gravfreq, (2D Swirl, 2D Waverly)
void limitSampleDynamics(void) {
- #ifdef SOUND_DYNAMICS_LIMITER
const float bigChange = 196; // just a representative number - a large, expected sample value
static unsigned long last_time = 0;
static float last_volumeSmth = 0.0f;
+ if(limiterOn == false) return;
+
long delta_time = millis() - last_time;
delta_time = constrain(delta_time , 1, 1000); // below 1ms -> 1ms; above 1sec -> sily lil hick-up
float deltaSample = volumeSmth - last_volumeSmth;
@@ -717,7 +715,6 @@ class AudioReactive : public Usermod {
last_volumeSmth = volumeSmth;
last_time = millis();
- #endif
}
@@ -1080,10 +1077,10 @@ class AudioReactive : public Usermod {
// peak sample from last 5 seconds
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
sampleMaxTimer = millis();
- maxSample5sec = (0.25 * maxSample5sec) + 0.75 *((soundAgc) ? sampleAgc : sampleAvg); // reset, with some smoothing
+ maxSample5sec = (0.15 * maxSample5sec) + 0.85 *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
if (sampleAvg < 1) maxSample5sec = 0; // noise gate
} else {
- maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? sampleAgc : sampleAvg); // follow maximum
+ if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
}
//UDP Microphone Sync - transmit mode
if ((audioSyncEnabled & 0x01) && (millis() - lastTime > 20)) {
@@ -1370,6 +1367,11 @@ class AudioReactive : public Usermod {
cfg[F("gain")] = sampleGain;
cfg[F("AGC")] = soundAgc;
+ JsonObject dynLim = top.createNestedObject("dynamics");
+ dynLim[F("Limiter")] = limiterOn;
+ dynLim[F("Rise")] = attackTime;
+ dynLim[F("Fall")] = decayTime;
+
JsonObject sync = top.createNestedObject("sync");
sync[F("port")] = audioSyncPort;
sync[F("mode")] = audioSyncEnabled;
@@ -1412,6 +1414,10 @@ class AudioReactive : public Usermod {
configComplete &= getJsonValue(top["cfg"][F("gain")], sampleGain);
configComplete &= getJsonValue(top["cfg"][F("AGC")], soundAgc);
+ configComplete &= getJsonValue(top["dynamics"][F("Limiter")], limiterOn);
+ configComplete &= getJsonValue(top["dynamics"][F("Rise")], attackTime);
+ configComplete &= getJsonValue(top["dynamics"][F("Fall")], decayTime);
+
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
@@ -1433,6 +1439,16 @@ class AudioReactive : public Usermod {
oappend(SET_F("addOption(dd,'Normal',1);"));
oappend(SET_F("addOption(dd,'Vivid',2);"));
oappend(SET_F("addOption(dd,'Lazy',3);"));
+
+ oappend(SET_F("dd=addDropdown('AudioReactive','dynamics:Limiter');"));
+ oappend(SET_F("addOption(dd,'Off',0);"));
+ oappend(SET_F("addOption(dd,'On',1);"));
+ oappend(SET_F("addInfo('AudioReactive:dynamics:Limiter',0,' Limiter On ');")); // 0 is field type, 1 is actual field
+ //oappend(SET_F("addInfo('AudioReactive:dynamics:Rise',0,'min. ');"));
+ oappend(SET_F("addInfo('AudioReactive:dynamics:Rise',1,' ms
(volume reactive FX only)');"));
+ //oappend(SET_F("addInfo('AudioReactive:dynamics:Fall',0,'min. ');"));
+ oappend(SET_F("addInfo('AudioReactive:dynamics:Fall',1,' ms
(volume reactive FX only)');"));
+
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
oappend(SET_F("addOption(dd,'Off',0);"));
oappend(SET_F("addOption(dd,'Send',1);"));