From 9c682571776ef47b63de022833cc024d259228be Mon Sep 17 00:00:00 2001 From: Theron Date: Sat, 21 Dec 2019 18:02:32 -0600 Subject: [PATCH] apu --- src/apu/dmc.rs | 6 +- src/apu/mod.rs | 42 +++++++------- src/apu/noise.rs | 6 +- src/apu/square.rs | 135 +++++++++++++++++++++++++++----------------- src/apu/triangle.rs | 6 +- src/audio.rs | 4 +- src/main.rs | 14 ++++- 7 files changed, 128 insertions(+), 85 deletions(-) diff --git a/src/apu/dmc.rs b/src/apu/dmc.rs index 57e1b75..efcee9d 100644 --- a/src/apu/dmc.rs +++ b/src/apu/dmc.rs @@ -18,7 +18,7 @@ impl DMC { } - pub fn control(&mut self, value: u8) { + pub fn write_control(&mut self, value: u8) { } @@ -26,11 +26,11 @@ impl DMC { } - pub fn sample_address(&mut self, value: u8) { + pub fn write_sample_address(&mut self, value: u8) { } - pub fn sample_length(&mut self, value: u8) { + pub fn write_sample_length(&mut self, value: u8) { } } \ No newline at end of file diff --git a/src/apu/mod.rs b/src/apu/mod.rs index bda4666..63d40eb 100644 --- a/src/apu/mod.rs +++ b/src/apu/mod.rs @@ -27,6 +27,8 @@ use dmc::DMC; // We need to take a sample 44100 times per second. The CPU clocks (not steps) at 1.789773 MHz. Meaning the APU, going half as fast, // clocks 894,886.5 times per second. 894,886.5/44,100=20.29 APU clocks per audio sample. +// TODO: organize APU structs + pub struct Apu { square1: Square, square2: Square, @@ -54,7 +56,7 @@ struct Envelope { } const FRAME_COUNTER_STEPS: [usize; 5] = [3728, 7456, 11185, 14914, 18640]; -const CYCLES_PER_SAMPLE: f32 = 894_886.5/44_100.0; // APU frequency over sample frequency +const CYCLES_PER_SAMPLE: f32 = 894_886.5/44_100.0; // APU frequency over sample frequency. May need to turn this down slightly as it's outputting less than 44_100Hz. impl Apu { pub fn new() -> Self { @@ -106,28 +108,28 @@ impl Apu { pub fn write_reg(&mut self, address: usize, value: u8) { match address { - 0x4000 => self.square1.duty(value), - 0x4001 => self.square1.sweep(value), - 0x4002 => self.square1.timer_low(value), - 0x4003 => self.square1.timer_high(value), - 0x4004 => self.square2.duty(value), - 0x4005 => self.square2.sweep(value), - 0x4006 => self.square2.timer_low(value), - 0x4007 => self.square2.timer_high(value), - 0x4008 => self.triangle.counter(value), + 0x4000 => self.square1.write_duty(value), + 0x4001 => self.square1.write_sweep(value), + 0x4002 => self.square1.write_timer_low(value), + 0x4003 => self.square1.write_timer_high(value), + 0x4004 => self.square2.write_duty(value), + 0x4005 => self.square2.write_sweep(value), + 0x4006 => self.square2.write_timer_low(value), + 0x4007 => self.square2.write_timer_high(value), + 0x4008 => self.triangle.write_counter(value), 0x4009 => (), - 0x400A => self.triangle.timer_low(value), - 0x400B => self.triangle.timer_high(value), - 0x400C => self.noise.envelope(value), + 0x400A => self.triangle.write_timer_low(value), + 0x400B => self.triangle.write_timer_high(value), + 0x400C => self.noise.write_envelope(value), 0x400D => (), - 0x400E => self.noise.loop_noise(value), - 0x400F => self.noise.load_length_counter(value), - 0x4010 => self.dmc.control(value), + 0x400E => self.noise.write_loop_noise(value), + 0x400F => self.noise.write_length_counter(value), + 0x4010 => self.dmc.write_control(value), 0x4011 => self.dmc.direct_load(value), - 0x4012 => self.dmc.sample_address(value), - 0x4013 => self.dmc.sample_length(value), + 0x4012 => self.dmc.write_sample_address(value), + 0x4013 => self.dmc.write_sample_length(value), 0x4014 => (), - 0x4015 => self.control(value), + 0x4015 => self.write_control(value), 0x4016 => (), 0x4017 => self.set_frame_counter(value), _ => panic!("bad address written: 0x{:X}", address), @@ -188,7 +190,7 @@ impl Apu { } } - fn control(&mut self, value: u8) { + fn write_control(&mut self, value: u8) { // Writing to this register clears the DMC interrupt flag. self.dmc.interrupt = false; // Writing a zero to any of the channel enable bits will silence that channel and immediately set its length counter to 0. diff --git a/src/apu/noise.rs b/src/apu/noise.rs index 5366f51..defe901 100644 --- a/src/apu/noise.rs +++ b/src/apu/noise.rs @@ -35,7 +35,7 @@ impl Noise { self.linear_feedback_sr |= feedback << 14; } - pub fn envelope(&mut self, value: u8) { + pub fn write_envelope(&mut self, value: u8) { } @@ -47,10 +47,10 @@ impl Noise { } - pub fn loop_noise(&mut self, value: u8) { + pub fn write_loop_noise(&mut self, value: u8) { } - pub fn load_length_counter(&mut self, value: u8) { + pub fn write_length_counter(&mut self, value: u8) { } } diff --git a/src/apu/square.rs b/src/apu/square.rs index 254566f..475bb72 100644 --- a/src/apu/square.rs +++ b/src/apu/square.rs @@ -7,45 +7,80 @@ const DUTY_CYCLE_SEQUENCES: [[u8; 8]; 4] = [ pub struct Square { pub sample: u16, - duty_cycle: [u8; 8], - duty_counter: u8, - length_counter_halt: bool, // (this bit is also the envelope's loop flag) - constant_volume_flag: bool, // (0: use volume from envelope; 1: use constant volume) - timer: u16, - pub length_counter: u8, - envelope: u8, - sweep: u8, + divider: u16, pub enabled: bool, - decay_counter: u8, + + duty_cycle: [u8; 8], + duty_counter: usize, + + envelope: u16, start: bool, - divider: u8, + decay_counter: u16, + constant_volume_flag: bool, // (0: use volume from envelope; 1: use constant volume) + + length_counter_halt: bool, // (this bit is also the envelope's loop flag) + pub length_counter: u8, + + timer: u16, + timer_period: u16, + sweep: u8, + sweep_divider: u8, + shift_count: u8, + sweep_adder_overflow: bool, + sweep_enabled: bool, + sweep_negate: bool, + sweep_reload: bool, } impl Square { pub fn new() -> Self { Square { + sample: 0, + divider: 0, + enabled: false, + duty_cycle: DUTY_CYCLE_SEQUENCES[0], duty_counter: 0, - length_counter_halt: false, - constant_volume_flag: false, - timer: 0, - length_counter: 0, + envelope: 0, - sweep: 0, - sample: 0, - enabled: false, - decay_counter: 0, start: false, - divider: 0, + decay_counter: 0, + constant_volume_flag: false, + + timer: 0, + timer_period: 0, + sweep: 0, + sweep_divider: 0, + shift_count: 0, + sweep_adder_overflow: false, + sweep_enabled: false, + sweep_negate: false, + sweep_reload: false, + + length_counter: 0, + length_counter_halt: false, } } pub fn clock(&mut self) { - - } - - pub fn clock_frame_counter(&mut self) { - + // The sequencer is clocked by an 11-bit timer. Given the timer value t = HHHLLLLLLLL formed by timer high and timer low, this timer is updated every APU cycle + // (i.e., every second CPU cycle), and counts t, t-1, ..., 0, t, t-1, ..., clocking the waveform generator when it goes from 0 to t. + if self.timer == 0 { + self.timer = self.timer_period; + self.duty_counter = (self.duty_counter + 1) % 8; + } else { + self.timer -= 1; + } + // Update volume for this channel + // The mixer receives the current envelope volume except when + self.sample = if self.duty_cycle[self.duty_counter] == 0 // The sequencer output is zero, or + || self.sweep_adder_overflow // overflow from the sweep unit's adder is silencing the channel, + || self.length_counter == 0 // the length counter is zero, or + || self.timer < 8 { // the timer has a value less than eight. + 0 + } else { + self.decay_counter + }; } pub fn clock_envelope(&mut self) { @@ -80,50 +115,46 @@ impl Square { } } + pub fn clock_sweep(&mut self) { + + } + // $4000/$4004 - pub fn duty(&mut self, value: u8) { + pub fn write_duty(&mut self, value: u8) { + // TODO: The duty cycle is changed (see table below), but the sequencer's current position isn't affected. self.duty_cycle = DUTY_CYCLE_SEQUENCES[(value >> 6) as usize]; self.length_counter_halt = value & (1<<5) != 0; self.constant_volume_flag = value & (1<<4) != 0; if self.constant_volume_flag { - self.envelope = value & 0b1111; + self.envelope = value as u16 & 0b1111; } else { self.envelope = self.decay_counter; } } // $4001/$4005 - pub fn sweep(&mut self, value: u8) { - + pub fn write_sweep(&mut self, value: u8) { + self.sweep_enabled = value >> 7 == 1; + self.sweep_divider = value >> 4 & 0b111; + self.sweep_negate = value & 0b1000 != 0; + self.shift_count = value & 0b111; } // $4002/$4006 - pub fn timer_low(&mut self, value: u8) { - + pub fn write_timer_low(&mut self, value: u8) { + self.timer &= 0b11111111_00000000; + self.timer |= value as u16; } // $4003/$4007 - pub fn timer_high(&mut self, value: u8) { - + pub fn write_timer_high(&mut self, value: u8) { + // LLLL.Lttt Pulse channel 1 length counter load and timer (write) + self.length_counter = value >> 3; + let timer_high = value as u16 & 0b0000_0111; + self.timer &= 0b11111000_11111111; // mask off high 3 bits of 11-bit timer + self.timer |= timer_high << 8; // apply high timer bits in their place + // The sequencer is immediately restarted at the first value of the current sequence. The envelope is also restarted. The period divider is not reset. + self.duty_counter = 0; + self.start = true; } } - -struct EnvelopeGenerator { - -} - -struct SweepUnit { - -} - -struct Timer { - -} - -struct Sequencer { - -} - -struct LengthCounter { - -} diff --git a/src/apu/triangle.rs b/src/apu/triangle.rs index 52b1d2c..e267b21 100644 --- a/src/apu/triangle.rs +++ b/src/apu/triangle.rs @@ -20,15 +20,15 @@ impl Triangle { } } - pub fn timer_low(&mut self, value: u8) { + pub fn write_timer_low(&mut self, value: u8) { } - pub fn timer_high(&mut self, value: u8) { + pub fn write_timer_high(&mut self, value: u8) { } - pub fn counter(&mut self, value: u8) { + pub fn write_counter(&mut self, value: u8) { } diff --git a/src/audio.rs b/src/audio.rs index f2dc71d..236734a 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -29,8 +29,8 @@ pub fn initialize(context: &sdl2::Sdl) -> Result, S let desired_spec = AudioSpecDesired { freq: Some(44_100), - channels: Some(1), // mono - samples: Some(4096), // default sample size + channels: Some(1), // mono + samples: None, // default sample size }; audio_subsystem.open_queue(None, &desired_spec) diff --git a/src/main.rs b/src/main.rs index 69727e5..52ddc7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ fn main() -> Result<(), String> { // Set up audio let mut audio_device = audio::initialize(&sdl_context).expect("Could not create audio device"); let mut half_cycle = false; + let mut audio_buffer = Vec::::new(); audio_device.resume(); // Initialize hardware components @@ -49,6 +50,7 @@ fn main() -> Result<(), String> { let mut timer = Instant::now(); let mut fps_timer = Instant::now(); let mut fps = 0; + let mut sps = 0; // PROFILER.lock().unwrap().start("./main.profile").unwrap(); 'running: loop { @@ -66,10 +68,14 @@ fn main() -> Result<(), String> { } for _ in 0..apu_cycles { match cpu.apu.clock() { - Some(sample) => audio_device.queue(&wav), - None => false, + Some(sample) => {sps += 1; audio_buffer.push(sample)}, + None => (), }; } + if audio_buffer.len() == 44_100 { + audio_device.queue(&audio_buffer); + audio_buffer = vec![]; + } // clock PPU three times for every CPU cycle for _ in 0..cpu_cycles * 3 { let (pixel, end_of_frame) = cpu.ppu.clock(); @@ -107,6 +113,10 @@ fn main() -> Result<(), String> { println!("fps: {}", fps); fps = 0; fps_timer = now; + + println!("samples per second: {}", sps); + sps = 0; + } } // PROFILER.lock().unwrap().stop().unwrap();