diff --git a/Cargo.lock b/Cargo.lock index 553f583..353e81d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,6 +279,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "errno" version = "0.2.8" @@ -413,6 +419,7 @@ dependencies = [ "cpal", "futures", "gilrs", + "itertools", "minifb", "once_cell", "rand", @@ -546,6 +553,15 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "jni" version = "0.19.0" diff --git a/Cargo.toml b/Cargo.toml index 432fdf9..d2352dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,4 @@ ringbuf = "0.3.2" async-ringbuf = "0.1.2" futures = "0.3.26" once_cell = "1.17.1" +itertools = "0.10.5" diff --git a/src/processor/memory/mmio/apu.rs b/src/processor/memory/mmio/apu.rs index 639c614..ba5b54f 100644 --- a/src/processor/memory/mmio/apu.rs +++ b/src/processor/memory/mmio/apu.rs @@ -13,6 +13,7 @@ use cpal::{ Device, Stream, StreamConfig, }; use futures::executor; +use itertools::izip; use samplerate::{ConverterType, Samplerate}; mod channels; @@ -27,8 +28,12 @@ const fn reg(a: Address) -> usize { impl DacSample { fn mixed(&self, mixer: &Mixer) -> Vec { - let left = (self.one * mixer.ch1.left.scale()) + (self.two * mixer.ch2.left.scale()); - let right = (self.one * mixer.ch1.right.scale()) + (self.two * mixer.ch2.right.scale()); + let left = (self.one * mixer.ch1.left.scale()) + + (self.two * mixer.ch2.left.scale()) + + (self.three * mixer.ch3.left.scale()); + let right = (self.one * mixer.ch1.right.scale()) + + (self.two * mixer.ch2.right.scale()) + + (self.three * mixer.ch3.right.scale()); vec![ self.mix_channel(left, mixer.vol_left), self.mix_channel(right, mixer.vol_right), @@ -150,19 +155,19 @@ impl Apu { // tick sound length timers self.channels.one.length_tick(); self.channels.two.length_tick(); + self.channels.three.length_tick(); } } fn tick(&mut self, steps: usize) { self.buffer.append( - &mut self - .channels - .one - .tick(steps) - .into_iter() - .zip(self.channels.two.tick(steps).into_iter()) - .map(|(one, two)| DacSample { one, two }) - .collect(), + &mut izip!( + self.channels.one.tick(steps).into_iter(), + self.channels.two.tick(steps).into_iter(), + self.channels.three.tick(steps).into_iter() + ) + .map(|(one, two, three)| DacSample { one, two, three }) + .collect(), ); if self.buffer.len() >= CYCLES_PER_FRAME { self.next_audio(); diff --git a/src/processor/memory/mmio/apu/channels.rs b/src/processor/memory/mmio/apu/channels.rs index 6935781..0e7ca7d 100644 --- a/src/processor/memory/mmio/apu/channels.rs +++ b/src/processor/memory/mmio/apu/channels.rs @@ -1,6 +1,6 @@ use crate::{ processor::memory::Address, - util::{get_bit, set_or_clear_bit}, + util::{get_bit, set_or_clear_bit, Nibbles}, }; #[derive(Clone, Copy, PartialEq)] @@ -180,7 +180,7 @@ impl PwmChannel { } pub(super) fn frequency_tick(&mut self) { - if self.sweep.slope == 0 || !self.enabled { + if self.sweep.slope == 0 || self.sweep.pace == 0 || !self.enabled { return; } self.sweep.counter += 1; @@ -279,6 +279,7 @@ impl PwmChannel { } } +#[derive(Debug, PartialEq)] enum ShiftVolumePercent { Zero, TwentyFive, @@ -286,6 +287,30 @@ enum ShiftVolumePercent { OneHundred, } +impl ShiftVolumePercent { + fn as_shift_amount(&self) -> u8 { + match self { + ShiftVolumePercent::Zero => 8, + ShiftVolumePercent::TwentyFive => 2, + ShiftVolumePercent::Fifty => 1, + ShiftVolumePercent::OneHundred => 0, + } + } +} + +struct WaveRam { + data: [u8; 16], +} + +impl WaveRam { + fn as_samples(&self) -> Vec { + self.data + .into_iter() + .flat_map(|v| [v.get_high_nibble(), v.get_low_nibble()]) + .collect() + } +} + pub(super) struct WaveChannel { pub(super) enabled: bool, dac_enabled: bool, @@ -295,7 +320,7 @@ pub(super) struct WaveChannel { wavelength: u16, wave_timer: u16, wave_position: usize, - wave_ram: [u8; 16], + wave_ram: WaveRam, } impl WaveChannel { @@ -308,9 +333,9 @@ impl WaveChannel { length_timer: 0xFF, volume: ShiftVolumePercent::Zero, wavelength, - wave_timer: (2048 - wavelength) * 4, + wave_timer: (2048 - wavelength) * 2, wave_position: 1, - wave_ram: [0; 16], + wave_ram: WaveRam { data: [0; 16] }, } } @@ -318,6 +343,41 @@ impl WaveChannel { self.enabled = true; } + pub fn tick(&mut self, steps: usize) -> Vec { + if self.enabled { + (0..steps) + .map(|_| { + let b; + (self.wave_timer, b) = self.wave_timer.overflowing_sub(1); + if b { + self.wave_position = (self.wave_position + 1) % 32; + self.set_wave_timer(); + } + self.dac(self.wave_ram.as_samples()[self.wave_position]) + }) + .collect() + } else { + vec![0.; steps] + } + } + + fn dac(&self, digital: u8) -> f32 { + if self.dac_enabled && self.volume != ShiftVolumePercent::Zero { + ((((digital >> self.volume.as_shift_amount()) as f32) * (-2.)) + 1.) / 15. + } else { + 0. + } + } + + pub(super) fn length_tick(&mut self) { + if self.length_enable && self.enabled { + self.length_timer += 1; + if self.length_timer >= 64 { + self.enabled = false; + } + } + } + pub(super) fn update_dac(&mut self, data: u8) { self.dac_enabled = get_bit(data, 7); } @@ -337,7 +397,7 @@ impl WaveChannel { 0b10 => ShiftVolumePercent::Fifty, 0b11 => ShiftVolumePercent::TwentyFive, _ => panic!("should be unreachable"), - } + }; } pub(super) fn get_volume(&self) -> u8 { @@ -370,14 +430,14 @@ impl WaveChannel { pub(super) fn update_wave_ram(&mut self, addr: Address, data: u8) { let real_addr = (addr - 0xFF30) as usize; - if real_addr >= self.wave_ram.len() { + if real_addr >= self.wave_ram.data.len() { panic!("sent the wrong address to update_wave_ram"); } - self.wave_ram[real_addr] = data; + self.wave_ram.data[real_addr] = data; } fn set_wave_timer(&mut self) { - self.wave_timer = (2048 - self.wavelength) * 4; + self.wave_timer = (2048 - self.wavelength) * 2; } } diff --git a/src/processor/memory/mmio/apu/types.rs b/src/processor/memory/mmio/apu/types.rs index 3da9f7a..ad3b5e4 100644 --- a/src/processor/memory/mmio/apu/types.rs +++ b/src/processor/memory/mmio/apu/types.rs @@ -93,10 +93,15 @@ impl Default for Mixer { pub(super) struct DacSample { pub(super) one: f32, pub(super) two: f32, + pub(super) three: f32, } impl Default for DacSample { fn default() -> Self { - Self { one: 0., two: 0. } + Self { + one: 0., + two: 0., + three: 0., + } } }