WIP still awful but builds and runs
This commit is contained in:
parent
c505428895
commit
5451aa735d
3 changed files with 44 additions and 110 deletions
|
@ -76,8 +76,8 @@ impl SimpleSdl2AudioComponent {
|
||||||
let mut audio_spec = None;
|
let mut audio_spec = None;
|
||||||
let audio_device = audio
|
let audio_device = audio
|
||||||
.open_playback(None, &desired_spec, |spec| {
|
.open_playback(None, &desired_spec, |spec| {
|
||||||
if spec.format != AudioFormat::S16LSB {
|
if spec.format != AudioFormat::s16_sys() {
|
||||||
eprintln!("unsupported audio format {:?}", spec.format);
|
panic!("unsupported audio format {:?}", spec.format);
|
||||||
}
|
}
|
||||||
audio_spec = Some(spec.clone());
|
audio_spec = Some(spec.clone());
|
||||||
MySdlAudio {
|
MySdlAudio {
|
||||||
|
|
|
@ -1,91 +1,38 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::mem::size_of;
|
use std::mem::{size_of, MaybeUninit};
|
||||||
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, AudioQueue};
|
use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired, AudioCVT, AudioFormatNum};
|
||||||
|
|
||||||
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 {
|
pub struct Sdl2RateControlledAudioComponent {
|
||||||
sample_rate: f64,
|
sample_rate: f64,
|
||||||
//audio_buffer: Vec<i16>,
|
dest_over_source_freq: f64,
|
||||||
queue: AudioQueueEnum,
|
audio_buffer: Vec<i16>,
|
||||||
spec: AudioSpec,
|
spec: AudioSpec,
|
||||||
convert: AudioCVT,
|
audio_device: AudioDevice<MySdlAudio<i16>>,
|
||||||
//audio_device: AudioDevice<>,
|
cvt_send: crossbeam_channel::Sender<AudioCVT>,
|
||||||
//cvt_send: crossbeam_channel::Sender<AudioCVT>,
|
send: crossbeam_channel::Sender<Vec<i16>>,
|
||||||
//send: crossbeam_channel::Sender<Vec<i16>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
let samples = stereo_pcm.len() / 2;
|
|
||||||
|
|
||||||
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.dest_spec) {
|
match Self::make_converter(av_info.timing.sample_rate, &self.spec).ok()
|
||||||
Ok(converter) => {
|
.and_then(|converter| self.cvt_send.send(converter).ok()) {
|
||||||
self.cvt_send.send(converter);
|
Some(()) => {
|
||||||
self.sample_rate = av_info.timing.sample_rate;
|
self.sample_rate = av_info.timing.sample_rate;
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
Err(_) => Some(false),
|
None => Some(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,67 +64,51 @@ impl Sdl2RateControlledAudioComponent {
|
||||||
channels: Some(2),
|
channels: Some(2),
|
||||||
samples: None,
|
samples: None,
|
||||||
};
|
};
|
||||||
let queue = audio.open_queue::<i16>(None, &desired_spec)?;
|
let mut spec_uninit = MaybeUninit::uninit();
|
||||||
/*let mut dest_spec_opt = None;
|
|
||||||
let audio_device = audio
|
let audio_device = audio
|
||||||
.open_playback(None, &desired_spec, |dest_spec| {
|
.open_playback(None, &desired_spec, |dest_spec| {
|
||||||
dest_spec_opt = Some(dest_spec.clone());
|
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();
|
let converter = Self::make_converter(src_freq, &dest_spec).unwrap();
|
||||||
let callback: Box<dyn AudioCallback> = match dest_spec.format {
|
spec_uninit.write(dest_spec);
|
||||||
AudioFormat::U8 => Box::new(
|
MySdlAudio { converter, cvt_recv, recv, }
|
||||||
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
|
|
||||||
})?;
|
})?;
|
||||||
*/
|
let spec = unsafe { spec_uninit.assume_init() };
|
||||||
|
|
||||||
|
let dest_over_source_freq = spec.freq as f64 / src_freq;
|
||||||
|
|
||||||
Ok(Sdl2RateControlledAudioComponent {
|
Ok(Sdl2RateControlledAudioComponent {
|
||||||
sample_rate: src_freq,
|
sample_rate: src_freq,
|
||||||
queue,
|
dest_over_source_freq,
|
||||||
spec,
|
|
||||||
convert,
|
|
||||||
/*
|
|
||||||
audio_buffer: Default::default(),
|
audio_buffer: Default::default(),
|
||||||
dest_spec: dest_spec_opt.unwrap(),
|
spec,
|
||||||
audio_device,
|
audio_device,
|
||||||
cvt_send,
|
cvt_send,
|
||||||
send,*/
|
send,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_converter(src_freq: f64, dest_spec: &AudioSpec) -> Result<AudioCVT, String> {
|
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(
|
AudioCVT::new(
|
||||||
AudioFormat::S16LSB,
|
AudioFormat::S16LSB,
|
||||||
2,
|
2,
|
||||||
(src_freq * 1000).round() as i32,
|
(src_freq * 64.0).round() as i32,
|
||||||
dest_spec.format,
|
dest_spec.format,
|
||||||
dest_spec.channels,
|
dest_spec.channels,
|
||||||
dest_spec.freq * 1000,
|
dest_spec.freq * 64,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn silence_buffer(&self) {
|
pub fn silence_buffer(&self) {
|
||||||
let _ = self.send.try_send(vec![0; self.dest_spec.samples as usize * self.dest_spec.channels as usize]);
|
let _ = self.send.try_send(vec![0; self.spec.samples as usize * self.spec.channels as usize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_audio_samples(&mut self) {
|
fn send_audio_samples(&mut self) {
|
||||||
let stereo_samples = self.dest_spec.samples as usize * 2;
|
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 {
|
while self.audio_buffer.len() >= stereo_samples {
|
||||||
let remainder = self.audio_buffer.split_off(stereo_samples);
|
let remainder = self.audio_buffer.split_off(stereo_samples);
|
||||||
let msg = std::mem::replace(&mut self.audio_buffer, remainder);
|
let msg = std::mem::replace(&mut self.audio_buffer, remainder);
|
||||||
|
@ -186,18 +117,19 @@ impl Sdl2RateControlledAudioComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MySdlAudio<C: AudioFormatNum> {
|
struct MySdlAudio<C: AudioFormatNum + Send + Copy + 'static> {
|
||||||
converter: AudioCVT,
|
converter: AudioCVT,
|
||||||
dest_spec: AudioSpec,
|
|
||||||
cvt_recv: crossbeam_channel::Receiver<AudioCVT>,
|
cvt_recv: crossbeam_channel::Receiver<AudioCVT>,
|
||||||
recv: crossbeam_channel::Receiver<Vec<i16>>,
|
recv: crossbeam_channel::Receiver<Vec<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: AudioFormatNum> MySdlAudio<C> {
|
unsafe impl<C: AudioFormatNum + Send + Copy + 'static> Send for MySdlAudio<C> {}
|
||||||
const BYTE_SIZE: usize = size_of::<Self::Channel>();
|
|
||||||
|
impl<C: AudioFormatNum + Send + Copy + 'static> MySdlAudio<C> {
|
||||||
|
const BYTE_SIZE: usize = size_of::<<Self as AudioCallback>::Channel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: AudioFormatNum> AudioCallback for MySdlAudio<C> {
|
impl<C: AudioFormatNum + Send + Copy + 'static> AudioCallback for MySdlAudio<C> {
|
||||||
type Channel = C;
|
type Channel = C;
|
||||||
|
|
||||||
fn callback(&mut self, out: &mut [Self::Channel]) {
|
fn callback(&mut self, out: &mut [Self::Channel]) {
|
||||||
|
@ -210,7 +142,7 @@ impl<C: AudioFormatNum> AudioCallback for MySdlAudio<C> {
|
||||||
let length = samples.len() * Self::BYTE_SIZE;
|
let length = samples.len() * Self::BYTE_SIZE;
|
||||||
let capacity = samples.capacity() * Self::BYTE_SIZE;
|
let capacity = samples.capacity() * Self::BYTE_SIZE;
|
||||||
std::mem::forget(samples);
|
std::mem::forget(samples);
|
||||||
let mut samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) };
|
let samples_u8 = unsafe { Vec::from_raw_parts(ptr, length, capacity) };
|
||||||
|
|
||||||
let mut resampled_u8 = self.converter.convert(samples_u8);
|
let mut resampled_u8 = self.converter.convert(samples_u8);
|
||||||
|
|
||||||
|
@ -220,7 +152,8 @@ impl<C: AudioFormatNum> AudioCallback for MySdlAudio<C> {
|
||||||
std::mem::forget(resampled_u8);
|
std::mem::forget(resampled_u8);
|
||||||
let resampled = unsafe { Vec::from_raw_parts(ptr, length, capacity) };
|
let resampled = unsafe { Vec::from_raw_parts(ptr, length, capacity) };
|
||||||
|
|
||||||
out.copy_from_slice(&resampled[..out.len()]);
|
let end = out.len().min(resampled.len());
|
||||||
|
out[..end].copy_from_slice(&resampled[..end]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ mod opengl;
|
||||||
mod surface;
|
mod surface;
|
||||||
|
|
||||||
pub use audio::SimpleSdl2AudioComponent;
|
pub use audio::SimpleSdl2AudioComponent;
|
||||||
|
pub use audio_ratecontrol::Sdl2RateControlledAudioComponent;
|
||||||
pub use canvas::SimpleSdl2CanvasComponent;
|
pub use canvas::SimpleSdl2CanvasComponent;
|
||||||
pub use fps::SimpleSdl2FramerateLimitComponent;
|
pub use fps::SimpleSdl2FramerateLimitComponent;
|
||||||
pub use gamepad::SimpleSdl2GamepadComponent;
|
pub use gamepad::SimpleSdl2GamepadComponent;
|
||||||
|
|
Loading…
Add table
Reference in a new issue