Skip to content
Snippets Groups Projects
Verified Commit 52ca43d3 authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

feat: test support for APU emulation with CPU

It's stil glitchy and noisy, need to check the problems.
parent 7ac55f96
No related branches found
No related tags found
1 merge request!19Initial tentative audio support 🔉
Pipeline #2268 failed
......@@ -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
......
......@@ -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();
......
......@@ -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"),
));
}
......
......@@ -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();
}
}
......@@ -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 {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment