Thread: New Jsfx: AnalogSum View Single Post
10-05-2019, 07:47 PM   #59
ErBird
Human being with feelings

Join Date: Jan 2017
Posts: 190

Quote:
 Originally Posted by sai'ke 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.