e14704acb9f5c475b49f760b7e3426dca272ef7e
[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 #define BUFSIZE 4096
10 #define WAVE_FREQ 44100.0
11
12 // The frequency to filter on, in Hertz. Larger values makes the
13 // compressor react faster, but if it is too large, you'll
14 // ruin the waveforms themselves.
15 #define LPFILTER_FREQ 50.0
16
17 // The minimum estimated sound level at any given point.
18 // If you decrease this, you'll be able to amplify really silent signals
19 // by more, but you'll also increase the level of silent (ie. noise-only) segments,
20 // possibly caused misdetected pulses in these segments.
21 #define MIN_LEVEL 0.05
22
23 // A final scalar to get the audio within approximately the right range.
24 // Increase to _lower_ overall volume.
25 #define DAMPENING_FACTOR 5.0
26
27 // 6dB/oct per round.
28 #define FILTER_DEPTH 4
29
30 struct stereo_sample {
31         short left, right;
32 };
33
34 inline short clip(int x)
35 {
36         if (x < -32768) {
37                 return x;
38         } else if (x > 32767) {
39                 return 32767;
40         } else {
41                 return short(x);
42         }
43 }
44
45 static float a1, a2, b0, b1, b2;
46 static float d0, d1;
47
48 static void filter_init(float cutoff_radians)
49 {
50         float resonance = 1.0f / sqrt(2.0f);
51         float sn, cs;
52         sincosf(cutoff_radians, &sn, &cs);
53
54         float alpha = float(sn / (2 * resonance));
55
56         // coefficients for lowpass filter
57         float a0 = 1 + alpha;
58         b0 = (1 - cs) * 0.5f;
59         b1 = 1 - cs;
60         b2 = b0;
61         a1 = -2 * cs;
62         a2 = 1 - alpha;
63
64         float invA0 = 1.0f / a0;
65         b0 *= invA0;
66         b1 *= invA0;
67         b2 *= invA0;
68         a1 *= invA0;
69         a2 *= invA0;
70
71         // reset filter delays
72         d0 = d1 = 0.0f;
73 }
74
75 static float filter_update(float in)
76 {
77         float out = b0*in + d0;
78         d0 = b1 * in - a1 * out + d1;
79         d1 = b2 * in - a2 * out;
80         return out;
81 }
82
83 int main(int argc, char **argv)
84 {
85         std::vector<short> pcm;
86
87         while (!feof(stdin)) {
88                 short buf[BUFSIZE];
89                 ssize_t ret = fread(buf, sizeof(short), BUFSIZE, stdin);
90                 if (ret >= 0) {
91                         pcm.insert(pcm.end(), buf, buf + ret);
92                 }
93         }
94         
95         // filter forwards, then backwards (perfect phase filtering)
96         std::vector<float> filtered_samples, refiltered_samples;
97         filtered_samples.resize(pcm.size());
98         refiltered_samples.resize(pcm.size());
99
100         filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
101         for (unsigned i = 0; i < pcm.size(); ++i) {
102                 filtered_samples[i] = filter_update(fabs(pcm[i]));
103         }
104         filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
105         for (unsigned i = pcm.size(); i --> 0; ) {
106                 refiltered_samples[i] = filter_update(filtered_samples[i]);
107         }
108
109         for (int i = 1; i < FILTER_DEPTH; ++i) {
110                 filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
111                 for (unsigned i = 0; i < pcm.size(); ++i) {
112                         filtered_samples[i] = filter_update(refiltered_samples[i]);
113                 }
114                 filter_init(M_PI * LPFILTER_FREQ / WAVE_FREQ);
115                 for (unsigned i = pcm.size(); i --> 0; ) {
116                         refiltered_samples[i] = filter_update(filtered_samples[i]);
117                 }
118         }
119
120         for (unsigned i = 0; i < pcm.size(); ++i) {
121                 float f = DAMPENING_FACTOR * std::max(refiltered_samples[i] * (1.0 / 32768.0), MIN_LEVEL);
122                 short s = clip(lrintf(pcm[i] / f));
123                 fwrite(&s, sizeof(s), 1, stdout);
124         }
125 }