diff --git a/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs b/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs index 487baef..20bdfbd 100644 --- a/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs +++ b/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs @@ -1,91 +1,95 @@ use crate::prelude::*; use std::error::Error; -use std::mem::{size_of, MaybeUninit}; +use std::mem::size_of; use std::path::Path; use core::time::Duration; use sdl2::Sdl; -use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired, AudioCVT, AudioFormatNum}; +use sdl2::audio::{AudioCVT, AudioFormat, AudioFormatNum, AudioQueue, AudioSpec, AudioSpecDesired}; + +use crate::base::ControlFlow; pub struct Sdl2RateControlledAudioComponent { - sample_rate: f64, - dest_over_source_freq: f64, + src_freq: f64, audio_buffer: Vec, - spec: AudioSpec, - audio_device: AudioDevice>, - cvt_send: crossbeam_channel::Sender, - send: crossbeam_channel::Sender>, + queue: AudioQueue, +} + +fn resample(converter: &AudioCVT, mut samples: Vec) -> Vec { + let sample_size = size_of::(); + + let ptr = samples.as_mut_ptr() as *mut u8; + let length = samples.len() * sample_size; + let capacity = samples.capacity() * sample_size; + std::mem::forget(samples); + let samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; + + let mut resampled_u8 = converter.convert(samples_u8); + + let ptr = resampled_u8.as_mut_ptr() as *mut C; + let length = (resampled_u8.len() + (sample_size - 1)) / sample_size; + let capacity = (resampled_u8.capacity() + (sample_size - 1)) / sample_size; + std::mem::forget(resampled_u8); + unsafe { Vec::from_raw_parts(ptr, length, capacity) } } impl RetroCallbacks for Sdl2RateControlledAudioComponent { fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize { self.audio_buffer.extend(stereo_pcm); - self.send_audio_samples(); stereo_pcm.len() / 2 } fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { - match Self::make_converter(av_info.timing.sample_rate, &self.spec).ok() - .and_then(|converter| self.cvt_send.send(converter).ok()) { - Some(()) => { - self.sample_rate = av_info.timing.sample_rate; - Some(true) - } - None => Some(false), + 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.audio_device.resume(); + 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 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... - // which is too late for this constructor to pass along to SDL, so... - // less than ideal, but default to the old libsnes default value i guess. - if src_freq == 0.0 { - src_freq = 32040.5; - } - - let (send, recv) = crossbeam_channel::bounded(4); - let (cvt_send, cvt_recv) = crossbeam_channel::bounded(4); - let audio = sdl_context.audio().unwrap(); let desired_spec = AudioSpecDesired { freq: None, channels: Some(2), samples: None, }; - let mut spec_uninit = MaybeUninit::uninit(); - let audio_device = audio - .open_playback(None, &desired_spec, |dest_spec| { - if dest_spec.format != AudioFormat::S16LSB && dest_spec.format != AudioFormat::S16MSB { - panic!("unsupported audio format {:?}", dest_spec.format); - } - let converter = Self::make_converter(src_freq, &dest_spec).unwrap(); - spec_uninit.write(dest_spec); - MySdlAudio { converter, cvt_recv, recv, } - })?; - let spec = unsafe { spec_uninit.assume_init() }; - let dest_over_source_freq = spec.freq as f64 / src_freq; + 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 { - sample_rate: src_freq, - dest_over_source_freq, + src_freq, audio_buffer: Default::default(), - spec, - audio_device, - cvt_send, - send, + queue, }) } @@ -102,58 +106,4 @@ impl Sdl2RateControlledAudioComponent { dest_spec.freq * 64, ) } - - pub fn silence_buffer(&self) { - let _ = self.send.try_send(vec![0; self.spec.samples as usize * self.spec.channels as usize]); - } - - fn send_audio_samples(&mut self) { - let stereo_samples = ((self.spec.samples as usize * 2) as f64 * self.dest_over_source_freq) as usize; - while self.audio_buffer.len() >= stereo_samples { - let remainder = self.audio_buffer.split_off(stereo_samples); - let msg = std::mem::replace(&mut self.audio_buffer, remainder); - let _ = self.send.try_send(msg); - } - } -} - -struct MySdlAudio { - converter: AudioCVT, - cvt_recv: crossbeam_channel::Receiver, - recv: crossbeam_channel::Receiver>, -} - -unsafe impl Send for MySdlAudio {} - -impl MySdlAudio { - const BYTE_SIZE: usize = size_of::<::Channel>(); -} - -impl AudioCallback for MySdlAudio { - type Channel = C; - - fn callback(&mut self, out: &mut [Self::Channel]) { - if let Ok(cvt) = self.cvt_recv.try_recv() { - self.converter = cvt; - } - if let Ok(mut samples) = self.recv.recv_timeout(Duration::from_millis(500)) { - - let ptr = samples.as_mut_ptr() as *mut u8; - let length = samples.len() * Self::BYTE_SIZE; - let capacity = samples.capacity() * Self::BYTE_SIZE; - std::mem::forget(samples); - let samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; - - let mut resampled_u8 = self.converter.convert(samples_u8); - - let ptr = resampled_u8.as_mut_ptr() as *mut Self::Channel; - let length = (resampled_u8.len() + (Self::BYTE_SIZE - 1)) / Self::BYTE_SIZE; - let capacity = (resampled_u8.capacity() + (Self::BYTE_SIZE - 1)) / Self::BYTE_SIZE; - std::mem::forget(resampled_u8); - let resampled = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; - - let end = out.len().min(resampled.len()); - out[..end].copy_from_slice(&resampled[..end]); - } - } }