fix(apu): correct frame counter timing, add LP filter, mute aliased triangle
- Fix frame counter running at 2× speed: clock_frame_counter now skips odd CPU cycles (APU cycle = CPU/2), so envelope, sweep, and length counters tick at the correct rate. Fixes sweep-driven whistle in Megaman II. - Switch audio sampling to per-CPU-cycle granularity in run_until_frame_complete_with_audio to eliminate square-wave harmonic aliasing caused by sampling only once per instruction. - Add IIR one-pole low-pass filter (~14 kHz) to AudioMixer to smooth abrupt level transitions (crackling) introduced by correct envelope timing. - Mute triangle channel when timer_period < 2 (≥27 kHz), which aliases into the audible range at 48 kHz. Real NES RC circuit removes these ultrasonics; emulator must suppress them explicitly. - Update all APU bus tests to use correct (doubled) CPU cycle counts.
This commit is contained in:
@@ -331,9 +331,13 @@ impl Apu {
|
||||
};
|
||||
|
||||
let triangle = {
|
||||
// Timer period < 2 produces ultrasonic output (~28-56 kHz) that aliases
|
||||
// to audible frequencies when sampled at 48 kHz. Real hardware filters
|
||||
// this via the RC output stage; mute here to match that behaviour.
|
||||
let active = (self.channel_enable_mask & 0x04) != 0
|
||||
&& self.length_counters[2] > 0
|
||||
&& self.triangle_linear_counter > 0;
|
||||
&& self.triangle_linear_counter > 0
|
||||
&& self.triangle_timer_period() >= 2;
|
||||
if active {
|
||||
TRIANGLE_SEQUENCE[self.triangle_step as usize & 0x1F]
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user