LCOV - code coverage report
Current view: top level - src/audio/effects - multiband_compressor.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 54 54
Test Date: 2026-06-01 11:15:25 Functions: 100.0 % 10 10

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

Generated by: LCOV version 2.0-1