 /**
 * JSFX Name: ClassicBus
 * About: Analog Desk Bus Emulation
 * Author: Giovanni Gramegna
 * Licence: GPL v3
 * ReaAnalog
 * Version: 1.0 
 */

// (C) 2020-2021, Giovanni Gramegna

// NO WARRANTY IS GRANTED. THIS PLUG-IN IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
// WARRANTY OF ANY KIND. NO LIABILITY IS GRANTED, INCLUDING, BUT NOT LIMITED TO,
// ANY DIRECT OR INDIRECT,  SPECIAL,  INCIDENTAL OR CONSEQUENTIAL DAMAGE ARISING
// OUT OF  THE  USE  OR INABILITY  TO  USE  THIS PLUG-IN,  COMPUTER FAILTURE  OF
// MALFUNCTION INCLUDED.  THE USE OF THE SOURCE CODE,  EITHER  PARTIALLY  OR  IN
// TOTAL, IS ONLY GRANTED,  IF USED IN THE SENSE OF THE AUTHOR'S INTENTION,  AND
// USED WITH ACKNOWLEDGEMENT OF THE AUTHOR. FURTHERMORE IS THIS PLUG-IN A  THIRD
// PARTY CONTRIBUTION,  EVEN IF INCLUDED IN REAPER(TM),  COCKOS INCORPORATED  OR
// ITS AFFILIATES HAVE NOTHING TO DO WITH IT.  LAST BUT NOT LEAST, BY USING THIS
// PLUG-IN YOU RELINQUISH YOUR CLAIM TO SUE IT'S AUTHOR, AS WELL AS THE CLAIM TO
// ENTRUST SOMEBODY ELSE WITH DOING SO.

//tags: saturation glue tonal shaping


desc:ClassicBus

options:no_meter

slider1:0<0,1,{-12dB,+0dB,+12dB}>Input Trim:

@init

r1 = rand()*2 -1;
r2 = rand()*2 -1;

function bqd(xn)
  instance(a0,a1,a2,b1,b2,z1,z2,xn,xn_1,xn_2,yn,yn_1,yn_2)
(
    yn = xn * a0 + z1;
    z1 = xn * a1 + z2 - b1 * yn;
    z2 = xn * a2 - b2 * yn;
    yn;
);


function hpSet(F,Q)
  instance(a0,a1,a2,b1,b2,xn,xn_1,xn_2,yn,yn_1,yn_2,V,K,norm,F,Q,peakGain)
(
  F ?
  (
    V = 10^(abs(peakGain)/ 20.0);
    K = tan($pi * F/srate);
    norm = 1 / (1 + K / Q + K * K);
    a0 = 1 * norm;
    a1 = -2 * a0;
    a2 = a0;
    b1 = 2 * (K * K - 1) * norm;
    b2 = (1 - K / Q + K * K) * norm;
  ):(a0 = 1; a1 = a2 = b1 = b2 = 0;)
);


function lpSet(F,Q)
  instance(a0,a1,a2,b1,b2,c0,d0,xn,xn_1,xn_2,yn,yn_1,yn_2,V,K,norm,F,Q,peakGain)
(
  F ?
  (
    V = 10^(abs(peakGain)/ 20.0);
    K = tan($pi * F/srate);
    norm = 1 / (1 + K / Q + K * K);
    a0 = K * K * norm;
    a1 = 2 * a0;
    a2 = a0;
    b1 = 2 * (K * K - 1) * norm;
    b2 = (1 - K / Q + K * K) * norm;
  ):(a0 = 1; a1 = a2 = b1 = b2 = 0;)
);

function lsSet(F,peakGain)
  instance(a0,a1,a2,b1,b2,c0,d0,xn,xn_1,xn_2,yn,yn_1,yn_2,V,K,norm,F,Q,peakGain)
(
  V = 10^(abs(peakGain)/ 20.0);
  K = tan($pi * F/srate);
  peakGain >= 0  ?  // boost
  (
      norm = 1 / (1 + sqrt(2) * K + K * K);
      a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
      a1 = 2 * (V * K * K - 1) * norm;
      a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
      b1 = 2 * (K * K - 1) * norm;
      b2 = (1 - sqrt(2) * K + K * K) * norm;
  ):(
      norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
      a0 = (1 + sqrt(2) * K + K * K) * norm;
      a1 = 2 * (K * K - 1) * norm;
      a2 = (1 - sqrt(2) * K + K * K) * norm;
      b1 = 2 * (V * K * K - 1) * norm;
      b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
  );
);

function hsSet(F,peakGain)
  instance(a0,a1,a2,b1,b2,c0,d0,xn,xn_1,xn_2,yn,yn_1,yn_2,V,K,norm,F,Q,peakGain)
