
// Softwall Limiting
//
// by Sault, 11-13-13
//
// Uses a succession of boosts + soft-clipping to create what can be a rather
// transparent limiter/volume boost. Or it can make fuzz.
//
// Built largely around schwa's Soft Clipper code.
//
// Not optimized, and can be pretty CPU hungry when 10+ instances are active.
//


desc:Softwall Limiting

slider1:-9<-60,0,1>Threshold (dB)
slider2:0<-30,0,0.1>Ceiling (dB)
slider3:5<0,30,1>Drive (dB)
slider4:10<1,20,1>Number of Times Run
slider5:5<0,500,1>RMS window (ms)
slider6:50<0,3000,1>release window (ms)
slider7:0<0,1,1{off,yes}>Side-chaining 3&4
slider8:0<0,15,0.1>Sensitivity (dB)
slider9:1<0,4,1{off,0 dB,-0.2 dB,-1 dB,-2 dB}>Limiting

@init

ceil_array = 0;
ceil_array[0] = 0;
ceil_array[1] = 0;
ceil_array[2] = -0.2;
ceil_array[3] = -1;
ceil_array[4] = -2;

function RMS(input, ms)
  instance(out, rms_s, coeff)
(
  coeff = exp(-1/(ms / 1000 * srate)); // usually 0.999..
  rms_s = (rms_s * coeff) + ((1 - coeff) * input * input);
  out = sqrt(rms_s);
  out;
);

function schwa_clip_init(thresh_dB, ceil_dB, boost_dB)
 instance(out,a,b,amp_dB)
(
 amp_dB = 8.6562;
 a = 1.017;
 b = -0.025;
 this.thresh_dB = thresh_dB;
 this.ceil_dB = ceil_dB;
 this.boost_dB = boost_dB;
);


function schwa_clip(input)
 instance(out, dB0, over_dB)
(
 dB0 = this.amp_dB * log(abs(input)) + this.boost_dB;

(dB0 > this.thresh_dB) ? (
  over_dB = dB0 - this.thresh_dB;
  over_dB = this.a * over_dB + this.b * over_dB * over_dB;
  dB0 = min(this.thresh_dB + over_dB, this.ceil_dB);
  );

 out = exp(db0/this.amp_dB) * sign(input);
 out;
);

// initialize variables

saturate.schwa_clip_init(slider1,slider2,0);
limit.schwa_clip_init(-3,ceil_array[slider9],0);
detector.RMS(0,slider5);
sensitivity = 2^(slider8/6);
release = exp(-1/(slider6 * 0.001 * srate));

@slider

// initialize variables

saturate.schwa_clip_init(slider1,slider2,0);
limit.schwa_clip_init(-3,ceil_array[slider9],0);
detector.RMS(0,slider5);
sensitivity = 2^(slider8/6);
release = exp(-1/(slider6 * 0.001 * srate));


@sample

 t = sqrt((spl0*spl0 + spl1*spl1)/2);
 slider7 ? t = sqrt((spl2*spl2 + spl3*spl3)/2); 

 env_signal = detector.RMS(t,slider6);
 
 (env_signal < env_signalo) ? env_signal *= release;

 env_signalo = env_signal; 

 saturate.boost_dB = slider3 / slider4 * env_signal * sensitivity;

loop(slider4,
 spl0 = saturate.schwa_clip(spl0);
 spl1 = saturate.schwa_clip(spl1);
 );

slider9 ? (
 spl0 = limit.schwa_clip(spl0);
 spl1 = limit.schwa_clip(spl1);
 );
