core: APU channel disable - fix bitmasks and LQ-mode volume application

Two related bugs in the user-facing fceumm_apu_1..5 channel disable
toggles. Pre-existing since commit 448a231 (set_apu_channels) and at
least as far back as the LQ rewrite for the second one.

set_apu_channels (libretro.c)
-----------------------------

The bitmask built by check_variables uses bit i for fceumm_apu_(i+1)
enabled, ordered SQ1, SQ2, Triangle, Noise, DMC. The old code:

    FSettings.SquareVolume[1] = (chan & 1) ? 256 : 0;
    FSettings.SquareVolume[0] = (chan & 2) ? 256 : 0;
    FSettings.TriangleVolume  = (chan & 3) ? 256 : 0;
    FSettings.NoiseVolume     = (chan & 4) ? 256 : 0;
    FSettings.PCMVolume       = (chan & 5) ? 256 : 0;

crossed SQ1/SQ2 and used non-power-of-two masks for the remaining
three. Practical effect: apu_1 muted SQ2 and apu_2 muted SQ1; apu_3
through apu_5 left their channels audible in any normal config (the
masks happened to be true any time another bit was set).

Fixed to read each channel's own bit:

    FSettings.SquareVolume[0] = (chan & 0x01) ? 256 : 0;  /* SQ1  */
    FSettings.SquareVolume[1] = (chan & 0x02) ? 256 : 0;  /* SQ2  */
    FSettings.TriangleVolume  = (chan & 0x04) ? 256 : 0;  /* Tri  */
    FSettings.NoiseVolume     = (chan & 0x08) ? 256 : 0;  /* Nse  */
    FSettings.PCMVolume       = (chan & 0x10) ? 256 : 0;  /* DMC  */

RDoTriangleNoisePCMLQ (sound.c)
-------------------------------

The combined Triangle/Noise/DMC LQ mix uses a non-linear lookup
table:

    totalout = wlookup2[lq_tcout + noiseout + RawDALatch];

The old code took the noise envelope (EnvUnits[2], not Triangle -
Triangle has no envelope; EnvUnits[0]=SQ1, [1]=SQ2, [2]=Noise) and
scaled it by FSettings.TriangleVolume, despite the comment claiming
to modify Triangle. Meanwhile lq_tcout (Triangle) and RawDALatch
(DMC) went into wlookup2 unscaled, so neither TriangleVolume nor
PCMVolume had any effect in LQ mode.

Fixed:
* amptab[0] (noise envelope) is now scaled by FSettings.NoiseVolume.
* lq_tcout is scaled by FSettings.TriangleVolume into a local
  scaled_tcout fed to wlookup2.
* RawDALatch is scaled by FSettings.PCMVolume into scaled_dmc.

The volume scalars are loop-invariant within this function, so the
Triangle volume is cached once in tri_vol at function entry to keep
the inner loops tight; the != 256 fast paths preserve byte-exact
behavior at full volume.

Verification
------------

Built per-channel-only test ROMs (test_sq1_only, test_sq2_only,
test_tri_only, test_nse_only, test_dmc_only) and an apu_test
runner that measures output RMS with each fceumm_apu_N toggle.
After the fix, in both LQ and HQ modes, each apu_N toggle mutes
exactly the channel whose ROM is the only sound source, and no
other toggle has any effect on it.

Default (all-on) audio output is bit-exact identical to the
baseline across the existing test_silent / test_idle / test_active /
test_stress ROMs (1500 frames each), so games and homebrew that
never touch the apu_N options see no change. No performance
regression in the default path; the != 256 guards skip the multiplies.
This commit is contained in:
U-DESKTOP-SPFP6AQ\twistedtechre
2026-05-04 07:40:08 +02:00
parent 6ffed08948
commit 6c97c3fff4
2 changed files with 50 additions and 17 deletions

View File

@@ -1975,11 +1975,23 @@ void retro_reset(void)
static void set_apu_channels(int chan)
{
FSettings.SquareVolume[1] = (chan & 1) ? 256 : 0;
FSettings.SquareVolume[0] = (chan & 2) ? 256 : 0;
FSettings.TriangleVolume = (chan & 3) ? 256 : 0;
FSettings.NoiseVolume = (chan & 4) ? 256 : 0;
FSettings.PCMVolume = (chan & 5) ? 256 : 0;
/* Bitmask layout (set up by check_variables): bit i = fceumm_apu_(i+1)
* is enabled. Channel order matches the libretro core options:
* bit 0 = Square 1 (apu_1)
* bit 1 = Square 2 (apu_2)
* bit 2 = Triangle (apu_3)
* bit 3 = Noise (apu_4)
* bit 4 = PCM / DMC (apu_5)
*
* The previous masks crossed the SQ1/SQ2 indices and used non-power-of-
* two values for the remaining channels, which silently broke the
* apu_3..apu_5 toggles entirely (any combination of bits 0/1/2 left
* those channels audible) and made apu_1 mute SQ2 and vice versa. */
FSettings.SquareVolume[0] = (chan & 0x01) ? 256 : 0;
FSettings.SquareVolume[1] = (chan & 0x02) ? 256 : 0;
FSettings.TriangleVolume = (chan & 0x04) ? 256 : 0;
FSettings.NoiseVolume = (chan & 0x08) ? 256 : 0;
FSettings.PCMVolume = (chan & 0x10) ? 256 : 0;
}
static void check_variables(bool startup)