Triple buffer the sound to avoid skipping

This commit is contained in:
Gwilym Kuiper 2022-06-11 16:08:00 +01:00
parent 7d8890b03e
commit 5f7a1007a2

View file

@ -68,11 +68,10 @@ impl Mixer {
} }
pub fn frame(&mut self) { pub fn frame(&mut self) {
if !self.buffer.has_swapped() { if !self.buffer.should_calculate() {
return; return;
} }
self.buffer.clear();
self.buffer self.buffer
.write_channels(self.channels.iter_mut().flatten()); .write_channels(self.channels.iter_mut().flatten());
} }
@ -151,16 +150,48 @@ fn set_asm_buffer_size() {
#[repr(C, align(4))] #[repr(C, align(4))]
struct SoundBuffer([i8; constants::SOUND_BUFFER_SIZE * 2]); struct SoundBuffer([i8; constants::SOUND_BUFFER_SIZE * 2]);
impl Default for SoundBuffer {
fn default() -> Self {
Self([0; constants::SOUND_BUFFER_SIZE * 2])
}
}
struct MixerBuffer { struct MixerBuffer {
buffer1: SoundBuffer, // alternating bytes left and right channels buffers: [SoundBuffer; 3],
buffer2: SoundBuffer,
state: Mutex<RefCell<MixerBufferState>>, state: Mutex<RefCell<MixerBufferState>>,
} }
struct MixerBufferState { struct MixerBufferState {
buffer_1_active: bool, active_buffer: usize,
swapped: bool, playing_buffer: usize,
}
/// Only returns a valid result if 0 <= x <= 3
const fn mod3_estimate(x: usize) -> usize {
match x & 0b11 {
0 => 0,
1 => 1,
2 => 2,
3 => 0,
_ => unreachable!(),
}
}
impl MixerBufferState {
fn should_calculate(&self) -> bool {
mod3_estimate(self.active_buffer + 1) != mod3_estimate(self.playing_buffer)
}
fn playing_advanced(&mut self) -> usize {
self.playing_buffer = mod3_estimate(self.playing_buffer + 1);
self.playing_buffer
}
fn active_advanced(&mut self) -> usize {
self.active_buffer = mod3_estimate(self.active_buffer + 1);
self.active_buffer
}
} }
impl MixerBuffer { impl MixerBuffer {
@ -168,37 +199,28 @@ impl MixerBuffer {
set_asm_buffer_size(); set_asm_buffer_size();
MixerBuffer { MixerBuffer {
buffer1: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]), buffers: Default::default(),
buffer2: SoundBuffer([0; constants::SOUND_BUFFER_SIZE * 2]),
state: Mutex::new(RefCell::new(MixerBufferState { state: Mutex::new(RefCell::new(MixerBufferState {
buffer_1_active: true, active_buffer: 0,
swapped: false, playing_buffer: 0,
})), })),
} }
} }
fn has_swapped(&self) -> bool { fn should_calculate(&self) -> bool {
free(|cs| self.state.borrow(*cs).borrow().swapped) free(|cs| self.state.borrow(*cs).borrow().should_calculate())
} }
fn swap(&self, cs: &CriticalSection) { fn swap(&self, cs: &CriticalSection) {
let (left_buffer, right_buffer) = let buffer = self.state.borrow(*cs).borrow_mut().playing_advanced();
self.write_buffer(cs).split_at(constants::SOUND_BUFFER_SIZE);
let (left_buffer, right_buffer) = self.buffers[buffer]
.0
.split_at(constants::SOUND_BUFFER_SIZE);
hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left); hw::enable_dma_for_sound(left_buffer, LeftOrRight::Left);
hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right); hw::enable_dma_for_sound(right_buffer, LeftOrRight::Right);
{
let mut state = self.state.borrow(*cs).borrow_mut();
state.buffer_1_active = !state.buffer_1_active;
state.swapped = true;
}
}
fn clear(&mut self) {
self.write_buffer_mut().fill(0);
} }
fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) { fn write_channels<'a>(&mut self, channels: impl Iterator<Item = &'a mut SoundChannel>) {
@ -253,33 +275,12 @@ impl MixerBuffer {
channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE; channel.pos += playback_speed * constants::SOUND_BUFFER_SIZE;
} }
let write_buffer = self.write_buffer_mut(); let write_buffer_index = free(|cs| self.state.borrow(*cs).borrow_mut().active_advanced());
let write_buffer = &mut self.buffers[write_buffer_index].0;
write_buffer.fill(0);
unsafe { unsafe {
agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr()); agb_rs__mixer_collapse(write_buffer.as_mut_ptr(), buffer.as_ptr());
} }
free(|cs| {
self.state.borrow(*cs).borrow_mut().swapped = false;
});
}
fn write_buffer_mut(&mut self) -> &mut [i8; constants::SOUND_BUFFER_SIZE * 2] {
let buffer_1_active = free(|cs| self.state.borrow(*cs).borrow().buffer_1_active);
if buffer_1_active {
&mut self.buffer2.0
} else {
&mut self.buffer1.0
}
}
fn write_buffer(&self, cs: &CriticalSection) -> &[i8; constants::SOUND_BUFFER_SIZE * 2] {
let buffer_1_active = self.state.borrow(*cs).borrow().buffer_1_active;
if buffer_1_active {
&self.buffer2.0
} else {
&self.buffer1.0
}
} }
} }