SB: mixer volume control inital implementation

This commit is contained in:
wbcbz7 committed 2026-03-21 00:34:06 +07:00
1 parent 1810d43b26
commit b9f3a902da
4 files changed
+141 -8

No files matched your search

+2 -2
View File
@@ -71,10 +71,10 @@ void set_volume(uint16_t mode) {
volume.opl[0] = volume.opl[1] = set_volume_scale(settings.Volume.oplVol);
break;
case CMD_SBVOL:
volume.sb_pcm[0] = volume.sb_pcm[0] = set_volume_scale(settings.Volume.sbVol);
volume.sb_pcm[0] = volume.sb_pcm[1] = set_volume_scale(settings.Volume.sbVol);
break;
case CMD_CDVOL:
volume.cd_audio[0] = volume.cd_audio[0] = set_volume_scale(settings.Volume.cdVol);
volume.cd_audio[0] = volume.cd_audio[1] = set_volume_scale(settings.Volume.cdVol);
break;
case CMD_GUSVOL:
volume.gus = set_volume_scale(settings.Volume.gusVol);
+131 -3
View File
@@ -116,6 +116,25 @@ static dma_inst_t dma_config;
#define MIXER_IRQ_STATUS 0x82
#define MIXER_STEREO 0xE
#define MIXER_VOL_VOICE 0x04
#define MIXER_VOL_MASTER 0x22
#define MIXER_VOL_MIDI 0x26
#define MIXER_VOL_CD 0x28
#define MIXER_VOL_LINE 0x2E
#define MIXER_VOL_MASTER_L 0x30 // SB16
#define MIXER_VOL_MASTER_R 0x31 // SB16
#define MIXER_VOL_VOICE_L 0x32 // SB16
#define MIXER_VOL_VOICE_R 0x33 // SB16
#define MIXER_VOL_MIDI_L 0x34 // SB16
#define MIXER_VOL_MIDI_R 0x35 // SB16
#define MIXER_VOL_CD_L 0x36 // SB16
#define MIXER_VOL_CD_R 0x37 // SB16
#define MIXER_VOL_LINE_L 0x38 // SB16
#define MIXER_VOL_LINE_R 0x39 // SB16
#define MIXER_OUT_SWITCH 0x3C // SB16
#define DSP_UNUSED_STATUS_BITS_PULLED_HIGH 0x7F
static char const copyright_string[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
@@ -272,10 +291,13 @@ uint16_t sbdsp_sample_rate() {
return sbdsp.sample_rate;
}
static void sbmixer_reset(void);
void sbdsp_init() {
puts("Initing ISA DMA PIO...");
SBDSP_DMA_isr_pt = sbdsp_dma_isr;
sbdsp.dsp_version.major = DSP_VERSION_MAJOR;
sbdsp.dsp_version.minor = DSP_VERSION_MINOR;
sbdsp.dma = 0x2; // force DMA 1 for now
sbdsp.interrupt = 0x2; // force IRQ 5 for now
@@ -286,6 +308,9 @@ void sbdsp_init() {
sb_8051_ram[0x0e] = 0xff;
sb_8051_ram[0x0f] = 0x07;
sb_8051_ram[0x37] = 0x38;
// Reset mixer
sbmixer_reset();
}
@@ -437,11 +462,11 @@ void sbdsp_process(void) {
case DSP_VERSION:
if (sbdsp.current_command_index == 0) {
sbdsp.current_command_index = 1;
sbdsp_output(DSP_VERSION_MAJOR);
sbdsp_output(sbdsp.dsp_version.major);
} else {
if (!sbdsp.dav_pc) {
sbdsp.current_command = 0;
sbdsp_output(DSP_VERSION_MINOR);
sbdsp_output(sbdsp.dsp_version.minor);
}
}
break;
@@ -675,6 +700,98 @@ static __force_inline void sbdsp_reset(uint8_t value) {
}
}
// --------------
// SB Mixer functions
// volume table, -4dB per step (matches SB16 mixer scale)
static const int32_t sbmixer_voltab[32] = {
0, 66, 83, 104, 131, 165, 207, 261, // vol 0..7
328, 414, 521, 655, 825, 1039, 1308, 1646, // vol 8..15
2072, 2609, 3285, 4135, 5206, 6554, 8250, 10387, // vol 16..23
13076, 16462, 20724, 26090, 32846, 41350, 52057, 65535 // vol 24..31
};
// set volume using SBPro mixer registers
static void sbmixer_pro_set(uint8_t sbpro_reg, uint8_t value) {
mixer_state[sbpro_reg] = value;
// update SB16 and actual mixer registers
int vol_r = ((value >> 0) & 0xF), vol_l = (value >> 4) & 0xF;
vol_l = (vol_l << 1) | (vol_l >> 3);
vol_r = (vol_r << 1) | (vol_r >> 3);
int sb16_reg = 0;
int32_t *volreg = NULL;
switch (sbpro_reg) {
case MIXER_VOL_MASTER: sb16_reg = MIXER_VOL_MASTER_L; volreg = &volume.sb_master[0]; break;
case MIXER_VOL_VOICE: sb16_reg = MIXER_VOL_VOICE_L; volreg = &volume.sb_pcm[0]; break;
case MIXER_VOL_MIDI: sb16_reg = MIXER_VOL_MIDI_L; volreg = &volume.opl[0]; break;
case MIXER_VOL_CD: sb16_reg = MIXER_VOL_CD_L; volreg = &volume.cd_audio[0]; break;
case MIXER_VOL_LINE: sb16_reg = MIXER_VOL_LINE_L; volreg = NULL; break;
default: break;
}
if (sb16_reg) {
mixer_state[sb16_reg + 0] = vol_l << 3;
mixer_state[sb16_reg + 1] = vol_r << 3;
}
if (volreg) {
volreg[0] = sbmixer_voltab[vol_l];
volreg[1] = sbmixer_voltab[vol_r];
}
}
// set volume using SB16 mixer registers
static void sbmixer_16_set(uint8_t sb16_reg, uint8_t value) {
mixer_state[sb16_reg] = value & 0xF8;
int sbpro_reg = 0;
int32_t *volreg = NULL;
switch (sb16_reg) {
case MIXER_VOL_MASTER_L: sbpro_reg = MIXER_VOL_MASTER; volreg = &volume.sb_master[0]; break;
case MIXER_VOL_MASTER_R: sbpro_reg = MIXER_VOL_MASTER; volreg = &volume.sb_master[1]; break;
case MIXER_VOL_VOICE_L: sbpro_reg = MIXER_VOL_VOICE; volreg = &volume.sb_pcm[0]; break;
case MIXER_VOL_VOICE_R: sbpro_reg = MIXER_VOL_VOICE; volreg = &volume.sb_pcm[1]; break;
case MIXER_VOL_MIDI_L: sbpro_reg = MIXER_VOL_MIDI; volreg = &volume.opl[0]; break;
case MIXER_VOL_MIDI_R: sbpro_reg = MIXER_VOL_MIDI; volreg = &volume.opl[1]; break;
case MIXER_VOL_CD_L: sbpro_reg = MIXER_VOL_CD; volreg = &volume.cd_audio[0]; break;
case MIXER_VOL_CD_R: sbpro_reg = MIXER_VOL_CD; volreg = &volume.cd_audio[1]; break;
case MIXER_VOL_LINE_L: sbpro_reg = MIXER_VOL_LINE; volreg = NULL; break;
case MIXER_VOL_LINE_R: sbpro_reg = MIXER_VOL_LINE; volreg = NULL; break;
default: break;
}
if (sbpro_reg) {
int regmask = (sb16_reg & 1) ? 0x0F : 0xF0;
int proval = (value & 0xF0) >> ((sb16_reg & 1) ? 0 : 4);
mixer_state[sbpro_reg] = (mixer_state[sbpro_reg] & regmask) | proval;
}
if (volreg) {
*volreg = sbmixer_voltab[value >> 3];
}
}
static void sbmixer_reset(void) {
memset(mixer_state, 0, sizeof(mixer_state));
// initialize default values
sbmixer_pro_set(MIXER_VOL_MASTER, 0xFF);
sbmixer_pro_set(MIXER_VOL_VOICE, 0xEE);
sbmixer_pro_set(MIXER_VOL_MIDI, 0xEE);
sbmixer_pro_set(MIXER_VOL_CD, 0xEE);
sbmixer_pro_set(MIXER_VOL_LINE, 0x00);
// preinit (non-functional) bass/treble settings and output switches
mixer_state[0x3C] = 0x6; // CD L/R enabled, Line/Mic disabled
mixer_state[0x44] = mixer_state[0x45] = mixer_state[0x46] = mixer_state[0x47] = 0x80;
}
static __force_inline uint8_t sbmixer_read(void) {
switch (sbdsp.mixer_command) {
case MIXER_INTERRUPT:
@@ -696,7 +813,17 @@ static __force_inline uint8_t sbmixer_read(void) {
static __force_inline void sbmixer_write(uint8_t value) {
switch (sbdsp.mixer_command) {
case 0x00: // Mixer reset
memset(mixer_state, 0, sizeof(mixer_state));
sbmixer_reset();
break;
case MIXER_VOL_VOICE:
case MIXER_VOL_MASTER:
case MIXER_VOL_MIDI:
case MIXER_VOL_CD:
case MIXER_VOL_LINE:
sbmixer_pro_set(sbdsp.mixer_command, value);
break;
case MIXER_VOL_MASTER_L ... MIXER_VOL_LINE_R:
sbmixer_16_set(sbdsp.mixer_command, value);
break;
case MIXER_INTERRUPT:
// sbdsp.interrupt = value;
@@ -715,6 +842,7 @@ static __force_inline void sbmixer_write(uint8_t value) {
}
}
uint8_t sbdsp_read(uint8_t address) {
// emulate address aliasing seen on pre-SB16
switch (address) {
+5
View File
@@ -73,6 +73,11 @@ typedef struct sbdsp_t {
volatile bool irq_8_pending;
volatile bool irq_16_pending;
union {
struct { uint8_t minor, major; };
uint16_t w;
} dsp_version;
uint8_t interrupt;
uint8_t dma;
+3 -3
View File
@@ -105,7 +105,7 @@ static int16_t get_opl_sample()
{
int16_t opl_current_sample;
OPL_Pico_simple(&opl_current_sample, 1);
return opl_current_sample;
return opl_current_sample << 2; // TODO: adjust
}
typedef union {
@@ -157,8 +157,8 @@ void audio_sample_handler(void) {
const uint32_t has_opl_samples = fifo_take_samples_inline(&opl_out_fifo, 1);
if (has_opl_samples) {
int16_t opl_sample = opl_out_fifo.buffer[opl_out_index++];
sample_l += scale_sample(opl_sample, volume.opl[0] << 1, 0);
sample_r += scale_sample(opl_sample, volume.opl[1] << 1, 0);
sample_l += scale_sample(opl_sample, volume.opl[0], 0);
sample_r += scale_sample(opl_sample, volume.opl[1], 0);
opl_out_index &= AUDIO_FIFO_BITS;
}