LCOV - code coverage report
Current view: top level - src/audio/effects/dynamics - multiband_compressor.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 98.1 % 53 52
Test Date: 2026-06-07 15:51:50 Functions: 90.9 % 11 10

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include <atomic>
       4              : 
       5              : #include "audio/dsp/biquad.h"
       6              : #include "audio/dsp/envelope_follower.h"
       7              : #include "audio/effects/core/effect.h"
       8              : 
       9              : namespace Amplitron {
      10              : 
      11              : /**
      12              :  * @brief 2nd-order Butterworth Low-Pass filter stage.
      13              :  * Two cascaded stages form a Linkwitz-Riley 4th-order (LR4) filter.
      14              :  */
      15              : struct ButterworthLP {
      16              :     Biquad b1, b2;
      17              : 
      18          186 :     void calculate_coefficients(float cutoff, float sample_rate) {
      19          186 :         float w0 = TWO_PI * cutoff / sample_rate;
      20          186 :         float cos_w0 = std::cos(w0);
      21          186 :         float sin_w0 = std::sin(w0);
      22          186 :         float alpha = sin_w0 / (2.0f * 0.70710678f);  // Q = 1/sqrt(2)
      23          186 :         float a0 = 1.0f + alpha;
      24              : 
      25          186 :         float b0 = (1.0f - cos_w0) / 2.0f;
      26          186 :         float b1_val = 1.0f - cos_w0;
      27          186 :         float b2_val = (1.0f - cos_w0) / 2.0f;
      28          186 :         float a1 = -2.0f * cos_w0;
      29          186 :         float a2 = 1.0f - alpha;
      30              : 
      31          186 :         b1.b0 = b2.b0 = b0 / a0;
      32          186 :         b1.b1 = b2.b1 = b1_val / a0;
      33          186 :         b1.b2 = b2.b2 = b2_val / a0;
      34          186 :         b1.a1 = b2.a1 = a1 / a0;
      35          186 :         b1.a2 = b2.a2 = a2 / a0;
      36          186 :     }
      37              : 
      38        67584 :     float process(float x) { return b2.process(b1.process(x)); }
      39              : 
      40           45 :     void reset() {
      41           45 :         b1.reset();
      42           45 :         b2.reset();
      43           36 :     }
      44              : };
      45              : 
      46              : /**
      47              :  * @brief 2nd-order Butterworth High-Pass filter stage.
      48              :  * Two cascaded stages form a Linkwitz-Riley 4th-order (LR4) filter.
      49              :  */
      50              : struct ButterworthHP {
      51              :     Biquad b1, b2;
      52              : 
      53          186 :     void calculate_coefficients(float cutoff, float sample_rate) {
      54          186 :         float w0 = TWO_PI * cutoff / sample_rate;
      55          186 :         float cos_w0 = std::cos(w0);
      56          186 :         float sin_w0 = std::sin(w0);
      57          186 :         float alpha = sin_w0 / (2.0f * 0.70710678f);  // Q = 1/sqrt(2)
      58          186 :         float a0 = 1.0f + alpha;
      59              : 
      60          186 :         float b0 = (1.0f + cos_w0) / 2.0f;
      61          186 :         float b1_val = -(1.0f + cos_w0);
      62          186 :         float b2_val = (1.0f + cos_w0) / 2.0f;
      63          186 :         float a1 = -2.0f * cos_w0;
      64          186 :         float a2 = 1.0f - alpha;
      65              : 
      66          186 :         b1.b0 = b2.b0 = b0 / a0;
      67          186 :         b1.b1 = b2.b1 = b1_val / a0;
      68          186 :         b1.b2 = b2.b2 = b2_val / a0;
      69          186 :         b1.a1 = b2.a1 = a1 / a0;
      70          186 :         b1.a2 = b2.a2 = a2 / a0;
      71          186 :     }
      72              : 
      73        67584 :     float process(float x) { return b2.process(b1.process(x)); }
      74              : 
      75           45 :     void reset() {
      76           45 :         b1.reset();
      77           45 :         b2.reset();
      78           36 :     }
      79              : };
      80              : 
      81              : /**
      82              :  * @brief A professional 3-band dynamics compressor.
      83              :  * Crossover filters split the signal into Low, Mid, and High bands.
      84              :  * Each band is compressed independently and summed back to the output.
      85              :  */
      86            2 : class MultiBandCompressor : public Effect {
      87              :    public:
      88              :     MultiBandCompressor();
      89              :     void process(float* buffer, int num_samples) override;
      90              :     void reset() override;
      91         3477 :     const char* name() const override { return "MultiBand Compressor"; }
      92            3 :     const char* type_id() const override { return "MultiBand Compressor"; }
      93          300 :     std::vector<EffectParam>& params() override { return params_; }
      94            0 :     const std::vector<EffectParam>& params() const override { return params_; }
      95              :     void set_sample_rate(int sample_rate) override;
      96              : 
      97              :     // Thread-safe gain reduction query for the GUI meters
      98          558 :     float get_gain_reduction_db(int band) const {
      99          558 :         if (band < 0 || band >= 3) return 0.0f;
     100          554 :         return gain_reduction_db_[band].load(std::memory_order_relaxed);
     101          186 :     }
     102              : 
     103              :    private:
     104              :     std::vector<EffectParam> params_;
     105              : 
     106              :     // Crossover filters (LR4 crossovers)
     107              :     ButterworthLP low_pass_filter_;
     108              :     ButterworthHP low_high_pass_filter_;
     109              :     ButterworthLP mid_pass_filter_;
     110              :     ButterworthHP mid_high_pass_filter_;
     111              : 
     112              :     // Per-band compressors
     113              :     EnvelopeFollower env_[3];
     114              :     float smoothed_attack_ms_[3] = {5.0f, 5.0f, 5.0f};
     115              :     float smoothed_release_ms_[3] = {100.0f, 100.0f, 100.0f};
     116              : 
     117              :     // Thread-safe gain reduction tracking (instantly updated)
     118              :     std::atomic<float> gain_reduction_db_[3];
     119              : 
     120              :     // Peak-decay envelope for smoother GUI meter visual response
     121              :     float gr_peak_[3] = {0.0f, 0.0f, 0.0f};
     122              : 
     123              :     // Cached states to determine when to recompute crossover coefficients
     124              :     float cached_low_xover_ = -1.0f;
     125              :     float cached_high_xover_ = -1.0f;
     126              :     int cached_sample_rate_ = -1;
     127              : 
     128              :     void recompute_coefficients();
     129              : };
     130              : 
     131              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1