From 52ca43d3ab2bc021a80f6f5e52cff99c5526032f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Mon, 27 Feb 2023 17:59:19 +0000 Subject: [PATCH] feat: test support for APU emulation with CPU It's stil glitchy and noisy, need to check the problems. --- frontends/sdl/src/audio.rs | 18 ++++++++++++++++-- frontends/sdl/src/graphics.rs | 26 ++++++++++++++++++-------- frontends/sdl/src/main.rs | 6 +++++- src/apu.rs | 26 +++++++++++++++++++++++--- src/gb.rs | 11 +++++++++++ 5 files changed, 73 insertions(+), 14 deletions(-) diff --git a/frontends/sdl/src/audio.rs b/frontends/sdl/src/audio.rs index d0dabfc1..cb028262 100644 --- a/frontends/sdl/src/audio.rs +++ b/frontends/sdl/src/audio.rs @@ -25,6 +25,20 @@ impl AudioCallback for AudioWave { fn callback(&mut self, out: &mut [f32]) { self.ticks = self.ticks.wrapping_add(out.len() as usize); + out.fill(0.0); + + match self.audio_provider.try_lock() { + Ok(provider) => { + for (place, data) in out.iter_mut().zip(provider.output_buffer_apu().iter()) { + *place = *data as f32 / 7.0; + } + } + Err(_) => (), + } + + self.audio_provider.lock().unwrap().clear_buffer_apu(); + + /* for x in out.iter_mut() { *x = match self.audio_provider.lock() { Ok(mut provider) => { @@ -33,7 +47,7 @@ impl AudioCallback for AudioWave { } Err(_) => 0.0, } - } + }*/ } } @@ -49,7 +63,7 @@ impl Audio { let desired_spec = AudioSpecDesired { freq: Some(44100), channels: Some(1), - samples: Some(2), + samples: None, }; let device = audio_subsystem diff --git a/frontends/sdl/src/graphics.rs b/frontends/sdl/src/graphics.rs index 0833705d..62941ddb 100644 --- a/frontends/sdl/src/graphics.rs +++ b/frontends/sdl/src/graphics.rs @@ -19,7 +19,15 @@ impl Graphics { /// Start the SDL sub-system and all of its structure and returns /// a structure with all the needed stuff to handle SDL graphics /// and sound. - pub fn new(sdl: &Sdl, title: &str, width: u32, height: u32, scale: f32) -> Self { + pub fn new( + sdl: &Sdl, + title: &str, + width: u32, + height: u32, + scale: f32, + accelerated: bool, + vsync: bool, + ) -> Self { // initializes the SDL sub-system, making it ready to be // used for display of graphics and audio let video_subsystem = sdl.video().unwrap(); @@ -41,14 +49,16 @@ impl Graphics { .build() .unwrap(); - // creates an accelerated canvas to be used in the drawing + // creates a canvas (according to spec) to be used in the drawing // then clears it so that is can be presented empty initially - let mut canvas = window - .into_canvas() - .accelerated() - .present_vsync() - .build() - .unwrap(); + let mut canvas_builder = window.into_canvas(); + if accelerated { + canvas_builder = canvas_builder.accelerated(); + } + if vsync { + canvas_builder = canvas_builder.present_vsync(); + } + let mut canvas = canvas_builder.build().unwrap(); canvas.set_logical_size(width, height).unwrap(); canvas.clear(); diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs index 5aa26b49..2d97c7c7 100644 --- a/frontends/sdl/src/main.rs +++ b/frontends/sdl/src/main.rs @@ -50,6 +50,7 @@ pub struct Emulator { visual_frequency: f32, next_tick_time: f32, next_tick_time_i: u32, + features: Vec<&'static str>, palettes: [PaletteInfo; 3], palette_index: usize, } @@ -61,9 +62,10 @@ impl Emulator { graphics: None, audio: None, logic_frequency: GameBoy::CPU_FREQ, - visual_frequency: GameBoy::VISUAL_FREQ, + visual_frequency: GameBoy::VISUAL_FREQ * 2.0, next_tick_time: 0.0, next_tick_time_i: 0, + features: vec!["no-vsync"], palettes: [ PaletteInfo::new( "basic", @@ -110,6 +112,8 @@ impl Emulator { DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32, screen_scale, + !self.features.contains(&"no-accelerated"), + !self.features.contains(&"no-vsync"), )); } diff --git a/src/apu.rs b/src/apu.rs index 2c9cd6b0..924e348c 100644 --- a/src/apu.rs +++ b/src/apu.rs @@ -46,6 +46,9 @@ pub struct Apu { ch3_enabled: bool, wave_ram: [u8; 16], + + output_timer: u16, + output_buffer: Vec<u8> } impl Apu { @@ -89,6 +92,9 @@ impl Apu { ch3_enabled: false, wave_ram: [0u8; 16], + + output_timer: 0, + output_buffer: Vec::new() } } @@ -141,7 +147,7 @@ impl Apu { (self.ch1_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); self.ch1_sound_length |= value & 0x40 == 0x40; self.ch1_enabled |= value & 0x80 == 0x80; - println!("CH1 Enabled {}", self.ch1_enabled); + //println!("CH1 Enabled {}", self.ch1_enabled); } // 0xFF16 — NR21: Channel 2 length timer & duty cycle @@ -170,7 +176,7 @@ impl Apu { //self.ch2_sequence = 0; //@todo improve this reset operation } - println!("CH2 Enabled {}", self.ch2_enabled); + //println!("CH2 Enabled {}", self.ch2_enabled); } // 0xFF1A — NR30: Channel 3 DAC enable @@ -195,7 +201,7 @@ impl Apu { (self.ch3_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8); self.ch3_sound_length |= value & 0x40 == 0x40; self.ch3_enabled |= value & 0x80 == 0x80; - println!("CH3 Enabled {}", self.ch3_enabled); + //println!("CH3 Enabled {}", self.ch3_enabled); } // 0xFF30-0xFF3F — Wave pattern RAM @@ -244,9 +250,23 @@ impl Apu { self.ch2_output = 0; } } + + self.output_timer = self.output_timer.saturating_sub(1); + if self.output_timer == 0 { + self.output_buffer.push(self.output()); + self.output_timer = (freq as f32 / 44100.0) as u16; // @todo target sampling rate is hardcoded + } } pub fn output(&self) -> u8 { self.ch1_output + self.ch2_output } + + pub fn output_buffer(&self) -> &Vec<u8> { + &self.output_buffer + } + + pub fn clear_buffer(&mut self) { + self.output_buffer.clear(); + } } diff --git a/src/gb.rs b/src/gb.rs index bb779fe1..8861dc70 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -56,6 +56,8 @@ pub struct Registers { pub trait AudioProvider { fn output_apu(&self) -> u8; fn output_clock_apu(&mut self, cycles: u8, freq: u32) -> u8; + fn output_buffer_apu(&self) -> &Vec<u8>; + fn clear_buffer_apu(&mut self); } #[cfg_attr(feature = "wasm", wasm_bindgen)] @@ -80,6 +82,7 @@ impl GameBoy { pub fn clock(&mut self) -> u8 { let cycles = self.cpu_clock(); self.ppu_clock(cycles); + self.apu_clock(cycles); self.timer_clock(cycles); cycles } @@ -389,6 +392,14 @@ impl AudioProvider for GameBoy { self.apu().clock_f(cycles, freq); self.apu_i().output() } + + fn output_buffer_apu(&self) -> &Vec<u8> { + self.apu_i().output_buffer() + } + + fn clear_buffer_apu(&mut self) { + self.apu().clear_buffer() + } } impl Default for GameBoy { -- GitLab