From 5451aa735df957bf862ee575d03713d0dbc91033 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Wed, 3 Nov 2021 02:12:38 -0700 Subject: [PATCH] WIP still awful but builds and runs --- .../src/provided/sdl2/audio.rs | 4 +- .../src/provided/sdl2/audio_ratecontrol.rs | 149 +++++------------- ferretro_components/src/provided/sdl2/mod.rs | 1 + 3 files changed, 44 insertions(+), 110 deletions(-) diff --git a/ferretro_components/src/provided/sdl2/audio.rs b/ferretro_components/src/provided/sdl2/audio.rs index bda1fe7..0074acb 100644 --- a/ferretro_components/src/provided/sdl2/audio.rs +++ b/ferretro_components/src/provided/sdl2/audio.rs @@ -76,8 +76,8 @@ impl SimpleSdl2AudioComponent { 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); + if spec.format != AudioFormat::s16_sys() { + panic!("unsupported audio format {:?}", spec.format); } audio_spec = Some(spec.clone()); MySdlAudio { diff --git a/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs b/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs index 466b613..487baef 100644 --- a/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs +++ b/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs @@ -1,91 +1,38 @@ use crate::prelude::*; use std::error::Error; -use std::mem::size_of; +use std::mem::{size_of, MaybeUninit}; use std::path::Path; use core::time::Duration; use sdl2::Sdl; -use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired, AudioCVT, AudioFormatNum, AudioQueue}; +use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired, AudioCVT, AudioFormatNum}; -enum AudioQueueEnum { - U8(AudioQueue), - S8(AudioQueue), - U16(AudioQueue), - S16(AudioQueue), - S32(AudioQueue), - F32(AudioQueue), -} - -impl AudioQueueEnum { - fn sample_size(&self) -> usize { - match self { - AudioQueueEnum::U8(_) => size_of::(), - AudioQueueEnum::S8(_) => size_of::(), - AudioQueueEnum::U16(_) => size_of::(), - AudioQueueEnum::S16(_) => size_of::(), - AudioQueueEnum::S32(_) => size_of::(), - AudioQueueEnum::F32(_) => size_of::(), - } - } - - fn queue(&mut self, samples_i16: &[i16], cvt: &AudioCVT) -> bool { - let data = samples_i16.as_mut_ptr() as *mut u8; - let src_len = samples_i16.len() * size_of::(); - let samples_u8 = unsafe { core::slice::from_raw_parts(data, src_len) }; - - let src = Vec::with_capacity(cvt.capacity(src_len)); - src.copy_from_slice(samples_u8); - let resampled_u8 = cvt.convert(src); - - fn reinterp(data_u8: Vec) -> Vec { - const BYTE_SIZE: usize = size_of::(); - let ptr = data_u8.as_mut_ptr() as *mut Self::Channel; - let length = (data_u8.len() + (BYTE_SIZE - 1)) / BYTE_SIZE; - let capacity = (data_u8.capacity() + (BYTE_SIZE - 1)) / BYTE_SIZE; - std::mem::forget(data_u8); - unsafe { Vec::from_raw_parts(ptr, length, capacity) } - } - - match self { - AudioQueueEnum::U8(q) => q.queue(reinterp(resampled_u8)), - AudioQueueEnum::S8(q) => q.queue(reinterp(resampled_u8)), - AudioQueueEnum::U16(q) => q.queue(reinterp(resampled_u8)), - AudioQueueEnum::S16(q) => q.queue(reinterp(resampled_u8)), - AudioQueueEnum::S32(q) => q.queue(reinterp(resampled_u8)), - AudioQueueEnum::F32(q) => q.queue(reinterp(resampled_u8)), - } - } -} - -/// Trivially sends the core's audio data to the SDL audio subsystem for playback. pub struct Sdl2RateControlledAudioComponent { sample_rate: f64, - //audio_buffer: Vec, - queue: AudioQueueEnum, + dest_over_source_freq: f64, + audio_buffer: Vec, spec: AudioSpec, - convert: AudioCVT, - //audio_device: AudioDevice<>, - //cvt_send: crossbeam_channel::Sender, - //send: crossbeam_channel::Sender>, + audio_device: AudioDevice>, + cvt_send: crossbeam_channel::Sender, + send: crossbeam_channel::Sender>, } impl RetroCallbacks for Sdl2RateControlledAudioComponent { fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize { - let samples = stereo_pcm.len() / 2; - 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.dest_spec) { - Ok(converter) => { - self.cvt_send.send(converter); + 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) } - Err(_) => Some(false), + None => Some(false), } } } @@ -117,67 +64,51 @@ impl Sdl2RateControlledAudioComponent { channels: Some(2), samples: None, }; - let queue = audio.open_queue::(None, &desired_spec)?; - /*let mut dest_spec_opt = None; + let mut spec_uninit = MaybeUninit::uninit(); let audio_device = audio .open_playback(None, &desired_spec, |dest_spec| { - dest_spec_opt = Some(dest_spec.clone()); + 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(); - let callback: Box = match dest_spec.format { - AudioFormat::U8 => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - AudioFormat::S8 => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - AudioFormat::U16LSB | AudioFormat::U16MSB => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - AudioFormat::S16LSB | AudioFormat::S16MSB => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - AudioFormat::S32LSB | AudioFormat::S32MSB => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - AudioFormat::F32LSB | AudioFormat::F32MSB => Box::new( - MySdlAudio:: { converter, dest_spec, cvt_recv, recv, } - ), - }; - callback + 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; Ok(Sdl2RateControlledAudioComponent { sample_rate: src_freq, - queue, - spec, - convert, - /* + dest_over_source_freq, audio_buffer: Default::default(), - dest_spec: dest_spec_opt.unwrap(), + spec, audio_device, cvt_send, - send,*/ + send, }) } 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::S16LSB, 2, - (src_freq * 1000).round() as i32, + (src_freq * 64.0).round() as i32, dest_spec.format, dest_spec.channels, - dest_spec.freq * 1000, + dest_spec.freq * 64, ) } pub fn silence_buffer(&self) { - let _ = self.send.try_send(vec![0; self.dest_spec.samples as usize * self.dest_spec.channels as usize]); + 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.dest_spec.samples as usize * 2; + 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); @@ -186,18 +117,19 @@ impl Sdl2RateControlledAudioComponent { } } -struct MySdlAudio { +struct MySdlAudio { converter: AudioCVT, - dest_spec: AudioSpec, cvt_recv: crossbeam_channel::Receiver, - recv: crossbeam_channel::Receiver>, + recv: crossbeam_channel::Receiver>, } -impl MySdlAudio { - const BYTE_SIZE: usize = size_of::(); +unsafe impl Send for MySdlAudio {} + +impl MySdlAudio { + const BYTE_SIZE: usize = size_of::<::Channel>(); } -impl AudioCallback for MySdlAudio { +impl AudioCallback for MySdlAudio { type Channel = C; fn callback(&mut self, out: &mut [Self::Channel]) { @@ -210,7 +142,7 @@ impl AudioCallback for MySdlAudio { let length = samples.len() * Self::BYTE_SIZE; let capacity = samples.capacity() * Self::BYTE_SIZE; std::mem::forget(samples); - let mut samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; + let samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; let mut resampled_u8 = self.converter.convert(samples_u8); @@ -220,7 +152,8 @@ impl AudioCallback for MySdlAudio { std::mem::forget(resampled_u8); let resampled = unsafe { Vec::from_raw_parts(ptr, length, capacity) }; - out.copy_from_slice(&resampled[..out.len()]); + let end = out.len().min(resampled.len()); + out[..end].copy_from_slice(&resampled[..end]); } } } diff --git a/ferretro_components/src/provided/sdl2/mod.rs b/ferretro_components/src/provided/sdl2/mod.rs index 8c91708..22cf44f 100644 --- a/ferretro_components/src/provided/sdl2/mod.rs +++ b/ferretro_components/src/provided/sdl2/mod.rs @@ -10,6 +10,7 @@ mod opengl; mod surface; pub use audio::SimpleSdl2AudioComponent; +pub use audio_ratecontrol::Sdl2RateControlledAudioComponent; pub use canvas::SimpleSdl2CanvasComponent; pub use fps::SimpleSdl2FramerateLimitComponent; pub use gamepad::SimpleSdl2GamepadComponent;