feat(audio): non-linear APU mixing and mapper expansion audio (VRC6, FME-7, Namco163)

This commit is contained in:
2026-03-15 11:17:37 +03:00
parent d94fbb894b
commit c77be7c84b
9 changed files with 362 additions and 28 deletions

View File

@@ -56,11 +56,27 @@ impl AudioMixer {
let samples = self.sample_accumulator.floor() as usize;
self.sample_accumulator -= samples as f64;
let pulse_out = 0.00752 * (f32::from(channels.pulse1) + f32::from(channels.pulse2));
let tnd_out = 0.00851 * f32::from(channels.triangle)
+ 0.00494 * f32::from(channels.noise)
+ 0.00335 * f32::from(channels.dmc);
let sample = pulse_out + tnd_out;
// NES non-linear APU mixing (Blargg's reference formulas).
// Pulse channels use a shared lookup:
// pulse_out = 95.88 / (8128 / (p1 + p2) + 100)
// TND channels use a separate lookup:
// tnd_out = 159.79 / (1 / (tri/8227 + noise/12241 + dmc/22638) + 100)
// Both formulas produce 0.0 when all contributing channels are silent.
let p_sum = f32::from(channels.pulse1) + f32::from(channels.pulse2);
let pulse_out = if p_sum == 0.0 {
0.0
} else {
95.88 / (8128.0 / p_sum + 100.0)
};
let tnd_sum = f32::from(channels.triangle) / 8227.0
+ f32::from(channels.noise) / 12241.0
+ f32::from(channels.dmc) / 22638.0;
let tnd_out = if tnd_sum == 0.0 {
0.0
} else {
159.79 / (1.0 / tnd_sum + 100.0)
};
let sample = pulse_out + tnd_out + channels.expansion;
if samples == 0 {
return;
@@ -118,6 +134,7 @@ mod tests {
triangle: 15,
noise: 15,
dmc: 127,
expansion: 0.0,
};
let mut out = Vec::new();
mixer.push_cycles(50, channels, &mut out);
@@ -143,6 +160,7 @@ mod tests {
triangle: 15,
noise: 15,
dmc: 127,
expansion: 0.0,
},
&mut out,
);