Switching filter implementation to use SB16 filters; fixing CD audio volume
This commit is contained in:
@@ -150,7 +150,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
|||||||
/* Initialize ESS regs. */
|
/* Initialize ESS regs. */
|
||||||
mixer->regs[0x14] = mixer->regs[0x32] = 0x88;
|
mixer->regs[0x14] = mixer->regs[0x32] = 0x88;
|
||||||
mixer->regs[0x36] = 0x88;
|
mixer->regs[0x36] = 0x88;
|
||||||
mixer->regs[0x38] = 0x00;
|
mixer->regs[0x38] = 0x88;
|
||||||
mixer->regs[0x3a] = 0x00;
|
mixer->regs[0x3a] = 0x00;
|
||||||
mixer->regs[0x3e] = 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? */
|
/* TODO: Implement the stereo switch on the mixer instead of on the dsp? */
|
||||||
if (mixer->output_filter) {
|
if (mixer->output_filter) {
|
||||||
out_l += (sb_iir(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.9;
|
out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0;
|
||||||
out_r += (sb_iir(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.9;
|
out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
|
||||||
} else {
|
} else {
|
||||||
out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
|
out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
|
||||||
out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 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 cd = channel ? mixer->cd_r : mixer->cd_l;
|
||||||
double master = channel ? mixer->master_r : mixer->master_l;
|
double master = channel ? mixer->master_r : mixer->master_l;
|
||||||
|
|
||||||
if (mixer->output_filter)
|
c = (*buffer * cd) / 3.0;
|
||||||
c = (sb_iir(2, channel, *buffer) * cd) / 3.9;
|
|
||||||
else
|
|
||||||
c = (*buffer * cd) / 3.0;
|
|
||||||
*buffer = c * master;
|
*buffer = c * master;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -175,6 +175,37 @@ recalc_sb16_filter(int c, int playback_freq)
|
|||||||
low_fir_sb16_coef[c][n] /= gain;
|
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
|
static void
|
||||||
sb_irq_update_pic(void *priv, int set)
|
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;
|
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.
|
/* 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.) */
|
(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)
|
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)
|
if (dsp->sb_freq >= 22050)
|
||||||
ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq);
|
ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq);
|
||||||
else
|
else
|
||||||
ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq);
|
ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq);
|
||||||
|
|
||||||
unsigned int freq = ((dsp->sb_freq * 4) / (5 * 2)); /* 80% of 1/2 the sample rate */
|
sb_ess_update_reg_a2(dsp, (uint8_t) temp);
|
||||||
ESSreg(0xA2) = 256 - (7160000 / (freq * 82));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg)
|
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);
|
dsp->sb_freq = 795500UL / (256ul - data);
|
||||||
else
|
else
|
||||||
dsp->sb_freq = 397700UL / (128ul - data);
|
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;
|
temp = 1000000.0 / dsp->sb_freq;
|
||||||
dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp;
|
dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp;
|
||||||
dsp->sb_timei = dsp->sb_timeo;
|
dsp->sb_timei = dsp->sb_timeo;
|
||||||
pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho);
|
pclog("ess: Sample rate - %ihz (%f)\n", dsp->sb_freq, dsp->sblatcho);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */
|
case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */
|
||||||
ESSreg(reg) = data;
|
sb_ess_update_reg_a2(dsp, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xA4: /* DMA Transfer Count Reload (low) */
|
case 0xA4: /* DMA Transfer Count Reload (low) */
|
||||||
@@ -1147,6 +1184,7 @@ sb_exec_command(sb_dsp_t *dsp)
|
|||||||
temp = 1000000 / temp;
|
temp = 1000000 / temp;
|
||||||
sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho);
|
sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho);
|
||||||
pclog("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))
|
if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16))
|
||||||
recalc_sb16_filter(0, temp);
|
recalc_sb16_filter(0, temp);
|
||||||
dsp->sb_freq = temp;
|
dsp->sb_freq = temp;
|
||||||
@@ -1165,7 +1203,7 @@ sb_exec_command(sb_dsp_t *dsp)
|
|||||||
dsp->sb_timeo = 256LL + dsp->sb_freq;
|
dsp->sb_timeo = 256LL + dsp->sb_freq;
|
||||||
dsp->sblatchi = dsp->sblatcho;
|
dsp->sblatchi = dsp->sblatcho;
|
||||||
dsp->sb_timei = dsp->sb_timeo;
|
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);
|
recalc_sb16_filter(0, dsp->sb_freq);
|
||||||
dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff;
|
dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff;
|
||||||
dsp->sb_8051_ram[0x14] = (dsp->sb_freq >> 8) & 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->wb_timer, NULL, dsp, 0);
|
||||||
timer_add(&dsp->irq_timer, sb_dsp_irq_poll, 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
|
if (IS_ESS(dsp))
|
||||||
a set frequency command is sent. */
|
/* Initialize ESS filter to 8 kHz. This will be recalculated when a set frequency command is
|
||||||
recalc_sb16_filter(0, 3200 * 2);
|
sent. */
|
||||||
if (dsp->sb_has_real_opl)
|
recalc_sb16_filter(0, 8000 * 2);
|
||||||
recalc_sb16_filter(1, FREQ_49716);
|
|
||||||
else
|
else
|
||||||
recalc_sb16_filter(1, FREQ_48000);
|
/* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when
|
||||||
recalc_sb16_filter(2, FREQ_44100);
|
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);
|
recalc_sb16_filter(3, 18939);
|
||||||
|
|
||||||
/* Initialize SB16 8051 RAM and ASP internal RAM */
|
/* Initialize SB16 8051 RAM and ASP internal RAM */
|
||||||
|
Reference in New Issue
Block a user