jevsnes.git / third-party / rust / jgenesis / common / jgenesis-common / src / audio / cubic_resampler.rs
1use crate::audio::{RESAMPLE_SCALING_FACTOR, interpolate_cubic_hermite_6p};
2use bincode::{Decode, Encode};
3use std::array;
4use std::collections::VecDeque;
5
6const BUFFER_LEN: usize = 6;
7
8#[derive(Debug, Clone, Encode, Decode)]
9pub struct CubicResampler<const CHANNELS: usize> {
10    scaled_source_frequency: u64,
11    output_frequency: u64,
12    cycle_counter_product: u64,
13    scaled_x_counter: u64,
14    input_samples: VecDeque<[f64; CHANNELS]>,
15    output_samples: VecDeque<[f64; CHANNELS]>,
16}
17
18impl<const CHANNELS: usize> CubicResampler<CHANNELS> {
19    #[must_use]
20    pub fn new(source_frequency: f64, output_frequency: u64) -> Self {
21        let scaled_source_frequency = scale_source_frequency(source_frequency);
22
23        let mut resampler = Self {
24            scaled_source_frequency,
25            output_frequency,
26            cycle_counter_product: 0,
27            scaled_x_counter: 0,
28            input_samples: VecDeque::with_capacity(2 * BUFFER_LEN),
29            output_samples: VecDeque::with_capacity(48000 / 60 * 2),
30        };
31
32        resampler.input_samples.extend([[0.0; CHANNELS]; BUFFER_LEN]);
33
34        resampler
35    }
36
37    pub fn collect_sample(&mut self, samples: [f64; CHANNELS]) {
38        self.input_samples.push_back(samples);
39
40        let scaled_output_frequency = self.output_frequency * RESAMPLE_SCALING_FACTOR;
41        self.cycle_counter_product += scaled_output_frequency;
42        while self.cycle_counter_product >= self.scaled_source_frequency {
43            self.cycle_counter_product -= self.scaled_source_frequency;
44
45            // Having fewer than N samples in the buffers _shouldn't_ happen, but don't crash if it does
46            while self.input_samples.len() < BUFFER_LEN {
47                self.input_samples
48                    .push_front(self.input_samples.front().copied().unwrap_or([0.0; CHANNELS]));
49            }
50
51            let x = (self.scaled_x_counter as f64) / (scaled_output_frequency as f64);
52            let output: [f64; CHANNELS] = array::from_fn(|channel| {
53                let samples: [f64; 6] = array::from_fn(|i| self.input_samples[i][channel]);
54                interpolate_cubic_hermite_6p(samples, x).clamp(-1.0, 1.0)
55            });
56
57            self.output_samples.push_back(output);
58
59            self.scaled_x_counter += self.scaled_source_frequency;
60            while self.scaled_x_counter >= scaled_output_frequency {
61                self.scaled_x_counter -= scaled_output_frequency;
62
63                self.input_samples.pop_front();
64            }
65        }
66
67        // Having more than N+1 samples in the buffers here also _shouldn't_ happen, but do something reasonable if it does
68        while self.input_samples.len() > BUFFER_LEN + 1 {
69            self.input_samples.pop_front();
70        }
71    }
72
73    #[must_use]
74    pub fn output_buffer_len(&self) -> usize {
75        self.output_samples.len()
76    }
77
78    #[must_use]
79    pub fn output_buffer_pop_front(&mut self) -> Option<[f64; CHANNELS]> {
80        self.output_samples.pop_front()
81    }
82
83    pub fn update_source_frequency(&mut self, source_frequency: f64) {
84        self.convert_sample_distance_counter(source_frequency);
85        self.scaled_source_frequency = scale_source_frequency(source_frequency);
86    }
87
88    pub fn update_output_frequency(&mut self, output_frequency: u64) {
89        self.convert_interpolation_idx_counter(output_frequency);
90        self.output_frequency = output_frequency;
91    }
92
93    fn convert_sample_distance_counter(&mut self, new_source_frequency: f64) {
94        // cycle_counter_product represents the distance towards the next output sample as a percentage
95        // of scaled_source_frequency
96        let output_distance =
97            self.cycle_counter_product as f64 / self.scaled_source_frequency as f64;
98        let new_scaled_source_frequency = new_source_frequency * RESAMPLE_SCALING_FACTOR as f64;
99        self.cycle_counter_product = (output_distance * new_scaled_source_frequency).floor() as u64;
100    }
101
102    fn convert_interpolation_idx_counter(&mut self, new_output_frequency: u64) {
103        // scaled_x_counter represents the interpolation index as a percentage of scaled_output_frequency
104        let scaled_output_frequency = (self.output_frequency * RESAMPLE_SCALING_FACTOR) as f64;
105        let new_scaled_output_frequency = (new_output_frequency * RESAMPLE_SCALING_FACTOR) as f64;
106        let interpolation_idx = self.scaled_x_counter as f64 / scaled_output_frequency;
107        self.scaled_x_counter = (interpolation_idx * new_scaled_output_frequency).floor() as u64;
108    }
109}
110
111fn scale_source_frequency(source_frequency: f64) -> u64 {
112    (source_frequency * RESAMPLE_SCALING_FACTOR as f64).round() as u64
113}