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:
committed by
U-DESKTOP-SPFP6AQ\twistedtechre
parent
d3cd26e922
commit
5f8a864bf3
24
src/sound.c
24
src/sound.c
@@ -313,7 +313,25 @@ static DECLFW(Write_DMCRegs) {
|
||||
DMCFormat = V;
|
||||
break;
|
||||
case 0x01: DoPCM();
|
||||
RawDALatch = V & 0x7F;
|
||||
{
|
||||
/* $4011 is the 7-bit DAC latch. Games like Castlevania II
|
||||
* pulse this register directly to produce sample-style audio
|
||||
* out-of-band from the DPCM bit-stream; the abrupt steps
|
||||
* between successive writes are the audible "pop". When
|
||||
* ReduceDMCPopping is on, take only the midpoint of the
|
||||
* old-vs-new transition - i.e. step the DAC halfway toward
|
||||
* the requested value rather than jumping straight to it.
|
||||
* This is the same algorithm libretro-fceumm_next ships
|
||||
* (backport reference: negativeExponent's fceumm_next).
|
||||
* The DPCM playback path (the +/-2 per bit update further
|
||||
* down) is left untouched. */
|
||||
uint8_t newval = V & 0x7F;
|
||||
uint8_t lastval = RawDALatch;
|
||||
RawDALatch = newval;
|
||||
if (FSettings.ReduceDMCPopping) {
|
||||
RawDALatch = (uint8_t)(newval - ((int)newval - (int)lastval) / 2);
|
||||
}
|
||||
}
|
||||
if (RawDALatch)
|
||||
DMC_7bit = 1;
|
||||
break;
|
||||
@@ -1261,6 +1279,10 @@ void FCEUI_RemoveTriangleNoise(int d) {
|
||||
FSettings.RemoveTriangleNoise = d ? 1 : 0;
|
||||
}
|
||||
|
||||
void FCEUI_ReduceDmcPopping(int d) {
|
||||
FSettings.ReduceDMCPopping = d ? 1 : 0;
|
||||
}
|
||||
|
||||
void FCEUI_SetSoundQuality(int quality) {
|
||||
FSettings.soundq = quality;
|
||||
SetSoundVariables();
|
||||
|
||||
Reference in New Issue
Block a user