From df21f69128e727a6e2a4d8eaa3b75872cc8482c2 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Tue, 14 Dec 2021 03:15:56 -0800 Subject: [PATCH] attempt to use OS's audio resampling first --- .../examples/multifunction_emulator.rs | 2 +- .../src/provided/sdl2/audio.rs | 86 ++++++++++++------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index d735ff7..a848f37 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -98,7 +98,7 @@ pub fn main() -> Result<(), Box> { } } - emu.register_component(Sdl2RateControlledAudioComponent::new(&mut sdl_context)?)?; + emu.register_component(SimpleSdl2AudioComponent::new(&mut sdl_context)?)?; emu.register_component(SimpleSdl2KeyboardComponent::new(&mut sdl_context)?)?; emu.register_component(SimpleSdl2GamepadComponent::new(&mut sdl_context))?; diff --git a/ferretro_components/src/provided/sdl2/audio.rs b/ferretro_components/src/provided/sdl2/audio.rs index 96c19bc..3023024 100644 --- a/ferretro_components/src/provided/sdl2/audio.rs +++ b/ferretro_components/src/provided/sdl2/audio.rs @@ -10,9 +10,11 @@ use sdl2::audio::{AudioCVT, AudioFormat, AudioFormatNum, AudioQueue, AudioSpec, use crate::base::ControlFlow; pub struct SimpleSdl2AudioComponent { + sdl_audio: sdl2::AudioSubsystem, src_freq: f64, audio_buffer: Vec, - queue: AudioQueue, + queue: Option>, + must_resample: bool, started: bool, } @@ -51,9 +53,13 @@ impl RetroCallbacks for SimpleSdl2AudioComponent { } fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { - if Self::make_converter(av_info.timing.sample_rate, self.queue.spec()).is_ok() { - self.src_freq = av_info.timing.sample_rate; - Some(true) + if let Some(queue) = &self.queue { + if Self::make_converter(av_info.timing.sample_rate, queue.spec()).is_ok() { + self.src_freq = av_info.timing.sample_rate; + Some(true) + } else { + Some(false) + } } else { Some(false) } @@ -62,29 +68,53 @@ impl RetroCallbacks for SimpleSdl2AudioComponent { impl RetroComponent for SimpleSdl2AudioComponent { fn post_load_game(&mut self, retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box> { - self.src_freq = retro.get_system_av_info().timing.sample_rate; - self.queue.resume(); + let timing = retro.get_system_av_info().timing; + self.src_freq = timing.sample_rate; + + let samples_per_frame = timing.sample_rate / timing.fps; + + let desired_spec = AudioSpecDesired { + freq: Some(self.src_freq.round() as i32), + channels: Some(2), + samples: Some((2.0 * samples_per_frame).ceil() as u16), + }; + + let queue = AudioQueue::open_queue(&self.sdl_audio, None, &desired_spec)?; + if queue.spec().freq != desired_spec.freq.unwrap() { + self.must_resample = true; + eprintln!("warning: using naive resampling"); + } + queue.resume(); + + self.queue = Some(queue); self.started = false; Ok(()) } fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { if self.src_freq != 0.0 { - match Self::make_converter(self.src_freq, self.queue.spec()) { - Ok(converter) => { - if !self.audio_buffer.is_empty() { - let mut samples = std::mem::take(&mut self.audio_buffer); - samples = resample(&converter, samples); - self.audio_buffer = samples; - self.queue.queue(&self.audio_buffer); - self.audio_buffer.clear(); + let queue = self.queue.as_mut().unwrap(); + if self.must_resample { + match Self::make_converter(self.src_freq, queue.spec()) { + Ok(converter) => { + if !self.audio_buffer.is_empty() { + let mut samples = std::mem::take(&mut self.audio_buffer); + samples = resample(&converter, samples); + self.audio_buffer = samples; + queue.queue(&self.audio_buffer); + self.audio_buffer.clear(); + } + ControlFlow::Continue + } + Err(e) => { + eprintln!("Audio sample rate conversion failed: {:?}", e); + ControlFlow::Break } - ControlFlow::Continue - } - Err(e) => { - eprintln!("Audio sample rate conversion failed: {:?}", e); - ControlFlow::Break } + } else { + queue.queue(self.audio_buffer.as_slice()); + self.audio_buffer.clear(); + ControlFlow::Continue } } else { ControlFlow::Continue @@ -94,22 +124,14 @@ impl RetroComponent for SimpleSdl2AudioComponent { impl SimpleSdl2AudioComponent { pub fn new(sdl_context: &mut Sdl) -> Result> { - let audio = sdl_context.audio().unwrap(); - let desired_spec = AudioSpecDesired { - freq: None, - channels: Some(2), - samples: None, - }; - - let queue = AudioQueue::open_queue(&audio, None, &desired_spec)?; - - // default to the old libsnes default value until after load_game or set_system_av_info. - let src_freq = 32040.5; + let sdl_audio = sdl_context.audio()?; Ok(SimpleSdl2AudioComponent { - src_freq, + sdl_audio, + src_freq: 32040.5, // nod to the old libsnes default til load_game or set_system_av_info audio_buffer: Default::default(), - queue, + queue: None, + must_resample: false, started: false, }) }