LCOV - code coverage report
Current view: top level - src/audio/effects/delay_reverb - reverb.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 99.3 % 138 137
Test Date: 2026-06-07 15:51:50 Functions: 100.0 % 6 6

            Line data    Source code
       1              : #include "audio/effects/delay_reverb/reverb.h"
       2              : 
       3              : #include "audio/effects/core/effect_factory.h"
       4              : 
       5              : namespace Amplitron {
       6              : 
       7            2 : static EffectRegistrar<Reverb> reg("Reverb");
       8              : 
       9              : // Comb filter delay lengths (in samples at 44100Hz, will be scaled)
      10              : static const int COMB_LENGTHS[] = {1116, 1188, 1277, 1356};
      11              : static const int ALLPASS_LENGTHS[] = {556, 441};
      12              : // Right-channel lengths add a prime offset for decorrelation (classic Freeverb technique)
      13              : static constexpr int STEREO_SPREAD = 23;
      14              : static const int COMB_LENGTHS_R[] = {1116 + STEREO_SPREAD, 1188 + STEREO_SPREAD,
      15              :                                      1277 + STEREO_SPREAD, 1356 + STEREO_SPREAD};
      16              : static const int ALLPASS_LENGTHS_R[] = {556 + STEREO_SPREAD, 441 + STEREO_SPREAD};
      17              : 
      18          196 : Reverb::Reverb() {
      19          441 :     params_ = {
      20           98 :         {"Decay", 0.6f, 0.1f, 0.99f, 0.6f, "",
      21           49 :          "Length of the reverb tail. Higher values simulate larger acoustic spaces like halls or "
      22              :          "caves."},
      23           49 :         {"Damp", 0.4f, 0.0f, 1.0f, 0.4f, "",
      24           49 :          "High-frequency damping. Higher values absorb treble faster, simulating softer room "
      25              :          "materials."},
      26           49 :         {"Level", 0.3f, 0.0f, 1.0f, 0.3f, "",
      27           49 :          "Mix volume of the reverb effect. Controls how wet or distant the overall sound feels."},
      28          441 :     };
      29          147 :     init_filters();
      30          343 : }
      31              : 
      32          165 : void Reverb::set_sample_rate(int sample_rate) {
      33          165 :     Effect::set_sample_rate(sample_rate);
      34          165 :     init_filters();
      35          165 : }
      36              : 
      37          312 : void Reverb::init_filters() {
      38          312 :     const float scale = static_cast<float>(sample_rate_) / 44100.0f;
      39              : 
      40         1560 :     for (int i = 0; i < NUM_COMBS; ++i) {
      41         1248 :         int len = static_cast<int>(COMB_LENGTHS[i] * scale);
      42         1248 :         combs_[i].buffer.assign(len, 0.0f);
      43         1248 :         combs_[i].write_pos = 0;
      44         1248 :         combs_[i].lp_state = 0.0f;
      45              : 
      46         1248 :         int len_r = static_cast<int>(COMB_LENGTHS_R[i] * scale);
      47         1248 :         combs_r_[i].buffer.assign(len_r, 0.0f);
      48         1248 :         combs_r_[i].write_pos = 0;
      49         1248 :         combs_r_[i].lp_state = 0.0f;
      50          416 :     }
      51              : 
      52          936 :     for (int i = 0; i < NUM_ALLPASS; ++i) {
      53          624 :         int len = static_cast<int>(ALLPASS_LENGTHS[i] * scale);
      54          624 :         allpasses_[i].buffer.assign(len, 0.0f);
      55          624 :         allpasses_[i].write_pos = 0;
      56          624 :         allpasses_[i].feedback = 0.5f;
      57              : 
      58          624 :         int len_r = static_cast<int>(ALLPASS_LENGTHS_R[i] * scale);
      59          624 :         allpasses_r_[i].buffer.assign(len_r, 0.0f);
      60          624 :         allpasses_r_[i].write_pos = 0;
      61          624 :         allpasses_r_[i].feedback = 0.5f;
      62          208 :     }
      63          312 : }
      64              : 
      65           99 : void Reverb::process(float* buffer, int num_samples) {
      66           99 :     if (!enabled_) return;
      67              : 
      68           96 :     float decay = params_[0].value;
      69           96 :     float damp = params_[1].value;
      70           96 :     float level = params_[2].value;
      71              : 
      72       189408 :     for (int i = 0; i < num_samples; ++i) {
      73       189312 :         float dry = buffer[i];
      74       189312 :         float input = buffer[i] * 0.2f;
      75       189312 :         float out = 0.0f;
      76              : 
      77              :         // Parallel comb filters
      78       946560 :         for (int c = 0; c < NUM_COMBS; ++c) {
      79       757248 :             auto& comb = combs_[c];
      80       757248 :             int buf_len = static_cast<int>(comb.buffer.size());
      81       757248 :             int read_pos = comb.write_pos;
      82              : 
      83       757248 :             float delayed = comb.buffer[read_pos];
      84              : 
      85              :             // Damping LP filter
      86       757248 :             comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
      87       757248 :             float fb_sample = comb.lp_state * decay;
      88              : 
      89       757248 :             comb.buffer[comb.write_pos] = input + fb_sample;
      90       757248 :             comb.write_pos = (comb.write_pos + 1) % buf_len;
      91              : 
      92       757248 :             out += delayed;
      93       252416 :         }
      94              : 
      95              :         // Series allpass filters
      96       567936 :         for (int a = 0; a < NUM_ALLPASS; ++a) {
      97       378624 :             auto& ap = allpasses_[a];
      98       378624 :             int buf_len = static_cast<int>(ap.buffer.size());
      99              : 
     100       378624 :             float delayed = ap.buffer[ap.write_pos];
     101       378624 :             float temp = out + delayed * ap.feedback;
     102       378624 :             ap.buffer[ap.write_pos] = temp;
     103       378624 :             out = delayed - out * ap.feedback;
     104              : 
     105       378624 :             ap.write_pos = (ap.write_pos + 1) % buf_len;
     106       126208 :         }
     107              : 
     108       189312 :         buffer[i] = dry * (1.0f - level) + out * level;
     109        63104 :     }
     110           33 : }
     111              : 
     112            3 : void Reverb::process_stereo(float* left, float* right, int num_samples) {
     113            3 :     if (!enabled_) {
     114            0 :         return;
     115              :     }
     116              : 
     117            3 :     const float decay = params_[0].value;
     118            3 :     const float damp = params_[1].value;
     119            3 :     const float level = params_[2].value;
     120              : 
     121         6147 :     for (int i = 0; i < num_samples; ++i) {
     122         6144 :         const float input_l = left[i] * 0.2f;
     123         6144 :         const float input_r = right[i] * 0.2f;
     124         6144 :         float out_l = 0.0f;
     125         6144 :         float out_r = 0.0f;
     126              : 
     127              :         // Parallel comb filters — left
     128        30720 :         for (int c = 0; c < NUM_COMBS; ++c) {
     129        24576 :             auto& comb = combs_[c];
     130        24576 :             const int buf_len = static_cast<int>(comb.buffer.size());
     131        24576 :             float delayed = comb.buffer[comb.write_pos];
     132        24576 :             comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
     133        24576 :             comb.buffer[comb.write_pos] = input_l + comb.lp_state * decay;
     134        24576 :             comb.write_pos = (comb.write_pos + 1) % buf_len;
     135        24576 :             out_l += delayed;
     136         8192 :         }
     137              :         // Parallel comb filters — right
     138        30720 :         for (int c = 0; c < NUM_COMBS; ++c) {
     139        24576 :             auto& comb = combs_r_[c];
     140        24576 :             const int buf_len = static_cast<int>(comb.buffer.size());
     141        24576 :             float delayed = comb.buffer[comb.write_pos];
     142        24576 :             comb.lp_state = delayed * (1.0f - damp) + comb.lp_state * damp;
     143        24576 :             comb.buffer[comb.write_pos] = input_r + comb.lp_state * decay;
     144        24576 :             comb.write_pos = (comb.write_pos + 1) % buf_len;
     145        24576 :             out_r += delayed;
     146         8192 :         }
     147              : 
     148              :         // Series allpass filters — left
     149        18432 :         for (int a = 0; a < NUM_ALLPASS; ++a) {
     150        12288 :             auto& ap = allpasses_[a];
     151        12288 :             const int buf_len = static_cast<int>(ap.buffer.size());
     152        12288 :             float delayed = ap.buffer[ap.write_pos];
     153        12288 :             float temp = out_l + delayed * ap.feedback;
     154        12288 :             ap.buffer[ap.write_pos] = temp;
     155        12288 :             out_l = delayed - out_l * ap.feedback;
     156        12288 :             ap.write_pos = (ap.write_pos + 1) % buf_len;
     157         4096 :         }
     158              :         // Series allpass filters — right
     159        18432 :         for (int a = 0; a < NUM_ALLPASS; ++a) {
     160        12288 :             auto& ap = allpasses_r_[a];
     161        12288 :             const int buf_len = static_cast<int>(ap.buffer.size());
     162        12288 :             float delayed = ap.buffer[ap.write_pos];
     163        12288 :             float temp = out_r + delayed * ap.feedback;
     164        12288 :             ap.buffer[ap.write_pos] = temp;
     165        12288 :             out_r = delayed - out_r * ap.feedback;
     166        12288 :             ap.write_pos = (ap.write_pos + 1) % buf_len;
     167         4096 :         }
     168              : 
     169         6144 :         left[i] = left[i] * (1.0f - level) + out_l * level;
     170         6144 :         right[i] = right[i] * (1.0f - level) + out_r * level;
     171         2048 :     }
     172            1 : }
     173              : 
     174          168 : void Reverb::reset() {
     175          840 :     for (auto& c : combs_) {
     176          672 :         std::fill(c.buffer.begin(), c.buffer.end(), 0.0f);
     177          672 :         c.write_pos = 0;
     178          672 :         c.lp_state = 0.0f;
     179              :     }
     180          840 :     for (auto& c : combs_r_) {
     181          672 :         std::fill(c.buffer.begin(), c.buffer.end(), 0.0f);
     182          672 :         c.write_pos = 0;
     183          672 :         c.lp_state = 0.0f;
     184              :     }
     185          504 :     for (auto& a : allpasses_) {
     186          336 :         std::fill(a.buffer.begin(), a.buffer.end(), 0.0f);
     187          336 :         a.write_pos = 0;
     188              :     }
     189          504 :     for (auto& a : allpasses_r_) {
     190          336 :         std::fill(a.buffer.begin(), a.buffer.end(), 0.0f);
     191          336 :         a.write_pos = 0;
     192              :     }
     193          168 : }
     194              : 
     195              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1