Soft Clipper with Adjustable Threshold

Hi. I know there are at least a couple of other good soft clippers available here, so I want to contribute my current take on the concept.
Adjustable Soft Clipper.ny (1.35 KB)
The core of this soft clipper is
(mult s (diff 1.0 (mult 0.5 (s-abs s))))
a simple quadratic that I think has been used at least once to good effect in a common nyquist plugin before, and which I have used raw on a lot of sources in the past few years.

Although it’s mathematically as gentle a soft clipper as is possible, and though it doesn’t damage lower-level material too much while taming peaks, over time I grew more and more frustrated with the fact that I had to sacrifice any small degree of clarity on the low-level material at all while leveling the high-level audio.

So, this allows you to leave the lower-level material completely unmolested while clipping peaks as gently as possible above the selectable threshold.

My frustration with variable-threshold soft clippers in the past has been that the knee is to some extent arbitrary and therefore not necessarily maximally-soft. This plugin guarantees maximum softness for any given threshold.

There’s some apparently-unnecessary complexity built into this, but it’s there to leave room to provide controls for asymmetrical clipping at some point. Also, there’s no support for stereo source material on this right now.

Any comments and suggestions welcome. Thanks for taking a look.

Isn’t that what Effect > Leveller did? I know it’s a standing joke that Leveller is used to create “air traffic controller” distorted voices, but in its more gentle form, it just rounded off the transfer curve so aggressive sound didn’t increase as fast as it wanted. Koz

I’m not familiar with Leveller, but running it through its paces just now it looks like a premium example of the arbitrary-knee/nasty kink issue I resolved with this submission. Can’t get it to look anywhere near as nice as mine.

I’ve restructured your code a bit so that I can see what it is doing (also added support for stereo tracks). Perhaps you can confirm that this produces the same effect as your code (I think it does):

;nyquist plug-in
;version 1
;type process
;name "Adjustable Soft Clipper"
;action "Clipping Softly..."
;control thresh "threshold" real "" .25 0 1


(defun softclip (sig)
  (sum sig
       (mult -0.5 (top sig)(top sig))
       (mult 0.5 (bottom sig)(bottom sig))))

;; positive peaks
(defun top (sig)
  (diff (s-max thresh sig)
        thresh))

;; negative peaks
(defun bottom (sig)
  (sum (s-min (- thresh) sig)
       thresh))

(multichan-expand #'softclip s)

I think the phrase “maximum softness” could be open to some discussion. How does this relate to soft clipping signals that are over 0 dB? (probably an important feature for a soft clipping effect.)

An alternative approach to the same problem:

;control thresh "threshold" real "" .25 0 1

(setf table (abs-env 
  (sim (pwlv -1 2 1 2.5 1)
       (mult 0.5 
        (pwlv (- 1 thresh) (- 1 thresh) 0 (1+ thresh) 0 2 (1- thresh) 2.5 (1- thresh))
        (pwlv (- 1 thresh) (- 1 thresh) 0 (1+ thresh) 0 2 (- 1 thresh) 2.5 (- 1 thresh))))))
        
(multichan-expand #'shape s table 1)

Working out how to produce an appropriate look-up table (‘table’) is a bit of a headache, but the beauty of this approach is that the actual processing is done by the one command “SHAPE”, which can be reused for any look-up table you choose.

Steve, thanks a lot for taking the time to slog through that. Also, thanks for reworking it and providing the alternative. I’m slow with this stuff so will have to take some time to examine.

Agreed about the “maximum softness” claim, and I believe this formula freaks out above 0dB and provides nonsensical results. Not sure how I’d want to deal with that yet…

Not really “nonsense” but not what we’d want for soft clipping. You need to ensure that the transfer function never goes negative, otherwise peaks become “inverted”.

Putting it another way, if “x” is the height above the threshold, then your quadratic is (I think) y = x - (0.5 * x²)
When x < 1 there’s no problem because y increases for increasing values of x, but above x = 1, y decreases.

Steve, yes that’s true, I see that wave-folding as nonsense in that you can get the resulting waveforms to fly off rapidly toward infinite values if not careful.
Using some clues from your listing, I condensed my script and added a second control for adjusting the degree of smooshing. (At hight thresholds, without a pre-gain or ratio control, there was almost no clipping at alland repeated applications were required.) Since that introduced a potential divide-by-zero situation, I had to limit th range of the threshold to below 1, which is annoying and inelegant, but since there’s no reason to run this at a fraction of a decibel of reduction anyway…I guess it’s fine for now.

;nyquist plug-in
;version 1
;type process
;name "Adjustable Soft Clipper"
;action "Clipping..."
;control thresh "Threshold" real "" .25 0 .999
;control rat "Ratio" real "" 1 0 1

(defun residue (s) (diff s (clip s thresh)))

(defun softClip (s)
	(sum
		(clip s thresh) 
  		(mult
			(residue s)
			(diff 1
				(mult
					0.5
					(s-abs
						(mult
							rat
							(/ 1 (diff 1 thresh))
							(residue s)
						)
					)
				)
			)
		)
	)
)

(softClip s)

Your listing using s-min and s-max appears to null almost exactly with mine, although the two appear to do something different deep down low because if I amplify the nulled results all the way up to the last bit there’s static.