92 lines
2.9 KiB
Rust
92 lines
2.9 KiB
Rust
use crate::prelude::*;
|
|
|
|
use std::error::Error;
|
|
use std::path::Path;
|
|
|
|
use sdl2::Sdl;
|
|
use sdl2::audio::{AudioCVT, AudioFormat, AudioQueue, AudioSpec, AudioSpecDesired};
|
|
|
|
use crate::base::ControlFlow;
|
|
|
|
use super::audio::resample;
|
|
|
|
pub struct Sdl2RateControlledAudioComponent {
|
|
src_freq: f64,
|
|
audio_buffer: Vec<i16>,
|
|
queue: AudioQueue<i16>,
|
|
}
|
|
|
|
impl RetroCallbacks for Sdl2RateControlledAudioComponent {
|
|
fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize {
|
|
self.audio_buffer.extend(stereo_pcm);
|
|
stereo_pcm.len() / 2
|
|
}
|
|
|
|
fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option<bool> {
|
|
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<dyn Error>> {
|
|
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<Self, Box<dyn std::error::Error>> {
|
|
let audio = sdl_context.audio().unwrap();
|
|
let desired_spec = AudioSpecDesired {
|
|
freq: None,
|
|
channels: Some(2),
|
|
samples: None,
|
|
};
|
|
|
|
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 {
|
|
src_freq,
|
|
audio_buffer: Default::default(),
|
|
queue,
|
|
})
|
|
}
|
|
|
|
fn make_converter(src_freq: f64, dest_spec: &AudioSpec) -> Result<AudioCVT, String> {
|
|
// 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::s16_sys(),
|
|
2,
|
|
(src_freq * 64.0).round() as i32,
|
|
dest_spec.format,
|
|
dest_spec.channels,
|
|
dest_spec.freq * 64,
|
|
)
|
|
}
|
|
}
|