Skip to content

SPU: reverb, noise, sweep volumes and IRQ timing are not modeled #103

Description

@mstan

runtime/src/spu.c states it plainly in its own header:

Reverb, noise, sweep volumes, and IRQ timing are not modeled yet.

Everything else works — 24 ADPCM voices, pitch, ADSR envelopes, volumes, CD audio on the input bus, Gaussian interpolation. These four are what remain.

What each one costs us

Missing Effect Severity
Reverb Games switch it on so caves sound cavernous and halls sound big. Without it everything is dry — right notes, no sense of space. Fidelity — most audible
Noise generator A voice can be routed to a hardware white-noise source instead of a sample: cymbals, explosions, wind, static. Without it those play wrong or not at all. Fidelity
Volume sweeps Hardware-driven per-voice fades. Without them a sweep just sits at fixed volume instead of gliding. Fidelity
SPU IRQ The chip interrupts the CPU when playback crosses a marked address; some games sync music/events to it. The only one with correctness risk — a game that waits on it could stall

So: three are "sounds flatter than hardware", one could in principle affect behaviour. Tomba, Tomba 2, Ape Escape and MMX6 all boot and play fine on the current SPU, so none of them wait on SPU IRQ.

We are not short of test cases

Nearly every PS1 game enables reverb, so any of our titles exercises it — no special title needed. What is missing is not a game to test with, it is a faithful implementation plus something to check it against, and we already have the checker: the Beetle PSX oracle runs the same disc and exposes the same debug protocol, so its audio output is a direct reference.

Prior art, and why it was not merged

PR #13 contained an implementation of all four. It was rejected, and the code is parked on park/pr13-spu-reverb-rejected (commit 5fc8e15d) so it is not lost. The reason it was not merged:

  • It gated reverb on bit 15 of 0x1F801DC0 — that is dAPF1, an all-pass offset register, not a control register. Its top bit is set for any offset >= 0x8000, so reverb switched on and off according to an address value. The real gate is SPUCNT (0x1F801DAA) bit 7.
  • It read dAPF2 / vIIR / vCOMB1 as "feedback" / "wet level" / "early-reflection level". Those registers are not those things.
  • The result is a generic float reverb, not the console's, with nothing verifying it.

It produces a reverb. Master produces none and says so. A known silence is easier to reason about than an unverified wrongness, which is why it stays parked rather than shipped.

What a real fix looks like

  1. Implement the documented structure from psx-spx — the reverb work area in SPU RAM, the APF/comb/IIR taps with their actual register meanings, vLOUT/vROUT, gated on SPUCNT bit 7.
  2. Noise LFSR at the documented rate; per-voice noise mode from 0x1F801D94/96.
  3. Sweep envelopes on the volume registers (bit 15 selects sweep vs direct).
  4. SPU IRQ on the ADPCM decoder crossing the IRQ address.
  5. Verify against Beetle on a real disc — same scene, compare output — rather than by ear alone.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions