Line data Source code
1 : #include "audio/effects/wah.h"
2 : #include "audio/effects/effect_factory.h"
3 :
4 : namespace Amplitron {
5 :
6 2 : static EffectRegistrar<WahPedal> reg("Wah");
7 :
8 56 : WahPedal::WahPedal() {
9 210 : params_ = {
10 42 : {"Mode", 0.0f, 0.0f, 1.0f, 0.0f, "", "0 = Manual sweep; 1 = Auto-wah driven by envelope follower."},
11 14 : {"Sweep", 0.5f, 0.0f, 1.0f, 0.5f, "", "Manual wah sweep position. Heel-down (0) = low frequency; toe-down (1) = high frequency. Active in Manual mode."},
12 14 : {"Resonance", 3.5f, 1.0f, 8.0f, 3.5f, "Q", "Bandpass filter Q factor. Higher values give a sharper, more vocal wah character."},
13 14 : {"Sensitivity", 0.5f, 0.0f, 1.0f, 0.5f, "", "How strongly the input signal amplitude drives the sweep in Auto-wah mode."},
14 14 : {"Attack", 5.0f, 1.0f, 50.0f, 5.0f, "ms", "Envelope follower attack time. Faster values track transients more aggressively."},
15 14 : {"Release", 100.0f, 20.0f, 500.0f, 100.0f, "ms", "Envelope follower release time. Controls how quickly the filter falls back after a note dies."},
16 210 : };
17 84 : }
18 :
19 39 : void WahPedal::process(float* buffer, int num_samples) {
20 39 : if (!enabled_) return;
21 :
22 36 : bool is_auto = (params_[0].value > 0.5f);
23 36 : float sweep = params_[1].value;
24 36 : float q = params_[2].value;
25 36 : float sens = params_[3].value;
26 36 : float atk_ms = params_[4].value;
27 36 : float rel_ms = params_[5].value;
28 :
29 36 : float atk_coeff = EnvelopeFollower::time_to_coeff(atk_ms, sample_rate_);
30 36 : float rel_coeff = EnvelopeFollower::time_to_coeff(rel_ms, sample_rate_);
31 :
32 : // Sweep/Q smoothing (~5 ms time constant -- removes zipper noise on knob moves)
33 36 : float smooth_coeff = std::exp(-1.0f / (sample_rate_ * 0.005f));
34 :
35 33828 : for (int i = 0; i < num_samples; ++i) {
36 33792 : float dry = buffer[i];
37 :
38 : // --- Compute target sweep position ---
39 11264 : float target_sweep;
40 33792 : if (is_auto) {
41 : // Peak-tracking envelope follower
42 3072 : float envelope = env_.process(dry, atk_coeff, rel_coeff);
43 :
44 : // Map envelope -> sweep (sensitivity scales the detection range)
45 3072 : target_sweep = clamp(envelope * sens * 4.0f, 0.0f, 1.0f);
46 1024 : } else {
47 20480 : target_sweep = sweep;
48 : }
49 :
50 : // Smooth sweep and Q positions to prevent discontinuities
51 33792 : sweep_smooth_ += (1.0f - smooth_coeff) * (target_sweep - sweep_smooth_);
52 33792 : q_smooth_ += (1.0f - smooth_coeff) * (q - q_smooth_);
53 :
54 : // Damping factor for SVF (inverse of smoothed Q)
55 33792 : float q_damp = 1.0f / q_smooth_;
56 :
57 : // --- Map sweep 0->1 to centre frequency 350 Hz -> 2500 Hz (exponential) ---
58 33792 : constexpr float FREQ_LO = 350.0f;
59 33792 : constexpr float FREQ_HI = 2500.0f;
60 33792 : float fc = FREQ_LO * std::pow(FREQ_HI / FREQ_LO, sweep_smooth_);
61 :
62 : // --- Chamberlin state-variable filter ---
63 33792 : float f_coeff = 2.0f * std::sin(PI * fc / static_cast<float>(sample_rate_));
64 :
65 33792 : float hp = dry - q_damp * svf_bp_ - svf_lp_;
66 33792 : svf_bp_ = f_coeff * hp + svf_bp_;
67 33792 : svf_lp_ = f_coeff * svf_bp_ + svf_lp_;
68 :
69 : // Bandpass output boosted by Q for the classic resonant wah peak
70 33792 : float wet = svf_bp_ * q_smooth_ * 0.5f;
71 :
72 33792 : buffer[i] = dry * (1.0f - mix_) + wet * mix_;
73 11264 : }
74 13 : }
75 :
76 33 : void WahPedal::reset() {
77 33 : svf_lp_ = 0.0f;
78 33 : svf_bp_ = 0.0f;
79 33 : env_.reset();
80 33 : sweep_smooth_ = 0.5f;
81 33 : q_smooth_ = 3.5f;
82 33 : }
83 :
84 : } // namespace Amplitron
|