From 39a3c8b1c78bedaf65a22fe7265a95de4e235f2c Mon Sep 17 00:00:00 2001 From: TomW Date: Tue, 23 Jul 2013 22:04:17 +0100 Subject: [PATCH] SB AWE32 emulation. --- src/Makefile.mingw | 4 +- src/sound.c | 1 + src/sound_emu8k.c | 668 +++++++++++++++++++++++++++++++++++++++++++++ src/sound_emu8k.h | 81 ++++++ src/sound_sb.c | 61 +++++ src/sound_sb.h | 1 + 6 files changed, 814 insertions(+), 2 deletions(-) create mode 100644 src/sound_emu8k.c create mode 100644 src/sound_emu8k.h diff --git a/src/Makefile.mingw b/src/Makefile.mingw index 4e39d18..4774748 100644 --- a/src/Makefile.mingw +++ b/src/Makefile.mingw @@ -8,8 +8,8 @@ OBJ = 386.o 808x.o acer386sx.o ali1429.o amstrad.o cdrom-ioctl.o \ headland.o i430vx.o ide.o io.o jim.o keyboard.o keyboard_amstrad.o keyboard_at.o \ keyboard_olim24.o keyboard_xt.o lpt.o mcr.o mem.o model.o \ mouse.o mouse_ps2.o mouse_serial.o neat.o nvr.o olivetti_m24.o \ - opti.o pc.o pci.o pic.o piix.o pit.o ppi.o serial.o sound.o sound_adlib.o \ - sound_adlibgold.o sound_cms.o sound_gus.o sound_mpu401_uart.o sound_opl.o \ + opti.o pc.o pci.o pic.o piix.o pit.o ppi.o serial.o sis496.o sound.o sound_adlib.o \ + sound_adlibgold.o sound_cms.o sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o \ sound_pas16.o sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o \ sound_wss.o soundopenal.o timer.o um8881f.o um8669f.o vid_ati_eeprom.o \ vid_ati_mach64.o vid_ati18800.o vid_ati28800.o vid_ati68860_ramdac.o vid_cga.o \ diff --git a/src/sound.c b/src/sound.c index f771e96..57cf79d 100644 --- a/src/sound.c +++ b/src/sound.c @@ -36,6 +36,7 @@ static SOUND_CARD sound_cards[] = {"Sound Blaster Pro v1", &sb_pro_v1_device}, {"Sound Blaster Pro v2", &sb_pro_v1_device}, {"Sound Blaster 16", &sb_16_device}, + {"Sound Blaster AWE32", &sb_awe32_device}, {"Adlib Gold", &adgold_device}, {"Windows Sound System", &wss_device}, {"Pro Audio Spectrum 16", &pas16_device}, diff --git a/src/sound_emu8k.c b/src/sound_emu8k.c new file mode 100644 index 0000000..6f9550f --- /dev/null +++ b/src/sound_emu8k.c @@ -0,0 +1,668 @@ +/*12log2(r) * 4096 + + freq = 2^((in - 0xe000) / 4096)*/ +/*LFO - lowest (0.042 Hz) = 2^20 steps = 1048576 + highest (10.72 Hz) = 2^12 steps = 4096*/ +#include +#include +#include "ibm.h" +#include "device.h" +#include "sound.h" +#include "sound_emu8k.h" +#include "timer.h" + +enum +{ + ENV_STOPPED = 0, + ENV_ATTACK = 1, + ENV_DECAY = 2, + ENV_SUSTAIN = 3, + ENV_RELEASE = 4 +}; + +static int64_t freqtable[65536]; +static int attentable[256]; +static int envtable[4096]; +static int lfotable[4096]; + +static int32_t filt_w0[256]; +/*static float filt_w0[256];*/ + +#define READ16(addr, var) switch ((addr) & 2) \ + { \ + case 0: ret = (var) & 0xffff; break; \ + case 2: ret = ((var) >> 16) & 0xffff; break; \ + } + +#define WRITE16(addr, var, val) switch ((addr) & 2) \ + { \ + case 0: var = (var & 0xffff0000) | (val); break; \ + case 2: var = (var & 0x0000ffff) | ((val) << 16); break; \ + } + +static inline int16_t EMU8K_READ(emu8k_t *emu8k, uint32_t addr) +{ + addr &= 0xffffff; + if (addr < 0x80000) + return emu8k->rom[addr]; + if (addr < 0x200000 || addr >= 0x240000) + return 0; + return emu8k->ram[addr & 0x3ffff]; +} + +static inline int16_t EMU8K_READ_INTERP(emu8k_t *emu8k, uint32_t addr) +{ + int16_t dat1 = EMU8K_READ(emu8k, addr >> 8); + int16_t dat2 = EMU8K_READ(emu8k, (addr >> 8) + 1); + return ((dat1 * (0xff - (addr & 0xff))) + (dat2 * (addr & 0xff))) >> 8; +} + +static inline void EMU8K_WRITE(emu8k_t *emu8k, uint32_t addr, uint16_t val) +{ + addr &= 0xffffff; + if (addr >= 0x200000 && addr <= 0x240000) + emu8k->ram[addr & 0x3ffff] = val; +} + +uint16_t emu8k_inw(uint32_t addr, void *p) +{ + emu8k_t *emu8k = (emu8k_t *)p; + uint16_t ret; +/* pclog("emu8k_inw %04X reg=%i voice=%i\n", addr, emu8k->cur_reg, emu8k->cur_voice);*/ + addr -= 0x220; + switch (addr & 0xc02) + { + case 0x400: case 0x402: /*Data0*/ + switch (emu8k->cur_reg) + { + case 0: + READ16(addr, emu8k->voice[emu8k->cur_voice].cpf); + return ret; + + case 1: + READ16(addr, emu8k->voice[emu8k->cur_voice].ptrx); + return ret; + + case 2: + READ16(addr, emu8k->voice[emu8k->cur_voice].cvcf); + return ret; + + case 3: + READ16(addr, emu8k->voice[emu8k->cur_voice].vtft); + return ret; + + case 4: case 5: /*???*/ + return 0xffff; + + case 6: + READ16(addr, emu8k->voice[emu8k->cur_voice].psst); + return ret; + + case 7: + READ16(addr, emu8k->voice[emu8k->cur_voice].cpf); + return ret; + } + break; + + case 0x800: /*Data1*/ + switch (emu8k->cur_reg) + { + case 0: + { + uint32_t val = (emu8k->voice[emu8k->cur_voice].ccca & 0xff000000) | (emu8k->voice[emu8k->cur_voice].addr >> 8); + READ16(addr, emu8k->voice[emu8k->cur_voice].ccca); + return ret; + } + + case 1: + switch (emu8k->cur_voice) + { + case 20: + READ16(addr, emu8k->smalr); + return ret; + case 21: + READ16(addr, emu8k->smarr); + return ret; + case 22: + READ16(addr, emu8k->smalw); + return ret; + case 23: + READ16(addr, emu8k->smarw); + return ret; + + case 26: + { + uint16_t val = emu8k->smld_buffer; + emu8k->smld_buffer = EMU8K_READ(emu8k, emu8k->smalr); + emu8k->smalr++; + return val; + } + + case 29: /*Configuration Word 1*/ + return emu8k->hwcf1; + case 30: /*Configuration Word 2*/ + return emu8k->hwcf2 | 3; + case 31: /*Configuration Word 3*/ + return emu8k->hwcf3; + } + break; + + case 2: /*INIT1*/ + case 3: /*INIT3*/ + return 0xffff; /*Can we read anything useful from here?*/ + + case 5: + return emu8k->voice[emu8k->cur_voice].dcysusv; + + case 7: + return emu8k->voice[emu8k->cur_voice].dcysus; + } + break; + + case 0x802: /*Data2*/ + switch (emu8k->cur_reg) + { + case 0: + { + uint32_t val = (emu8k->voice[emu8k->cur_voice].ccca & 0xff000000) | (emu8k->voice[emu8k->cur_voice].addr >> 8); + READ16(addr, emu8k->voice[emu8k->cur_voice].ccca); + return ret; + } + + case 1: + switch (emu8k->cur_voice) + { + case 20: + READ16(addr, emu8k->smalr); + return ret; + case 21: + READ16(addr, emu8k->smarr); + return ret; + case 22: + READ16(addr, emu8k->smalw); + return ret; + case 23: + READ16(addr, emu8k->smarw); + return ret; + + case 26: + { + uint16_t val = emu8k->smrd_buffer; + emu8k->smrd_buffer = EMU8K_READ(emu8k, emu8k->smarr); + emu8k->smarr++; + return val; + } + + case 27: /*Sample Counter*/ + return emu8k->wc; + } + break; + + case 2: /*INIT2*/ + case 3: /*INIT4*/ + return 0xffff; /*Can we read anything useful from here?*/ + + case 4: + return emu8k->voice[emu8k->cur_voice].atkhldv; + } + break; + + case 0xc00: /*Data3*/ + switch (emu8k->cur_reg) + { + case 0: + return emu8k->voice[emu8k->cur_voice].ip; + + case 1: + return emu8k->voice[emu8k->cur_voice].ifatn; + + case 2: + return emu8k->voice[emu8k->cur_voice].pefe; + + case 3: + return emu8k->voice[emu8k->cur_voice].fmmod; + + case 4: + return emu8k->voice[emu8k->cur_voice].tremfrq; + + case 5: + return emu8k->voice[emu8k->cur_voice].fm2frq2; + + case 6: + return 0xffff; + + case 7: /*ID?*/ + return 0xc; + } + break; + case 0xc02: /*Status - I think!*/ + emu8k->c02_read ^= 0x1000; + return emu8k->c02_read; + } +/* fatal("Bad EMU8K inw from %08X\n", addr);*/ + return 0xffff; +} + +void emu8k_outw(uint32_t addr, uint16_t val, void *p) +{ + emu8k_t *emu8k = (emu8k_t *)p; + +/* pclog("emu8k_outw : addr=%08X reg=%i voice=%i val=%04X\n", addr, emu8k->cur_reg, emu8k->cur_voice, val);*/ + addr -= 0x220; + switch (addr & 0xc02) + { + case 0x400: case 0x402: /*Data0*/ + switch (emu8k->cur_reg) + { + case 0: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].cpf, val); + return; + + case 1: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].ptrx, val); + return; + + case 2: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].cvcf, val); + return; + + case 3: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].vtft, val); + return; + + case 6: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].psst, val); + emu8k->voice[emu8k->cur_voice].loop_start = (uint64_t)(emu8k->voice[emu8k->cur_voice].psst & 0xffffff) << 32; + if (addr & 2) + { + emu8k->voice[emu8k->cur_voice].vol_l = val >> 8; + emu8k->voice[emu8k->cur_voice].vol_r = 255 - (val >> 8); + } +/* pclog("emu8k_outl : write PSST %08X l %i r %i\n", emu8k->voice[emu8k->cur_voice].psst, emu8k->voice[emu8k->cur_voice].vol_l, emu8k->voice[emu8k->cur_voice].vol_r);*/ + return; + + case 7: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].cpf, val); + emu8k->voice[emu8k->cur_voice].loop_end = (uint64_t)(emu8k->voice[emu8k->cur_voice].cpf & 0xffffff) << 32; +/* pclog("emu8k_outl : write CPF %08X\n", emu8k->voice[emu8k->cur_voice].cpf);*/ + return; + } + break; + + case 0x800: /*Data1*/ + switch (emu8k->cur_reg) + { + case 0: + WRITE16(addr, emu8k->voice[emu8k->cur_voice].ccca, val); + emu8k->voice[emu8k->cur_voice].addr = (uint64_t)(emu8k->voice[emu8k->cur_voice].ccca & 0xffffff) << 32; +/* pclog("emu8k_outl : write CCCA %08X\n", emu8k->voice[emu8k->cur_voice].ccca);*/ + return; + + case 1: + switch (emu8k->cur_voice) + { + case 20: + WRITE16(addr, emu8k->smalr, val); + return; + case 21: + WRITE16(addr, emu8k->smarr, val); + return; + case 22: + WRITE16(addr, emu8k->smalw, val); + return; + case 23: + WRITE16(addr, emu8k->smarw, val); + return; + + case 26: + EMU8K_WRITE(emu8k, emu8k->smalw, val); + emu8k->smalw++; + break; + + case 29: /*Configuration Word 1*/ + emu8k->hwcf1 = val; + return; + case 30: /*Configuration Word 2*/ + emu8k->hwcf2 = val; + return; + case 31: /*Configuration Word 3*/ + emu8k->hwcf3 = val; + return; + } + break; + + case 5: +/* pclog("emu8k_outw : write DCYSUSV %04X\n", val);*/ + emu8k->voice[emu8k->cur_voice].dcysusv = val; + emu8k->voice[emu8k->cur_voice].env_sustain = (((val >> 8) & 0x7f) << 5) << 9; + if (val & 0x8000) /*Release*/ + { + emu8k->voice[emu8k->cur_voice].env_state = ENV_RELEASE; + emu8k->voice[emu8k->cur_voice].env_release = val & 0x7f; + } + else /*Decay*/ + emu8k->voice[emu8k->cur_voice].env_decay = val & 0x7f; + if (val & 0x80) + emu8k->voice[emu8k->cur_voice].env_state = ENV_STOPPED; + return; + + case 7: +/* pclog("emu8k_outw : write DCYSUS %04X\n", val);*/ + emu8k->voice[emu8k->cur_voice].dcysus = val; + emu8k->voice[emu8k->cur_voice].menv_sustain = (((val >> 8) & 0x7f) << 5) << 9; + if (val & 0x8000) /*Release*/ + { + emu8k->voice[emu8k->cur_voice].menv_state = ENV_RELEASE; + emu8k->voice[emu8k->cur_voice].menv_release = val & 0x7f; + } + else /*Decay*/ + emu8k->voice[emu8k->cur_voice].menv_decay = val & 0x7f; + if (val & 0x80) + emu8k->voice[emu8k->cur_voice].menv_state = ENV_STOPPED; + return; + } + break; + + case 0x802: /*Data2*/ + switch (emu8k->cur_reg) + { + case 0: + { + float q; + + WRITE16(addr, emu8k->voice[emu8k->cur_voice].ccca, val); + emu8k->voice[emu8k->cur_voice].addr = (uint64_t)(emu8k->voice[emu8k->cur_voice].ccca & 0xffffff) << 32; + + q = (float)(emu8k->voice[emu8k->cur_voice].ccca >> 28) / 15.0f; + q /= 10.0f; /*Horrible and wrong hack*/ + emu8k->voice[emu8k->cur_voice].q = (int32_t)((1.0f / (0.707f + q)) * 256.0f); + +/* pclog("emu8k_outl : write CCCA %08X Q %f invQ %X\n", emu8k->voice[emu8k->cur_voice].ccca, q, emu8k->voice[emu8k->cur_voice].q);*/ + } + return; + + case 1: + switch (emu8k->cur_voice) + { + case 20: + WRITE16(addr, emu8k->smalr, val); + return; + case 21: + WRITE16(addr, emu8k->smarr, val); + return; + case 22: + WRITE16(addr, emu8k->smalw, val); + return; + case 23: + WRITE16(addr, emu8k->smarw, val); + return; + + case 26: + EMU8K_WRITE(emu8k, emu8k->smarw, val); + emu8k->smarw++; + break; + } + break; + + case 4: +/* pclog("emu8k_outw : write ATKHLDV %04X\n", val);*/ + emu8k->voice[emu8k->cur_voice].atkhldv = val; + emu8k->voice[emu8k->cur_voice].env_attack = (val & 0x7f) << 6; + if (!(val & 0x8000)) /*Trigger attack*/ + emu8k->voice[emu8k->cur_voice].env_state = ENV_ATTACK; + return; + + case 6: +/* pclog("emu8k_outw : write ATKHLD %04X\n", val);*/ + emu8k->voice[emu8k->cur_voice].atkhld = val; + emu8k->voice[emu8k->cur_voice].menv_attack = (val & 0x7f) << 6; + if (!(val & 0x8000)) /*Trigger attack*/ + emu8k->voice[emu8k->cur_voice].menv_state = ENV_ATTACK; + return; + } + break; + + case 0xc00: /*Data3*/ + switch (emu8k->cur_reg) + { + case 0: + emu8k->voice[emu8k->cur_voice].ip = val; + emu8k->voice[emu8k->cur_voice].pitch = val; + return; + + case 1: + emu8k->voice[emu8k->cur_voice].ifatn = val; + emu8k->voice[emu8k->cur_voice].attenuation = attentable[val & 0xff]; + emu8k->voice[emu8k->cur_voice].cutoff = (val >> 8); +/* pclog("Attenuation now %02X %i\n", val & 0xff, emu8k->voice[emu8k->cur_voice].attenuation);*/ + return; + + case 2: + emu8k->voice[emu8k->cur_voice].pefe = val; + emu8k->voice[emu8k->cur_voice].fe_height = (int8_t)(val & 0xff); + return; + + case 3: + emu8k->voice[emu8k->cur_voice].fmmod = val; + emu8k->voice[emu8k->cur_voice].lfo1_fmmod = (val >> 8); + return; + + case 4: + emu8k->voice[emu8k->cur_voice].tremfrq = val; + emu8k->voice[emu8k->cur_voice].lfo1_trem = (val >> 8); + return; + + case 5: + emu8k->voice[emu8k->cur_voice].fm2frq2 = val; + emu8k->voice[emu8k->cur_voice].lfo2_fmmod = (val >> 8); + return; + } + break; + + case 0xc02: /*Pointer*/ + emu8k->cur_voice = (val & 31); + emu8k->cur_reg = ((val >> 5) & 7); + return; + } +} + +void emu8k_poll(void *p) +{ + emu8k_t *emu8k = (emu8k_t *)p; + int c; + int32_t out_l = 0, out_r = 0; + + emu8k->timer_count += (int)(TIMER_USEC * (1000000.0 / 44100.0)); + + for (c = 0; c < 32; c++) + { + int32_t voice_l, voice_r; + int32_t dat; + int lfo1_vibrato, lfo2_vibrato; + int tremolo; + + tremolo = ((lfotable[(emu8k->voice[c].lfo1_count >> 8) & 4095] * emu8k->voice[c].lfo1_trem) * 4) >> 12; + + if (freqtable[emu8k->voice[c].pitch] >> 32) + dat = EMU8K_READ(emu8k, emu8k->voice[c].addr >> 32); + else + dat = EMU8K_READ_INTERP(emu8k, emu8k->voice[c].addr >> 24); + + dat = (dat * emu8k->voice[c].attenuation) >> 16; + + dat = (dat * envtable[emu8k->voice[c].env_vol >> 9]) >> 16; + + if ((emu8k->voice[c].ccca >> 28) || (emu8k->voice[c].cutoff != 0xff)) + { + int cutoff = emu8k->voice[c].cutoff + ((emu8k->voice[c].menv_vol * emu8k->voice[c].fe_height) >> 20); + if (cutoff < 0) + cutoff = 0; + if (cutoff > 255) + cutoff = 255; + + emu8k->voice[c].vhp = ((-emu8k->voice[c].vbp * emu8k->voice[c].q) >> 8) - emu8k->voice[c].vlp - dat; + emu8k->voice[c].vlp += (emu8k->voice[c].vbp * filt_w0[cutoff]) >> 8; + emu8k->voice[c].vbp += (emu8k->voice[c].vhp * filt_w0[cutoff]) >> 8; + if (emu8k->voice[c].vlp < -32767) + dat = -32767; + else if (emu8k->voice[c].vlp > 32767) + dat = 32767; + else + dat = (int16_t)emu8k->voice[c].vlp; + } + + voice_l = (dat * emu8k->voice[c].vol_l) >> 7; + voice_r = (dat * emu8k->voice[c].vol_r) >> 7; + + out_l += voice_l * 8192; + out_r += voice_r * 8192; + + switch (emu8k->voice[c].env_state) + { + case ENV_ATTACK: + emu8k->voice[c].env_vol += emu8k->voice[c].env_attack; + if (emu8k->voice[c].env_vol >= (1 << 21)) + { + emu8k->voice[c].env_vol = 1 << 21; + emu8k->voice[c].env_state = ENV_DECAY; + } + break; + + case ENV_DECAY: + emu8k->voice[c].env_vol -= emu8k->voice[c].env_decay; + if (emu8k->voice[c].env_vol <= emu8k->voice[c].env_sustain) + { + emu8k->voice[c].env_vol = emu8k->voice[c].env_sustain; + emu8k->voice[c].env_state = ENV_SUSTAIN; + } + break; + + case ENV_RELEASE: + emu8k->voice[c].env_vol -= emu8k->voice[c].env_release; + if (emu8k->voice[c].env_vol <= 0) + { + emu8k->voice[c].env_vol = 0; + emu8k->voice[c].env_state = ENV_STOPPED; + } + break; + } + + switch (emu8k->voice[c].menv_state) + { + case ENV_ATTACK: + emu8k->voice[c].menv_vol += emu8k->voice[c].menv_attack; + if (emu8k->voice[c].menv_vol >= (1 << 21)) + { + emu8k->voice[c].menv_vol = 1 << 21; + emu8k->voice[c].menv_state = ENV_DECAY; + } + break; + + case ENV_DECAY: + emu8k->voice[c].menv_vol -= emu8k->voice[c].menv_decay; + if (emu8k->voice[c].menv_vol <= emu8k->voice[c].menv_sustain) + { + emu8k->voice[c].menv_vol = emu8k->voice[c].menv_sustain; + emu8k->voice[c].menv_state = ENV_SUSTAIN; + } + break; + + case ENV_RELEASE: + emu8k->voice[c].menv_vol -= emu8k->voice[c].menv_release; + if (emu8k->voice[c].menv_vol <= 0) + { + emu8k->voice[c].menv_vol = 0; + emu8k->voice[c].menv_state = ENV_STOPPED; + } + break; + } + + lfo1_vibrato = (lfotable[(emu8k->voice[c].lfo1_count >> 8) & 4095] * emu8k->voice[c].lfo1_fmmod) >> 9; + lfo2_vibrato = (lfotable[(emu8k->voice[c].lfo2_count >> 8) & 4095] * emu8k->voice[c].lfo2_fmmod) >> 9; + + emu8k->voice[c].addr += freqtable[(emu8k->voice[c].pitch + lfo1_vibrato + lfo2_vibrato) & 0xffff]; + if (emu8k->voice[c].addr >= emu8k->voice[c].loop_end) + emu8k->voice[c].addr -= (emu8k->voice[c].loop_end - emu8k->voice[c].loop_start); + + emu8k->voice[c].lfo1_count += (emu8k->voice[c].tremfrq & 0xff); + emu8k->voice[c].lfo2_count += (emu8k->voice[c].fm2frq2 & 0xff); + } + + emu8k->out_l = out_l >> 16; + emu8k->out_r = out_r >> 16; + + emu8k->wc++; +} + +void emu8k_poll_getsamp(emu8k_t *emu8k, int16_t *l, int16_t *r) +{ + *l = emu8k->out_l; + *r = emu8k->out_r; +} + +void emu8k_init(emu8k_t *emu8k) +{ + FILE *f; + int c; + double out; + + f = romfopen("roms/awe32.raw", "rb"); + if (!f) + fatal("ROMS/AWE32.RAW not found\n"); + + emu8k->ram = malloc(512 * 1024); + emu8k->rom = malloc(1024 * 1024); + + fread(emu8k->rom, 1024 * 1024, 1, f); + fclose(f); + + io_sethandler(0x0620, 0x0004, NULL, emu8k_inw, NULL, NULL, emu8k_outw, NULL, emu8k); + io_sethandler(0x0a20, 0x0004, NULL, emu8k_inw, NULL, NULL, emu8k_outw, NULL, emu8k); + io_sethandler(0x0e20, 0x0004, NULL, emu8k_inw, NULL, NULL, emu8k_outw, NULL, emu8k); + + timer_add(emu8k_poll, &emu8k->timer_count, TIMER_ALWAYS_ENABLED, emu8k); + + /*Create frequency table*/ + for (c = 0; c < 0x10000; c++) + { + freqtable[c] = (uint64_t)(exp2((double)(c - 0xe000) / 4096.0) * 65536.0 * 65536.0); + } + + out = 65536.0; + + for (c = 0; c < 256; c++) + { + attentable[c] = (int)out; + out /= sqrt(1.09018); /*0.375 dB steps*/ + } + + out = 65536; + + for (c = 0; c < 4096; c++) + { + envtable[4095 - c] = (int)out; + out /= 1.002709201; /*0.0235 dB Steps*/ + } + + for (c = 0; c < 4096; c++) + { + int d = (c + 1024) & 4095; + if (d >= 2048) + lfotable[c] = 4096 - ((2048 - d) * 4); + else + lfotable[c] = (d * 4) - 4096; + } + + out = 125.0; + for (c = 0; c < 256; c++) + { +/* filt_w0[c] = (int32_t)((2.0 * 3.142 * (out / 44100.0)) * 0.707 * 256.0);*/ +/* filt_w0[c] = 2.0 * 3.142 * (out / 44100.0);*/ + filt_w0[c] = (int32_t)(2.0 * 3.142 * (out / 44100.0) * 256.0); + out *= 1.016378315; + } + + emu8k->hwcf1 = 0x59; + emu8k->hwcf2 = 0x03; +} + diff --git a/src/sound_emu8k.h b/src/sound_emu8k.h new file mode 100644 index 0000000..727b40b --- /dev/null +++ b/src/sound_emu8k.h @@ -0,0 +1,81 @@ +typedef struct emu8k_t +{ + struct + { + uint32_t cpf; + uint32_t ptrx; + uint32_t cvcf; + uint32_t vtft; + uint32_t psst; + uint32_t csl; + + uint32_t ccca; + + uint16_t init1, init2, init3, init4; + + uint16_t envvol; + uint16_t dcysusv; + uint16_t envval; + uint16_t dcysus; + uint16_t atkhldv; + uint16_t lfo1val, lfo2val; + uint16_t atkhld; + uint16_t ip; + uint16_t ifatn; + uint16_t pefe; + uint16_t fmmod; + uint16_t tremfrq; + uint16_t fm2frq2; + + int voice_on; + + uint64_t addr; + uint64_t loop_start, loop_end; + + uint16_t pitch; + int attenuation; + int env_state, env_vol; + int env_attack, env_decay, env_sustain, env_release; + + int menv_state, menv_vol; + int menv_attack, menv_decay, menv_sustain, menv_release; + + int lfo1_count, lfo2_count; + int8_t lfo1_fmmod, lfo2_fmmod; + int8_t lfo1_trem; + int vol_l, vol_r; + + int8_t fe_height; + + int64_t vlp, vbp, vhp; + int32_t q; + + int filter_offset; + +/* float vlp, vbp, vhp; + float q;*/ + + int cutoff; + } voice[32]; + + uint32_t hwcf1, hwcf2, hwcf3; + uint32_t hwcf4, hwcf5, hwcf6; + + uint32_t smalr, smarr, smalw, smarw; + uint16_t smld_buffer, smrd_buffer; + + uint16_t wc; + + uint16_t c02_read; + + int16_t *ram, *rom; + + int cur_reg, cur_voice; + + int timer_count; + + int16_t out_l, out_r; +} emu8k_t; + +void emu8k_init(emu8k_t *emu8k); +void emu8k_poll_getsamp(emu8k_t *dsp, int16_t *l, int16_t *r); diff --git a/src/sound_sb.c b/src/sound_sb.c index f2e9449..506dc60 100644 --- a/src/sound_sb.c +++ b/src/sound_sb.c @@ -1,6 +1,7 @@ #include #include "ibm.h" #include "device.h" +#include "sound_emu8k.h" #include "sound_mpu401_uart.h" #include "sound_opl.h" #include "sound_sb.h" @@ -27,9 +28,11 @@ typedef struct sb_t sb_dsp_t dsp; sb_mixer_t mixer; mpu401_uart_t mpu; + emu8k_t emu8k; int16_t opl_buffer[SOUNDBUFLEN * 2]; int16_t dsp_buffer[SOUNDBUFLEN * 2]; + int16_t emu8k_buffer[SOUNDBUFLEN * 2]; int pos; } sb_t; @@ -65,6 +68,18 @@ static void sb_opl3_poll(void *p) sb->pos++; } +static void sb_emu8k_poll(void *p) +{ + sb_t *sb = (sb_t *)p; + + if (sb->pos >= SOUNDBUFLEN) return; + + opl3_poll(&sb->opl, &sb->opl_buffer[sb->pos * 2], &sb->opl_buffer[(sb->pos * 2) + 1]); + sb_dsp_poll(&sb->dsp, &sb->dsp_buffer[sb->pos * 2], &sb->dsp_buffer[(sb->pos * 2) + 1]); + emu8k_poll_getsamp(&sb->emu8k, &sb->emu8k_buffer[sb->pos * 2], &sb->emu8k_buffer[(sb->pos * 2) + 1]); + sb->pos++; +} + static void sb_get_buffer(int16_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; @@ -78,6 +93,10 @@ static void sb_get_buffer(int16_t *buffer, int len, void *p) out_l = ((sb->opl_buffer[c] * mixer->fm_l) >> 16); out_r = ((sb->opl_buffer[c + 1] * mixer->fm_r) >> 16); + + out_l += ((sb->emu8k_buffer[c] * mixer->fm_l) >> 16); + out_r += ((sb->emu8k_buffer[c + 1] * mixer->fm_l) >> 16); + if (sb->mixer.filter) { out_l += (int)(((sb_iir(0, (float)sb->dsp_buffer[c]) / 1.3) * mixer->voice_l) / 3) >> 16; @@ -357,6 +376,40 @@ void *sb_16_init() return sb; } +void *sb_awe32_init() +{ + sb_t *sb = malloc(sizeof(sb_t)); + memset(sb, 0, sizeof(sb_t)); + + opl3_init(&sb->opl); + sb_dsp_init(&sb->dsp, SB16); + sb_dsp_setaddr(&sb->dsp, 0x0220); + sb_mixer_init(&sb->mixer); + io_sethandler(0x0220, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); + io_sethandler(0x0228, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); + io_sethandler(0x0388, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); + io_sethandler(0x0224, 0x0002, sb_16_mixer_read, NULL, NULL, sb_16_mixer_write, NULL, NULL, sb); + sound_add_handler(sb_emu8k_poll, sb_get_buffer, sb); + mpu401_uart_init(&sb->mpu, 0x330); + emu8k_init(&sb->emu8k); + + sb->mixer.regs[0x30] = 31 << 3; + sb->mixer.regs[0x31] = 31 << 3; + sb->mixer.regs[0x32] = 31 << 3; + sb->mixer.regs[0x33] = 31 << 3; + sb->mixer.regs[0x34] = 31 << 3; + sb->mixer.regs[0x35] = 31 << 3; + sb->mixer.regs[0x44] = 8 << 4; + sb->mixer.regs[0x45] = 8 << 4; + sb->mixer.regs[0x46] = 8 << 4; + sb->mixer.regs[0x47] = 8 << 4; + sb->mixer.regs[0x22] = (sb->mixer.regs[0x30] & 0xf0) | (sb->mixer.regs[0x31] >> 4); + sb->mixer.regs[0x04] = (sb->mixer.regs[0x32] & 0xf0) | (sb->mixer.regs[0x33] >> 4); + sb->mixer.regs[0x26] = (sb->mixer.regs[0x34] & 0xf0) | (sb->mixer.regs[0x35] >> 4); + + return sb; +} + void sb_close(void *p) { sb_t *sb = (sb_t *)p; @@ -419,3 +472,11 @@ device_t sb_16_device = sb_speed_changed, NULL }; +device_t sb_awe32_device = +{ + "Sound Blaster AWE32", + sb_awe32_init, + sb_close, + sb_speed_changed, + NULL +}; diff --git a/src/sound_sb.h b/src/sound_sb.h index 087d362..c2eada6 100644 --- a/src/sound_sb.h +++ b/src/sound_sb.h @@ -4,3 +4,4 @@ extern device_t sb_2_device; extern device_t sb_pro_v1_device; extern device_t sb_pro_v2_device; extern device_t sb_16_device; +extern device_t sb_awe32_device;