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
- 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.
- Noise LFSR at the documented rate; per-voice noise mode from
0x1F801D94/96.
- Sweep envelopes on the volume registers (bit 15 selects sweep vs direct).
- SPU IRQ on the ADPCM decoder crossing the IRQ address.
- Verify against Beetle on a real disc — same scene, compare output — rather than by ear alone.
runtime/src/spu.cstates it plainly in its own header: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
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(commit5fc8e15d) so it is not lost. The reason it was not merged:0x1F801DC0— that isdAPF1, 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.dAPF2/vIIR/vCOMB1as "feedback" / "wet level" / "early-reflection level". Those registers are not those things.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
vLOUT/vROUT, gated on SPUCNT bit 7.0x1F801D94/96.