View Single Post
Old 10-05-2019, 07:47 PM   #59
ErBird
Human being with feelings
 
Join Date: Jan 2017
Posts: 90
Default

Quote:
Originally Posted by sai'ke View Post
Thanks for sharing it by the way!
No problem. You started the AA ball rolling after all. I wanted to make sure you had the fix.

Anyway, I tried for ages to no avail to get the linear kernel working for tanh. I didn't resort to a lookup table, but tried, with no concern for CPU, to use the power series for Li2 with some abs() and sign() applied accordingly. Alas, it doesn't work.

I did, however, get it working for x/sqrt(1+x^2). Not as nice for audio as tanh, but a good proof that the linear kernel can work. In the end, I think the improvement is not enough to warrant the extra complexity over the rect version. Also it seems more finicky and needs a much larger epsilon and sometimes clicks and pops when fed an already saturated signal. Because of that, I think the latest tanh with AA is the best possible implementation right now.

Code:
desc:Algebraic Saturation with Anti-Aliasing

Author: Erich M. Burg
Adapted from "Tanh Saturation with anti aliasing" JS by Joep Vanlier (Sai'ke)

Uses technique from: "REDUCING THE ALIASING OF NONLINEAR WAVESHAPING USING CONTINUOUS-TIME CONVOLUTION", Parker et al,
Proceedings of the 19th International Conference on Digital Audio Effects (DAFx-16), Brno, Czech Republic, September 5–9, 2016

in_pin:left input
in_pin:right input
out_pin:left output
out_pin:right output

slider1:0<-6,24,1>Gain (dB)
slider2:0<-18,0,1>Ceiling (dB)
slider3:1<0,2,1{No AA,Rectangular Kernel,Linear Kernel}>Mode

@slider

  gain        = 10^(slider1/20);
  ceiling     = 10^(-slider2/20);
  inv_ceiling = 10^(slider2/20);

@sample

  function f(x)
  (
    x/sqrt(1 + x^2);
  );

  function F0(x)
  (
    sqrt(1 + x^2);
  );
  
  function F1(x)
  (
    0.5*(x*sqrt(1 + x^2) - log(sqrt(1 + x^2) + x));
  );

  function antialiased_algebraic_sat_rect(x)
  local(eps, F0_xn, out)
  instance(xnm1, F0_xnm1)
  (
    F0_xn   = F0(x);
    eps     = 0.0000000001;
    out     = eps < abs(x - xnm1) ? (F0_xn - F0_xnm1)/(x - xnm1) : f(0.5*(x+xnm1));
    F0_xnm1 = F0_xn;
    xnm1    = x;
    out;
  );
  
  function antialiased_algebraic_sat_linear(x)
  local(eps, F0_xn, F1_xn, out1, out2, out)
  instance(xnm1, xnm2, F0_xnm1, F0_xnm2, F1_xnm1, F1_xnm2)
  (
    F0_xn   = F0(x);
    F1_xn   = sign(x)*F1(abs(x));
    eps     = 0.0001;
    
    out1    = eps < abs(x - xnm1) ? 
              (
                (x*(F0_xn - F0_xnm1) - (F1_xn - F1_xnm1))/(x - xnm1)^2;
              ):(
                0.5*f((x + 2*xnm1)/3);
              );
    
    out2    = eps < abs(xnm1 - xnm2) ?
              (
                (xnm2*(F0_xnm2 - F0_xnm1) - (F1_xnm2 - F1_xnm1))/(xnm2 - xnm1)^2;
              ):(
                0.5*f((xnm2 + 2*xnm1)/3);
              );
    
    out     = (out1 + out2);
    F0_xnm2 = F0_xnm1;
    F0_xnm1 = F0_xn;
    F1_xnm2 = F1_xnm1;
    F1_xnm1 = F1_xn;
    xnm2    = xnm1;
    xnm1    = x;
    out;
  );

  spl0 *= gain;
  spl1 *= gain;
  
  spl0 *= ceiling;
  spl1 *= ceiling;

  slider3 == 0 ?
  (
    spl0 = spl0/sqrt(1+spl0^2);
    spl1 = spl1/sqrt(1+spl1^2);
  ):
  slider3 == 1 ?
  (
    spl0 = ch0.antialiased_algebraic_sat_rect(spl0);
    spl1 = ch1.antialiased_algebraic_sat_rect(spl1);
  ):
  slider3 == 2 ?
  (
    spl0 = ch0.antialiased_algebraic_sat_linear(spl0);
    spl1 = ch1.antialiased_algebraic_sat_linear(spl1);  
  );
  
  spl0 *= inv_ceiling;
  spl1 *= inv_ceiling;

Last edited by ErBird; 10-05-2019 at 07:53 PM.
ErBird is offline   Reply With Quote