use crate::prelude::*; use std::error::Error; use std::path::Path; use std::time::Duration; use sdl2::Sdl; use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired}; struct MySdlAudio { audio_spec: AudioSpec, audio_receiver: crossbeam_channel::Receiver>, } impl AudioCallback for MySdlAudio { type Channel = i16; fn callback(&mut self, out: &mut [Self::Channel]) { if self.audio_spec.format == AudioFormat::S16LSB { if let Ok(samples) = self.audio_receiver.recv_timeout(Duration::from_millis(500)) { out.copy_from_slice(&samples[..out.len()]); } } } } pub struct SimpleSdl2AudioComponent { sample_rate: f64, audio_buffer: Vec, audio_spec: AudioSpec, audio_device: AudioDevice, audio_sender: crossbeam_channel::Sender>, } impl RetroCallbacks for SimpleSdl2AudioComponent { fn audio_sample(&mut self, left: i16, right: i16) { self.audio_buffer.push(left); self.audio_buffer.push(right); self.send_audio_samples() } fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { self.audio_buffer.extend(stereo_pcm); self.send_audio_samples(); stereo_pcm.len() } fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { self.sample_rate = av_info.timing.sample_rate; // TODO: change/reinitialize Some(true) } } impl RetroComponent for SimpleSdl2AudioComponent { fn post_load_game(&mut self, _retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box> { self.audio_device.resume(); Ok(()) } } impl SimpleSdl2AudioComponent { pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Self { let mut sample_rate = 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 sample_rate == 0.0 { sample_rate = 32040.0; } let (audio_sender, audio_receiver) = crossbeam_channel::bounded(2); let audio = sdl_context.audio().unwrap(); let desired_spec = AudioSpecDesired { freq: Some(sample_rate.round() as i32), channels: Some(2), samples: None, }; let mut audio_spec = None; let audio_device = audio .open_playback(None, &desired_spec, |spec| { if spec.format != AudioFormat::S16LSB { eprintln!("unsupported audio format {:?}", spec.format); } audio_spec = Some(spec.clone()); MySdlAudio { audio_spec: spec, audio_receiver, } }) .unwrap(); SimpleSdl2AudioComponent { sample_rate, audio_buffer: Default::default(), audio_spec: audio_spec.unwrap(), audio_device, audio_sender, } } fn send_audio_samples(&mut self) { let stereo_samples = self.audio_spec.samples as usize * 2; 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.audio_sender.try_send(msg); } } }