diff --git a/src/driver.h b/src/driver.h index 8c2cda3..b17376c 100644 --- a/src/driver.h +++ b/src/driver.h @@ -160,6 +160,7 @@ void FCEUI_CheatSearchShowExcluded(void); void FCEUI_CheatSearchSetCurrentAsOriginal(void); void FCEUI_SetLowPass(int q); +void FCEUI_RemoveTriangleNoise(int d); void FCEUI_NSFSetVis(int mode); int FCEUI_NSFChange(int amount); diff --git a/src/drivers/libretro/libretro.c b/src/drivers/libretro/libretro.c index 96dece6..efeb8d8 100644 --- a/src/drivers/libretro/libretro.c +++ b/src/drivers/libretro/libretro.c @@ -1569,10 +1569,11 @@ static bool update_option_visibility(void) struct retro_core_option_display option_display; unsigned i; unsigned size; - char options_list[][25] = { + char options_list[][32] = { "fceumm_sndvolume", "fceumm_sndquality", "fceumm_sndlowpass", + "fceumm_removetrianglenoise", "fceumm_sndstereodelay", "fceumm_swapduty", "fceumm_apu_1", @@ -2391,6 +2392,14 @@ static void check_variables(bool startup) FCEUI_SetLowPass(lowpass); } + var.key = "fceumm_removetrianglenoise"; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int newval = (!strcmp(var.value, "enabled")) ? 1 : 0; + FCEUI_RemoveTriangleNoise(newval); + } + var.key = "fceumm_sndstereodelay"; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) diff --git a/src/drivers/libretro/libretro_core_options.h b/src/drivers/libretro/libretro_core_options.h index a324d92..ffceb68 100644 --- a/src/drivers/libretro/libretro_core_options.h +++ b/src/drivers/libretro/libretro_core_options.h @@ -321,6 +321,20 @@ struct retro_core_option_v2_definition option_defs[] = { }, "disabled", }, + { + "fceumm_removetrianglenoise", + "Reduce Triangle Channel Popping", + NULL, + "Mute the triangle channel when its period drops into the ultrasonic range (period <= 3, > ~12 kHz at NTSC). Eliminates the audible popping some games (Castlevania II: Simon's Quest, Jackal, etc.) produce in High / Very High sound quality, where the DAC reconstruction filter would otherwise fold the ultrasonic content back into the audible range. Low quality is unaffected (it already silences ultrasonic triangle output).", + NULL, + "audio", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled", + }, { "fceumm_sndstereodelay", "Stereo Sound Effect", diff --git a/src/fceu.h b/src/fceu.h index 74c9026..4ee4f2d 100644 --- a/src/fceu.h +++ b/src/fceu.h @@ -94,6 +94,16 @@ typedef struct { uint32_t SndRate; int soundq; int lowpass; + int RemoveTriangleNoise; /* Mute triangle channel when its period + * is low enough to produce only ultrasonic + * output (period <= 3, > ~12 kHz at NTSC), + * which the DAC reconstruction filter + * folds back as audible popping in HQ + * mode. LQ already silences these + * unconditionally; this option mirrors + * that behaviour in HQ. Default off to + * preserve existing HQ output bit-exactly + * when the option is not set. */ } FCEUS; extern FCEUS FSettings; diff --git a/src/sound.c b/src/sound.c index e1c6438..97ffbd7 100644 --- a/src/sound.c +++ b/src/sound.c @@ -733,10 +733,21 @@ static void RDoSQLQ(void) { static void RDoTriangle(void) { uint32_t V; int32_t tcout = (tristep & 0xF); + uint32_t triangle_raw_period = (PSG[0xa] | ((PSG[0xb] & 7) << 8)); if (!(tristep & 0x10)) tcout ^= 0xF; tcout = (tcout * 3) << 16; /* (tcout<<1); */ - if (!lengthcount[2] || !TriCount) { /* Counter is halted, but we still need to output. */ + /* The LQ tri/noise/PCM mixer (RDoTriangleNoisePCMLQ below) already + * forces the triangle channel quiet when its period is low enough + * to produce only ultrasonic output - games like Castlevania II + * and Jackal repeatedly drop the triangle into that range when + * they want silence, and without the gate the DAC reconstruction + * filter folds the high-frequency content back as audible + * popping. Mirror the gate in the HQ path, conditional on the + * RemoveTriangleNoise option so the default HQ output stays + * bit-exact with the original code unless the user opts in. */ + if (!lengthcount[2] || !TriCount + || (FSettings.RemoveTriangleNoise && triangle_raw_period <= 3)) { /* Counter is halted, but we still need to output. */ int32_t *start = &WaveHi[ChannelBC[2]]; int32_t count = SOUNDTS - ChannelBC[2]; while (count--) { @@ -1246,6 +1257,10 @@ void FCEUI_SetLowPass(int q) { FSettings.lowpass = q; } +void FCEUI_RemoveTriangleNoise(int d) { + FSettings.RemoveTriangleNoise = d ? 1 : 0; +} + void FCEUI_SetSoundQuality(int quality) { FSettings.soundq = quality; SetSoundVariables();