Skip to content

Conversation

@DissidentPenguin
Copy link

@DissidentPenguin DissidentPenguin commented Oct 15, 2025

Description

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 first option makes the algorithm clearer. Also the second option might produce a discontinuity if the neighbouring samples are being modified by the saturation algorithm.

Something similar happens when val == 1.0f. This case is also not handled in the conditionals, so it will also remain zero

Note 1: I didn't commit the changes done by util/fix_style.sh since none of them are relevant to my changes
Note 2: NaN will be mapped to 0, since according to the IEEE 754 standard, a comparison to NaN will always evaluate to false

Testing

I created a small Qt app to visualize the effects of this code. Project is attached. Just copy all the files to the same folder and open the project file with QtCreator , or create a subdirectory called build and from there run (there needs to be a Qt 5.15 version installed in the system):

qmake ../*.pro
make

I also added a screenshot of the issue taken with the test app

Demos / Example Code

testDaisySoftSaturationFix.zip

Original plot of the problem x = a
Screenshot from 2025-10-16 16-44-45
Fix described in 1)
Screenshot from 2025-10-16 16-45-00
Fix described in 2)
Screenshot from 2025-10-16 16-45-16
Illustrating the issue with the commented -out version
Screenshot from 2025-10-16 16-45-39
Illustrating the issue with x = 1.0f
Screenshot from 2025-10-16 16-53-18
Screenshot from 2025-10-16 16-47-04

@DissidentPenguin DissidentPenguin changed the title Update dsp.h Fix glitches in soft saturation algorithm - dsp.h Oct 15, 2025
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant