Expansion Audio¶
References: the authoritative spec is apu-2a03.md § Expansion-chip audio; levels are tracked in accuracy-ledger.md. This page is a curated handbook entry point.
Several NES cartridge boards carry their own sound hardware that mixes into
the console's external-audio input alongside the 2A03. RustyNES synthesizes six
such expansion chips and sums each into the mix through the
Mapper::mix_audio(&mut self) -> i16 hook (default 0 for boards with no audio
hardware). Each synth core lives in the owning mapper crate, not the 2A03
APU crate, because it is cartridge hardware.
Supported chips¶
| Chip | Channels | Core | Clocking |
|---|---|---|---|
| VRC6 | 2 pulse + 1 saw | Vrc6Pulse ×2 + Vrc6Saw (rustynes-mappers) |
every CPU cycle |
| VRC7 | 6 FM (OPLL) | rustynes_apu::Opll (emu2413-derived, MIT) |
OPLL calc() every 36 CPU cycles (~49,716 Hz) |
| FDS | 1 wavetable + FM | FdsAudio (fds.rs) |
wave/mod every 16 CPU cycles |
| MMC5 | 2 pulse + 7-bit PCM | Mmc5Audio (m005_mmc5.rs) |
pulse every other CPU cycle |
| Namco 163 | 1–8 time-multiplexed wavetable | Namco163Audio |
round-robin every 15 CPU cycles |
| Sunsoft 5B | 3 tone + noise + envelope | Sunsoft5BAudio (FME-7) |
every CPU cycle |
All cores are behind the default-on mapper-audio Cargo feature; with it off
(e.g. the no_std build) the register decoders still latch so save-state
round-trip is preserved, but clock/mix become silent no-op shims. The VRC7
OPLL core is deliberately the MIT emu2413 lineage, not the license-
incompatible Nuked-OPLL.
Relative levels (v2.1.6 "Expansion Audio", VRC6 recalibrated v2.2.7)¶
Each chip's mix_audio() is scaled so its full-volume output sits at the
loudness the hardware produces relative to the 2A03 pulse — calibrated against
the reference-emulator field (Mesen2 was RustyNES's historical accuracy bar, but
VRC6 was recalibrated away from it in v2.2.7 because Mesen2 is the loud outlier
for VRC6; see below), measured by the bbbradsmith db_* decibel-comparison ROMs
and asserted by the audio_expansion.rs level_db_* oracle. VRC6 (≈1.0, v2.2.7),
MMC5 (≈1.0, "equivalent to the APU") and Namco 163 1-channel (≈6.02) are the
pinned corrections. VRC6 was recalibrated in v2.2.7 "Timbre II": the v2.1.6
target (≈1.506) mirrored Mesen2's specifically louder ×5 mixer weight, but a
NESdev-forum report plus a cross-reference across eleven reference emulators and
the NESdev wiki found Mesen2 to be the loud outlier — the wiki states the VRC6
pulse channels are roughly loudness-equivalent to the 2A03's, and rustico /
tetanes / BizHawk each encode that equivalence exactly — so the target moved to
≈1.0 and VRC6_MIX_SCALE moved 979 → 650 (m024_vrc6.rs). One level remains an
honest documented gap: the VRC7 FM absolute level (the FM synth is implemented
and its instrument ROM is verified canonical, but its patch/feedback-dependent
pseudo-sine output is not cleanly oracle-pinned, so db_vrc7 stays a byte-exact
snapshot regression guard). (The Sunsoft 5B absolute level was itself an
i16-mix_audio-contract gap until v2.2.3 widened the trait return to i32
and pinned it via level_db_5b; its log-DAC shape is hardware-exact and, since
v2.2.7, its envelope uses the exact 5-bit 1.5 dB/step DAC — see below.) Separately, v2.2.7 also
completed the Sunsoft 5B envelope DAC: the envelope-mode volume now indexes
a 32-level, 1.5 dB/step table (SUNSOFT5B_LOG_VOL32) instead of truncating to
the 4-bit, 3 dB/step approximation, matching nestopia / rustico; the fixed
4-bit tone volume and the absolute 5B level are unchanged. See
accuracy-ledger.md § Expansion-audio levels.
Because a non-expansion mapper's mix_audio() returns 0, the expansion mix is
a separate additive term — the level corrections leave the base 2A03 mix
byte-identical, so AccuracyCoin / blargg / nestest are unaffected.
NSF playback¶
A classic .nsf may declare expansion audio in its $07B bitfield (bit 0 VRC6,
1 VRC7, 2 FDS, 3 MMC5, 4 N163, 5 5B). The NSF player does not reimplement any
synthesis: NsfExpansion (nsf_expansion.rs) owns instances of the exact same
cores and routes the NSF register windows into them, so an NSF VRC6 tune sounds
identical to a VRC6 cartridge. NsfExpansion is unreachable from any oracle
cartridge ROM, so it cannot perturb the existing audio suites.
Frontend¶
The Audio Mixer tool panel (v2.1.6, debugger::audio_mixer) exposes
per-source balance sliders and per-channel scopes / VU meters, including the
on-cart expansion channel. See frontend.md.