use crate::sdl_frontend::sdl_audio::{AudioConsumer, AudioStats};
use crate::sdl_frontend::sdl_audio_resampler::SdlAudioResampler;
use ringbuf::traits::Consumer;
use sdl2::audio::AudioCallback;
use std::sync::{
Arc,
atomic::{AtomicU32, AtomicUsize, Ordering},
};
pub(crate) struct SdlAudioCallbackImpl {
pub(crate) sample_consumer: AudioConsumer,
pub(crate) volume: Arc<AtomicU32>,
pub(crate) stats: Arc<AudioStats>,
pub(crate) fill_level: Arc<AtomicUsize>,
pub(crate) resampler: SdlAudioResampler,
}
impl AudioCallback for SdlAudioCallbackImpl {
type Channel = f32;
fn callback(&mut self, out: &mut [f32]) {
let volume = f32::from_bits(self.volume.load(Ordering::Relaxed));
let fill_level = self.fill_level.load(Ordering::Relaxed);
self.resampler.update_rate(fill_level);
for sample in out.iter_mut() {
let raw_sample = self.resampler.render_next(&mut || {
let sample = self.sample_consumer.try_pop();
if sample.is_some() {
self.fill_level.fetch_sub(1, Ordering::Relaxed);
}
sample
});
match raw_sample {
Some(raw_sample) => {
self.stats.received_samples.fetch_add(1, Ordering::Relaxed);
const NES_APU_MAX: f32 = 1.177;
let normalized = raw_sample / NES_APU_MAX;
let final_sample = normalized * volume;
*sample = final_sample.clamp(-1.0, 1.0);
}
None => {
self.stats.underrun_samples.fetch_add(1, Ordering::Relaxed);
*sample = 0.0;
}
}
}
}
}