From 08bd33b4d4d8af509b352ef9ea701591c065b299 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 14 Jul 2021 22:35:37 -0500 Subject: [PATCH] fix(snd): replace Audio Sync solution with a faster one --- src/bus.rs | 8 ++++-- src/cpu.rs | 4 ++- src/sound.rs | 75 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index c99f11b..1b4a3e7 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -70,8 +70,12 @@ impl Bus { self.snd.set_audio_src(sender) } - pub(crate) fn is_mpsc_still_full(&mut self) -> bool { - self.snd.is_mpsc_still_full() + pub(crate) fn is_full(&self) -> bool { + self.snd.is_full() + } + + pub(crate) fn flush_samples(&mut self) { + self.snd.flush_samples() } pub(crate) fn clock(&mut self) { diff --git a/src/cpu.rs b/src/cpu.rs index 4b9bafa..60f2aa6 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -128,9 +128,11 @@ impl Cpu { let mut elapsed = 0x00; let pending: u32 = cycles.into(); while elapsed < pending { - if !self.bus.is_mpsc_still_full() { + if !self.bus.is_full() { self.bus.clock(); elapsed += 1; + } else { + self.bus.flush_samples(); } } diff --git a/src/sound.rs b/src/sound.rs index 87c812e..58b9f69 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -1,3 +1,5 @@ +use std::collections::VecDeque; + use bitfield::bitfield; use crossbeam_channel::{Receiver, Sender, TrySendError}; use rodio::Source; @@ -7,8 +9,8 @@ use crate::emu::SM83_CLOCK_SPEED; const WAVE_PATTERN_RAM_LEN: usize = 0x10; const SAMPLE_RATE: u32 = 48000; // Hz -const AUDIO_BUFFER_LEN: usize = 1024; -const CHANNEL_COUNT: u16 = 2; +const AUDIO_BUFFER_LEN: usize = 512; +const CHANNEL_COUNT: usize = 2; const SAMPLE_INCREMENT: u64 = SAMPLE_RATE as u64; #[derive(Default, Debug, Clone)] @@ -30,7 +32,7 @@ pub(crate) struct Sound { sender: Option>, sample_counter: u64, - is_mpsc_full: bool, + buffer: AudioBuffer<(f32, f32)>, } impl Sound { @@ -70,7 +72,7 @@ impl Sound { self.div_prev = Some(bit_5); - if self.sample_counter >= SM83_CLOCK_SPEED { + if self.sender.is_some() && self.sample_counter >= SM83_CLOCK_SPEED { self.sample_counter %= SM83_CLOCK_SPEED; // Sample the APU @@ -93,14 +95,7 @@ impl Sound { let left = (ch1_left + ch2_left + ch3_left + ch4_left) / 4.0; let right = (ch1_right + ch2_right + ch3_right + ch4_right) / 4.0; - if let Some(s) = self.sender.as_ref() { - let _ = s.send_samples(left, right).map_err(|e| match e { - TrySendError::Full(_) => self.is_mpsc_full = true, - TrySendError::Disconnected(_) => { - panic!("Audio Sample MPSC Channel Disconnected") - } - }); - } + self.buffer.push_back((left, right)); } } @@ -108,12 +103,25 @@ impl Sound { self.sender = Some(sender); } - pub(crate) fn is_mpsc_still_full(&mut self) -> bool { - if let Some(sender) = self.sender.as_ref() { - self.is_mpsc_full = sender.is_full(); - } + pub(crate) fn is_full(&self) -> bool { + self.buffer.len() >= AUDIO_BUFFER_LEN * CHANNEL_COUNT + } - self.is_mpsc_full + pub(crate) fn flush_samples(&mut self) { + if let Some(sender) = self.sender.as_ref() { + while self.buffer.len() >= CHANNEL_COUNT { + match self.buffer.pop_front() { + Some((left, right)) => { + sender + .send_samples(left, right) + .expect("Successfully sent samples across threads"); + } + None => unreachable!( + "While loop ensures that there are at least two items in AudioBuffer" + ), + } + } + } } fn clock_length(freq_hi: &FrequencyHigh, length_timer: &mut u16, enabled: &mut bool) { @@ -1195,10 +1203,6 @@ impl AudioSender { self.inner.try_send(left).and(self.inner.try_send(right))?; Ok(()) } - - fn is_full(&self) -> bool { - self.inner.is_full() - } } pub struct AudioReceiver { @@ -1224,7 +1228,7 @@ impl Source for AudioReceiver { fn channels(&self) -> u16 { // The Gameboy supports two channels - CHANNEL_COUNT + CHANNEL_COUNT as u16 } fn sample_rate(&self) -> u32 { @@ -1236,3 +1240,30 @@ impl Source for AudioReceiver { None } } + +#[derive(Debug, Clone)] +struct AudioBuffer { + inner: VecDeque, +} + +impl Default for AudioBuffer { + fn default() -> Self { + Self { + inner: VecDeque::with_capacity(AUDIO_BUFFER_LEN * CHANNEL_COUNT), + } + } +} + +impl AudioBuffer { + pub fn push_back(&mut self, value: T) { + self.inner.push_back(value) + } + + pub fn pop_front(&mut self) -> Option { + self.inner.pop_front() + } + + pub fn len(&self) -> usize { + self.inner.len() + } +}