saturation 0.2.3

Real-time saturation and clipping designed for use with vst's
Documentation
use real_time_fir_iir_filters::param::FilterFloat;

use super::{PentodeClassA, PentodeModel};

use crate::f;

pub trait PentodeCalc<F, M>
where
    F: FilterFloat,
    M: PentodeModel
{
    fn reset(&mut self)
    {
        
    }
    fn param(&self) -> &PentodeClassA<F>;
    fn param_mut(&mut self) -> &mut PentodeClassA<F>;
    fn vp_a(&mut self, vg: F) -> [F; 2];
}
impl<F, M> PentodeCalc<F, M> for PentodeClassA<F>
where
    F: FilterFloat,
    M: PentodeModel
{
    fn param(&self) -> &PentodeClassA<F>
    {
        self
    }
    fn param_mut(&mut self) -> &mut PentodeClassA<F>
    {
        self
    }
    fn vp_a(&mut self, vg: F) -> [F; 2]
    {
        let PentodeClassA {r_i: _, r_p: rp, v_g2: vg2, v_pp: vpp, v_c: _} = *self;
        let two_rp = rp + rp;
        let one = F::one();
        let zero = F::zero();

        let mu_inv = f!(1.0/M::MU);
        let kp = f!(M::K_P);
        let kvb = f!(M::K_VB);
        let kg1 = f!(M::K_G1);
        let ex = f!(M::EX);

        let vg2_d_kp = vg2/kp;
        let c = kp*(mu_inv + vg/vg2);
        
        let v1 = vg2_d_kp*crate::exp_ln_1p(c);

        if v1.is_sign_positive()
        {
            let rp_inv = two_rp.recip();
            let vpp_d_rp = vpp/two_rp;

            let b = v1.powf(ex)/kg1;

            let mut vp = vpp/(one + b*two_rp/kvb);
            if vp > one
            {
                vp = vpp - b*two_rp*F::FRAC_PI_2();
            }
            vp = vp.max(zero).min(vpp);

            const NEWTON: usize = 2;

            for _ in 0..NEWTON
            {
                let vp_d_kvb = vp/kvb;

                let f = vpp_d_rp - vp/two_rp - b*vp.atan2(kvb);
                let df_dvp = -rp_inv - b/(vp*vp_d_kvb + kvb);

                let delta = f/df_dvp;
                vp = vp - delta;
            }

            vp = vp.max(zero).min(vpp);
            let vp_d_kvb = vp/kvb;

            let df_dvp = -rp_inv - b/(vp*vp_d_kvb + kvb);

            let dv1_dvg = (one + (-c).exp()).recip();
            let df_dv1 = -ex*v1.powf(ex - one)/kg1*vp.atan2(kvb);
            let dvp_dvg = dv1_dvg*df_dv1/df_dvp;

            [vp, dvp_dvg]
        }
        else
        {
            [vpp, zero]
        }
    }
}