From 9d52f679ba18968c9778295e4f95ab868dcd16df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sun, 26 Feb 2023 01:18:28 +0000
Subject: [PATCH] feat: initial APU and continuous audio

---
 frontends/sdl/src/audio.rs | 29 ++++++++++++++++++-----------
 src/apu.rs                 | 24 ++++++++++++++++++++++++
 src/gb.rs                  |  5 +++--
 src/lib.rs                 |  1 +
 src/mmu.rs                 | 10 ++++++++--
 5 files changed, 54 insertions(+), 15 deletions(-)
 create mode 100644 src/apu.rs

diff --git a/frontends/sdl/src/audio.rs b/frontends/sdl/src/audio.rs
index 7b9562cd..7a41d23a 100644
--- a/frontends/sdl/src/audio.rs
+++ b/frontends/sdl/src/audio.rs
@@ -1,23 +1,27 @@
 use sdl2::{
-    audio::{AudioCallback, AudioSpecDesired},
+    audio::{AudioCallback, AudioDevice, AudioSpecDesired},
     AudioSubsystem, Sdl,
 };
-use std::time::Duration;
 
-struct SquareWave {
+pub struct AudioWave {
     phase_inc: f32,
+
     phase: f32,
+
     volume: f32,
+
+    /// The relative amount of time (as a percentage decimal) the low level
+    /// is going to be present during a period (cycle).
+    /// From [Wikipedia](https://en.wikipedia.org/wiki/Duty_cycle).
+    duty_cycle: f32,
 }
 
-impl AudioCallback for SquareWave {
+impl AudioCallback for AudioWave {
     type Channel = f32;
 
     fn callback(&mut self, out: &mut [f32]) {
         for x in out.iter_mut() {
-            // this is a square wave with 50% of down
-            // and 50% of up values
-            *x = if self.phase <= 0.5 {
+            *x = if self.phase < (1.0 - self.duty_cycle) {
                 self.volume
             } else {
                 -self.volume
@@ -28,6 +32,7 @@ impl AudioCallback for SquareWave {
 }
 
 pub struct Audio {
+    pub device: AudioDevice<AudioWave>,
     pub audio_subsystem: AudioSubsystem,
 }
 
@@ -42,10 +47,11 @@ impl Audio {
         };
 
         let device = audio_subsystem
-            .open_playback(None, &desired_spec, |spec| SquareWave {
+            .open_playback(None, &desired_spec, |spec| AudioWave {
                 phase_inc: 440.0 / spec.freq as f32,
                 phase: 0.0,
                 volume: 0.25,
+                duty_cycle: 0.5,
             })
             .unwrap();
 
@@ -53,8 +59,9 @@ impl Audio {
         // device's activity
         device.resume();
 
-        std::thread::sleep(Duration::from_millis(2000));
-
-        Self { audio_subsystem }
+        Self {
+            device,
+            audio_subsystem,
+        }
     }
 }
diff --git a/src/apu.rs b/src/apu.rs
new file mode 100644
index 00000000..dc25415c
--- /dev/null
+++ b/src/apu.rs
@@ -0,0 +1,24 @@
+pub struct Apu {
+}
+
+impl Apu {
+    pub fn new() -> Self {
+        Self {}
+    }
+
+    pub fn read(&mut self, addr: u16) -> u8 {
+        match addr {
+            0xff26 => 1 as u8, // todo implement this
+            _ => 0x00
+        }
+    }
+
+    pub fn write(&mut self, addr: u16, value: u8) {
+        match addr {
+            0xff26 => {
+                // @todo implement the logic here
+            },
+            _ => {}
+        }
+    }
+}
diff --git a/src/gb.rs b/src/gb.rs
index 8f7074d8..e7a1952f 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -7,7 +7,7 @@ use crate::{
     ppu::{Ppu, PpuMode, Tile, FRAME_BUFFER_SIZE},
     rom::Cartridge,
     timer::Timer,
-    util::read_file,
+    util::read_file, apu::Apu,
 };
 
 #[cfg(feature = "wasm")]
@@ -57,9 +57,10 @@ impl GameBoy {
     #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))]
     pub fn new() -> Self {
         let ppu = Ppu::new();
+        let apu = Apu::new();
         let pad = Pad::new();
         let timer = Timer::new();
-        let mmu = Mmu::new(ppu, pad, timer);
+        let mmu = Mmu::new(ppu, apu, pad, timer);
         let cpu = Cpu::new(mmu);
         Self { cpu }
     }
diff --git a/src/lib.rs b/src/lib.rs
index 0bc2f37c..7decfab2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 #![allow(clippy::uninlined_format_args)]
 
+pub mod apu;
 pub mod cpu;
 pub mod data;
 pub mod gb;
diff --git a/src/mmu.rs b/src/mmu.rs
index 2f277fbb..ed1324b0 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -1,4 +1,4 @@
-use crate::{debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer};
+use crate::{debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer, apu::Apu};
 
 pub const BOOT_SIZE: usize = 2304;
 pub const RAM_SIZE: usize = 8192;
@@ -13,6 +13,11 @@ pub struct Mmu {
     /// some of the access operations.
     ppu: Ppu,
 
+    /// Reference to the APU (Audio Processing Unit) that is going
+    /// to be used both for register reading/writing and to forward
+    /// some of the access operations.
+    apu: Apu,
+
     /// Reference to the Gamepad structure that is going to control
     /// the I/O access to this device.
     pad: Pad,
@@ -44,9 +49,10 @@ pub struct Mmu {
 }
 
 impl Mmu {
-    pub fn new(ppu: Ppu, pad: Pad, timer: Timer) -> Self {
+    pub fn new(ppu: Ppu, apu: Apu, pad: Pad, timer: Timer) -> Self {
         Self {
             ppu,
+            apu,
             pad,
             timer,
             rom: Cartridge::new(),
-- 
GitLab