ferretro/ferretro_components/src/provided/sdl2/audio_ratecontrol.rs

227 lines
8.3 KiB
Rust

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, AudioQueue};
enum AudioQueueEnum {
U8(AudioQueue<u8>),
S8(AudioQueue<i8>),
U16(AudioQueue<u16>),
S16(AudioQueue<i16>),
S32(AudioQueue<i32>),
F32(AudioQueue<f32>),
}
impl AudioQueueEnum {
fn sample_size(&self) -> usize {
match self {
AudioQueueEnum::U8(_) => size_of::<u8>(),
AudioQueueEnum::S8(_) => size_of::<i8>(),
AudioQueueEnum::U16(_) => size_of::<u16>(),
AudioQueueEnum::S16(_) => size_of::<i16>(),
AudioQueueEnum::S32(_) => size_of::<i32>(),
AudioQueueEnum::F32(_) => size_of::<f32>(),
}
}
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::<i16>();
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<C: AudioFormatNum>(data_u8: Vec<u8>) -> Vec<C> {
const BYTE_SIZE: usize = size_of::<C>();
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<i16>,
queue: AudioQueueEnum,
spec: AudioSpec,
convert: AudioCVT,
//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 {
let samples = stereo_pcm.len() / 2;
self.audio_buffer.extend(stereo_pcm);
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 queue = audio.open_queue::<i16>(None, &desired_spec)?;
/*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
})?;
*/
Ok(Sdl2RateControlledAudioComponent {
sample_rate: src_freq,
queue,
spec,
convert,
/*
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()]);
}
}
}