New attack and release behavior for a-comp
[ardour.git] / libs / plugins / a-comp.lv2 / a-comp.c
index 89bd5c9270b18328ca0a7dfb38a72f3144ed9e09..a77893782be4169c0e5c183d3bc4028397a99c49 100644 (file)
@@ -78,9 +78,8 @@ typedef struct {
        float* output1;
 
        float srate;
-       float old_yl;
-       float old_y1;
-       float old_yg;
+
+       float old_in_peak_db;
 
        float makeup_gain;
 
@@ -100,8 +99,6 @@ typedef struct {
        float v_thresdb;
        float v_gainr;
        float v_makeup;
-       float v_lvl;
-       float v_lv1;
        float v_lvl_in;
        float v_lvl_out;
 #endif
@@ -124,7 +121,6 @@ instantiate(const LV2_Descriptor* descriptor,
        }
 
        acomp->srate = rate;
-       acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
        acomp->makeup_gain = 1.f;
 #ifdef LV2_EXTENDED
        acomp->need_expose = true;
@@ -134,7 +130,6 @@ instantiate(const LV2_Descriptor* descriptor,
        return (LV2_Handle)acomp;
 }
 
-
 static void
 connect_port(LV2_Handle instance,
              uint32_t port,
@@ -256,7 +251,7 @@ activate(LV2_Handle instance)
 
        *(acomp->gainr) = 0.0f;
        *(acomp->outlevel) = -45.0f;
-       acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
+       acomp->old_in_peak_db = -160.0f;
 }
 
 static void
@@ -270,14 +265,16 @@ run_mono(LV2_Handle instance, uint32_t n_samples)
 
        float srate = acomp->srate;
        float width = (6.f * *(acomp->knee)) + 0.01;
-       float cdb=0.f;
        float attack_coeff = exp(-1000.f/(*(acomp->attack) * srate));
        float release_coeff = exp(-1000.f/(*(acomp->release) * srate));
 
        float max = 0.f;
        float lgaininp = 0.f;
        float Lgain = 1.f;
-       float Lxg, Lxl, Lyg, Lyl, Ly1;
+       float Lxg, Lyg;
+       float current_gainr;
+       float old_gainr = *acomp->gainr;
+
        int usesidechain = (*(acomp->sidechain) <= 0.f) ? 0 : 1;
        uint32_t i;
        float ingain;
@@ -333,7 +330,6 @@ run_mono(LV2_Handle instance, uint32_t n_samples)
                Lxg = (ingain==0.f) ? -160.f : to_dB(ingain);
                Lxg = sanitize_denormal(Lxg);
 
-
                if (2.f*(Lxg-thresdb) < -width) {
                        Lyg = Lxg;
                } else if (2.f*(Lxg-thresdb) > width) {
@@ -343,21 +339,23 @@ run_mono(LV2_Handle instance, uint32_t n_samples)
                        Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width);
                }
 
-               Lxl = Lxg - Lyg;
+               current_gainr = Lxg - Lyg;
+
+               if (current_gainr < old_gainr) {
+                       current_gainr = release_coeff*old_gainr + (1.f-release_coeff)*current_gainr;
+               } else if (current_gainr > old_gainr) {
+                       current_gainr = attack_coeff*old_gainr + (1.f-attack_coeff)*current_gainr;
+               }
+
+               current_gainr = sanitize_denormal(current_gainr);
 
-               acomp->old_y1 = sanitize_denormal(acomp->old_y1);
-               acomp->old_yl = sanitize_denormal(acomp->old_yl);
-               Ly1 = fmaxf(Lxl, release_coeff * acomp->old_y1+(1.f-release_coeff)*Lxl);
-               Lyl = attack_coeff * acomp->old_yl+(1.f-attack_coeff)*Ly1;
-               Ly1 = sanitize_denormal(Ly1);
-               Lyl = sanitize_denormal(Lyl);
+               Lgain = from_dB(-current_gainr);
 
-               cdb = -Lyl;
-               Lgain = from_dB(cdb);
+               old_gainr = current_gainr;
 
-               *(acomp->gainr) = Lyl;
-               if (Lyl > max_gainr) {
-                       max_gainr = Lyl;
+               *(acomp->gainr) = current_gainr;
+               if (current_gainr > max_gainr) {
+                       max_gainr = current_gainr;
                }
 
                lgaininp = in0 * Lgain;
@@ -365,12 +363,6 @@ run_mono(LV2_Handle instance, uint32_t n_samples)
                output[i] = lgaininp * makeup_gain;
 
                max = (fabsf(output[i]) > max) ? fabsf(output[i]) : sanitize_denormal(max);
-
-               // TODO re-use local variables on stack
-               // store values back to acomp at the end of the inner-loop
-               acomp->old_yl = Lyl;
-               acomp->old_y1 = Ly1;
-               acomp->old_yg = Lyg;
        }
 
        if ( fabsf(makeup_target - makeup_gain) < 1e-6 ) {
@@ -385,17 +377,25 @@ run_mono(LV2_Handle instance, uint32_t n_samples)
 #ifdef LV2_EXTENDED
        acomp->v_gainr = max_gainr;
 
-       const float old_v_lv1 = acomp->v_lv1;
-       const float old_v_lvl = acomp->v_lvl;
+       float in_peak_db = to_dB(in_peak);
+
+       if (!isfinite_local (in_peak_db)) {
+               in_peak_db = -160.0f;
+       }
        const float tot_rel_c = exp(-1000.f/(*(acomp->release) * srate) * n_samples);
        const float tot_atk_c = exp(-1000.f/(*(acomp->attack) * srate) * n_samples);
-       acomp->v_lv1 = fmaxf (in_peak, tot_rel_c*old_v_lv1 + (1.f-tot_rel_c)*in_peak);
-       acomp->v_lvl = tot_atk_c*old_v_lvl + (1.f-tot_atk_c)*acomp->v_lv1;
 
-       if (!isfinite_local (acomp->v_lvl)) {
-               acomp->v_lvl = 0.f;
+       const float old_in_peak_db = acomp->old_in_peak_db;
+
+       if (in_peak_db < old_in_peak_db && in_peak_db < thresdb) {
+               in_peak_db = tot_rel_c*old_in_peak_db + (1.f-tot_rel_c)*in_peak_db;
+       } else if (in_peak_db > acomp->old_in_peak_db && in_peak_db > thresdb) {
+               in_peak_db = tot_atk_c*old_in_peak_db+ (1.f*tot_atk_c)*in_peak_db;
        }
-       const float v_lvl_in = (acomp->v_lvl < 0.001f) ? -60.f : to_dB(acomp->v_lvl);
+
+       acomp->old_in_peak_db = in_peak_db;
+
+       const float v_lvl_in = in_peak_db;
        const float v_lvl_out = (max < 0.001f) ? -60.f : to_dB(max);
        if (fabsf (acomp->v_lvl_out - v_lvl_out) >= 1 || fabsf (acomp->v_lvl_in - v_lvl_in) >= 1) {
                // >= 1dB difference
@@ -424,7 +424,6 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
 
        float srate = acomp->srate;
        float width = (6.f * *(acomp->knee)) + 0.01;
-       float cdb=0.f;
        float attack_coeff = exp(-1000.f/(*(acomp->attack) * srate));
        float release_coeff = exp(-1000.f/(*(acomp->release) * srate));
 
@@ -432,7 +431,10 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
        float lgaininp = 0.f;
        float rgaininp = 0.f;
        float Lgain = 1.f;
-       float Lxg, Lxl, Lyg, Lyl, Ly1;
+       float Lxg, Lyg;
+       float current_gainr;
+       float old_gainr = *acomp->gainr;
+
        int usesidechain = (*(acomp->sidechain) <= 0.f) ? 0 : 1;
        uint32_t i;
        float ingain;
@@ -492,7 +494,6 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
                Lxg = (ingain==0.f) ? -160.f : to_dB(ingain);
                Lxg = sanitize_denormal(Lxg);
 
-
                if (2.f*(Lxg-thresdb) < -width) {
                        Lyg = Lxg;
                } else if (2.f*(Lxg-thresdb) > width) {
@@ -502,21 +503,23 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
                        Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width);
                }
 
-               Lxl = Lxg - Lyg;
+               current_gainr = Lxg - Lyg;
+
+               if (current_gainr < old_gainr) {
+                       current_gainr = release_coeff*old_gainr + (1.f-release_coeff)*current_gainr;
+               } else if (current_gainr > old_gainr) {
+                       current_gainr = attack_coeff*old_gainr + (1.f-attack_coeff)*current_gainr;
+               }
 
-               acomp->old_y1 = sanitize_denormal(acomp->old_y1);
-               acomp->old_yl = sanitize_denormal(acomp->old_yl);
-               Ly1 = fmaxf(Lxl, release_coeff * acomp->old_y1+(1.f-release_coeff)*Lxl);
-               Lyl = attack_coeff * acomp->old_yl+(1.f-attack_coeff)*Ly1;
-               Ly1 = sanitize_denormal(Ly1);
-               Lyl = sanitize_denormal(Lyl);
+               current_gainr = sanitize_denormal(current_gainr);
 
-               cdb = -Lyl;
-               Lgain = from_dB(cdb);
+               Lgain = from_dB(-current_gainr);
 
-               *(acomp->gainr) = Lyl;
-               if (Lyl > max_gainr) {
-                       max_gainr = Lyl;
+               old_gainr = current_gainr;
+
+               *(acomp->gainr) = current_gainr;
+               if (current_gainr > max_gainr) {
+                       max_gainr = current_gainr;
                }
 
                lgaininp = in0 * Lgain;
@@ -526,12 +529,6 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
                output1[i] = rgaininp * makeup_gain;
 
                max = (fmaxf(fabs(output0[i]), fabs(output1[i])) > max) ? fmaxf(fabs(output0[i]), fabs(output1[i])) : sanitize_denormal(max);
-
-               // TODO re-use local variables on stack
-               // store values back to acomp at the end of the inner-loop
-               acomp->old_yl = Lyl;
-               acomp->old_y1 = Ly1;
-               acomp->old_yg = Lyg;
        }
 
        if ( fabsf(makeup_target - makeup_gain) < 1e-6 ) {
@@ -546,16 +543,26 @@ run_stereo(LV2_Handle instance, uint32_t n_samples)
 #ifdef LV2_EXTENDED
        acomp->v_gainr = max_gainr;
 
-       const float old_v_lv1 = acomp->v_lv1;
-       const float old_v_lvl = acomp->v_lvl;
+       float in_peak_db = to_dB(in_peak);
+
+       if (!isfinite_local (in_peak_db)) {
+               in_peak_db = -160.0f;
+       }
+
        const float tot_rel_c = exp(-1000.f/(*(acomp->release) * srate) * n_samples);
        const float tot_atk_c = exp(-1000.f/(*(acomp->attack) * srate) * n_samples);
-       acomp->v_lv1 = fmaxf (in_peak, tot_rel_c*old_v_lv1 + (1.f-tot_rel_c)*in_peak);
-       acomp->v_lvl = tot_atk_c*old_v_lvl + (1.f-tot_atk_c)*acomp->v_lv1;
-       if (!isfinite_local (acomp->v_lvl)) {
-               acomp->v_lvl = 0.f;
+
+       const float old_in_peak_db = acomp->old_in_peak_db;
+
+       if (in_peak_db < old_in_peak_db && in_peak_db < thresdb) {
+               in_peak_db = tot_rel_c*old_in_peak_db + (1.f-tot_rel_c)*in_peak_db;
+       } else if (in_peak_db > acomp->old_in_peak_db && in_peak_db > thresdb) {
+               in_peak_db = tot_atk_c*old_in_peak_db+ (1.f*tot_atk_c)*in_peak_db;
        }
-       const float v_lvl_in = (acomp->v_lvl < 0.001f) ? -60.f : to_dB(acomp->v_lvl);
+
+       acomp->old_in_peak_db = in_peak_db;
+
+       const float v_lvl_in = in_peak_db;
        const float v_lvl_out = (max < 0.001f) ? -60.f : to_dB(max);
        if (fabsf (acomp->v_lvl_out - v_lvl_out) >= 1 || fabsf (acomp->v_lvl_in - v_lvl_in) >= 1) {
                // >= 1dB difference