Sound Sync: prevent GEQ hickups and stalls

UDP transfers have some delays (measured between 2ms and 400ms). As consequence, FFT results sometimes "freeze" for a short time as no new data is arriving.
To hide these freezes, we apply the same "dynamics limiter" method as for volumeSmth.
This commit is contained in:
Frank
2023-11-29 12:25:22 +01:00
parent e7c34e9e47
commit 38352c9d20
2 changed files with 38 additions and 4 deletions

View File

@@ -116,7 +116,9 @@ static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of pe
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0}; // Our calculated freq. channel result table to be used by effects
static float fftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // Try and normalize fftBin values to a max of 4096, so that 4096/16 = 256. (also used by dynamics limiter)
static float fftAvg[NUM_GEQ_CHANNELS] = {0.0f}; // Calculated frequency channel results, with smoothing (used if dynamics limiter is ON)
// TODO: probably best not used by receive nodes
static float agcSensitivity = 128; // AGC sensitivity estimation, based on agc gain (multAgc). calculated by getSensitivity(). range 0..255
@@ -285,8 +287,6 @@ static float sampleTime = 0; // avg (blocked) time for reading I2S sample
// FFT Task variables (filtering and post-processing)
static float lastFftCalc[NUM_GEQ_CHANNELS] = {0.0f}; // backup of last FFT channels (before postprocessing)
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)
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
// audio source parameters and constant
@@ -1435,6 +1435,39 @@ class AudioReactive : public Usermod {
last_time = millis();
}
// MM experimental: limiter to smooth GEQ samples (only for UDP sound receiver mode)
// target value (if gotNewSample) : fftCalc
// last filtered value: fftAvg
void limitGEQDynamics(bool gotNewSample) {
constexpr float bigChange = 202; // just a representative number - a large, expected sample value
constexpr float smooth = 0.8f; // a bit of filtering
static unsigned long last_time = 0;
if (limiterOn == false) return;
if (gotNewSample) { // take new FFT samples as target values
for(unsigned i=0; i < NUM_GEQ_CHANNELS; i++) {
fftCalc[i] = fftResult[i];
fftResult[i] = fftAvg[i];
}
}
long delta_time = millis() - last_time;
delta_time = constrain(delta_time , 1, 1000); // below 1ms -> 1ms; above 1sec -> silly lil hick-up
float maxAttack = (attackTime <= 0) ? 255.0f : (bigChange * float(delta_time) / float(attackTime));
float maxDecay = (decayTime <= 0) ? -255.0f : (-bigChange * float(delta_time) / float(decayTime));
for(unsigned i=0; i < NUM_GEQ_CHANNELS; i++) {
float deltaSample = fftCalc[i] - fftAvg[i];
if (deltaSample > maxAttack) deltaSample = maxAttack;
if (deltaSample < maxDecay) deltaSample = maxDecay;
deltaSample = deltaSample * smooth;
fftAvg[i] = fmaxf(0.0f, fminf(255.0f, fftAvg[i] + deltaSample));
fftResult[i] = fftAvg[i];
}
last_time = millis();
}
//////////////////////
// UDP Sound Sync //
//////////////////////
@@ -2008,6 +2041,7 @@ class AudioReactive : public Usermod {
if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample
else volumeSmth = syncVolumeSmth; // restore originally received sample for next run of dynamics limiter
limitSampleDynamics(); // run dynamics limiter on received volumeSmth, to hide jumps and hickups
limitGEQDynamics(have_new_sample); // WLEDMM experimental: smooth FFT (GEQ) samples
} else {
receivedFormat = 0;
}

View File

@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2311272
#define VERSION 2311290
//WLEDMM + Moustachauve/Wled-Native
// You can define custom product info from build flags.