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