use crate::prelude::*; use std::error::Error; use std::path::Path; use sdl2::Sdl; use sdl2::audio::{AudioCVT, AudioFormat, AudioQueue, AudioSpec, AudioSpecDesired}; use crate::base::ControlFlow; use super::audio::resample; pub struct Sdl2RateControlledAudioComponent { src_freq: f64, audio_buffer: Vec, queue: AudioQueue, } impl RetroCallbacks for Sdl2RateControlledAudioComponent { fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize { self.audio_buffer.extend(stereo_pcm); stereo_pcm.len() / 2 } 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) } else { Some(false) } } } impl RetroComponent for Sdl2RateControlledAudioComponent { fn post_load_game(&mut self, _retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box> { self.queue.resume(); Ok(()) } fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { if let Ok(converter) = Self::make_converter(self.src_freq, self.queue.spec()) { 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(); } ControlFlow::Continue } } impl Sdl2RateControlledAudioComponent { pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> 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)?; let mut src_freq = retro.get_system_av_info().timing.sample_rate; // HACK: some cores don't report this 'til we get an env call to set_system_av_info, // so we can just default to the old libsnes default value. if src_freq == 0.0 { src_freq = 32040.5; } Ok(Sdl2RateControlledAudioComponent { src_freq, audio_buffer: Default::default(), queue, }) } fn make_converter(src_freq: f64, dest_spec: &AudioSpec) -> Result { // note on the `* 64`: as long as the ratio between src_rate and dst_rate is right, // we should be in the clear -- this is to make up for SDL not giving us floats for // this, we can at least get some quasi-fixed-point precision going on... AudioCVT::new( AudioFormat::s16_sys(), 2, (src_freq * 64.0).round() as i32, dest_spec.format, dest_spec.channels, dest_spec.freq * 64, ) } }