diff options
author | Astatin <[email protected]> | 2025-07-12 02:45:38 +0200 |
---|---|---|
committer | Astatin <[email protected]> | 2025-07-12 02:45:38 +0200 |
commit | 9d24a19cd9a392d5a2067a3e233a5a26a1518c35 (patch) | |
tree | 0a47acf10b6a029977ba7bd4c3fad59f5f5f28e2 | |
parent | bfe34191a52e356ce92df8e6b10d3ba2b4598fe8 (diff) |
Update audio registers without trigger + add stereo
-rw-r--r-- | src/audio.rs | 139 | ||||
-rw-r--r-- | src/desktop/audio.rs | 24 | ||||
-rw-r--r-- | src/io.rs | 6 | ||||
-rw-r--r-- | src/mmio.rs | 64 |
4 files changed, 176 insertions, 57 deletions
diff --git a/src/audio.rs b/src/audio.rs index 6efd0d8..cd8eeb2 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,13 +1,13 @@ // You are entering a very scary territory of magic numbers and arbitrary math operations. // I don't remember why I did all of this but it works I guess :3 -use crate::io::Audio; +use crate::io::{self, Audio}; use std::sync::{Arc, Mutex}; pub const SAMPLE_RATE: u32 = 65536; -const SAMPLE_AVERAGING: usize = 5; //20; +const SAMPLE_AVERAGING: usize = 1; //20; const SQUARE_WAVE_PATTERN_DUTY_0: [u8; 32] = [ 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, @@ -15,18 +15,18 @@ const SQUARE_WAVE_PATTERN_DUTY_0: [u8; 32] = [ ]; const SQUARE_WAVE_PATTERN_DUTY_1: [u8; 32] = [ - 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, - 0xf, 0xf, 0xf, 0xf, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, + 0xd, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, + 0xf, 0xe, 0xf, 0xe, 0xd, 2, 0, 1, 0, 1, 0, 1, 2, ]; const SQUARE_WAVE_PATTERN_DUTY_2: [u8; 32] = [ - 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0xd, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xf, 0xd, 2, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, ]; const SQUARE_WAVE_PATTERN_DUTY_3: [u8; 32] = [ - 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, + 0xd, 0xf, 0xe, 0xf, 0xe, 0xf, 0xe, 0xd, 2, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 2, ]; /* @@ -83,7 +83,7 @@ const SQUARE_WAVE_PATTERNS: [[u8; 32]; 4] = [ #[derive(Clone, Debug)] pub struct Wave { period_value: u16, - num_sample: usize, + pub num_sample: usize, wave_pattern: [u8; 32], length_timer: u8, length_timer_enabled: bool, @@ -95,10 +95,14 @@ pub struct Wave { period_sweep_pace: u8, period_sweep_direction: u8, period_sweep_slope: u8, + + left_volume: u8, + right_volume: u8, } impl Wave { pub fn new( + num_sample: usize, period_value: u16, wave_pattern: [u8; 32], env_initial_volume: u8, @@ -109,10 +113,12 @@ impl Wave { period_sweep_pace: u8, period_sweep_direction: u8, period_sweep_slope: u8, + left_volume: u8, + right_volume: u8, ) -> Wave { Wave { period_value, - num_sample: 0, + num_sample, wave_pattern, env_initial_volume: env_initial_volume as f32, env_direction: if env_direction == 0 { -1. } else { 1. }, @@ -122,15 +128,17 @@ impl Wave { period_sweep_pace, period_sweep_direction, period_sweep_slope, + left_volume, + right_volume, } } } -impl Iterator for Wave { - type Item = f32; - - fn next(&mut self) -> Option<f32> { - self.num_sample = self.num_sample.wrapping_add(1); +impl io::Wave for Wave { + fn next(&mut self, left: bool) -> Option<f32> { + if left { + self.num_sample = self.num_sample.wrapping_add(1); + } let mut period_value = self.period_value; @@ -202,13 +210,19 @@ impl Iterator for Wave { } } - Some((avg / SAMPLE_AVERAGING as f32) * envelope_boundaries / 64.) + if left { + avg = (self.left_volume as f32 / 8.) * avg; + } else { + avg = (self.right_volume as f32 / 8.) * avg; + } + + Some((avg / SAMPLE_AVERAGING as f32) * envelope_boundaries / 32.) } } #[derive(Clone, Debug)] pub struct NoiseWave { - num_sample: usize, + pub num_sample: usize, length_timer: u8, length_timer_enabled: bool, @@ -223,6 +237,7 @@ pub struct NoiseWave { impl NoiseWave { pub fn new( + num_sample: usize, env_initial_volume: u8, env_direction: u8, env_sweep_pace: u8, @@ -233,7 +248,7 @@ impl NoiseWave { clock_divider: u8, ) -> NoiseWave { NoiseWave { - num_sample: 0, + num_sample, env_initial_volume: env_initial_volume as f32, env_direction: if env_direction == 0 { -1. } else { 1. }, env_sweep_pace, @@ -246,11 +261,11 @@ impl NoiseWave { } } -impl Iterator for NoiseWave { - type Item = f32; - - fn next(&mut self) -> Option<f32> { - self.num_sample = self.num_sample.wrapping_add(1); +impl io::Wave for NoiseWave { + fn next(&mut self, left: bool) -> Option<f32> { + if left { + self.num_sample = self.num_sample.wrapping_add(1); + } let clock_divider = if self.clock_divider == 0 { 0.5 @@ -300,7 +315,7 @@ impl Iterator for NoiseWave { } } - Some((avg / SAMPLE_AVERAGING as f32) * envelope_boundaries / 64.) + Some((avg / SAMPLE_AVERAGING as f32) * envelope_boundaries / 32.) } } @@ -328,17 +343,15 @@ impl MutableWave { } } -impl Iterator for MutableWave { - type Item = f32; - - fn next(&mut self) -> Option<f32> { +impl io::Wave for MutableWave { + fn next(&mut self, left: bool) -> Option<f32> { let mut res = 0.; // Imagine using an Arc<Mutex<>> in a sound wave generation function that needs to reliably // run 65536 times a second. Couldn't be me :3 if let Ok(mut wave_o) = self.wave_ch1.lock() { if let Some(wave) = wave_o.as_mut() { - if let Some(result) = wave.next() { + if let Some(result) = wave.next(left) { res += result / 4.; } else { *wave_o = None; @@ -348,7 +361,7 @@ impl Iterator for MutableWave { if let Ok(mut wave_o) = self.wave_ch2.lock() { if let Some(wave) = wave_o.as_mut() { - if let Some(result) = wave.next() { + if let Some(result) = wave.next(left) { res += result / 4.; } else { *wave_o = None; @@ -358,7 +371,7 @@ impl Iterator for MutableWave { if let Ok(mut wave_o) = self.wave_ch3.lock() { if let Some(wave) = wave_o.as_mut() { - if let Some(result) = wave.next() { + if let Some(result) = wave.next(left) { res += result / 4.; } else { *wave_o = None; @@ -368,7 +381,7 @@ impl Iterator for MutableWave { if let Ok(mut wave_o) = self.wave_ch4.lock() { if let Some(wave) = wave_o.as_mut() { - if let Some(result) = wave.next() { + if let Some(result) = wave.next(left) { res += result / 4.; } else { *wave_o = None; @@ -394,6 +407,10 @@ pub struct AudioSquareChannel { pub period_sweep_pace: u8, pub period_sweep_direction: u8, pub period_sweep_slope: u8, + pub left: bool, + pub right: bool, + pub left_volume: u8, + pub right_volume: u8 } impl AudioSquareChannel { @@ -411,13 +428,23 @@ impl AudioSquareChannel { period_sweep_pace: 0, period_sweep_direction: 0, period_sweep_slope: 0, + left: true, + right: true, + left_volume: 7, + right_volume: 7, } } - pub fn update(&mut self) { + pub fn update(&mut self, reset: bool) { if let Ok(mut wave) = self.wave.lock() { - if self.on { + let num_sample = if let Some(w) = &*wave { + w.num_sample + } else { + 0 + }; + if self.on && (reset || wave.is_some()) { *wave = Some(Wave::new( + if reset { 0 } else { num_sample }, 2048 - self.period_value, SQUARE_WAVE_PATTERNS[self.duty as usize], self.initial_volume, @@ -428,6 +455,8 @@ impl AudioSquareChannel { self.period_sweep_pace, self.period_sweep_direction, self.period_sweep_slope, + if self.left { self.left_volume + 1 } else { 0 }, + if self.right { self.right_volume + 1 } else { 0 }, )); } else { *wave = None; @@ -449,6 +478,11 @@ pub struct AudioCustomChannel { pub on: bool, pub period_value: u16, pub initial_volume: u8, + + pub left: bool, + pub right: bool, + pub left_volume: u8, + pub right_volume: u8 } impl AudioCustomChannel { @@ -461,13 +495,24 @@ impl AudioCustomChannel { wave, length_timer: 0, length_timer_enabled: false, + + left: true, + right: true, + left_volume: 7, + right_volume: 7, } } - pub fn update(&mut self) { + pub fn update(&mut self, reset: bool) { if let Ok(mut wave) = self.wave.lock() { - if self.on { + let num_sample = if let Some(w) = &*wave { + w.num_sample + } else { + 0 + }; + if self.on && (reset || wave.is_some()) { *wave = Some(Wave::new( + if reset { 0 } else { num_sample }, 2 * (2048 - (self.period_value * 2)), self.wave_pattern, self.initial_volume, @@ -478,6 +523,8 @@ impl AudioCustomChannel { 0, 0, 0, + if self.left { self.left_volume + 1 } else { 0 }, + if self.right { self.right_volume + 1 } else { 0 }, )); } else { *wave = None; @@ -502,6 +549,11 @@ pub struct AudioNoiseChannel { pub clock_shift: u8, pub lsfr_width: u8, pub clock_divider: u8, + + pub left: bool, + pub right: bool, + pub left_volume: u8, + pub right_volume: u8 } impl AudioNoiseChannel { @@ -517,13 +569,24 @@ impl AudioNoiseChannel { clock_shift: 0, lsfr_width: 0, clock_divider: 0, + + left: true, + right: true, + left_volume: 7, + right_volume: 7, } } - pub fn update(&mut self) { + pub fn update(&mut self, reset: bool) { if let Ok(mut wave) = self.wave.lock() { - if self.on { + let num_sample = if let Some(w) = &*wave { + w.num_sample + } else { + 0 + }; + if self.on && (reset || wave.is_some()) { *wave = Some(NoiseWave::new( + if reset { 0 } else { num_sample }, self.initial_volume, self.env_direction, self.sweep, diff --git a/src/desktop/audio.rs b/src/desktop/audio.rs index c32433b..db15a4f 100644 --- a/src/desktop/audio.rs +++ b/src/desktop/audio.rs @@ -1,7 +1,7 @@ use rodio::{OutputStream, Sink, Source}; use crate::audio::SAMPLE_RATE; -use crate::io::Audio; +use crate::io::{Wave, Audio}; use std::time::Duration; pub struct RodioAudio { @@ -9,29 +9,27 @@ pub struct RodioAudio { _sink: Sink, } -struct RodioWave<W: Iterator + Send + 'static>(W); +struct RodioWave<W: Wave + Send + 'static>(W, usize); -impl<W: Iterator + Send + 'static> Iterator for RodioWave<W> -where - <W as Iterator>::Item: rodio::Sample, +impl<W: Wave + Send + 'static> Iterator for RodioWave<W> { - type Item = W::Item; + type Item = f32; fn next(&mut self) -> Option<Self::Item> { - self.0.next() + self.1 += 1; + let left = self.1 % 2 == 0; + self.0.next(left) } } -impl<W: Iterator + Send + 'static> Source for RodioWave<W> -where - <W as Iterator>::Item: rodio::Sample, +impl<W: Wave + Send + 'static> Source for RodioWave<W> { fn current_frame_len(&self) -> Option<usize> { None } fn channels(&self) -> u16 { - 1 + 2 } fn sample_rate(&self) -> u32 { @@ -44,11 +42,11 @@ where } impl Audio for RodioAudio { - fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self { + fn new<S: Wave + Send + 'static>(wave: S) -> Self { let (stream, stream_handle) = OutputStream::try_default().unwrap(); let sink = Sink::try_new(&stream_handle).unwrap(); - sink.append(RodioWave(wave)); + sink.append(RodioWave(wave, 0)); RodioAudio { _stream: stream, @@ -60,8 +60,12 @@ impl<T: Serial + ?Sized> Serial for Box<T> { } } +pub trait Wave { + fn next(&mut self, left: bool) -> Option<f32>; +} + pub trait Audio { - fn new<S: Iterator<Item = f32> + Send + 'static>(wave: S) -> Self; + fn new<S: Wave + Send + 'static>(wave: S) -> Self; } pub trait LoadSave diff --git a/src/mmio.rs b/src/mmio.rs index fe37b7d..d9f2a5a 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -92,47 +92,58 @@ impl<S: Serial, A: Audio> Memory<S, A> { self.audio.ch1.period_sweep_pace = (0b1110000 & value) >> 4; self.audio.ch1.period_sweep_direction = (0b1000 & value) >> 3; self.audio.ch1.period_sweep_slope = 0b111 & value; + self.audio.ch1.update(false); } 0x11 => { self.audio.ch1.duty = value >> 6; self.audio.ch1.length_timer = value & 0b111111; + self.audio.ch1.update(false); } 0x12 => { self.audio.ch1.initial_volume = value >> 4; self.audio.ch1.env_direction = (value & 0xf) >> 3; self.audio.ch1.sweep = value & 0b111; + self.audio.ch1.update(false); } 0x13 => { self.audio.ch1.period_value &= 0xff00; self.audio.ch1.period_value |= value as u16; + self.audio.ch1.update(false); } 0x14 => { self.audio.ch1.period_value &= 0xff; self.audio.ch1.period_value |= ((value & 0b111) as u16) << 8; self.audio.ch1.length_timer_enabled = value & 0b01000000 != 0; if value >> 7 == 1 { - self.audio.ch1.update(); + self.audio.ch1.update(true); + } else { + self.audio.ch1.update(false); } } 0x16 => { self.audio.ch2.duty = value >> 6; self.audio.ch2.length_timer = value & 0b111111; + self.audio.ch2.update(false); } 0x17 => { self.audio.ch2.initial_volume = value >> 4; self.audio.ch2.env_direction = (value & 0xf) >> 3; self.audio.ch2.sweep = value & 0b111; + self.audio.ch2.update(false); } 0x18 => { self.audio.ch2.period_value &= 0xff00; self.audio.ch2.period_value |= value as u16; + self.audio.ch2.update(false); } 0x19 => { self.audio.ch2.period_value &= 0xff; self.audio.ch2.period_value |= ((value & 0b111) as u16) << 8; self.audio.ch2.length_timer_enabled = value & 0b01000000 != 0; if value >> 7 == 1 { - self.audio.ch2.update(); + self.audio.ch2.update(true); + } else { + self.audio.ch2.update(false); } } 0x1a => { @@ -141,10 +152,11 @@ impl<S: Serial, A: Audio> Memory<S, A> { } else { self.audio.ch3.on = false; } - self.audio.ch3.update(); + self.audio.ch3.update(true); } 0x1b => { self.audio.ch3.length_timer = value & 0b111111; + self.audio.ch3.update(false); } 0x1c => { let s = (value >> 5) & 0b11; @@ -153,10 +165,12 @@ impl<S: Serial, A: Audio> Memory<S, A> { } else { self.audio.ch3.initial_volume = 0xf >> (s - 1); } + self.audio.ch3.update(false); } 0x1d => { self.audio.ch3.period_value &= 0xff00; self.audio.ch3.period_value |= value as u16; + self.audio.ch3.update(false); } 0x1e => { self.audio.ch3.period_value &= 0xff; @@ -164,28 +178,68 @@ impl<S: Serial, A: Audio> Memory<S, A> { self.audio.ch3.period_value /= 2; self.audio.ch3.length_timer_enabled = value & 0b01000000 != 0; if value >> 7 == 1 { - self.audio.ch3.update(); + self.audio.ch3.update(true); + } else { + self.audio.ch3.update(false); } } 0x20 => { self.audio.ch4.length_timer = value & 0b111111; + self.audio.ch4.update(false); } 0x21 => { self.audio.ch4.initial_volume = value >> 4; self.audio.ch4.env_direction = (value & 0xf) >> 3; self.audio.ch4.sweep = value & 0b111; + self.audio.ch4.update(false); } 0x22 => { self.audio.ch4.clock_shift = value >> 4; self.audio.ch4.lsfr_width = (value & 0xf) >> 3; self.audio.ch4.clock_divider = value & 0b111; + self.audio.ch4.update(false); } 0x23 => { self.audio.ch4.length_timer_enabled = value & 0b01000000 != 0; if value >> 7 == 1 { - self.audio.ch4.update(); + self.audio.ch4.update(true); + } else { + self.audio.ch4.update(false); } } + 0x24 => { + let right_volume = value & 0x7; + let left_volume = value & 0x70 >> 4; + + self.audio.ch1.right_volume = right_volume; + self.audio.ch2.right_volume = right_volume; + self.audio.ch3.right_volume = right_volume; + self.audio.ch4.right_volume = right_volume; + + self.audio.ch1.left_volume = left_volume; + self.audio.ch2.left_volume = left_volume; + self.audio.ch3.left_volume = left_volume; + self.audio.ch4.left_volume = left_volume; + + self.audio.ch1.update(false); + self.audio.ch2.update(false); + self.audio.ch3.update(false); + self.audio.ch4.update(false); + } + 0x25 => { + self.audio.ch1.right = value & 0x01 != 0; + self.audio.ch2.right = value & 0x02 != 0; + self.audio.ch3.right = value & 0x04 != 0; + self.audio.ch4.right = value & 0x08 != 0; + self.audio.ch1.left = value & 0x10 != 0; + self.audio.ch2.left = value & 0x20 != 0; + self.audio.ch3.left = value & 0x40 != 0; + self.audio.ch4.left = value & 0x80 != 0; + self.audio.ch1.update(false); + self.audio.ch2.update(false); + self.audio.ch3.update(false); + self.audio.ch4.update(false); + } 0x40 => self.display.lcdc = value, 0x41 => { if value & 0b01000000 != 0 { |