View Single Post
Old 01-20-2019, 05:20 AM   #46
sai'ke
Human being with feelings
 
sai'ke's Avatar
 
Join Date: Aug 2009
Location: Germoney
Posts: 330
Default

Code:
desc:Tanh Saturation with anti aliasing
tags: saturation distortion anti-aliased
version: 1.03
author: Joep Vanlier
changelog: 
  + Tweaked epsilon
  + Tweaked epsilon
  + Tweaked epsilon removed factor 0.5
license: MIT

Uses technique from: Parker et al, "REDUCING THE ALIASING OF NONLINEAR WAVESHAPING USING CONTINUOUS-TIME CONVOLUTION",
Proceedings of the 19th International Conference on Digital Audio Effects (DAFx-16), Brno, Czech Republic, September 5–9, 2016
I have only implemented the rect version, since the linear one depends on Li2 and LUTs aren't so fast in JSFX.

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,1,1>Antialias?
slider4:0<0,1,1>Fix DC?

@init
bpos=0;

@slider
preamp      = 10^(slider1/20);
ceiling     = 10^(-slider2/20);
inv_ceiling = 10^(slider2/20);

@block
blah+=samplesblock;

@sample
spl0=spl0;
spl1=spl1;

@sample 
  function F0(x, em2x)
  local()
  global()
  instance()
  (
    x - log(2/(1 + em2x))
  );
  
  function tanh_prec(x, em2x)
  local() 
  global()
  instance()
  (
    (2/(1+em2x))-1
  );
  
  function tanh(x)
  local()
  global()
  instance()
  (
    (2/(1+exp(-2*x)))-1
  );
  
  function antialiased_tanh_rect(x)
  local(eps, em2x, F0_xn, diff)
  global(slider4)
  instance(antialias, F0_xnm1, xnm1)
  (
    em2x      = exp(-2*x);
    F0_xn     = F0(x, em2x);
    
    diff      = ( x - xnm1 );
    eps       = 0.0000000001;
    antialias = (abs(diff) > eps) ? ( F0_xn - F0_xnm1 ) / diff : tanh(.5*(x+xnm1));
    F0_xnm1   = F0_xn;
    xnm1      = x;

    antialias
  );  

  function fix_dc(x)
  local()
  global()
  instance(DC_fixed, prev)
  (
    DC_fixed=0.999*DC_fixed + x - prev;
    prev=x;
    DC_fixed
  );

  spl0 *= preamp;
  spl1 *= preamp;
  
  spl0 *= ceiling;
  spl1 *= ceiling;
  
  slider3 ? (
    spl0 = ch0.antialiased_tanh_rect(spl0);
    spl1 = ch1.antialiased_tanh_rect(spl1);
  ) : (
    spl0 = tanh(spl0);
    spl1 = tanh(spl1);
  );
  
  slider4 ? (
    spl0 = dc0.fix_dc(spl0);
    spl1 = dc1.fix_dc(spl1);
  );
  
  spl0 *= inv_ceiling;
  spl1 *= inv_ceiling;
I had another look at it with fresh eyes this morning, I was pretty tired last night. There's no reason the numerical issue 'fix' should cause such high frequency spiking even if the epsilon is too high. I noticed that I was missing a factor two somewhere in the before last version, so where I was supposed to replace things near the singularity with an approximation of the function, I was actually putting in samples at half the signal strength. Whoops! The epsilon I was using before was actually better. I think this version is best. No DC when silent, none of that annoying grain when not silent. I tried various tones (high and low frequency) as well as actual audio samples and silence. None of them exhibited pathological behavior as far as I could tell. Let me know if you find another failure mode as I am actually interested in using this architecture in more plugs

Also, I didn't need the ext_nodenorm, but thanks for the heads up. It's a good variable to be aware about
__________________
[Tunes] | [Tracker Plugin: Thread|Github|Reapack] | [Routing Plugin: Thread|Github|Reapack] | [Filther: Thread|Github|Reapack]

Last edited by sai'ke; 01-20-2019 at 06:13 AM.
sai'ke is offline   Reply With Quote