Missed update to private test repo version.
[dcpomatic.git] / src / lib / audio_filter.cc
1 /*
2     Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #include "audio_filter.h"
23 #include "audio_buffers.h"
24 #include "maths_util.h"
25 #include "util.h"
26 #include <cmath>
27
28
29 using std::make_shared;
30 using std::min;
31 using std::shared_ptr;
32
33
34 std::vector<float>
35 AudioFilter::sinc_blackman (float cutoff, bool invert) const
36 {
37         auto ir = std::vector<float>();
38         ir.resize(_M + 1);
39
40         /* Impulse response */
41
42         for (int i = 0; i <= _M; ++i) {
43                 if (i == (_M / 2)) {
44                         ir[i] = 2 * M_PI * cutoff;
45                 } else {
46                         /* sinc */
47                         ir[i] = sin (2 * M_PI * cutoff * (i - _M / 2)) / (i - _M / 2);
48                         /* Blackman window */
49                         ir[i] *= (0.42 - 0.5 * cos (2 * M_PI * i / _M) + 0.08 * cos (4 * M_PI * i / _M));
50                 }
51         }
52
53         /* Normalise */
54
55         float sum = 0;
56         for (int i = 0; i <= _M; ++i) {
57                 sum += ir[i];
58         }
59
60         for (int i = 0; i <= _M; ++i) {
61                 ir[i] /= sum;
62         }
63
64         /* Frequency inversion (swapping low-pass for high-pass, or whatever) */
65
66         if (invert) {
67                 for (int i = 0; i <= _M; ++i) {
68                         ir[i] = -ir[i];
69                 }
70                 ir[_M / 2] += 1;
71         }
72
73         return ir;
74 }
75
76
77 shared_ptr<AudioBuffers>
78 AudioFilter::run (shared_ptr<const AudioBuffers> in)
79 {
80         auto out = make_shared<AudioBuffers>(in->channels(), in->frames());
81
82         if (!_tail) {
83                 _tail = make_shared<AudioBuffers>(in->channels(), _M + 1);
84                 _tail->make_silent ();
85         }
86
87         int const channels = in->channels ();
88         int const frames = in->frames ();
89
90         for (int i = 0; i < channels; ++i) {
91                 auto tail_p = _tail->data (i);
92                 auto in_p = in->data (i);
93                 auto out_p = out->data (i);
94                 for (int j = 0; j < frames; ++j) {
95                         float s = 0;
96                         for (int k = 0; k <= _M; ++k) {
97                                 if ((j - k) < 0) {
98                                         s += tail_p[j - k + _M + 1] * _ir[k];
99                                 } else {
100                                         s += in_p[j - k] * _ir[k];
101                                 }
102                         }
103
104                         out_p[j] = s;
105                 }
106         }
107
108         int const amount = min (in->frames(), _tail->frames());
109         if (amount < _tail->frames ()) {
110                 _tail->move (_tail->frames() - amount, amount, 0);
111         }
112         _tail->copy_from (in.get(), amount, in->frames() - amount, _tail->frames () - amount);
113
114         return out;
115 }
116
117
118 void
119 AudioFilter::flush ()
120 {
121         _tail.reset ();
122 }
123
124
125 LowPassAudioFilter::LowPassAudioFilter (float transition_bandwidth, float cutoff)
126         : AudioFilter (transition_bandwidth)
127 {
128         _ir = sinc_blackman (cutoff, false);
129 }
130
131
132 HighPassAudioFilter::HighPassAudioFilter (float transition_bandwidth, float cutoff)
133         : AudioFilter (transition_bandwidth)
134 {
135         _ir = sinc_blackman (cutoff, true);
136 }
137
138
139 BandPassAudioFilter::BandPassAudioFilter (float transition_bandwidth, float lower, float higher)
140         : AudioFilter (transition_bandwidth)
141 {
142         auto lpf = sinc_blackman (lower, false);
143         auto hpf = sinc_blackman (higher, true);
144
145         _ir.resize(_M + 1);
146         for (int i = 0; i <= _M; ++i) {
147                 _ir[i] = lpf[i] + hpf[i];
148         }
149
150         /* We now have a band-stop, so invert for band-pass */
151         for (int i = 0; i <= _M; ++i) {
152                 _ir[i] = -_ir[i];
153         }
154
155         _ir[_M / 2] += 1;
156 }