sound: add core option to reduce DMC channel popping (fixes #638)

Games like Castlevania II: Simon's Quest drive the DMC DAC directly
via $4011 writes to produce sample-style audio (separate from the
DPCM bit-stream path).  The DAC jumps straight to the newly-written
7-bit value on every write, and those abrupt steps are audible as
clicks/pops when the writes form a low-frequency envelope.

Backport reference: negativeExponent's libretro-fceumm_next applies
a one-step midpoint smoother at the $4011 write site - take the
old DAC value and the new requested value, and store only the
average (rounded toward the new value).  Successive writes still
converge on the target, so percussive samples retain their attack;
they just lose half of the per-write step amplitude, which is the
component that produces the click.

Backported as-is, gated on a new core option
`fceumm_reducedmcpopping` (default disabled, so the DAC
trajectory is bit-exact with the original code when the option is
not set).  Verified to match _next's algebra exactly across the
boundary cases (full-range jumps in both directions, single-step
deltas, idle no-ops).

The DPCM bit-decode playback path (the +/-2 per bit RawDALatch
update further down in the DMC channel inner loop) is intentionally
NOT touched - that path is the well-behaved bit-stream output, no
popping, and smoothing it would attenuate normal samples.

This addresses the second half of issue #638 (the first being the
triangle ultrasonic gate, already landed as d3cd26e).  The
follow-up comment on that issue asked specifically for DMC popping
reduction to land in libretro-fceumm since _next has no Android
port.

Build verified: sound.c -O2 / -O3 / aarch64 -O2 clean, libretro.c
clean.
This commit is contained in:
libretroadmin
2026-06-14 15:09:04 +00:00
committed by U-DESKTOP-SPFP6AQ\twistedtechre
parent d3cd26e922
commit 5f8a864bf3
5 changed files with 59 additions and 1 deletions

View File

@@ -1574,6 +1574,7 @@ static bool update_option_visibility(void)
"fceumm_sndquality",
"fceumm_sndlowpass",
"fceumm_removetrianglenoise",
"fceumm_reducedmcpopping",
"fceumm_sndstereodelay",
"fceumm_swapduty",
"fceumm_apu_1",
@@ -2400,6 +2401,14 @@ static void check_variables(bool startup)
FCEUI_RemoveTriangleNoise(newval);
}
var.key = "fceumm_reducedmcpopping";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
int newval = (!strcmp(var.value, "enabled")) ? 1 : 0;
FCEUI_ReduceDmcPopping(newval);
}
var.key = "fceumm_sndstereodelay";
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)