From bd183caaea263dff19b18a3bac800c995d66329b Mon Sep 17 00:00:00 2001 From: Carlos Ruiz <4998804+DissidentPenguin@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:49:32 +0200 Subject: [PATCH] Fix soft_saturatuion glitches This patch fixes some identified glitches in the soft saturation algorithm (mentioned in the comments of the code itself), and removes a commented-out single return implementation. I removed this comment since it only affects the positive side of the signal, so it's not equivalent to the multi-line algorithm and fixing it would require using some mathematical functions or other tricks that would make it even harder to understand and less performant than the full implementation. In the referred origin for the algorithm it is stated that the threshold is bound between 0 and 1. I added this info in the description of the function. The glitches in this implementation stem from the fact that it is possible for val to evaluate as being equal to thresh while not being greater than 1. Consider val = thresh = 1.0f In this case, none of the branches of the if else statements will be evaluated. Since out is being initialized to 0.0f, the output will be 0. If the neighboring samples are close to 1 (for example near the peak of a sinus wave), this would produce 1 sample equal to zero near samples that are close to 1.0f, therefore creating crackling sounds. There were two options to solve this issue: 1) Change val < thresh to val <= thresh (line 291) 2) Initializing out to in instead of 0.0f (line 288) I preferred the first option since in theory none of them would increase clock cycles needed to calculate the output, and the second makes the algorithm clearer. Something similar happens when val == 1.0f. This case is also not handled in the conditionals, so it will also remain zero Note: I didn't commit the changes done by util/fix_style.sh since none of them are relevant to my changes --- Source/Utility/dsp.h | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Source/Utility/dsp.h b/Source/Utility/dsp.h index 619abdb5..506bb20a 100644 --- a/Source/Utility/dsp.h +++ b/Source/Utility/dsp.h @@ -269,14 +269,14 @@ inline void TestFloat(float &x, float y = 0.f) /** Based on soft saturate from: [musicdsp.org](musicdsp.org/en/latest/Effects/42-soft-saturation.html) +with 0 <= thresh <= 1 Bram de Jong (2002-01-17) -This still needs to be tested/fixed. Definitely does some weird stuff described as: -x < a: +x <= a: f(x) = x x > a: f(x) = a + (x-a)/(1+((x-a)/(1-a))^2) -x > 1: +x >= 1: f(x) = (a + 1)/2 */ inline float soft_saturate(float in, float thresh) @@ -287,11 +287,11 @@ inline float soft_saturate(float in, float thresh) out = 0.f; flip = in < 0.0f; val = flip ? -in : in; - if(val < thresh) + if(val <= thresh) { out = in; } - else if(val > 1.0f) + else if(val >= 1.0f) { out = (thresh + 1.0f) / 2.0f; if(flip) @@ -306,16 +306,8 @@ inline float soft_saturate(float in, float thresh) out *= -1.0f; } return out; - // return val < thresh - // ? val - // : val > 1.0f - // ? (thresh + 1.0f) / 2.0f - // : thresh - // + (val - thresh) - // / (1.0f - // + (((val - thresh) / (1.0f - thresh)) - // * ((val - thresh) / (1.0f - thresh)))); } + constexpr bool is_power2(uint32_t x) { return ((x - 1) & x) == 0;