feat(apu): add ChannelOutputs struct and channel_outputs() method

This commit is contained in:
2026-03-13 16:08:55 +03:00
parent 49568a582b
commit e63b5783bd
5 changed files with 86 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
use super::types::{Apu, ApuStateTail};
use super::types::{Apu, ApuStateTail, ChannelOutputs};
impl Apu {
pub fn new() -> Self {
@@ -287,4 +287,74 @@ impl Apu {
self.noise_timer_counter = state.noise_timer_counter;
self.noise_lfsr = if state.noise_lfsr == 0 { 1 } else { state.noise_lfsr };
}
pub fn channel_outputs(&self) -> ChannelOutputs {
const PULSE_DUTY_TABLE: [[u8; 8]; 4] = [
[0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 0, 0, 0],
[1, 0, 0, 1, 1, 1, 1, 1],
];
const TRIANGLE_SEQUENCE: [u8; 32] = [
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
];
let pulse1 = {
let duty = (self.io[0x00] >> 6) as usize;
let step = self.pulse_duty_step[0] as usize;
let volume = if (self.io[0x00] & 0x10) != 0 {
self.io[0x00] & 0x0F
} else {
self.envelope_decay[0]
};
let active = (self.channel_enable_mask & 0x01) != 0
&& self.length_counters[0] > 0
&& PULSE_DUTY_TABLE[duty][step] != 0
&& !self.sweep_mutes_channel(0, 0x02);
if active { volume } else { 0 }
};
let pulse2 = {
let duty = (self.io[0x04] >> 6) as usize;
let step = self.pulse_duty_step[1] as usize;
let volume = if (self.io[0x04] & 0x10) != 0 {
self.io[0x04] & 0x0F
} else {
self.envelope_decay[1]
};
let active = (self.channel_enable_mask & 0x02) != 0
&& self.length_counters[1] > 0
&& PULSE_DUTY_TABLE[duty][step] != 0
&& !self.sweep_mutes_channel(1, 0x06);
if active { volume } else { 0 }
};
let triangle = {
let active = (self.channel_enable_mask & 0x04) != 0
&& self.length_counters[2] > 0
&& self.triangle_linear_counter > 0;
if active {
TRIANGLE_SEQUENCE[self.triangle_step as usize & 0x1F]
} else {
0
}
};
let noise = {
let volume = if (self.io[0x0C] & 0x10) != 0 {
self.io[0x0C] & 0x0F
} else {
self.envelope_decay[2]
};
let active = (self.channel_enable_mask & 0x08) != 0
&& self.length_counters[3] > 0
&& (self.noise_lfsr & 1) == 0;
if active { volume } else { 0 }
};
let dmc = self.dmc_output_level;
ChannelOutputs { pulse1, pulse2, triangle, noise, dmc }
}
}