Cockos Incorporated Forums Under the Hood : Loser/Saturation
 Register Track Bugs/Feature Requests Search Today's Posts Mark Forums Read

 06-21-2015, 02:26 AM #1 SaulT Human being with feelings   Join Date: Oct 2013 Location: Seattle, WA Posts: 809 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.
 06-21-2015, 04:00 AM #2 Tale Human being with feelings     Join Date: Jul 2008 Location: The Netherlands Posts: 3,084 Cool, thanks for explaining. The cubic soft clipper formula seems interesting, because it is probably faster. __________________ Martinic Kee Bass - Scanner Vibrato - Elka Panther - Lem Echo Music - Tale's JSFX Pack
 06-21-2015, 10:28 AM #3 Tale Human being with feelings     Join Date: Jul 2008 Location: The Netherlands Posts: 3,084 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. __________________ Martinic Kee Bass - Scanner Vibrato - Elka Panther - Lem Echo Music - Tale's JSFX Pack
 06-21-2015, 12:00 PM #4 Fabian Human being with feelings     Join Date: Sep 2008 Location: Sweden Posts: 5,710 Thanks for this explanation. Really cool and informative. __________________ // MVHMF I never always did the right thing, but all I did wasn't wrong...
 01-16-2020, 12:27 PM #5 jpanderson80 Human being with feelings   Join Date: Nov 2011 Location: near Memphis, TN Posts: 515 Just found this. Good stuff Saul, thanks! __________________ www.andersonmastering.com

 Thread Tools Display Modes Hybrid Mode

 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 Rules
 Forum Jump User Control Panel Private Messages Subscriptions Who's Online Search Forums Forums Home REAPER Forums     REAPER General Discussion Forum     newbieland     REAPER Q&A, Tips, Tricks and Howto     Recording Technologies and Techniques     REAPER Compatibility     REAPER Color Themes and Icon Sets     MIDI Hardware, Control Surfaces, and OSC     REAPER Non-English Speaking User Forums         Forum de REAPER en français         Foro de REAPER en Español         Fórum do REAPER em português         Forum di REAPER in italiano         Deutschsprachiges REAPER Userforum         Pyccкоязычный фopyм REAPER     REAPER Bug Reports     REAPER Feature Requests     Dstruct's Casa De Nitpicks     REAPER for Live Use     REAPER for Video Editing/Mangling     REAPER for Ambisonic and 3D positional audio uses     ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum     REAPER for macOS X     REAPER for Linux     REAPER Pre-Release Discussion     REAPER Music/Collaboration Discussion NINJAM Discussion     NINJAM User Discussion     NINJAM Developer Discussion Other Software Discussion     WDL users forum     LICEcap Discussion     OSCII-bot forum     Old Cockos Products Forum

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

 -- Cockos ---- REAPER 5 ---- Reaper 3 ---- Reaper 2 ---- Reaper 1 Contact Us - Çockos Incorporated - Archive - Top