Master System / Game Gear audio resampling code

3use bincode::{Decode, Encode};
4use dsp::design::FilterType;
5use dsp::iir::FirstOrderIirFilter;
6use dsp::sinc::PerformanceSincResampler;
7use jgenesis_common::frontend::{AudioOutput, TimingMode};
9pub const NTSC_MCLK_FREQUENCY: f64 = 53_693_175.0;
10pub const PAL_MCLK_FREQUENCY: f64 = 53_203_424.0;
11
12pub(crate) trait TimingModeExt {
13    fn mclk_frequency(self) -> f64;
14}
15
16impl TimingModeExt for TimingMode {
17    fn mclk_frequency(self) -> f64 {
18        match self {
19            Self::Ntsc => NTSC_MCLK_FREQUENCY,
20            Self::Pal => PAL_MCLK_FREQUENCY,
21        }
22    }
23}
24
25fn compute_psg_frequency(console_mclk_frequency: f64) -> f64 {
26    console_mclk_frequency / 15.0 / 16.0
27}
28
29#[must_use]
30pub fn new_psg_dc_offset(timing_mode: TimingMode) -> FirstOrderIirFilter {
31    dsp::design::butterworth(
32        5.0,
33        compute_psg_frequency(timing_mode.mclk_frequency()),
34        FilterType::HighPass,
35    )
36}
37
38#[derive(Debug, Clone, Encode, Decode)]
39pub(crate) struct AudioResampler {
40    dc_offset_l: FirstOrderIirFilter,
41    dc_offset_r: FirstOrderIirFilter,
42    psg_resampler: PerformanceSincResampler<2>,
43}
44
45impl AudioResampler {
46    pub fn new(timing_mode: TimingMode) -> Self {
47        Self {
48            dc_offset_l: new_psg_dc_offset(timing_mode),
49            dc_offset_r: new_psg_dc_offset(timing_mode),
50            psg_resampler: PerformanceSincResampler::new(
51                compute_psg_frequency(timing_mode.mclk_frequency()),
52                48000.0,
53            ),
54        }
55    }
56
57    pub fn update_timing_mode(&mut self, timing_mode: TimingMode) {
58        let psg_frequency = compute_psg_frequency(timing_mode.mclk_frequency());
59        self.psg_resampler.update_source_frequency(psg_frequency);
60    }
61
62    pub fn collect_sample(&mut self, sample_l: f64, sample_r: f64) {
63        let sample_l = self.dc_offset_l.filter(sample_l);
64        let sample_r = self.dc_offset_r.filter(sample_r);
65        self.psg_resampler.collect([sample_l, sample_r]);
66    }
67
68    pub fn output_samples<A: AudioOutput>(&mut self, audio_output: &mut A) -> Result<(), A::Err> {
69        while let Some([sample_l, sample_r]) = self.psg_resampler.output_buffer_pop_front() {
70            audio_output.push_sample(sample_l, sample_r)?;
71        }
72
73        Ok(())
74    }
75
76    pub fn update_output_frequency(&mut self, output_frequency: u64) {
77        self.psg_resampler.update_output_frequency(output_frequency as f64);
78    }
79}