Line data Source code
1 : #include "audio/effects/phaser.h"
2 : #include "audio/effects/effect_factory.h"
3 : #include <cmath>
4 :
5 : namespace Amplitron {
6 :
7 2 : static EffectRegistrar<Phaser> reg("Phaser");
8 :
9 : // Param indices
10 : static constexpr int P_RATE = 0;
11 : static constexpr int P_DEPTH = 1;
12 : static constexpr int P_STAGES = 2;
13 : static constexpr int P_FEEDBACK = 3;
14 : static constexpr int P_MIX = 4;
15 :
16 : // Stage count table: Stages param 0..3 -> 4/6/8/12 stages
17 : static constexpr int STAGE_COUNTS[4] = {4, 6, 8, 12};
18 :
19 76 : Phaser::Phaser() {
20 247 : params_ = {
21 57 : {"Rate", 0.5f, 0.05f, 10.0f, 0.5f, "Hz",
22 19 : "LFO speed. Low values create a slow, hypnotic sweep; high values create a fast vibrato-like effect."},
23 19 : {"Depth", 0.7f, 0.0f, 1.0f, 0.7f, "",
24 19 : "Modulation depth. Controls how wide the all-pass frequency sweeps across the spectrum."},
25 19 : {"Stages", 0.0f, 0.0f, 3.0f, 0.0f, "",
26 19 : "Number of all-pass stages: 0=4 (Phase 90), 1=6, 2=8, 3=12. More stages add more notches."},
27 19 : {"Feedback", 0.5f, 0.0f, 0.95f, 0.5f, "",
28 19 : "Feeds the chain output back to the input, adding resonance and intensity to the phasing notches."},
29 19 : {"Mix", 0.5f, 0.0f, 1.0f, 0.5f, "",
30 19 : "Dry/wet blend. At 0.5 the notch effect is most pronounced (classic phaser mix point)."},
31 247 : };
32 76 : set_sample_rate(DEFAULT_SAMPLE_RATE);
33 114 : }
34 :
35 108 : void Phaser::set_sample_rate(int sample_rate) {
36 108 : Effect::set_sample_rate(sample_rate);
37 108 : reset();
38 89 : }
39 :
40 45 : void Phaser::process(float* buffer, int num_samples) {
41 45 : if (!enabled_) return;
42 :
43 42 : const float rate = params_[P_RATE].value;
44 42 : const float depth = params_[P_DEPTH].value;
45 42 : const int nstages = STAGE_COUNTS[(int)clamp(params_[P_STAGES].value + 0.5f, 0.0f, 3.0f)];
46 42 : const float feedback = params_[P_FEEDBACK].value;
47 42 : const float mix = params_[P_MIX].value;
48 :
49 42 : const float lfo_inc = rate / static_cast<float>(sample_rate_);
50 : // Logarithmic sweep: base_freq * exp(lfo * depth * ln(ratio))
51 : // fc range: ~200 Hz (lfo=0) to ~4000 Hz (lfo=1, depth=1)
52 42 : const float log_ratio = std::log(20.0f); // ln(4000/200)
53 :
54 15402 : for (int i = 0; i < num_samples; ++i) {
55 15360 : const float dry = buffer[i];
56 :
57 : // LFO in [0, 1]
58 15360 : const float lfo = 0.5f * (1.0f + std::sin(TWO_PI * lfo_phase_));
59 :
60 : // Modulated all-pass corner frequency (log sweep)
61 20480 : const float fc = clamp(200.0f * std::exp(lfo * depth * log_ratio),
62 10240 : 80.0f, static_cast<float>(sample_rate_) * 0.40f);
63 :
64 : // 1st-order all-pass coefficient: c = (tan(π*fc/fs) - 1) / (tan(π*fc/fs) + 1)
65 15360 : const float t = std::tan(3.14159265f * fc / static_cast<float>(sample_rate_));
66 15360 : const float apc = (t - 1.0f) / (t + 1.0f);
67 :
68 : // Feed input + feedback into the all-pass cascade
69 15360 : float x = dry + feedback * feedback_state_;
70 :
71 125952 : for (int s = 0; s < nstages; ++s) {
72 : // y[n] = c * (x[n] - y[n-1]) + x[n-1]
73 110592 : const float y = apc * (x - apf_yprev_[s]) + apf_xprev_[s];
74 110592 : apf_xprev_[s] = x;
75 110592 : apf_yprev_[s] = y;
76 110592 : x = y;
77 36864 : }
78 :
79 15360 : feedback_state_ = x;
80 :
81 15360 : buffer[i] = dry * (1.0f - mix) + x * mix;
82 :
83 15360 : lfo_phase_ += lfo_inc;
84 15360 : if (lfo_phase_ >= 1.0f) lfo_phase_ -= 1.0f;
85 5120 : }
86 15 : }
87 :
88 0 : void Phaser::process_stereo(float* left, float* right, int num_samples) {
89 0 : if (!enabled_) {
90 0 : return;
91 : }
92 :
93 0 : const float rate = params_[P_RATE].value;
94 0 : const float depth = params_[P_DEPTH].value;
95 0 : const int nstages = STAGE_COUNTS[(int)clamp(params_[P_STAGES].value + 0.5f, 0.0f, 3.0f)];
96 0 : const float feedback = params_[P_FEEDBACK].value;
97 0 : const float mix = params_[P_MIX].value;
98 :
99 0 : const float lfo_inc = rate / static_cast<float>(sample_rate_);
100 0 : const float log_ratio = std::log(20.0f);
101 :
102 0 : for (int i = 0; i < num_samples; ++i) {
103 0 : const float dry_l = left[i];
104 0 : const float dry_r = right[i];
105 :
106 : // Left LFO
107 0 : const float lfo_l = 0.5f * (1.0f + std::sin(TWO_PI * lfo_phase_));
108 0 : const float fc_l = clamp(200.0f * std::exp(lfo_l * depth * log_ratio),
109 0 : 80.0f, static_cast<float>(sample_rate_) * 0.40f);
110 0 : const float t_l = std::tan(3.14159265f * fc_l / static_cast<float>(sample_rate_));
111 0 : const float apc_l = (t_l - 1.0f) / (t_l + 1.0f);
112 :
113 : // Right LFO — 180° offset (0.5 of normalised cycle)
114 0 : const float lfo_r = 0.5f * (1.0f + std::sin(TWO_PI * (lfo_phase_ + 0.5f)));
115 0 : const float fc_r = clamp(200.0f * std::exp(lfo_r * depth * log_ratio),
116 0 : 80.0f, static_cast<float>(sample_rate_) * 0.40f);
117 0 : const float t_r = std::tan(3.14159265f * fc_r / static_cast<float>(sample_rate_));
118 0 : const float apc_r = (t_r - 1.0f) / (t_r + 1.0f);
119 :
120 : // Left APF cascade
121 0 : float x_l = dry_l + feedback * feedback_state_;
122 0 : for (int s = 0; s < nstages; ++s) {
123 0 : const float y = apc_l * (x_l - apf_yprev_[s]) + apf_xprev_[s];
124 0 : apf_xprev_[s] = x_l;
125 0 : apf_yprev_[s] = y;
126 0 : x_l = y;
127 0 : }
128 0 : feedback_state_ = x_l;
129 :
130 : // Right APF cascade
131 0 : float x_r = dry_r + feedback * feedback_state_r_;
132 0 : for (int s = 0; s < nstages; ++s) {
133 0 : const float y = apc_r * (x_r - apf_yprev_r_[s]) + apf_xprev_r_[s];
134 0 : apf_xprev_r_[s] = x_r;
135 0 : apf_yprev_r_[s] = y;
136 0 : x_r = y;
137 0 : }
138 0 : feedback_state_r_ = x_r;
139 :
140 0 : left[i] = dry_l * (1.0f - mix) + x_l * mix;
141 0 : right[i] = dry_r * (1.0f - mix) + x_r * mix;
142 :
143 0 : lfo_phase_ += lfo_inc;
144 0 : if (lfo_phase_ >= 1.0f) lfo_phase_ -= 1.0f;
145 0 : }
146 0 : }
147 :
148 168 : void Phaser::reset() {
149 168 : lfo_phase_ = 0.0f;
150 168 : feedback_state_ = 0.0f;
151 168 : feedback_state_r_ = 0.0f;
152 168 : apf_xprev_.fill(0.0f);
153 168 : apf_yprev_.fill(0.0f);
154 168 : apf_xprev_r_.fill(0.0f);
155 168 : apf_yprev_r_.fill(0.0f);
156 168 : }
157 :
158 : } // namespace Amplitron
|