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 228 : void calculate_coefficients(float cutoff, float sample_rate) {
18 228 : float w0 = TWO_PI * cutoff / sample_rate;
19 228 : float cos_w0 = std::cos(w0);
20 228 : float sin_w0 = std::sin(w0);
21 228 : float alpha = sin_w0 / (2.0f * 0.70710678f); // Q = 1/sqrt(2)
22 228 : float a0 = 1.0f + alpha;
23 :
24 228 : float b0 = (1.0f - cos_w0) / 2.0f;
25 228 : float b1_val = 1.0f - cos_w0;
26 228 : float b2_val = (1.0f - cos_w0) / 2.0f;
27 228 : float a1 = -2.0f * cos_w0;
28 228 : float a2 = 1.0f - alpha;
29 :
30 228 : b1.b0 = b2.b0 = b0 / a0;
31 228 : b1.b1 = b2.b1 = b1_val / a0;
32 228 : b1.b2 = b2.b2 = b2_val / a0;
33 228 : b1.a1 = b2.a1 = a1 / a0;
34 228 : b1.a2 = b2.a2 = a2 / a0;
35 228 : }
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 228 : void calculate_coefficients(float cutoff, float sample_rate) {
55 228 : float w0 = TWO_PI * cutoff / sample_rate;
56 228 : float cos_w0 = std::cos(w0);
57 228 : float sin_w0 = std::sin(w0);
58 228 : float alpha = sin_w0 / (2.0f * 0.70710678f); // Q = 1/sqrt(2)
59 228 : float a0 = 1.0f + alpha;
60 :
61 228 : float b0 = (1.0f + cos_w0) / 2.0f;
62 228 : float b1_val = -(1.0f + cos_w0);
63 228 : float b2_val = (1.0f + cos_w0) / 2.0f;
64 228 : float a1 = -2.0f * cos_w0;
65 228 : float a2 = 1.0f - alpha;
66 :
67 228 : b1.b0 = b2.b0 = b0 / a0;
68 228 : b1.b1 = b2.b1 = b1_val / a0;
69 228 : b1.b2 = b2.b2 = b2_val / a0;
70 228 : b1.a1 = b2.a1 = a1 / a0;
71 228 : b1.a2 = b2.a2 = a2 / a0;
72 228 : }
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 4851 : const char* name() const override { return "MultiBand Compressor"; }
95 3 : const char* type_id() const override { return "MultiBand Compressor"; }
96 372 : 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 765 : float get_gain_reduction_db(int band) const {
101 765 : if (band < 0 || band >= 3) return 0.0f;
102 761 : return gain_reduction_db_[band].load(std::memory_order_relaxed);
103 255 : }
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
|