mirror of
https://github.com/sarah-walker-pcem/pcem.git
synced 2025-07-23 03:33:02 +02:00
Implemented Adlib Gold mixer.
This commit is contained in:
BIN
nvr/adgold.bin
Normal file
BIN
nvr/adgold.bin
Normal file
Binary file not shown.
103
src/filters.h
103
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];
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user