]> git.sesse.net Git - nageru/blob - stereocompressor.cpp
Add a compressor (kindly relicensed by Rune Holm). Fixed for now, and only to get...
[nageru] / stereocompressor.cpp
1 #include <math.h>
2 #include <assert.h>
3 #include <algorithm>
4
5 #include "stereocompressor.h"
6
7 namespace {
8
9 inline float compressor_knee(float x, float threshold, float inv_threshold, float inv_ratio_minus_one, float postgain)
10 {
11         assert(inv_ratio_minus_one <= 0.0f);
12         if (x > threshold && inv_ratio_minus_one < 0.0f) {
13                 return postgain * pow(x * inv_threshold, inv_ratio_minus_one);
14         } else {
15                 return postgain;
16         }
17 }
18
19 }  // namespace
20
21 void StereoCompressor::process(float *buf, size_t num_samples, float threshold, float ratio,
22             float attack_time, float release_time, float makeup_gain)
23 {
24         float attack_increment = float(pow(2.0f, 1.0f / (attack_time * sample_rate + 1)));
25         if (attack_time == 0.0f) attack_increment = 100000;  // For instant attack reaction.
26
27         const float release_increment = float(pow(2.0f, -1.0f / (release_time * sample_rate + 1)));
28         const float peak_increment = float(pow(2.0f, -1.0f / (0.003f * sample_rate + 1)));
29
30         float inv_ratio_minus_one = 1.0f / ratio - 1.0f;
31         if (ratio > 63) inv_ratio_minus_one = -1.0f;  // Infinite ratio.
32         float inv_threshold = 1.0f / threshold;
33
34         float *left_ptr = buf;
35         float *right_ptr = buf + 1;
36
37         float peak_level = this->peak_level;
38         float compr_level = this->compr_level;
39
40         for (size_t i = 0; i < num_samples; ++i) {
41                 if (fabs(*left_ptr) > peak_level) peak_level = float(fabs(*left_ptr));
42                 if (fabs(*right_ptr) > peak_level) peak_level = float(fabs(*right_ptr));
43
44                 if (peak_level > compr_level) {
45                         compr_level = std::min(compr_level * attack_increment, peak_level);
46                 } else {
47                         compr_level = std::max(compr_level * release_increment, 0.0001f);
48                 }
49
50                 float scalefactor_with_gain = compressor_knee(compr_level, threshold, inv_threshold, inv_ratio_minus_one, makeup_gain);
51
52                 *left_ptr *= scalefactor_with_gain;
53                 left_ptr += 2;
54
55                 *right_ptr *= scalefactor_with_gain;
56                 right_ptr += 2;
57
58                 peak_level = std::max(peak_level * peak_increment, 0.0001f);
59         }
60
61         // Store attenuation level for debug/visualization.
62         scalefactor = compressor_knee(compr_level, threshold, inv_threshold, inv_ratio_minus_one, 1.0f);
63
64         this->peak_level = peak_level;
65         this->compr_level = compr_level;
66 }
67