rodio/source/
blt.rs

1use crate::common::{ChannelCount, SampleRate};
2use crate::Source;
3use std::f32::consts::PI;
4use std::time::Duration;
5
6use super::SeekError;
7
8// Implemented following http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
9
10/// Internal function that builds a `BltFilter` object.
11pub fn low_pass<I>(input: I, freq: u32) -> BltFilter<I>
12where
13    I: Source<Item = f32>,
14{
15    low_pass_with_q(input, freq, 0.5)
16}
17
18pub fn high_pass<I>(input: I, freq: u32) -> BltFilter<I>
19where
20    I: Source<Item = f32>,
21{
22    high_pass_with_q(input, freq, 0.5)
23}
24
25/// Same as low_pass but allows the q value (bandwidth) to be changed
26pub fn low_pass_with_q<I>(input: I, freq: u32, q: f32) -> BltFilter<I>
27where
28    I: Source<Item = f32>,
29{
30    BltFilter {
31        input,
32        formula: BltFormula::LowPass { freq, q },
33        applier: None,
34        x_n1: 0.0,
35        x_n2: 0.0,
36        y_n1: 0.0,
37        y_n2: 0.0,
38    }
39}
40
41/// Same as high_pass but allows the q value (bandwidth) to be changed
42pub fn high_pass_with_q<I>(input: I, freq: u32, q: f32) -> BltFilter<I>
43where
44    I: Source<Item = f32>,
45{
46    BltFilter {
47        input,
48        formula: BltFormula::HighPass { freq, q },
49        applier: None,
50        x_n1: 0.0,
51        x_n2: 0.0,
52        y_n1: 0.0,
53        y_n2: 0.0,
54    }
55}
56
57/// This applies an audio filter, it can be a high or low pass filter.
58#[derive(Clone, Debug)]
59pub struct BltFilter<I> {
60    input: I,
61    formula: BltFormula,
62    applier: Option<BltApplier>,
63    x_n1: f32,
64    x_n2: f32,
65    y_n1: f32,
66    y_n2: f32,
67}
68
69impl<I> BltFilter<I> {
70    /// Modifies this filter so that it becomes a low-pass filter.
71    pub fn to_low_pass(&mut self, freq: u32) {
72        self.to_low_pass_with_q(freq, 0.5);
73    }
74
75    /// Modifies this filter so that it becomes a high-pass filter
76    pub fn to_high_pass(&mut self, freq: u32) {
77        self.to_high_pass_with_q(freq, 0.5);
78    }
79
80    /// Same as to_low_pass but allows the q value (bandwidth) to be changed
81    pub fn to_low_pass_with_q(&mut self, freq: u32, q: f32) {
82        self.formula = BltFormula::LowPass { freq, q };
83        self.applier = None;
84    }
85
86    /// Same as to_high_pass but allows the q value (bandwidth) to be changed
87    pub fn to_high_pass_with_q(&mut self, freq: u32, q: f32) {
88        self.formula = BltFormula::HighPass { freq, q };
89        self.applier = None;
90    }
91
92    /// Returns a reference to the inner source.
93    #[inline]
94    pub fn inner(&self) -> &I {
95        &self.input
96    }
97
98    /// Returns a mutable reference to the inner source.
99    #[inline]
100    pub fn inner_mut(&mut self) -> &mut I {
101        &mut self.input
102    }
103
104    /// Returns the inner source.
105    #[inline]
106    pub fn into_inner(self) -> I {
107        self.input
108    }
109}
110
111impl<I> Iterator for BltFilter<I>
112where
113    I: Source<Item = f32>,
114{
115    type Item = f32;
116
117    #[inline]
118    fn next(&mut self) -> Option<f32> {
119        let last_in_span = self.input.current_span_len() == Some(1);
120
121        if self.applier.is_none() {
122            self.applier = Some(self.formula.to_applier(self.input.sample_rate()));
123        }
124
125        let sample = self.input.next()?;
126        let result = self
127            .applier
128            .as_ref()
129            .unwrap()
130            .apply(sample, self.x_n1, self.x_n2, self.y_n1, self.y_n2);
131
132        self.y_n2 = self.y_n1;
133        self.x_n2 = self.x_n1;
134        self.y_n1 = result;
135        self.x_n1 = sample;
136
137        if last_in_span {
138            self.applier = None;
139        }
140
141        Some(result)
142    }
143
144    #[inline]
145    fn size_hint(&self) -> (usize, Option<usize>) {
146        self.input.size_hint()
147    }
148}
149
150impl<I> ExactSizeIterator for BltFilter<I> where I: Source<Item = f32> + ExactSizeIterator {}
151
152impl<I> Source for BltFilter<I>
153where
154    I: Source<Item = f32>,
155{
156    #[inline]
157    fn current_span_len(&self) -> Option<usize> {
158        self.input.current_span_len()
159    }
160
161    #[inline]
162    fn channels(&self) -> ChannelCount {
163        self.input.channels()
164    }
165
166    #[inline]
167    fn sample_rate(&self) -> SampleRate {
168        self.input.sample_rate()
169    }
170
171    #[inline]
172    fn total_duration(&self) -> Option<Duration> {
173        self.input.total_duration()
174    }
175
176    #[inline]
177    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
178        self.input.try_seek(pos)
179    }
180}
181
182#[derive(Clone, Debug)]
183enum BltFormula {
184    LowPass { freq: u32, q: f32 },
185    HighPass { freq: u32, q: f32 },
186}
187
188impl BltFormula {
189    fn to_applier(&self, sampling_frequency: u32) -> BltApplier {
190        match *self {
191            BltFormula::LowPass { freq, q } => {
192                let w0 = 2.0 * PI * freq as f32 / sampling_frequency as f32;
193
194                let alpha = w0.sin() / (2.0 * q);
195                let b1 = 1.0 - w0.cos();
196                let b0 = b1 / 2.0;
197                let b2 = b0;
198                let a0 = 1.0 + alpha;
199                let a1 = -2.0 * w0.cos();
200                let a2 = 1.0 - alpha;
201
202                BltApplier {
203                    b0: b0 / a0,
204                    b1: b1 / a0,
205                    b2: b2 / a0,
206                    a1: a1 / a0,
207                    a2: a2 / a0,
208                }
209            }
210            BltFormula::HighPass { freq, q } => {
211                let w0 = 2.0 * PI * freq as f32 / sampling_frequency as f32;
212                let cos_w0 = w0.cos();
213                let alpha = w0.sin() / (2.0 * q);
214
215                let b0 = (1.0 + cos_w0) / 2.0;
216                let b1 = -1.0 - cos_w0;
217                let b2 = b0;
218                let a0 = 1.0 + alpha;
219                let a1 = -2.0 * cos_w0;
220                let a2 = 1.0 - alpha;
221
222                BltApplier {
223                    b0: b0 / a0,
224                    b1: b1 / a0,
225                    b2: b2 / a0,
226                    a1: a1 / a0,
227                    a2: a2 / a0,
228                }
229            }
230        }
231    }
232}
233
234#[derive(Clone, Debug)]
235struct BltApplier {
236    b0: f32,
237    b1: f32,
238    b2: f32,
239    a1: f32,
240    a2: f32,
241}
242
243impl BltApplier {
244    #[inline]
245    fn apply(&self, x_n: f32, x_n1: f32, x_n2: f32, y_n1: f32, y_n2: f32) -> f32 {
246        self.b0 * x_n + self.b1 * x_n1 + self.b2 * x_n2 - self.a1 * y_n1 - self.a2 * y_n2
247    }
248}