WIP figuring out DRC audio component impl
This commit is contained in:
		
							parent
							
								
									11e014b9ed
								
							
						
					
					
						commit
						a1e28e9838
					
				
					 3 changed files with 169 additions and 2 deletions
				
			
		
							
								
								
									
										168
									
								
								ferretro_components/src/provided/sdl2/audio_ratecontrol.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								ferretro_components/src/provided/sdl2/audio_ratecontrol.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | |||
| use crate::prelude::*; | ||||
| 
 | ||||
| use std::error::Error; | ||||
| 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}; | ||||
| 
 | ||||
| /// Trivially sends the core's audio data to the SDL audio subsystem for playback.
 | ||||
| pub struct Sdl2RateControlledAudioComponent { | ||||
|     sample_rate: f64, | ||||
|     audio_buffer: Vec<i16>, | ||||
|     dest_spec: AudioSpec, | ||||
|     audio_device: AudioDevice<>, | ||||
|     cvt_send: crossbeam_channel::Sender<AudioCVT>, | ||||
|     send: crossbeam_channel::Sender<Vec<i16>>, | ||||
| } | ||||
| 
 | ||||
| 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<bool> { | ||||
|         match Self::make_converter(av_info.timing.sample_rate, &self.dest_spec) { | ||||
|             Ok(converter) => { | ||||
|                 self.cvt_send.send(converter); | ||||
|                 self.sample_rate = av_info.timing.sample_rate; | ||||
|                 Some(true) | ||||
|             } | ||||
|             Err(_) => Some(false), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl RetroComponent for Sdl2RateControlledAudioComponent { | ||||
|     fn post_load_game(&mut self, _retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box<dyn Error>> { | ||||
|         self.audio_device.resume(); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sdl2RateControlledAudioComponent { | ||||
|     pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> { | ||||
|         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 dest_spec_opt = None; | ||||
|         let audio_device = audio | ||||
|             .open_playback(None, &desired_spec, |dest_spec| { | ||||
|                 dest_spec_opt = Some(dest_spec.clone()); | ||||
|                 let converter = Self::make_converter(src_freq, &dest_spec).unwrap(); | ||||
|                 let callback: Box<dyn AudioCallback> = match dest_spec.format { | ||||
|                     AudioFormat::U8 => Box::new( | ||||
|                         MySdlAudio::<u8> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                     AudioFormat::S8 => Box::new( | ||||
|                         MySdlAudio::<i8> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                     AudioFormat::U16LSB | AudioFormat::U16MSB => Box::new( | ||||
|                         MySdlAudio::<u16> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                     AudioFormat::S16LSB | AudioFormat::S16MSB => Box::new( | ||||
|                         MySdlAudio::<i16> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                     AudioFormat::S32LSB | AudioFormat::S32MSB => Box::new( | ||||
|                         MySdlAudio::<i32> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                     AudioFormat::F32LSB | AudioFormat::F32MSB => Box::new( | ||||
|                         MySdlAudio::<f32> { converter, dest_spec, cvt_recv, recv, } | ||||
|                     ), | ||||
|                 }; | ||||
|                 callback | ||||
|             }) | ||||
|             .unwrap(); | ||||
| 
 | ||||
|         Ok(Sdl2RateControlledAudioComponent { | ||||
|             sample_rate: src_freq, | ||||
|             audio_buffer: Default::default(), | ||||
|             dest_spec: dest_spec_opt.unwrap(), | ||||
|             audio_device, | ||||
|             cvt_send, | ||||
|             send, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fn make_converter(src_freq: f64, dest_spec: &AudioSpec) -> Result<AudioCVT, String> { | ||||
|         AudioCVT::new( | ||||
|             AudioFormat::S16LSB, | ||||
|             2, | ||||
|             (src_freq * 1000).round() as i32, | ||||
|             dest_spec.format, | ||||
|             dest_spec.channels, | ||||
|             dest_spec.freq * 1000, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     pub fn silence_buffer(&self) { | ||||
|         let _ = self.send.try_send(vec![0; self.dest_spec.samples as usize * self.dest_spec.channels as usize]); | ||||
|     } | ||||
| 
 | ||||
|     fn send_audio_samples(&mut self) { | ||||
|         let stereo_samples = self.dest_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.send.try_send(msg); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| struct MySdlAudio<C: AudioFormatNum> { | ||||
|     converter: AudioCVT, | ||||
|     dest_spec: AudioSpec, | ||||
|     cvt_recv: crossbeam_channel::Receiver<AudioCVT>, | ||||
|     recv: crossbeam_channel::Receiver<Vec<i16>>, | ||||
| } | ||||
| 
 | ||||
| impl<C: AudioFormatNum> MySdlAudio<C> { | ||||
|     const BYTE_SIZE: usize = size_of::<Self::Channel>(); | ||||
| } | ||||
| 
 | ||||
| impl<C: AudioFormatNum> AudioCallback for MySdlAudio<C> { | ||||
|     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 mut 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) }; | ||||
| 
 | ||||
|             out.copy_from_slice(&resampled[..out.len()]); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,8 +1,6 @@ | |||
| use crate::base::ControlFlow; | ||||
| use crate::prelude::*; | ||||
| 
 | ||||
| use std::os::raw::c_uint; | ||||
| 
 | ||||
| use sdl2::gfx::framerate::FPSManager; | ||||
| 
 | ||||
| pub struct SimpleSdl2FramerateLimitComponent { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| //! with SDL2.
 | ||||
| 
 | ||||
| mod audio; | ||||
| mod audio_ratecontrol; | ||||
| mod canvas; | ||||
| mod fps; | ||||
| mod gamepad; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 lifning
						lifning