(
  V = 10^(abs(peakGain)/ 20.0);
  K = tan($pi * F/srate);
  peakGain >= 0  ?  // boost
  (
      norm = 1 / (1 + sqrt(2) * K + K * K);
      a0 = (V + sqrt(2*V) * K + K * K) * norm;
      a1 = 2 * (K * K - V) * norm;
      a2 = (V - sqrt(2*V) * K + K * K) * norm;
      b1 = 2 * (K * K - 1) * norm;
      b2 = (1 - sqrt(2) * K + K * K) * norm;
  ):(
      norm = 1 / (V + sqrt(2*V) * K + K * K);
      a0 = (1 + sqrt(2) * K + K * K) * norm;
      a1 = 2 * (K * K - 1) * norm;
      a2 = (1 - sqrt(2) * K + K * K) * norm;
      b1 = 2 * (K * K - V) * norm;
      b2 = (V - sqrt(2*V) * K + K * K) * norm;
  );
);

function pkSet(F,Q,peakGain)
  instance(a0,a1,a2,b1,b2,c0,d0,xn,xn_1,xn_2,yn,yn_1,yn_2,V,K,norm,F,Q,peakGain)
(
  V = 10^(abs(peakGain)/ 20.0);
  K = tan($pi * F/srate);
  peakGain >= 0  ?  // boost
  (
      norm = 1 / (1 + 1/Q * K + K * K);
      a0 = (1 + V/Q * K + K * K) * norm;
      a1 = 2 * (K * K - 1) * norm;
      a2 = (1 - V/Q * K + K * K) * norm;
      b1 = a1;
      b2 = (1 - 1/Q * K + K * K) * norm;
  ):(
      norm = 1 / (1 + V/Q * K + K * K);
      a0 = (1 + 1/Q * K + K * K) * norm;
      a1 = 2 * (K * K - 1) * norm;
      a2 = (1 - 1/Q * K + K * K) * norm;
      b1 = a1;
      b2 = (1 - V/Q * K + K * K) * norm;
  );
);

c.hpF  = 20;     c.hpQ   =  0.75;
c.lpF  = 21000;  c.lpQ   =  0.8;
c.lsF  = 60;     c.lsG   =  0.1;
c.hsF  = 6000;   c.hsG   = -0.1;
c.p1F  = 900;    c.p1Q   =  0.05;  c.p1G = -0.4;
c.p2F  = 4500;   c.p2Q   =  0.7;  c.p2G = -0.1;
c.empF = 100;    c.empG  =  -6;

hpL.hpSet(c.hpF+r1*10,c.hpQ);
lpL.lpSet(c.lpF-(abs(r1)*500),c.lpQ);
lsL.lsSet(c.lsF,c.lsG+r1/20);
hsL.hsSet(c.hsF,c.hsG-r1/20);
p1L.pkSet(c.p1F+r1*50,c.p1Q,c.p1G+r1/20);
p2L.pkSet(c.p2F-r1*50,c.p2Q,c.p2G-r1/20);

hpR.hpSet(c.hpF+r2*10,c.hpQ);
lpR.lpSet(c.lpF-(abs(r2)*500),c.lpQ);
lsR.lsSet(c.lsF,c.lsG+r2/20);
hsR.hsSet(c.hsF,c.hsG-r2/20);
p1R.pkSet(c.p1F+r2*50,c.p1Q,c.p1G+r2/20);
p2R.pkSet(c.p2F-r2*50,c.p2Q,c.p2G-r2/20);

empL.hsSet(c.empF,c.empG);
dempL.hsSet(c.empF,-c.empG);
empR.hsSet(c.empF,c.empG);
dempR.hsSet(c.empF,-c.empG);


@slider

slider1 == 0 ? gainIn = 0.25 : 
slider1 == 1 ? gainIn = 1 : 
slider1 == 2 ? gainIn = 4;

@block


@sample

  spl0*=gainIn;
  spl1*=gainIn;
  
  spl0=empL.bqd(spl0);
  spl0=atan((8.5*spl0)/(8+abs(spl0))+0.0001);
  spl0=dempL.bqd(spl0);
  spl0=hpL.bqd(spl0);
  spl0=lpL.bqd(spl0);
  spl0=lsL.bqd(spl0);
  spl0=hsL.bqd(spl0);
  spl0=p1L.bqd(spl0);
  spl0=p2L.bqd(spl0);
  
  spl1=empR.bqd(spl1);
  spl1=atan((8.5*spl1)/(8+abs(spl1))+0.0001);
  spl1=dempR.bqd(spl1);
  spl1=hpR.bqd(spl1);
  spl1=lpR.bqd(spl1);
  spl1=lsR.bqd(spl1);
  spl1=hsR.bqd(spl1);
  spl1=p1R.bqd(spl1);
  spl1=p2R.bqd(spl1);
  
  
  spl0/=gainIn;
  spl1/=gainIn;
