sound: fix noise LFSR power-on state to match real hardware (fixes #466)
The internal noise shift register in this file uses a bit-reversed layout vs the canonical nesdev wiki model: output is read from bit 14 (not bit 0), the feedback taps are at 13/14 in long mode and 8/14 in short mode (mirrors of 0/1 and 0/6), and the register shifts left rather than right. The algorithm is algebraically equivalent to the wiki form, but the power-on state has to be mirrored as well. Real hardware initialises the LFSR to $0001 with bit 0 set. Under this file's mirroring, the equivalent state has bit 14 set - i.e. nreg = 0x4000, not nreg = 1. Starting at 1 placed the LFSR 14 steps off-phase from real hardware in long mode and at an entirely different position in the 93-cycle short-mode loop. Reported by NSFPlay author Brad Smith (rainwarrior) comparing moon8.nes audio against Mesen. Reproduced via headless libretro harness with per-channel solo bisection against negativeExponent's fceumm_next branch (which uses the un-mirrored nesdev layout): channel RMS diff vs _next (noise solo, 22-60s window) ------- ---------------------------------------------- square1 1 ( 0.4%) - bit-identical square2 1 ( 0.4%) - bit-identical triangle 178 (14.0%) - minor difference noise 1914 (125.8%) - completely off dmc 2 ( 0.4%) - bit-identical After this patch the noise-solo RMS drops to 6 (0.4%), bringing the noise channel into bit-exact agreement with _next/Mesen/NSFPlay. Full-mix moon8 audio RMS vs _next falls from 1135 to 126 across the 60-second test.
This commit is contained in:
committed by
U-DESKTOP-SPFP6AQ\twistedtechre
parent
71bbd10ad0
commit
3f23e2b98f
25
src/sound.c
25
src/sound.c
@@ -1128,7 +1128,30 @@ void FCEUSND_Reset(void) {
|
||||
|
||||
fhcnt = fhinc;
|
||||
fcnt = 0;
|
||||
nreg = 1;
|
||||
/* Power-on noise shift register state.
|
||||
*
|
||||
* Real hardware initializes the 15-bit noise LFSR to $0001 with bit
|
||||
* 0 set (the output bit, muting the channel until the first feedback
|
||||
* cycle). This file stores the LFSR with the bit order reversed -
|
||||
* the output is read from bit 14, the feedback taps are at 13/14
|
||||
* (long mode) or 8/14 (short mode), and the shift goes left rather
|
||||
* than right (see RDoNoise / NoLQNoise). Under that mirroring, the
|
||||
* real-hardware $0001 state corresponds to nreg = 0x4000 here, not
|
||||
* nreg = 1.
|
||||
*
|
||||
* Initialising to 1 left the LFSR running 14 long-mode steps ahead
|
||||
* of every other accurate emulator (Mesen, NSFPlay, _next), and made
|
||||
* short-mode output diverge entirely - the 93-cycle period is short
|
||||
* enough that the position offset is audible as "rougher" or
|
||||
* subtly wrong percussion. Reported as libretro-fceumm issue #466
|
||||
* (Moon8 audio inaccuracy, by NSFPlay author Brad Smith).
|
||||
*
|
||||
* Per-channel bisection of moon8.nes against negativeExponent's
|
||||
* _next branch (which uses the un-mirrored layout from nesdev wiki)
|
||||
* shows the noise channel as the only meaningful divergence after
|
||||
* the music kicks in at ~22 s; squares and DMC are bit-identical.
|
||||
*/
|
||||
nreg = 0x4000;
|
||||
|
||||
for (x = 0; x < 2; x++) {
|
||||
wlcount[x] = 2048;
|
||||
|
||||
Reference in New Issue
Block a user