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

feat: more flexible handling of audio

Still requires work to be able to be able to emulate APU together with the CPU instead of inverting the control.
parent 0f188280
No related branches found
No related tags found
1 merge request!19Initial tentative audio support 🔉
Pipeline #2267 failed
......@@ -28,7 +28,7 @@ impl AudioCallback for AudioWave {
for x in out.iter_mut() {
*x = match self.audio_provider.lock() {
Ok(mut provider) => {
let value = provider.tick_apu(self.spec.freq as u32) as f32 / 7.0;
let value = provider.output_clock_apu(1, self.spec.freq as u32) as f32 / 7.0;
value
}
Err(_) => 0.0,
......@@ -49,7 +49,7 @@ impl Audio {
let desired_spec = AudioSpecDesired {
freq: Some(44100),
channels: Some(1),
samples: None,
samples: Some(2),
};
let device = audio_subsystem
......
......@@ -6,7 +6,7 @@ pub mod graphics;
use audio::Audio;
use boytacean::{
gb::{AudioProvider, GameBoy},
gb::GameBoy,
pad::PadKey,
ppu::{PaletteInfo, PpuMode, DISPLAY_HEIGHT, DISPLAY_WIDTH},
};
......@@ -117,10 +117,6 @@ impl Emulator {
self.audio = Some(Audio::new(sdl, audio_provider));
}
pub fn tick_audio(&mut self, freq: u32) -> u8 {
self.system.lock().unwrap().tick_apu(freq)
}
pub fn load_rom(&mut self, path: &str) {
let mut system = self.system.lock().unwrap();
let rom = system.load_rom_file(path);
......@@ -288,10 +284,13 @@ impl Emulator {
// valid under the current block
let mut system = self.system.lock().unwrap();
// runs the Game Boy clock, this operations should
// include the advance of both the CPU and the PPU
// runs the Game Boy clock, this operation should
// include the advance of both the CPU, PPU, APU
// and any other frequency based component of the system
counter_cycles += system.clock() as u32;
// in case a V-Blank state has been reached a new frame is available
// then the frame must be pushed into SDL for display
if system.ppu_mode() == PpuMode::VBlank && system.ppu_frame() != last_frame
{
// obtains the frame buffer of the Game Boy PPU and uses it
......
......@@ -34,6 +34,18 @@ pub struct Apu {
ch2_wave_length: u16,
ch2_sound_length: bool,
ch2_enabled: bool,
ch3_timer: u16,
ch3_sequence: u8,
ch3_output: u8,
ch3_dac: bool,
ch3_length_timer: u8,
ch3_output_level: u8,
ch3_wave_length: u16,
ch3_sound_length: bool,
ch3_enabled: bool,
wave_ram: [u8; 16],
}
impl Apu {
......@@ -65,10 +77,27 @@ impl Apu {
ch2_wave_length: 0x0,
ch2_sound_length: false,
ch2_enabled: false,
ch3_timer: 0,
ch3_sequence: 0,
ch3_output: 0,
ch3_dac: false,
ch3_length_timer: 0x0,
ch3_output_level: 0x0,
ch3_wave_length: 0x0,
ch3_sound_length: false,
ch3_enabled: false,
wave_ram: [0u8; 16],
}
}
pub fn clock(&mut self, cycles: u8, freq: u32) {
pub fn clock(&mut self, cycles: u8) {
self.clock_f(cycles, 4194304);
}
pub fn clock_f(&mut self, cycles: u8, freq: u32) {
// @todo the performance here requires improvement
for _ in 0..cycles {
self.cycle(freq);
}
......@@ -76,7 +105,6 @@ impl Apu {
pub fn read(&mut self, addr: u16) -> u8 {
match addr {
0xff26 => 1 as u8, // @todo implement this
_ => {
warnln!("Reading from unknown APU location 0x{:04x}", addr);
0xff
......@@ -137,9 +165,44 @@ impl Apu {
(self.ch2_wave_length & 0x00ff) | (((value & 0x07) as u16) << 8);
self.ch2_sound_length |= value & 0x40 == 0x40;
self.ch2_enabled |= value & 0x80 == 0x80;
if value & 0x80 == 0x80 {
//self.ch2_timer = 0;
//self.ch2_sequence = 0;
//@todo improve this reset operation
}
println!("CH2 Enabled {}", self.ch2_enabled);
}
// 0xFF1A — NR30: Channel 3 DAC enable
0xff1a => {
self.ch3_dac = value & 0x80 == 0x80;
}
// 0xFF1B — NR31: Channel 3 length timer
0xff1b => {
self.ch3_length_timer = value;
}
// 0xFF1C — NR32: Channel 3 output level
0xff1c => {
self.ch3_output_level = value & 0x60 >> 5;
}
// 0xFF1D — NR33: Channel 3 wavelength low [write-only]
0xff1d => {
self.ch3_wave_length = (self.ch3_wave_length & 0xff00) | value as u16;
}
// 0xFF1E — NR34: Channel 3 wavelength high & control
0xff1e => {
self.ch3_wave_length =
(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);
}
// 0xFF30-0xFF3F — Wave pattern RAM
0xff30..=0xff3f => {
self.wave_ram[addr as usize - 0xff30] = value;
}
_ => warnln!("Writing in unknown APU location 0x{:04x}", addr),
}
}
......
......@@ -267,6 +267,11 @@ impl Cpu {
&mut self.mmu
}
#[inline(always)]
pub fn mmu_i(&self) -> &Mmu {
&self.mmu
}
#[inline(always)]
pub fn ppu(&mut self) -> &mut Ppu {
self.mmu().ppu()
......@@ -277,6 +282,11 @@ impl Cpu {
self.mmu().apu()
}
#[inline(always)]
pub fn apu_i(&self) -> &Apu {
self.mmu_i().apu_i()
}
#[inline(always)]
pub fn pad(&mut self) -> &mut Pad {
self.mmu().pad()
......
......@@ -54,7 +54,8 @@ pub struct Registers {
}
pub trait AudioProvider {
fn tick_apu(&mut self, freq: u32) -> u8;
fn output_apu(&self) -> u8;
fn output_clock_apu(&mut self, cycles: u8, freq: u32) -> u8;
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
......@@ -99,8 +100,8 @@ impl GameBoy {
self.ppu().clock(cycles)
}
pub fn apu_clock(&mut self, cycles: u8, freq: u32) {
self.apu().clock(cycles, freq)
pub fn apu_clock(&mut self, cycles: u8) {
self.apu().clock(cycles)
}
pub fn timer_clock(&mut self, cycles: u8) {
......@@ -260,6 +261,10 @@ impl GameBoy {
self.cpu.apu()
}
pub fn apu_i(&self) -> &Apu {
self.cpu.apu_i()
}
pub fn pad(&mut self) -> &mut Pad {
self.cpu.pad()
}
......@@ -376,9 +381,13 @@ pub fn hook_impl(info: &PanicInfo) {
}
impl AudioProvider for GameBoy {
fn tick_apu(&mut self, freq: u32) -> u8 {
self.apu_clock(1, freq);
self.apu().output()
fn output_apu(&self) -> u8 {
self.apu_i().output()
}
fn output_clock_apu(&mut self, cycles: u8, freq: u32) -> u8 {
self.apu().clock_f(cycles, freq);
self.apu_i().output()
}
}
......
......@@ -79,6 +79,10 @@ impl Mmu {
&mut self.apu
}
pub fn apu_i(&self) -> &Apu {
&self.apu
}
pub fn pad(&mut self) -> &mut Pad {
&mut self.pad
}
......
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