From f4007769c2f821f8b78e6927c30fdce81172cf2a Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Thu, 22 Nov 2018 14:58:34 +0000 Subject: [PATCH] Add optional low pass audio filter --- pico/pico.h | 2 ++ pico/sound/sound.c | 41 ++++++++++++++++++++++++++++++++++++ platform/libretro/libretro.c | 17 +++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/pico/pico.h b/pico/pico.h index ac1550d4..a669215d 100644 --- a/pico/pico.h +++ b/pico/pico.h @@ -99,6 +99,8 @@ typedef struct unsigned short overclockM68k; // overclock the emulated 68k, in % int sndRate; // rate in Hz + unsigned short sndFilter; // Set low pass sound filter 0: off, 1: on (use integer in case we want to add other filter types later) + int32_t sndFilterRange; // Low pass sound filter range [0, 65536] short *sndOut; // PCM output buffer void (*writeSound)(int len); // write .sndOut callback, called once per frame diff --git a/pico/sound/sound.c b/pico/sound/sound.c index 95aac128..c828b30e 100644 --- a/pico/sound/sound.c +++ b/pico/sound/sound.c @@ -28,6 +28,9 @@ short cdda_out_buffer[2*1152]; // sn76496 extern int *sn76496_regs; +// Low pass filter 'previous' samples +static int32_t lpf_lp; +static int32_t lpf_rp; static void dac_recalculate(void) { @@ -86,6 +89,10 @@ PICO_INTERNAL void PsndReset(void) // PsndRerate calls YM2612Init, which also resets PsndRerate(0); timers_reset(); + + // Reset low pass filter + lpf_lp = 0; + lpf_rp = 0; } @@ -318,6 +325,40 @@ static int PsndRender(int offset, int length) if ((PicoIn.AHW & PAHW_32X) && (PicoIn.opt & POPT_EN_PWM)) p32x_pwm_update(buf32, length, stereo); + // Apply low pass filter, if required + if (PicoIn.sndFilter == 1) + { + int samples = length; + int *out32 = buf32; + // Restore previous samples + int32_t lpf_l = lpf_lp; + int32_t lpf_r = lpf_rp; + + // Single-pole low-pass filter (6 dB/octave) + int32_t factor_a = PicoIn.sndFilterRange; + int32_t factor_b = 0x10000 - factor_a; + + do + { + // Apply low-pass filter + lpf_l = (lpf_l * factor_a) + (out32[0] * factor_b); + lpf_r = (lpf_r * factor_a) + (out32[1] * factor_b); + + // 16.16 fixed point + lpf_l >>= 16; + lpf_r >>= 16; + + // Update sound buffer + *out32++ = lpf_l; + *out32++ = lpf_r; + } + while (--samples); + + // Save last samples for next frame + lpf_lp = lpf_l; + lpf_rp = lpf_r; + } + // convert + limit to normal 16bit output PsndMix_32_to_16l(PicoIn.sndOut+offset, buf32, length); diff --git a/platform/libretro/libretro.c b/platform/libretro/libretro.c index 601f4d36..7eb55637 100644 --- a/platform/libretro/libretro.c +++ b/platform/libretro/libretro.c @@ -541,6 +541,8 @@ void retro_set_environment(retro_environment_t cb) #ifdef DRC_SH2 { "picodrive_drc", "Dynamic recompilers; enabled|disabled" }, #endif + { "picodrive_audio_filter", "Audio filter; disabled|low-pass" }, + { "picodrive_lowpass_range", "Low-pass filter %; 60|65|70|75|80|85|90|95|5|10|15|20|25|30|35|40|45|50|55"}, { NULL, NULL }, }; @@ -1337,6 +1339,21 @@ static void update_variables(void) if(!ctr_svchack_successful) PicoIn.opt &= ~POPT_EN_DRC; #endif + + var.value = NULL; + var.key = "picodrive_audio_filter"; + PicoIn.sndFilter = 0; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + if (strcmp(var.value, "low-pass") == 0) + PicoIn.sndFilter = 1; + } + + var.value = NULL; + var.key = "picodrive_lowpass_range"; + PicoIn.sndFilterRange = (60 * 65536) / 100; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + PicoIn.sndFilterRange = (atoi(var.value) * 65536) / 100; + } } void retro_run(void)