]> git.sesse.net Git - c64tapwav/blob - level.cpp
Make leveler minimum level configurable, since it is connected to hysteresis limit.
[c64tapwav] / level.cpp
1 // A small program to try to even out the audio levels within a file
2 // (essentially a compressor with infinite lookahead).
3
4 #include <stdio.h>
5 #include <math.h>
6 #include <vector>
7 #include <algorithm>
8
9 // The frequency to filter on, in Hertz. Larger values makes the
10 // compressor react faster, but if it is too large, you'll
11 // ruin the waveforms themselves.
12 #define LPFILTER_FREQ 50.0
13
14 // A final scalar to get the audio within approximately the right range.
15 // Increase to _lower_ overall volume.
16 #define DAMPENING_FACTOR 5.0
17
18 // 6dB/oct per round.
19 #define FILTER_DEPTH 4
20
21 static float a1, a2, b0, b1, b2;
22 static float d0, d1;
23
24 static void filter_init(float cutoff_radians)
25 {
26         float resonance = 1.0f / sqrt(2.0f);
27         float sn = sin(cutoff_radians), cs = cos(cutoff_radians);
28         float alpha = float(sn / (2 * resonance));
29
30         // coefficients for lowpass filter
31         float a0 = 1 + alpha;
32         b0 = (1 - cs) * 0.5f;
33         b1 = 1 - cs;
34         b2 = b0;
35         a1 = -2 * cs;
36         a2 = 1 - alpha;
37
38         b0 /= a0;
39         b1 /= a0;
40         b2 /= a0;
41         a1 /= a0;
42         a2 /= a0;
43
44         // reset filter delays
45         d0 = d1 = 0.0f;
46 }
47
48 static float filter_update(float in)
49 {
50         float out = b0*in + d0;
51         d0 = b1 * in - a1 * out + d1;
52         d1 = b2 * in - a2 * out;
53         return out;
54 }
55
56 std::vector<float> level_samples(const std::vector<float> &pcm, float min_level, int sample_rate)
57 {
58         // filter forwards, then backwards (perfect phase filtering)
59         std::vector<float> filtered_samples, refiltered_samples, leveled_samples;
60         filtered_samples.resize(pcm.size());
61         refiltered_samples.resize(pcm.size());
62         leveled_samples.resize(pcm.size());
63
64         filter_init(M_PI * LPFILTER_FREQ / sample_rate);
65         for (unsigned i = 0; i < pcm.size(); ++i) {
66                 filtered_samples[i] = filter_update(fabs(pcm[i]));
67         }
68         filter_init(M_PI * LPFILTER_FREQ / sample_rate);
69         for (unsigned i = pcm.size(); i --> 0; ) {
70                 refiltered_samples[i] = filter_update(filtered_samples[i]);
71         }
72
73         for (int i = 1; i < FILTER_DEPTH; ++i) {
74                 filter_init(M_PI * LPFILTER_FREQ / sample_rate);
75                 for (unsigned i = 0; i < pcm.size(); ++i) {
76                         filtered_samples[i] = filter_update(refiltered_samples[i]);
77                 }
78                 filter_init(M_PI * LPFILTER_FREQ / sample_rate);
79                 for (unsigned i = pcm.size(); i --> 0; ) {
80                         refiltered_samples[i] = filter_update(filtered_samples[i]);
81                 }
82         }
83
84         for (unsigned i = 0; i < pcm.size(); ++i) {
85                 float f = DAMPENING_FACTOR * std::max<float>(refiltered_samples[i], min_level);
86                 leveled_samples[i] = pcm[i] / f;
87         }
88
89         return leveled_samples;
90 }