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

            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
        

Generated by: LCOV version 2.0-1