From 7ad48f8d2996b5ced373b19dbd4f80a0d9827647 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 12 Mar 2024 10:10:00 -0300 Subject: [PATCH] Switching filter implementation to use SB16 filters; fixing CD audio volume --- src/sound/snd_ess.c | 11 ++---- src/sound/snd_sb_dsp.c | 88 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c index f771ee6db..9996ad006 100644 --- a/src/sound/snd_ess.c +++ b/src/sound/snd_ess.c @@ -150,7 +150,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) /* Initialize ESS regs. */ mixer->regs[0x14] = mixer->regs[0x32] = 0x88; mixer->regs[0x36] = 0x88; - mixer->regs[0x38] = 0x00; + mixer->regs[0x38] = 0x88; mixer->regs[0x3a] = 0x00; mixer->regs[0x3e] = 0x00; @@ -384,8 +384,8 @@ ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv) /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */ if (mixer->output_filter) { - out_l += (sb_iir(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.9; - out_r += (sb_iir(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9; + out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0; + out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0; } else { out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0; out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 3.0; @@ -446,10 +446,7 @@ ess_filter_cd_audio(int channel, double *buffer, void *priv) double cd = channel ? mixer->cd_r : mixer->cd_l; double master = channel ? mixer->master_r : mixer->master_l; - if (mixer->output_filter) - c = (sb_iir(2, channel, *buffer) * cd) / 3.9; - else - c = (*buffer * cd) / 3.0; + c = (*buffer * cd) / 3.0; *buffer = c * master; } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index ebda12652..f179ce284 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -175,6 +175,37 @@ recalc_sb16_filter(int c, int playback_freq) low_fir_sb16_coef[c][n] /= gain; } +static void +recalc_opl_filter(int c, int playback_freq) +{ + /* Cutoff frequency = playback / 2 */ + int n; + double w; + double h; + double fC = ((double) playback_freq) / (double) (FREQ_49716 * 2); + double gain; + + for (n = 0; n < SB16_NCoef; n++) { + /* Blackman window */ + w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); + /* Sinc filter */ + h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); + + /* Create windowed-sinc filter */ + low_fir_sb16_coef[c][n] = w * h; + } + + low_fir_sb16_coef[c][(SB16_NCoef - 1) / 2] = 1.0; + + gain = 0.0; + for (n = 0; n < SB16_NCoef; n++) + gain += low_fir_sb16_coef[c][n]; + + /* Normalise filter, to produce unity gain */ + for (n = 0; n < SB16_NCoef; n++) + low_fir_sb16_coef[c][n] /= gain; +} + static void sb_irq_update_pic(void *priv, int set) { @@ -678,17 +709,29 @@ sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate) dsp->sb_16_dma_translate = translate; } +static void sb_ess_update_reg_a2(sb_dsp_t *dsp, uint8_t val) +{ + double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0; + int temp = (int) freq; + ESSreg(0xA2) = val; + + if (dsp->sb_freq != temp) + recalc_sb16_filter(0, temp); + dsp->sb_freq = temp; +} + /* TODO: Investigate ESS cards' filtering on real hardware as well. (DOSBox-X did it purely off some laptop's ESS chip, which isn't a good look.) */ static void sb_ess_update_filter_freq(sb_dsp_t *dsp) { + double temp = (7160000.0 / (((((double) dsp->sb_freq) / 2.0) * 0.80) * 82.0)) - 256.0; + if (dsp->sb_freq >= 22050) ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq); else ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq); - unsigned int freq = ((dsp->sb_freq * 4) / (5 * 2)); /* 80% of 1/2 the sample rate */ - ESSreg(0xA2) = 256 - (7160000 / (freq * 82)); + sb_ess_update_reg_a2(dsp, (uint8_t) temp); } static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) @@ -720,20 +763,14 @@ static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) dsp->sb_freq = 795500UL / (256ul - data); else dsp->sb_freq = 397700UL / (128ul - data); - // TODO: check if this command updates the filter or not - sb_ess_update_filter_freq(dsp); - ESSreg(reg) = data; /* HACK: sb_ess_update_filter_freq updates 0xA1. - * I'm not sure if that could cause an off by one - * error, so this is just to be safe. */ temp = 1000000.0 / dsp->sb_freq; dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp; dsp->sb_timei = dsp->sb_timeo; pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho); - break; } case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */ - ESSreg(reg) = data; + sb_ess_update_reg_a2(dsp, data); break; case 0xA4: /* DMA Transfer Count Reload (low) */ @@ -1147,6 +1184,7 @@ sb_exec_command(sb_dsp_t *dsp) temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); pclog("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); + // if ((dsp->sb_freq != temp) && (IS_ESS(dsp) || (dsp->sb_type >= SB16))) if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, temp); dsp->sb_freq = temp; @@ -1165,7 +1203,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_timeo = 256LL + dsp->sb_freq; dsp->sblatchi = dsp->sblatcho; dsp->sb_timei = dsp->sb_timeo; - if (dsp->sb_freq != temp && dsp->sb_type >= SB16) + if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, dsp->sb_freq); dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff; dsp->sb_8051_ram[0x14] = (dsp->sb_freq >> 8) & 0xff; @@ -1731,14 +1769,30 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) timer_add(&dsp->wb_timer, NULL, dsp, 0); timer_add(&dsp->irq_timer, sb_dsp_irq_poll, dsp, 0); - /* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when - a set frequency command is sent. */ - recalc_sb16_filter(0, 3200 * 2); - if (dsp->sb_has_real_opl) - recalc_sb16_filter(1, FREQ_49716); + if (IS_ESS(dsp)) + /* Initialize ESS filter to 8 kHz. This will be recalculated when a set frequency command is + sent. */ + recalc_sb16_filter(0, 8000 * 2); else - recalc_sb16_filter(1, FREQ_48000); - recalc_sb16_filter(2, FREQ_44100); + /* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when + a set frequency command is sent. */ + recalc_sb16_filter(0, 3200 * 2); + if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO2)) { + /* OPL3 or dual OPL2 is stereo. */ + if (dsp->sb_has_real_opl) + recalc_opl_filter(1, FREQ_49716 * 2); + else + recalc_sb16_filter(1, FREQ_48000 * 2); + } else { + /* OPL2 is mono. */ + if (dsp->sb_has_real_opl) + recalc_opl_filter(1, FREQ_49716); + else + recalc_sb16_filter(1, FREQ_48000); + } + /* CD Audio is stereo. */ + recalc_sb16_filter(2, FREQ_44100 * 2); + /* PC speaker is mono. */ recalc_sb16_filter(3, 18939); /* Initialize SB16 8051 RAM and ASP internal RAM */