From 3030325bd278be9e7f13fb0ed744e14a59562f24 Mon Sep 17 00:00:00 2001 From: SarahW Date: Mon, 11 Apr 2016 21:42:43 +0100 Subject: [PATCH] Implemented Adlib Gold mixer. --- nvr/adgold.bin | Bin 0 -> 24 bytes src/filters.h | 103 ++++++++++++++++++++ src/sound_adlibgold.c | 218 ++++++++++++++++++++++++++++++++++++++++-- src/sound_ym7128.c | 8 +- 4 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 nvr/adgold.bin diff --git a/nvr/adgold.bin b/nvr/adgold.bin new file mode 100644 index 0000000000000000000000000000000000000000..566bffc4abf2b00a1ce550dd0cef57386acaf960 GIT binary patch literal 24 bcmZQzU|{(FpN;JtBM<=DKp<>T$iM&qH%kMN literal 0 HcmV?d00001 diff --git a/src/filters.h b/src/filters.h index 5a0625e..454a12a 100644 --- a/src/filters.h +++ b/src/filters.h @@ -176,3 +176,106 @@ static inline float sb_iir(int i, float NewSample) { return y[i][0]; } + + +#undef NCoef +#define NCoef 2 + +//fc=150Hz +static inline float adgold_highpass_iir(int i, float NewSample) { + float ACoef[NCoef+1] = { + 0.98657437157334349000, + -1.97314874314668700000, + 0.98657437157334349000 + }; + + float BCoef[NCoef+1] = { + 1.00000000000000000000, + -1.97223372919758360000, + 0.97261396931534050000 + }; + + static float y[2][NCoef+1]; //output samples + static float x[2][NCoef+1]; //input samples + int n; + + //shift the old samples + for(n=NCoef; n>0; n--) { + x[i][n] = x[i][n-1]; + y[i][n] = y[i][n-1]; + } + + //Calculate the new output + x[i][0] = NewSample; + y[i][0] = ACoef[0] * x[i][0]; + for(n=1; n<=NCoef; n++) + y[i][0] += ACoef[n] * x[i][n] - BCoef[n] * y[i][n]; + + return y[i][0]; +} + +//fc=150Hz +static inline float adgold_lowpass_iir(int i, float NewSample) { + float ACoef[NCoef+1] = { + 0.00009159473951071446, + 0.00018318947902142891, + 0.00009159473951071446 + }; + + float BCoef[NCoef+1] = { + 1.00000000000000000000, + -1.97223372919526560000, + 0.97261396931306277000 + }; + + static float y[2][NCoef+1]; //output samples + static float x[2][NCoef+1]; //input samples + int n; + + //shift the old samples + for(n=NCoef; n>0; n--) { + x[i][n] = x[i][n-1]; + y[i][n] = y[i][n-1]; + } + + //Calculate the new output + x[i][0] = NewSample; + y[i][0] = ACoef[0] * x[i][0]; + for(n=1; n<=NCoef; n++) + y[i][0] += ACoef[n] * x[i][n] - BCoef[n] * y[i][n]; + + return y[i][0]; +} + +//fc=56Hz +static inline float adgold_pseudo_stereo_iir(float NewSample) { + float ACoef[NCoef+1] = { + 0.00001409030866231767, + 0.00002818061732463533, + 0.00001409030866231767 + }; + + float BCoef[NCoef+1] = { + 1.00000000000000000000, + -1.98733021473466760000, + 0.98738361004063568000 + }; + + static float y[NCoef+1]; //output samples + static float x[NCoef+1]; //input samples + int n; + + //shift the old samples + for(n=NCoef; n>0; n--) { + x[n] = x[n-1]; + y[n] = y[n-1]; + } + + //Calculate the new output + x[0] = NewSample; + y[0] = ACoef[0] * x[0]; + for(n=1; n<=NCoef; n++) + y[0] += ACoef[n] * x[n] - BCoef[n] * y[n]; + + return y[0]; +} diff --git a/src/sound_adlibgold.c b/src/sound_adlibgold.c index c183a5a..c06c2bd 100644 --- a/src/sound_adlibgold.c +++ b/src/sound_adlibgold.c @@ -12,6 +12,8 @@ #include "sound.h" #include "timer.h" +#include "filters.h" + typedef struct adgold_t { int adgold_irq_status; @@ -47,6 +49,11 @@ typedef struct adgold_t opl_t opl; ym7128_t ym7128; + + int fm_vol_l, fm_vol_r; + int samp_vol_l, samp_vol_r; + int vol_l, vol_r; + int treble, bass; int16_t opl_buffer[SOUNDBUFLEN * 2]; int16_t mma_buffer[2][SOUNDBUFLEN]; @@ -56,6 +63,67 @@ typedef struct adgold_t int surround_enabled; } adgold_t; +static int attenuation[0x40]; +static int bass_attenuation[0x10] = +{ + (int)(1.995 * 16384), /*12 dB - filter output is at +6 dB so we use 6 dB here*/ + (int)(1.995 * 16384), + (int)(1.995 * 16384), + (int)(1.413 * 16384), /*9 dB*/ + (int)(1 * 16384), /*6 dB*/ + (int)(0.708 * 16384), /*3 dB*/ + (int)(0 * 16384), /*0 dB*/ + (int)(0.708 * 16384), /*3 dB*/ + (int)(1 * 16384), /*6 dB*/ + (int)(1.413 * 16384), /*9 dB*/ + (int)(1.995 * 16384), /*12 dB*/ + (int)(2.819 * 16384), /*15 dB*/ + (int)(2.819 * 16384), + (int)(2.819 * 16384), + (int)(2.819 * 16384), + (int)(2.819 * 16384) +}; + +static int bass_cut[6] = +{ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.178 * 16384), /*-9 dB*/ + (int)(0.251 * 16384), /*-6 dB*/ + (int)(0.354 * 16384) /*-3 dB - filter output is at +6 dB*/ +}; + +static int treble_attenuation[0x10] = +{ + (int)(1.995 * 16384), /*12 dB - filter output is at +6 dB so we use 6 dB here*/ + (int)(1.995 * 16384), + (int)(1.995 * 16384), + (int)(1.413 * 16384), /*9 dB*/ + (int)(1 * 16384), /*6 dB*/ + (int)(0.708 * 16384), /*3 dB*/ + (int)(0 * 16384), /*0 dB*/ + (int)(0.708 * 16384), /*3 dB*/ + (int)(1 * 16384), /*6 dB*/ + (int)(1.413 * 16384), /*9 dB*/ + (int)(1.995 * 16384), /*12 dB*/ + (int)(1.995 * 16384), + (int)(1.995 * 16384), + (int)(1.995 * 16384), + (int)(1.995 * 16384), + (int)(1.995 * 16384) +}; + +static int treble_cut[6] = +{ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.126 * 16384), /*-12 dB*/ + (int)(0.178 * 16384), /*-9 dB*/ + (int)(0.251 * 16384), /*-6 dB*/ + (int)(0.354 * 16384) /*-3 dB - filter output is at +6 dB*/ +}; + void adgold_timer_poll(); void adgold_update(adgold_t *adgold); @@ -114,7 +182,7 @@ void adgold_getsamp_dma(adgold_t *adgold, int channel) void adgold_write(uint16_t addr, uint8_t val, void *p) { adgold_t *adgold = (adgold_t *)p; - if (addr > 0x389) pclog("adgold_write : addr %04X val %02X %04X:%04X\n", addr, val, CS, pc); +// if (addr > 0x389) pclog("adgold_write : addr %04X val %02X %04X:%04X\n", addr, val, CS, pc); switch (addr & 7) { case 0: case 1: @@ -150,6 +218,40 @@ void adgold_write(uint16_t addr, uint8_t val, void *p) memcpy(adgold->adgold_eeprom, adgold->adgold_38x_regs, 0x19); break; + case 0x04: /*Final output volume left*/ + adgold->adgold_38x_regs[0x04] = val; + adgold->vol_l = attenuation[val & 0x3f]; + break; + case 0x05: /*Final output volume right*/ + adgold->adgold_38x_regs[0x05] = val; + adgold->vol_r = attenuation[val & 0x3f]; + break; + case 0x06: /*Bass*/ + adgold->adgold_38x_regs[0x06] = val; + adgold->bass = val & 0xf; + break; + case 0x07: /*Treble*/ + adgold->adgold_38x_regs[0x07] = val; + adgold->treble = val & 0xf; + break; + + case 0x09: /*FM volume left*/ + adgold->adgold_38x_regs[0x09] = val; + adgold->fm_vol_l = (int)(int8_t)(val - 128); + break; + case 0x0a: /*FM volume right*/ + adgold->adgold_38x_regs[0x0a] = val; + adgold->fm_vol_r = (int)(int8_t)(val - 128); + break; + case 0x0b: /*Sample volume left*/ + adgold->adgold_38x_regs[0x0b] = val; + adgold->samp_vol_l = (int)(int8_t)(val - 128); + break; + case 0x0c: /*Sample volume right*/ + adgold->adgold_38x_regs[0x0c] = val; + adgold->samp_vol_r = (int)(int8_t)(val - 128); + break; + case 0x18: /*Surround*/ adgold->adgold_38x_regs[0x18] = val; ym7128_write(&adgold->ym7128, val); @@ -557,19 +659,101 @@ static void adgold_get_buffer(int16_t *buffer, int len, void *p) int c; opl3_update2(&adgold->opl); - for (c = 0; c < len * 2; c++) + for (c = 0; c < len * 2; c += 2) { - adgold_buffer[c] = adgold->opl.buffer[c] / 2; - adgold_buffer[c] += adgold->mma_buffer[c & 1][c >> 1] / 4; - buffer[c] += adgold_buffer[c]; + adgold_buffer[c] = ((adgold->opl.buffer[c] * adgold->fm_vol_l) >> 7) / 2; + adgold_buffer[c] += ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; + adgold_buffer[c+1] = ((adgold->opl.buffer[c+1] * adgold->fm_vol_r) >> 7) / 2; + adgold_buffer[c+1] += ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; } if (adgold->surround_enabled) - { ym7128_apply(&adgold->ym7128, adgold_buffer, len); + switch (adgold->adgold_38x_regs[0x8] & 6) + { + case 0: for (c = 0; c < len * 2; c++) - buffer[c] += adgold_buffer[c]; + adgold_buffer[c] = 0; + break; + case 2: /*Left channel only*/ + for (c = 0; c < len * 2; c += 2) + adgold_buffer[c+1] = adgold_buffer[c]; + break; + case 4: /*Right channel only*/ + for (c = 0; c < len * 2; c += 2) + adgold_buffer[c] = adgold_buffer[c+1]; + break; + case 6: /*Left and right channels*/ + break; + } + + switch (adgold->adgold_38x_regs[0x8] & 0x18) + { + case 0x00: /*Forced mono*/ + for (c = 0; c < len * 2; c += 2) + adgold_buffer[c] = adgold_buffer[c+1] = ((int32_t)adgold_buffer[c] + (int32_t)adgold_buffer[c+1]) / 2; + break; + case 0x08: /*Linear stereo*/ + break; + case 0x10: /*Pseudo stereo*/ + /*Filter left channel, leave right channel unchanged*/ + /*Filter cutoff is largely a guess*/ + for (c = 0; c < len * 2; c += 2) + adgold_buffer[c] += adgold_pseudo_stereo_iir(adgold_buffer[c]); + break; + case 0x18: /*Spatial stereo*/ + /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet + and a very vague understanding of how op-amps work to go on*/ + for (c = 0; c < len * 2; c += 2) + { + int16_t l = adgold_buffer[c]; + int16_t r = adgold_buffer[c+1]; + + adgold_buffer[c] += (r / 3) + ((l * 2) / 3); + adgold_buffer[c+1] += (l / 3) + ((r * 2) / 3); + } + break; + } + + for (c = 0; c < len * 2; c += 2) + { + int32_t temp, lowpass, highpass; + + /*Output is deliberately halved to avoid clipping*/ + temp = ((int32_t)adgold_buffer[c] * adgold->vol_l) >> 17; + lowpass = adgold_lowpass_iir(0, temp); + highpass = adgold_highpass_iir(0, temp); + if (adgold->bass > 6) + temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; + else if (adgold->bass < 6) + temp = highpass + ((temp * bass_cut[adgold->bass]) >> 14); + if (adgold->treble > 6) + temp += (highpass * treble_attenuation[adgold->treble]) >> 14; + else if (adgold->treble < 6) + temp = lowpass + ((temp * treble_cut[adgold->treble]) >> 14); + if (temp < -32768) + temp = -32768; + if (temp > 32767) + temp = 32767; + buffer[c] += temp; + + temp = ((int32_t)adgold_buffer[c+1] * adgold->vol_r) >> 17; + lowpass = adgold_lowpass_iir(1, temp); + highpass = adgold_highpass_iir(1, temp); + if (adgold->bass > 6) + temp += (lowpass * bass_attenuation[adgold->bass]) >> 14; + else if (adgold->bass < 6) + temp = highpass + ((temp * bass_cut[adgold->bass]) >> 14); + if (adgold->treble > 6) + temp += (highpass * treble_attenuation[adgold->treble]) >> 14; + else if (adgold->treble < 6) + temp = lowpass + ((temp * treble_cut[adgold->treble]) >> 14); + if (temp < -32768) + temp = -32768; + if (temp > 32767) + temp = 32767; + buffer[c+1] += temp; } adgold->opl.pos = 0; @@ -580,6 +764,8 @@ static void adgold_get_buffer(int16_t *buffer, int len, void *p) void *adgold_init() { FILE *f; + int c; + double out; adgold_t *adgold = malloc(sizeof(adgold_t)); memset(adgold, 0, sizeof(adgold_t)); @@ -589,6 +775,15 @@ void *adgold_init() if (adgold->surround_enabled) ym7128_init(&adgold->ym7128); + out = 65536.0; /*Main volume control ranges from +6 dB to -64 dB in 2 dB steps, then remaining settings are -80 dB (effectively 0)*/ + for (c = 0x3f; c >= 0x1c; c--) + { + attenuation[c] = (int)out; + out /= 1.25963; /*2 dB steps*/ + } + for (; c >= 0; c--) + attenuation[c] = 0; + f = romfopen("nvr/adgold.bin", "rb"); if (f) { @@ -598,11 +793,18 @@ void *adgold_init() adgold->adgold_status = 0xf; adgold->adgold_38x_addr = 0; - adgold->adgold_eeprom[0x09] = adgold->adgold_eeprom[0x0a] = 255; adgold->adgold_eeprom[0x13] = 3 | (1 << 4); /*IRQ 7, DMA 1*/ adgold->adgold_eeprom[0x14] = 3 << 4; /*DMA 3*/ adgold->adgold_eeprom[0x15] = 0x388 / 8; /*Present at 388-38f*/ memcpy(adgold->adgold_38x_regs, adgold->adgold_eeprom, 0x19); + adgold->vol_l = attenuation[adgold->adgold_eeprom[0x04] & 0x3f]; + adgold->vol_r = attenuation[adgold->adgold_eeprom[0x05] & 0x3f]; + adgold->bass = adgold->adgold_eeprom[0x06] & 0xf; + adgold->treble = adgold->adgold_eeprom[0x07] & 0xf; + adgold->fm_vol_l = (int)(int8_t)(adgold->adgold_eeprom[0x09] - 128); + adgold->fm_vol_r = (int)(int8_t)(adgold->adgold_eeprom[0x0a] - 128); + adgold->samp_vol_l = (int)(int8_t)(adgold->adgold_eeprom[0x0b] - 128); + adgold->samp_vol_r = (int)(int8_t)(adgold->adgold_eeprom[0x0c] - 128); adgold->adgold_mma_enable[0] = 0; adgold->adgold_mma_fifo_start[0] = adgold->adgold_mma_fifo_end[0] = 0; diff --git a/src/sound_ym7128.c b/src/sound_ym7128.c index f194456..203bfd5 100644 --- a/src/sound_ym7128.c +++ b/src/sound_ym7128.c @@ -126,10 +126,10 @@ void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len) samp_l = (samp_l * ym7128->vl*2) >> 16; samp_r = (samp_r * ym7128->vr*2) >> 16; - buffer[c] = ((int32_t)samp_l + (int32_t)ym7128->prev_l) / 2; - buffer[c+1] = ((int32_t)samp_r + (int32_t)ym7128->prev_r) / 2; - buffer[c+2] = samp_l; - buffer[c+3] = samp_r; + buffer[c] += ((int32_t)samp_l + (int32_t)ym7128->prev_l) / 2; + buffer[c+1] += ((int32_t)samp_r + (int32_t)ym7128->prev_r) / 2; + buffer[c+2] += samp_l; + buffer[c+3] += samp_r; ym7128->delay_pos++; if (ym7128->delay_pos >= 2400)