diff options
author | Astatin <[email protected]> | 2025-07-22 13:18:50 +0200 |
---|---|---|
committer | Astatin <[email protected]> | 2025-07-22 13:18:50 +0200 |
commit | d630494d65129393645a414cf0ea1adbe9c401c4 (patch) | |
tree | 42dbbac277bd5ff96a1905752083eb5403be0b31 | |
parent | c6972abff6c81565a41df8731509435274a80c1f (diff) |
Make audio time accurate using audio buffers
-rw-r--r-- | src/audio.rs | 10 | ||||
-rw-r--r-- | src/desktop/audio.rs | 45 | ||||
-rw-r--r-- | src/io.rs | 11 |
3 files changed, 56 insertions, 10 deletions
diff --git a/src/audio.rs b/src/audio.rs index b8f6aa9..427ca26 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -293,7 +293,7 @@ impl io::Wave for NoiseWave { } #[derive(Clone, Debug)] -struct MutableWave { +pub struct MutableWave { wave_ch1: Arc<Mutex<Option<Wave>>>, wave_ch2: Arc<Mutex<Option<Wave>>>, wave_ch3: Arc<Mutex<Option<Wave>>>, @@ -583,7 +583,7 @@ impl AudioNoiseChannel { } pub struct Channels<A: Audio> { - _audio: A, + audio: A, pub ch1: AudioSquareChannel, pub ch2: AudioSquareChannel, @@ -606,11 +606,15 @@ impl<A: Audio> Channels<A> { )); Self { - _audio: audio, + audio: audio, ch1: AudioSquareChannel::new(wave_ch1), ch2: AudioSquareChannel::new(wave_ch2), ch3: AudioCustomChannel::new(wave_ch3), ch4: AudioNoiseChannel::new(wave_ch4), } } + + pub fn next(&mut self) { + self.audio.next(); + } } diff --git a/src/desktop/audio.rs b/src/desktop/audio.rs index 2438d01..486b41c 100644 --- a/src/desktop/audio.rs +++ b/src/desktop/audio.rs @@ -1,12 +1,18 @@ use rodio::{OutputStream, Sink, Source}; -use crate::audio::SAMPLE_RATE; +use crate::audio::{SAMPLE_RATE, MutableWave}; use crate::io::{Wave, Audio}; use std::time::Duration; +use std::mem; + +const BUFFER_SIZE: usize = 1024; pub struct RodioAudio { _stream: OutputStream, - _sink: Sink, + sink: Sink, + wave: RodioWave<MutableWave>, + buffer: Box<[f32; BUFFER_SIZE]>, + buffer_i: usize, } struct RodioWave<W: Wave + Send + 'static>(W, usize); @@ -24,7 +30,17 @@ impl<W: Wave + Send + 'static> Iterator for RodioWave<W> } } -impl<W: Wave + Send + 'static> Source for RodioWave<W> +struct RodioBuffer<I: Iterator<Item = f32>>(I); + +impl<I: Iterator<Item = f32>> Iterator for RodioBuffer<I> { + type Item = f32; + + fn next(&mut self) -> Option<Self::Item> { + self.0.next() + } +} + +impl<I: Iterator<Item = f32>> Source for RodioBuffer<I> { fn current_frame_len(&self) -> Option<usize> { None @@ -44,15 +60,32 @@ impl<W: Wave + Send + 'static> Source for RodioWave<W> } impl Audio for RodioAudio { - fn new<S: Wave + Send + 'static>(wave: S) -> Self { + fn new(wave: MutableWave) -> Self { let (stream, stream_handle) = OutputStream::try_default().unwrap(); let sink = Sink::try_new(&stream_handle).unwrap(); - sink.append(RodioWave(wave, 0)); + let wave = RodioWave(wave, 0); RodioAudio { _stream: stream, - _sink: sink, + sink: sink, + wave, + buffer: Box::new([0.0; BUFFER_SIZE]), + buffer_i: 0, + } + } + + fn next(&mut self) { + if let Some(v) = self.wave.next() { + self.buffer[self.buffer_i] = v; + self.buffer_i += 1; + + if self.buffer_i == BUFFER_SIZE { + self.buffer_i = 0; + let mut buffer = Box::new([0.0; BUFFER_SIZE]); + mem::swap(&mut self.buffer, &mut buffer); + self.sink.append(RodioBuffer(buffer.into_iter())); + } } } } @@ -1,6 +1,7 @@ use std::time::SystemTime; use std::{thread, time}; +use crate::audio::MutableWave; use crate::consts; use crate::state::GBState; @@ -65,7 +66,8 @@ pub trait Wave { } pub trait Audio { - fn new<S: Wave + Send + 'static>(wave: S) -> Self; + fn new(wave: MutableWave) -> Self; + fn next(&mut self); } pub trait LoadSave @@ -147,6 +149,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, let mut total_cycle_counter: u128 = 0; let mut nanos_sleep: f64 = 0.0; let mut halt_time = 0; + let mut audio_counter = 0; let mut was_previously_halted = false; let mut last_ram_bank_enabled = false; @@ -168,6 +171,12 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, state.cpu.dbg_cycle_counter += c; total_cycle_counter += c as u128; + audio_counter += c; + + if audio_counter >= 32 { + audio_counter -= 32; + state.mem.audio.next(); + } state.div_timer(c); state.tima_timer(c); |