Skip to content

APU: square channel sweep unit has wrong negate formula and muting threshold #608

@bfirsh

Description

@bfirsh

Summary

Two bugs in the square channel sweep unit (channel-square.js):

1. Negate formula inverted for Pulse 1 (line 100-103)

this.progTimerMax =
  this.progTimerMax -
  ((this.progTimerMax >> this.sweepShiftAmount) -
    (this.sqr1 ? 1 : 0));

This computes period - shifted + 1 for Pulse 1. Per nesdev, Pulse 1 uses ones' complement (bitwise NOT): the result should be period + (~shifted) which equals period - shifted - 1. The - 1 should be + 1 (or more precisely, the formula should be period - (period >> shift) - 1 for Pulse 1).

2. Sweep muting threshold is wrong (line 95, 118)

if (this.progTimerMax > 4095)  // Wrong

The muting cutoff should be > 0x7FF (2047), not 4095. The target period check in updateSampleValue() (line 118) also uses > 4095. This allows frequencies that should be silenced, producing audible artifacts.

Impact

Audible pitch errors in any game using sweep effects. The negate difference between Pulse 1 and Pulse 2 is what allows them to produce different sweep sounds — getting this wrong makes them sound identical.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    accuracyHardware accuracy improvementbugcomponent: apuAPU / audio (papu/)difficulty: easySmall, localized change (1-2 lines)priority: highSignificantly wrong behavior affecting many games

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions