Old 06-21-2015, 02:26 AM   #1
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 806
Default Under the Hood : Loser/Saturation

I saw a question on Reddit asking about how the Loser/Saturation and Schwa/Softclipper plugins work. Being as how Loser/Saturation is one of my favorite saturation algorithms, I figured I'd try and explain what's going on inside of it, maybe give some insight. Hopefully I'll have a chance to do the same for schwa/softclipper as well.

Minus the graphics code, Loser/saturation looks like this:

Code:
desc:Saturation
slider1:0<0,100,1>Amount (%)

@slider
foo=slider1/200*$pi;
bar = sin(slider1/200*$pi);

@sample
slider1 ? (
spl0 = min(max( sin(max(min(spl0,1),-1)*foo)/bar ,-1) ,1);
spl1 = min(max( sin(max(min(spl1,1),-1)*foo)/bar ,-1) ,1);
);

In short, this is a waveshaping function that uses the sine function. The curve is gentle and the effect is overall fairly subtle. The formula looks like this

f(x) = sin(x * a * 0.5 * pi)/sin(a * 0.5 * pi)

where a is (0,1]

https://www.desmos.com/calculator/qadjuqwva3


As an explanation for why it works (which may or may not make sense)... if the input x is less than or equal to 1, and a is less than or equal to 1, then the output of the sin() will range from 0 to 1, because the sin(0) = 0 and sin(pi/2) = 1. Since sin() takes radians as an input, when we take sin() of an input that is clamped to 1 our maximum value is less than 1. In this function, instead of a maximum input of 1 our maximum input is pi/2. In effect, this normalizes the input. The output would still range from 0 to a maximum of sin(a*pi/2), though, so dividing it by that value (bar), we normalize the output to [-1,1].

Or.... you know, go back and play with the slider and stare blankly at the formula until it makes sense.

One of my favorite aspects of this approach is that it stacks very well.

https://www.desmos.com/calculator/0xxkcccuge

So stepping through the code,

Code:
desc:Saturation
slider1:0<0,100,1>Amount (%)
Description and setting the slider value between 0 and 100.

Code:
@slider
foo=slider1/200*$pi;
bar = sin(slider1/200*$pi);
On startup and when the slider changes we use the slider value to calculate foo, a value between 0 and pi/2. You can see how slider1/200*$pi is another way to say a*0.5*$pi, right? So anyways, foo is our input normalizer and bar is our output normalizer.

Code:
slider1 ? (
spl0 = min(max( sin(max(min(spl0,1),-1)*foo)/bar ,-1) ,1);
Right off the bat, if slider1 is zero, then the plugin passes the audio through. After that we have to decode a nested statement, but once we look at our formula above it rolls apart pretty easily -

Code:
min(max( sin(max(min(spl0,1),-1)*foo)/bar ,-1) ,1);
Here we clamp our input to between -1 and 1.

Code:
min(max( sin(max(min(spl0,1),-1)*foo)/bar ,-1) ,1);
This is the waveshaping formula above.

Code:
min(max(sin(max(min(spl0,1),-1)*foo)/bar,-1),1);
While strictly speaking shouldn't be necessary, for safety's sake we clamp the output as well. We do this step for both channels... and we're done. That's all. The hardest part is grasping what exactly is happening in that formula, it's a very interesting little tidbit.

Note that you can also use an alternate formula to do this, e.g. the classic cubic soft clipper...

f(x) = ((x*a)-((x*a)^3)/3)/(a-(a^3)/3)

a = (0,1]


https://www.desmos.com/calculator/dylerpjqrw


One last way that I can help offer some insight into how the plugin works is to rewrite it, perhaps in a slightly more readable way (without graphics code, sorry). So, here you go!

Code:
Desc:Loser/Saturation rewrite

Slider1:0<0,1,0.01>Saturation

@slider

foo = slider1 * $pi/2;
bar = sin(foo);

@sample

slider1 ? (
  spl0 = max(min(spl0,1),-1);
  spl0 = sin(spl0*foo)/bar;

  spl1 = max(min(spl1,1),-1);
  spl1 = sin(spl1*foo)/bar;
);

And that's it! Hope this has been a useful insight into how this plugin works.
SaulT is offline   Reply With Quote
Old 06-21-2015, 04:00 AM   #2
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,057
Default

Cool, thanks for explaining. The cubic soft clipper formula seems interesting, because it is probably faster.
Tale is offline   Reply With Quote
Old 06-21-2015, 10:28 AM   #3
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,057
Default

Yep, the cubic version is faster, especially when you stack multiple stages:

Code:
desc:Another loser/Saturation rewrite, thanks to SaulT!
slider1:0<0,1,0.01>Saturation
slider2:1<1,10,1>Stages
slider3:1<0,1,1{Sine,Cubic}>Function

// Sine:  f(x) = sin(x * a * 0.5 * pi)/sin(a * 0.5 * pi)
// Cubic: f(x) = ((x*a)-((x*a)^3)/3)/(a-(a^3)/3)

@init

scale = 1.1 / 3;

@slider

a = max(0, min(1, slider1));
a > 0 ? (
  n = max(1, floor(slider2 + 0.5));
  f = slider3 < 0.5;

  gain = 1 - (1 - 1/n) * a;
  div = 1/(f ? sin(a *= 0.5*$pi) : a - a*a*a * scale);
);

@sample

a > 0 ? (
  x0 = max(-1, min(1, spl0));
  x1 = max(-1, min(1, spl1));

  f ? loop(n,
    x0 = sin(x0 * a) * div;
    x1 = sin(x1 * a) * div;
  ) : loop(n,
    x0 *= a; x0 = (x0 - x0*x0*x0 * scale) * div;
    x1 *= a; x1 = (x1 - x1*x1*x1 * scale) * div;
  );

  spl0 = x0 * gain;
  spl1 = x1 * gain;
);
Note that I have slightly scaled the cubic version, so it better matches the output level of the sine version.
Tale is offline   Reply With Quote
Old 06-21-2015, 12:00 PM   #4
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 5,637
Default

Thanks for this explanation. Really cool and informative.
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...
Fabian is offline   Reply With Quote
Old 01-16-2020, 12:27 PM   #5
jpanderson80
Human being with feelings
 
Join Date: Nov 2011
Location: near Memphis, TN
Posts: 494
Default

Just found this. Good stuff Saul, thanks!
__________________
www.andersonmastering.com
jpanderson80 is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 11:44 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.