diff --git a/src/filters.h b/src/filters.h index dfeb91b..c3431d4 100644 --- a/src/filters.h +++ b/src/filters.h @@ -345,3 +345,33 @@ static inline float dac_iir(int i, float NewSample) { return y[i][0]; } + + +#define SB16_NCoef 51 + +extern float low_fir_sb16_coef[SB16_NCoef]; + +static inline float low_fir_sb16(int i, float NewSample) +{ + static float x[2][SB16_NCoef+1]; //input samples + static int pos = 0; + float out = 0.0; + int n; + + //Calculate the new output + x[i][pos] = NewSample; + + for (n = 0; n < ((SB16_NCoef+1)-pos); n++) + out += low_fir_sb16_coef[n] * x[i][n+pos]; + for (; n < SB16_NCoef; n++) + out += low_fir_sb16_coef[n] * x[i][(n+pos) - (SB16_NCoef+1)]; + + if (i == 1) + { + pos++; + if (pos > SB16_NCoef) + pos = 0; + } + + return out; +} diff --git a/src/sound_sb.c b/src/sound_sb.c index 3cee369..cb3eaf9 100644 --- a/src/sound_sb.c +++ b/src/sound_sb.c @@ -261,9 +261,8 @@ static void sb_get_buffer_sb16(int32_t *buffer, int len, void *p) in_l = (mixer->input_selector_left&INPUT_MIDI_L) ? out_l : 0 + (mixer->input_selector_left&INPUT_MIDI_R) ? out_r : 0; in_r = (mixer->input_selector_right&INPUT_MIDI_L) ? out_l : 0 + (mixer->input_selector_right&INPUT_MIDI_R) ? out_r : 0; - /*TODO: CT1745 features dynamic filtering. https://www.vogons.org/viewtopic.php?f=62&t=51514 */ - out_l += ((int32_t)(sb->dsp.buffer[c] * mixer->voice_l) / 3) >> 15; - out_r += ((int32_t)(sb->dsp.buffer[c + 1] * mixer->voice_r) / 3) >> 15; + out_l += ((int32_t)(low_fir_sb16(0, (float)sb->dsp.buffer[c]) * mixer->voice_l) / 3) >> 15; + out_r += ((int32_t)(low_fir_sb16(1, (float)sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3) >> 15; out_l = (out_l * mixer->master_l) >> 15; out_r = (out_r * mixer->master_r) >> 15; @@ -341,10 +340,8 @@ static void sb_get_buffer_emu8k(int32_t *buffer, int len, void *p) in_l = (mixer->input_selector_left&INPUT_MIDI_L) ? out_l : 0 + (mixer->input_selector_left&INPUT_MIDI_R) ? out_r : 0; in_r = (mixer->input_selector_right&INPUT_MIDI_L) ? out_l : 0 + (mixer->input_selector_right&INPUT_MIDI_R) ? out_r : 0; - - /*TODO: CT1745 features dynamic filtering. https://www.vogons.org/viewtopic.php?f=62&t=51514 */ - out_l += ((int32_t)(sb->dsp.buffer[c] * mixer->voice_l) / 3) >> 15; - out_r += ((int32_t)(sb->dsp.buffer[c + 1] * mixer->voice_r) / 3) >> 15; + out_l += ((int32_t)(low_fir_sb16(0, (float)sb->dsp.buffer[c]) * mixer->voice_l) / 3) >> 15; + out_r += ((int32_t)(low_fir_sb16(1, (float)sb->dsp.buffer[c + 1]) * mixer->voice_r) / 3) >> 15; out_l = (out_l * mixer->master_l) >> 15; out_r = (out_r * mixer->master_r) >> 15; diff --git a/src/sound_sb_dsp.c b/src/sound_sb_dsp.c index 5d1f30f..d720478 100644 --- a/src/sound_sb_dsp.c +++ b/src/sound_sb_dsp.c @@ -6,10 +6,12 @@ #include #include +#include #include "ibm.h" #include "dma.h" +#include "filters.h" #include "io.h" #include "pic.h" #include "sound.h" @@ -108,6 +110,42 @@ uint16_t sb_dsp_versions[] = {0, 0, 0x105, 0x200, 0x201, 0x300, 0x302, 0x405, 0x 252, 0, 252, 0 }; +float low_fir_sb16_coef[SB16_NCoef]; + +static inline double sinc(double x) +{ + return sin(M_PI * x) / (M_PI * x); +} + +static void recalc_sb16_filter(int playback_freq) +{ + /*Cutoff frequency = playback / 2*/ + float fC = ((float)playback_freq / 2.0) / 48000.0; + float gain; + int n; + + for (n = 0; n < SB16_NCoef; n++) + { + /*Blackman window*/ + double 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*/ + double h = sinc(2.0 * fC * ((double)n - ((double)(SB16_NCoef-1) / 2.0))); + + /*Create windowed-sinc filter*/ + low_fir_sb16_coef[n] = w * h; + } + + low_fir_sb16_coef[(SB16_NCoef - 1) / 2] = 1.0; + + gain = 0.0; + for (n = 0; n < SB16_NCoef; n++) + gain += low_fir_sb16_coef[n]; + + /*Normalise filter, to produce unity gain*/ + for (n = 0; n < SB16_NCoef; n++) + low_fir_sb16_coef[n] /= gain; +} + void sb_irq(sb_dsp_t *dsp, int irq8) @@ -399,6 +437,8 @@ void sb_exec_command(sb_dsp_t *dsp) temp = 256 - dsp->sb_data[0]; temp = 1000000 / temp; // pclog("Sample rate - %ihz (%i)\n",temp, dsp->sblatcho); + if (dsp->sb_freq != temp && dsp->sb_type >= SB16) + recalc_sb16_filter(temp); dsp->sb_freq = temp; break; case 0x41: /*Set output sampling rate*/ @@ -406,10 +446,13 @@ void sb_exec_command(sb_dsp_t *dsp) if (dsp->sb_type < SB16) break; dsp->sblatcho = (int)(TIMER_USEC * (1000000.0f / (float)(dsp->sb_data[1] + (dsp->sb_data[0] << 8)))); // pclog("Sample rate - %ihz (%i)\n",dsp->sb_data[1]+(dsp->sb_data[0]<<8), dsp->sblatcho); + temp = dsp->sb_freq; dsp->sb_freq = dsp->sb_data[1] + (dsp->sb_data[0] << 8); dsp->sb_timeo = 256 + dsp->sb_freq; dsp->sblatchi = dsp->sblatcho; dsp->sb_timei = dsp->sb_timeo; + if (dsp->sb_freq != temp && dsp->sb_type >= SB16) + recalc_sb16_filter(dsp->sb_freq); break; case 0x48: /*Set DSP block transfer size*/ dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8);