WIP: refactor ratecontrol audio component to use SDL AudioQueue, which might just be better for the Simple audio component too
This commit is contained in:
parent
5451aa735d
commit
f60b2c579c
|
@ -1,91 +1,95 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::mem::{size_of, MaybeUninit};
|
use std::mem::size_of;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use sdl2::Sdl;
|
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 {
|
pub struct Sdl2RateControlledAudioComponent {
|
||||||
sample_rate: f64,
|
src_freq: f64,
|
||||||
dest_over_source_freq: f64,
|
|
||||||
audio_buffer: Vec<i16>,
|
audio_buffer: Vec<i16>,
|
||||||
spec: AudioSpec,
|
queue: AudioQueue<i16>,
|
||||||
audio_device: AudioDevice<MySdlAudio<i16>>,
|
}
|
||||||
cvt_send: crossbeam_channel::Sender<AudioCVT>,
|
|
||||||
send: crossbeam_channel::Sender<Vec<i16>>,
|
fn resample<C: AudioFormatNum>(converter: &AudioCVT, mut samples: Vec<C>) -> Vec<C> {
|
||||||
|
let sample_size = size_of::<C>();
|
||||||
|
|
||||||
|
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 {
|
impl RetroCallbacks for Sdl2RateControlledAudioComponent {
|
||||||
fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize {
|
fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize {
|
||||||
self.audio_buffer.extend(stereo_pcm);
|
self.audio_buffer.extend(stereo_pcm);
|
||||||
self.send_audio_samples();
|
|
||||||
stereo_pcm.len() / 2
|
stereo_pcm.len() / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option<bool> {
|
fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option<bool> {
|
||||||
match Self::make_converter(av_info.timing.sample_rate, &self.spec).ok()
|
if Self::make_converter(av_info.timing.sample_rate, self.queue.spec()).is_ok() {
|
||||||
.and_then(|converter| self.cvt_send.send(converter).ok()) {
|
self.src_freq = av_info.timing.sample_rate;
|
||||||
Some(()) => {
|
Some(true)
|
||||||
self.sample_rate = av_info.timing.sample_rate;
|
} else {
|
||||||
Some(true)
|
Some(false)
|
||||||
}
|
|
||||||
None => Some(false),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroComponent for Sdl2RateControlledAudioComponent {
|
impl RetroComponent for Sdl2RateControlledAudioComponent {
|
||||||
fn post_load_game(&mut self, _retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box<dyn Error>> {
|
fn post_load_game(&mut self, _retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box<dyn Error>> {
|
||||||
self.audio_device.resume();
|
self.queue.resume();
|
||||||
Ok(())
|
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 {
|
impl Sdl2RateControlledAudioComponent {
|
||||||
pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> {
|
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 audio = sdl_context.audio().unwrap();
|
||||||
let desired_spec = AudioSpecDesired {
|
let desired_spec = AudioSpecDesired {
|
||||||
freq: None,
|
freq: None,
|
||||||
channels: Some(2),
|
channels: Some(2),
|
||||||
samples: None,
|
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 {
|
Ok(Sdl2RateControlledAudioComponent {
|
||||||
sample_rate: src_freq,
|
src_freq,
|
||||||
dest_over_source_freq,
|
|
||||||
audio_buffer: Default::default(),
|
audio_buffer: Default::default(),
|
||||||
spec,
|
queue,
|
||||||
audio_device,
|
|
||||||
cvt_send,
|
|
||||||
send,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,58 +106,4 @@ impl Sdl2RateControlledAudioComponent {
|
||||||
dest_spec.freq * 64,
|
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<C: AudioFormatNum + Send + Copy + 'static> {
|
|
||||||
converter: AudioCVT,
|
|
||||||
cvt_recv: crossbeam_channel::Receiver<AudioCVT>,
|
|
||||||
recv: crossbeam_channel::Receiver<Vec<C>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<C: AudioFormatNum + Send + Copy + 'static> Send for MySdlAudio<C> {}
|
|
||||||
|
|
||||||
impl<C: AudioFormatNum + Send + Copy + 'static> MySdlAudio<C> {
|
|
||||||
const BYTE_SIZE: usize = size_of::<<Self as AudioCallback>::Channel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: AudioFormatNum + Send + Copy + 'static> 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 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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue