113 lines
3.5 KiB
Rust
113 lines
3.5 KiB
Rust
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<Vec<i16>>,
|
|
}
|
|
|
|
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<i16>,
|
|
audio_spec: AudioSpec,
|
|
audio_device: AudioDevice<MySdlAudio>,
|
|
audio_sender: crossbeam_channel::Sender<Vec<i16>>,
|
|
}
|
|
|
|
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<bool> {
|
|
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<dyn Error>> {
|
|
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);
|
|
}
|
|
}
|
|
}
|