radiorust/blocks/
resampling.rs

1//! Sample-rate conversion
2
3use crate::bufferpool::*;
4use crate::flow::*;
5use crate::impl_block_trait;
6use crate::math::*;
7use crate::numbers::*;
8use crate::signal::*;
9use crate::windowing::{self, Window};
10
11use tokio::task::spawn;
12
13/// Reduce sample rate
14pub struct Downsampler<Flt> {
15    receiver_connector: ReceiverConnector<Signal<Complex<Flt>>>,
16    sender_connector: SenderConnector<Signal<Complex<Flt>>>,
17}
18
19impl_block_trait! { <Flt> Consumer<Signal<Complex<Flt>>> for Downsampler<Flt> }
20impl_block_trait! { <Flt> Producer<Signal<Complex<Flt>>> for Downsampler<Flt> }
21
22impl<Flt> Downsampler<Flt>
23where
24    Flt: Float,
25{
26    /// Create new `Downsampler` block
27    ///
28    /// This call corresponds to [`Downsampler::with_quality`] with a `quality`
29    /// value of `3.0`.
30    ///
31    /// Connected [`Producer`]s must emit [`Signal::Samples`] with a
32    /// [`sample_rate`] equal to or higher than `output_rate`; otherwise a
33    /// panic occurs.
34    ///
35    /// Aliasing is suppressed for frequencies lower than `bandwidth`.
36    ///
37    /// [`sample_rate`]: Signal::Samples::sample_rate
38    pub fn new(output_chunk_len: usize, output_rate: f64, bandwidth: f64) -> Self {
39        Self::with_quality(output_chunk_len, output_rate, bandwidth, 3.0)
40    }
41    /// Create new `Downsampler` block with `quality` setting
42    ///
43    /// Same as [`Downsampler::new`], but allows to specify a `quality`
44    /// setting, which must be equal to or greater than `1.0`.
45    pub fn with_quality(
46        output_chunk_len: usize,
47        output_rate: f64,
48        bandwidth: f64,
49        quality: f64,
50    ) -> Self {
51        assert!(output_rate >= 0.0, "output sample rate must be positive");
52        assert!(bandwidth >= 0.0, "bandwidth must be positive");
53        assert!(
54            bandwidth < output_rate,
55            "bandwidth must be smaller than output sample rate"
56        );
57        let (mut receiver, receiver_connector) = new_receiver::<Signal<Complex<Flt>>>();
58        let (sender, sender_connector) = new_sender::<Signal<Complex<Flt>>>();
59        let mut buf_pool = ChunkBufPool::<Complex<Flt>>::new();
60        let mut output_chunk = buf_pool.get_with_capacity(output_chunk_len);
61        spawn(async move {
62            let margin = (output_rate - bandwidth) / 2.0;
63            let mut prev_input_rate: Option<f64> = None;
64            let mut ir: Vec<Flt> = Default::default();
65            let mut ringbuf: Vec<Complex<Flt>> = Default::default();
66            let mut ringbuf_pos: usize = Default::default();
67            let mut pos: f64 = Default::default();
68            loop {
69                let Ok(signal) = receiver.recv().await else { return; };
70                match signal {
71                    Signal::Samples {
72                        sample_rate: input_rate,
73                        chunk: input_chunk,
74                    } => {
75                        if Some(input_rate) != prev_input_rate {
76                            prev_input_rate = Some(input_rate);
77                            assert!(input_rate >= 0.0, "input sample rate must be positive");
78                            assert!(
79                                input_rate >= output_rate,
80                                "input sample rate must be greater than or equal to output sample rate"
81                            );
82                            let ir_len: usize = (input_rate / margin * quality).ceil() as usize;
83                            assert!(ir_len > 0);
84                            let ir_len_flt = ir_len as f64;
85                            let window = windowing::Kaiser::with_null_at_bin(
86                                ir_len_flt * margin / input_rate,
87                            );
88                            let mut ir_buf: Vec<f64> = Vec::with_capacity(ir_len);
89                            let mut energy = 0.0;
90                            for i in 0..ir_len {
91                                let x = (i as f64 + 0.5) - ir_len_flt / 2.0;
92                                let y = sinc(x * output_rate / input_rate)
93                                    * window.relative_value_at(x * 2.0 / ir_len_flt);
94                                ir_buf.push(y);
95                                energy += y * y;
96                            }
97                            let scale = energy.sqrt().recip();
98                            ir = ir_buf.into_iter().map(|y| flt!(y * scale)).collect();
99                            ringbuf = vec![Complex::from(Flt::zero()); ir_len];
100                            ringbuf_pos = 0;
101                            pos = 0.0;
102                        }
103                        for &sample in input_chunk.iter() {
104                            ringbuf[ringbuf_pos] = sample;
105                            ringbuf_pos += 1;
106                            if ringbuf_pos == ir.len() {
107                                ringbuf_pos = 0;
108                            }
109                            pos += output_rate;
110                            if pos >= input_rate {
111                                pos -= input_rate;
112                                let mut sum: Complex<Flt> = Complex::from(Flt::zero());
113                                let mut ir_iter = ir.iter();
114                                let mut next_ir = || *ir_iter.next().unwrap();
115                                for i in ringbuf_pos..ir.len() {
116                                    sum += ringbuf[i] * next_ir();
117                                }
118                                for i in 0..ringbuf_pos {
119                                    sum += ringbuf[i] * next_ir();
120                                }
121                                output_chunk.push(sum);
122                                if output_chunk.len() >= output_chunk_len {
123                                    let Ok(()) = sender
124                                        .send(Signal::Samples {
125                                            sample_rate: output_rate,
126                                            chunk: output_chunk.finalize(),
127                                        })
128                                        .await
129                                    else { return; };
130                                    output_chunk = buf_pool.get_with_capacity(output_chunk_len);
131                                }
132                            }
133                        }
134                    }
135                    event @ Signal::Event { .. } => {
136                        let Ok(()) = sender.send(event).await else { return; };
137                    }
138                }
139            }
140        });
141        Self {
142            receiver_connector,
143            sender_connector,
144        }
145    }
146}
147
148/// Increase sample rate
149pub struct Upsampler<Flt> {
150    receiver_connector: ReceiverConnector<Signal<Complex<Flt>>>,
151    sender_connector: SenderConnector<Signal<Complex<Flt>>>,
152}
153
154impl_block_trait! { <Flt> Consumer<Signal<Complex<Flt>>> for Upsampler<Flt> }
155impl_block_trait! { <Flt> Producer<Signal<Complex<Flt>>> for Upsampler<Flt> }
156
157impl<Flt> Upsampler<Flt>
158where
159    Flt: Float,
160{
161    /// Create new `Upsampler` block
162    ///
163    /// This call corresponds to [`Upsampler::with_quality`] with a `quality`
164    /// value of `3.0`.
165    ///
166    /// Connected [`Producer`]s must emit [`Signal::Samples`] with a
167    /// [`sample_rate`] equal to or smaller than `output_rate` but larger than
168    /// `bandwidth`; otherwise a panic occurs.
169    ///
170    /// Aliasing is suppressed for frequencies lower than `bandwidth`.
171    ///
172    /// [`sample_rate`]: Signal::Samples::sample_rate
173    pub fn new(output_chunk_len: usize, output_rate: f64, bandwidth: f64) -> Self {
174        Self::with_quality(output_chunk_len, output_rate, bandwidth, 3.0)
175    }
176    /// Create new `Upsampler` block with `quality` setting
177    ///
178    /// Same as [`Upsampler::new`], but allows to specify a `quality` setting,
179    /// which must be equal to or greater than `1.0`.
180    pub fn with_quality(
181        output_chunk_len: usize,
182        output_rate: f64,
183        bandwidth: f64,
184        quality: f64,
185    ) -> Self {
186        assert!(output_rate >= 0.0, "output sample rate must be positive");
187        assert!(bandwidth >= 0.0, "bandwidth must be positive");
188        let (mut receiver, receiver_connector) = new_receiver::<Signal<Complex<Flt>>>();
189        let (sender, sender_connector) = new_sender::<Signal<Complex<Flt>>>();
190        let mut buf_pool = ChunkBufPool::<Complex<Flt>>::new();
191        let mut output_chunk = buf_pool.get_with_capacity(output_chunk_len);
192        spawn(async move {
193            let mut prev_input_rate: Option<f64> = None;
194            let mut ir: Vec<Flt> = Default::default();
195            let mut ringbuf: Vec<Complex<Flt>> = Default::default();
196            let mut ringbuf_pos: usize = Default::default();
197            let mut pos: f64 = Default::default();
198            loop {
199                let Ok(signal) = receiver.recv().await else { return; };
200                match signal {
201                    Signal::Samples {
202                        sample_rate: input_rate,
203                        chunk: input_chunk,
204                    } => {
205                        if Some(input_rate) != prev_input_rate {
206                            prev_input_rate = Some(input_rate);
207                            assert!(input_rate >= 0.0, "input sample rate must be positive");
208                            assert!(
209                                input_rate <= output_rate,
210                                "input sample rate must be smaller than or equal to output sample rate"
211                            );
212                            assert!(
213                                bandwidth < input_rate,
214                                "bandwidth must be smaller than input sample rate"
215                            );
216                            let margin = (input_rate - bandwidth) / 2.0;
217                            let ir_len: usize = (output_rate / margin * quality).ceil() as usize;
218                            assert!(ir_len > 0);
219                            let ir_len_flt = ir_len as f64;
220                            let window = windowing::Kaiser::with_null_at_bin(
221                                ir_len_flt * margin / output_rate,
222                            );
223                            let mut ir_buf: Vec<f64> = Vec::with_capacity(ir_len);
224                            let mut energy = 0.0;
225                            for i in 0..ir_len {
226                                let x = (i as f64 + 0.5) - ir_len_flt / 2.0;
227                                let y = sinc(x * input_rate / output_rate)
228                                    * window.relative_value_at(x * 2.0 / ir_len_flt);
229                                ir_buf.push(y);
230                                energy += y * y;
231                            }
232                            let scale = energy.sqrt().recip();
233                            ir = ir_buf.into_iter().map(|y| flt!(y * scale)).collect();
234                            ringbuf = vec![Complex::from(Flt::zero()); ir_len];
235                            ringbuf_pos = 0;
236                            pos = 0.0;
237                        }
238                        for &sample in input_chunk.iter() {
239                            let mut ir_iter = ir.iter();
240                            let mut next_ir = || *ir_iter.next().unwrap();
241                            for i in ringbuf_pos..ir.len() {
242                                ringbuf[i] += sample * next_ir();
243                            }
244                            for i in 0..ringbuf_pos {
245                                ringbuf[i] += sample * next_ir();
246                            }
247                            while pos < output_rate {
248                                output_chunk.push(ringbuf[ringbuf_pos]);
249                                ringbuf[ringbuf_pos] = Complex::from(Flt::zero());
250                                if output_chunk.len() >= output_chunk_len {
251                                    let Ok(()) = sender
252                                        .send(Signal::Samples {
253                                            sample_rate: output_rate,
254                                            chunk: output_chunk.finalize(),
255                                        })
256                                        .await
257                                    else { return; };
258                                    output_chunk = buf_pool.get_with_capacity(output_chunk_len);
259                                }
260                                ringbuf_pos += 1;
261                                if ringbuf_pos >= ir.len() {
262                                    ringbuf_pos = 0;
263                                }
264                                pos += input_rate;
265                            }
266                            pos -= output_rate;
267                        }
268                    }
269                    event @ Signal::Event { .. } => {
270                        let Ok(()) = sender.send(event).await else { return; };
271                    }
272                }
273            }
274        });
275        Upsampler {
276            receiver_connector,
277            sender_connector,
278        }
279    }
280}
281
282#[cfg(test)]
283mod tests {}