aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-07-12 02:45:38 +0200
committerAstatin <[email protected]>2025-07-12 02:45:38 +0200
commit9d24a19cd9a392d5a2067a3e233a5a26a1518c35 (patch)
tree0a47acf10b6a029977ba7bd4c3fad59f5f5f28e2
parentbfe34191a52e356ce92df8e6b10d3ba2b4598fe8 (diff)
Update audio registers without trigger + add stereo
-rw-r--r--src/audio.rs139
-rw-r--r--src/desktop/audio.rs24
-rw-r--r--src/io.rs6
-rw-r--r--src/mmio.rs64
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,
diff --git a/src/io.rs b/src/io.rs
index 4b7ca08..fae191a 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -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 {