From 4ca6d67f87969d0be03f02a92bdd3acffe76a3a4 Mon Sep 17 00:00:00 2001 From: SarahW Date: Sat, 9 Sep 2017 14:58:07 +0100 Subject: [PATCH] Sound Blaster improvements : - Implemented audio recording on SB16 & AWE32 (MIDI only) - Mixer improvements - SB 2.0 CD mixer can be emulated - DSP tweaks, mainly for SB Pro. Implemented speaker on/off and limited high speed DMA to SB 2.0 & Pro - A few other things Patch from JosepMa. --- src/sound_emu8k.c | 12 +- src/sound_emu8k.h | 6 +- src/sound_sb.c | 1220 ++++++++++++++++++++++++++++++++++---------- src/sound_sb_dsp.c | 264 ++++++++-- src/sound_sb_dsp.h | 9 +- 5 files changed, 1201 insertions(+), 310 deletions(-) diff --git a/src/sound_emu8k.c b/src/sound_emu8k.c index 9b15d6c..e283922 100644 --- a/src/sound_emu8k.c +++ b/src/sound_emu8k.c @@ -1396,8 +1396,8 @@ void emu8k_outw(uint16_t addr, uint16_t val, void *p) if ((val&0xFF) == 0 && the_voice->cvcf_curr_volume == 0 && the_voice->vtft_vol_target == 0 && the_voice->dcysusv == 0x80 && the_voice->ip == 0) { - // Patch to avoid some clicking noises with Impulse tracker or other software that sets different values to 0 - // to set noteoff, but here, 0 means no attenuation = full volume. + // Patch to avoid some clicking noises with Impulse tracker or other software that sets + // different values to 0 to set noteoff, but here, 0 means no attenuation = full volume. return; } the_voice->ifatn = val; @@ -2100,7 +2100,7 @@ I've recopilated these sentences to get an idea of how to loop emu8k->pos = new_pos; } /* onboard_ram in kilobytes */ -void emu8k_init(emu8k_t *emu8k, int onboard_ram) +void emu8k_init(emu8k_t *emu8k, uint16_t emu_addr, int onboard_ram) { uint32_t const BLOCK_SIZE_WORDS = 0x10000; FILE *f; @@ -2160,9 +2160,9 @@ void emu8k_init(emu8k_t *emu8k, int onboard_ram) } - io_sethandler(0x0620, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); - io_sethandler(0x0a20, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); - io_sethandler(0x0e20, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); + io_sethandler(emu_addr, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); + io_sethandler(emu_addr+0x400, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); + io_sethandler(emu_addr+0x800, 0x0004, emu8k_inb, emu8k_inw, NULL, emu8k_outb, emu8k_outw, NULL, emu8k); /*Create frequency table. (Convert initial pitch register value to a linear speed change) * The input is encoded such as 0xe000 is center note (no pitch shift) diff --git a/src/sound_emu8k.h b/src/sound_emu8k.h index 7fad2ba..4094863 100644 --- a/src/sound_emu8k.h +++ b/src/sound_emu8k.h @@ -357,8 +357,6 @@ typedef struct emu8k_t uint16_t wc; - uint16_t c02_read; - uint16_t id; /* The empty block is used to act as an unallocated memory returning zero. */ @@ -370,8 +368,6 @@ typedef struct emu8k_t int cur_reg, cur_voice; - int timer_count; - int16_t out_l, out_r; emu8k_chorus_eng_t chorus_engine; @@ -385,7 +381,7 @@ typedef struct emu8k_t -void emu8k_init(emu8k_t *emu8k, int onboard_ram); +void emu8k_init(emu8k_t *emu8k, uint16_t emu_addr, int onboard_ram); void emu8k_close(emu8k_t *emu8k); void emu8k_update(emu8k_t *emu8k); diff --git a/src/sound_sb.c b/src/sound_sb.c index 3355bfe..945ed5f 100644 --- a/src/sound_sb.c +++ b/src/sound_sb.c @@ -14,25 +14,100 @@ #include "filters.h" -typedef struct sb_mixer_t +//#define SB_DSP_RECORD_DEBUG + +#ifdef SB_DSP_RECORD_DEBUG +FILE* soundfsb = 0/*NULL*/; +FILE* soundfsbin = 0/*NULL*/; +#endif + + + +/* SB 2.0 CD version */ +typedef struct sb_ct1335_mixer_t { - int master_l, master_r; - int voice_l, voice_r; - int fm_l, fm_r; - int cd_l, cd_r; + int32_t master; + int32_t voice; + int32_t fm; + int32_t cd; + + uint8_t index; + uint8_t regs[256]; +} sb_ct1335_mixer_t; +/* SB PRO */ +typedef struct sb_ct1345_mixer_t +{ + int32_t master_l, master_r; + int32_t voice_l, voice_r; + int32_t fm_l, fm_r; + int32_t cd_l, cd_r; + int32_t line_l, line_r; + int32_t mic; + /*see sb_ct1745_mixer for values for input selector*/ + int32_t input_selector; + + int input_filter; + int in_filter_freq; + int output_filter; + + int stereo; + int stereo_isleft; + + uint8_t index; + uint8_t regs[256]; + +} sb_ct1345_mixer_t; +/* SB16 and AWE32 */ +typedef struct sb_ct1745_mixer_t +{ + int32_t master_l, master_r; + int32_t voice_l, voice_r; + int32_t fm_l, fm_r; + int32_t cd_l, cd_r; + int32_t line_l, line_r; + int32_t mic; + int32_t speaker; + int bass_l, bass_r; int treble_l, treble_r; - int filter; + + int output_selector; + #define OUTPUT_MIC 1 + #define OUTPUT_CD_R 2 + #define OUTPUT_CD_L 4 + #define OUTPUT_LINE_R 8 + #define OUTPUT_LINE_L 16 - int index; + int input_selector_left; + int input_selector_right; + #define INPUT_MIC 1 + #define INPUT_CD_R 2 + #define INPUT_CD_L 4 + #define INPUT_LINE_R 8 + #define INPUT_LINE_L 16 + #define INPUT_MIDI_R 32 + #define INPUT_MIDI_L 64 + + int mic_agc; + + int32_t input_gain_L; + int32_t input_gain_R; + int32_t output_gain_L; + int32_t output_gain_R; + + uint8_t index; uint8_t regs[256]; -} sb_mixer_t; +} sb_ct1745_mixer_t; typedef struct sb_t { opl_t opl; sb_dsp_t dsp; - sb_mixer_t mixer; + union { + sb_ct1335_mixer_t mixer_sb2; + sb_ct1345_mixer_t mixer_sbpro; + sb_ct1745_mixer_t mixer_sb16; + }; mpu401_uart_t mpu; emu8k_t emu8k; @@ -49,17 +124,27 @@ const float sb_bass_treble_4bits[]= { 0, 0.25892541, 0.584893192, 1, 1.511886431, 2.16227766, 3, 4.011872336 }; -static int sb_att[]= +/* Attenuation tables for the mixer. Max volume = 32767 in order to give 6dB of + * headroom and avoid integer overflow */ +const int32_t sb_att_2dbstep_5bits[]= { - 50,65,82,103,130,164,207,260,328,413,520,655,825,1038,1307, - 1645,2072,2608,3283,4134,5205,6553,8250,10385,13075,16461,20724,26089, - 32845,41349,52055,65535 + 25,32,41,51,65,82,103,130,164,206,260,327,412,519,653, + 822,1036,1304,1641,2067,2602,3276,4125,5192,6537,8230,10362,13044, + 16422,20674,26027,32767 +}; +const int32_t sb_att_4dbstep_3bits[]= +{ + 164,2067,3276,5193,8230,13045,20675,32767 +}; +const int32_t sb_att_7dbstep_2bits[]= +{ + 164,6537,14637,32767 }; -static void sb_get_buffer_opl2(int32_t *buffer, int len, void *p) +/* sb 1, 1.5, 2, 2 mvc do not have a mixer, so signal is hardwired */ +static void sb_get_buffer_sb2(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; int c; @@ -67,40 +152,13 @@ static void sb_get_buffer_opl2(int32_t *buffer, int len, void *p) sb_dsp_update(&sb->dsp); for (c = 0; c < len * 2; c += 2) { - int32_t out_l, out_r; - - out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 16) * 51000) >> 16); - out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 16) * 51000) >> 16); - - if (sb->mixer.filter) - { - out_l += (int)(((sb_iir(0, (float)sb->dsp.buffer[c]) / 1.3) * mixer->voice_l) / 3) >> 16; - out_r += (int)(((sb_iir(1, (float)sb->dsp.buffer[c + 1]) / 1.3) * mixer->voice_r) / 3) >> 16; - } - else - { - out_l += ((int32_t)(sb->dsp.buffer[c] * mixer->voice_l) / 3) >> 16; - out_r += ((int32_t)(sb->dsp.buffer[c + 1] * mixer->voice_r) / 3) >> 16; - } - - out_l = (out_l * mixer->master_l) >> 16; - out_r = (out_r * mixer->master_r) >> 16; - - if (mixer->bass_l != 8 || mixer->bass_r != 8 || mixer->treble_l != 8 || mixer->treble_r != 8) - { - // This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the cpu usage - if (mixer->bass_l>8) out_l += (int32_t)(low_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->bass_l]); - if (mixer->bass_r>8) out_r += (int32_t)(low_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->bass_r]); - if (mixer->treble_l>8) out_l += (int32_t)(high_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->treble_l]); - if (mixer->treble_r>8) out_r += (int32_t)(high_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->treble_r]); - if (mixer->bass_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->bass_l] + low_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->bass_l])); - if (mixer->bass_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->bass_r] + low_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->bass_r])); - if (mixer->treble_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->treble_l] + high_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->treble_l])); - if (mixer->treble_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->treble_r] + high_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->treble_r])); - } - - buffer[c] += out_l; - buffer[c + 1] += out_r; + int32_t out; + out = ((sb->opl.buffer[c] * 51000) >> 16); + //TODO: Recording: Mic and line In with AGC + out += (int32_t)(((sb_iir(0, (float)sb->dsp.buffer[c]) / 1.3) * 65536) / 3) >> 16; + + buffer[c] += out; + buffer[c + 1] += out; } sb->pos = 0; @@ -108,49 +166,71 @@ static void sb_get_buffer_opl2(int32_t *buffer, int len, void *p) sb->dsp.pos = 0; } -static void sb_get_buffer_opl3(int32_t *buffer, int len, void *p) +static void sb_get_buffer_sb2_mixer(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; int c; - opl3_update2(&sb->opl); + opl2_update2(&sb->opl); + sb_dsp_update(&sb->dsp); + for (c = 0; c < len * 2; c += 2) + { + int32_t out; + + out = ((((sb->opl.buffer[c] * mixer->fm) >> 16) * 51000) >> 15); + /* TODO: Recording : I assume it has direct mic and line in like sb2 */ + /* It is unclear from the docs if it has a filter, but it probably does */ + out += (int32_t)(((sb_iir(0, (float)sb->dsp.buffer[c]) / 1.3) * mixer->voice) / 3) >> 15; + + out = (out * mixer->master) >> 15; + + buffer[c] += out; + buffer[c + 1] += out; + } + + sb->pos = 0; + sb->opl.pos = 0; + sb->dsp.pos = 0; +} + +static void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + + int c; + + if (sb->dsp.sb_type == SBPRO) + opl2_update2(&sb->opl); + else + opl3_update2(&sb->opl); + sb_dsp_update(&sb->dsp); for (c = 0; c < len * 2; c += 2) { int32_t out_l, out_r; - out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 16); - out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 16); - - if (sb->mixer.filter) + out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 15); + out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 15); + + /*TODO: Implement the stereo switch on the mixer instead of on the dsp? */ + if (mixer->output_filter) { - out_l += (int)(((sb_iir(0, (float)sb->dsp.buffer[c]) / 1.3) * mixer->voice_l) / 3) >> 16; - out_r += (int)(((sb_iir(1, (float)sb->dsp.buffer[c + 1]) / 1.3) * mixer->voice_r) / 3) >> 16; + out_l += (int32_t)(((sb_iir(0, (float)sb->dsp.buffer[c]) / 1.3) * mixer->voice_l) / 3) >> 15; + out_r += (int32_t)(((sb_iir(1, (float)sb->dsp.buffer[c + 1]) / 1.3) * mixer->voice_r) / 3) >> 15; } else { - out_l += ((int32_t)(sb->dsp.buffer[c] * mixer->voice_l) / 3) >> 16; - out_r += ((int32_t)(sb->dsp.buffer[c + 1] * mixer->voice_r) / 3) >> 16; + 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; } + //TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. - out_l = (out_l * mixer->master_l) >> 16; - out_r = (out_r * mixer->master_r) >> 16; + out_l = (out_l * mixer->master_l) >> 15; + out_r = (out_r * mixer->master_r) >> 15; - if (mixer->bass_l != 8 || mixer->bass_r != 8 || mixer->treble_l != 8 || mixer->treble_r != 8) - { - // This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the cpu usage - if (mixer->bass_l>8) out_l += (int32_t)(low_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->bass_l]); - if (mixer->bass_r>8) out_r += (int32_t)(low_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->bass_r]); - if (mixer->treble_l>8) out_l += (int32_t)(high_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->treble_l]); - if (mixer->treble_r>8) out_r += (int32_t)(high_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->treble_r]); - if (mixer->bass_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->bass_l] + low_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->bass_l])); - if (mixer->bass_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->bass_r] + low_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->bass_r])); - if (mixer->treble_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->treble_l] + high_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->treble_l])); - if (mixer->treble_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->treble_r] + high_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->treble_r])); - } - buffer[c] += out_l; buffer[c + 1] += out_r; } @@ -158,102 +238,358 @@ static void sb_get_buffer_opl3(int32_t *buffer, int len, void *p) sb->pos = 0; sb->opl.pos = 0; sb->dsp.pos = 0; - sb->emu8k.pos = 0; } +static void sb_get_buffer_sb16(int32_t *buffer, int len, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + + int c; + + opl3_update2(&sb->opl); + sb_dsp_update(&sb->dsp); + const int dsp_rec_pos = sb->dsp.record_pos_write; + for (c = 0; c < len * 2; c += 2) + { + int32_t out_l, out_r, in_l, in_r; + + out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 15); + out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 15); + + /*TODO: multi-recording mic with agc/+20db, cd and line in with channel inversion */ + 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 = (out_l * mixer->master_l) >> 15; + out_r = (out_r * mixer->master_r) >> 15; + + if (mixer->bass_l != 8 || mixer->bass_r != 8 || mixer->treble_l != 8 || mixer->treble_r != 8) + { + /* This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the cpu usage */ + if (mixer->bass_l>8) out_l += (int32_t)(low_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->bass_l]); + if (mixer->bass_r>8) out_r += (int32_t)(low_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->bass_r]); + if (mixer->treble_l>8) out_l += (int32_t)(high_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->treble_l]); + if (mixer->treble_r>8) out_r += (int32_t)(high_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->treble_r]); + if (mixer->bass_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->bass_l] + low_cut_iir(0, (float)out_l)*(1.f-sb_bass_treble_4bits[mixer->bass_l])); + if (mixer->bass_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->bass_r] + low_cut_iir(1, (float)out_r)*(1.f-sb_bass_treble_4bits[mixer->bass_r])); + if (mixer->treble_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->treble_l] + high_cut_iir(0, (float)out_l)*(1.f-sb_bass_treble_4bits[mixer->treble_l])); + if (mixer->treble_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->treble_r] + high_cut_iir(1, (float)out_r)*(1.f-sb_bass_treble_4bits[mixer->treble_r])); + } + if (sb->dsp.sb_enable_i) + { + int c_record = dsp_rec_pos; + c_record +=(((c/2) * sb->dsp.sb_freq) / 48000)*2; + in_l <<= mixer->input_gain_L; + in_r <<= mixer->input_gain_R; + // Clip signal + if (in_l < -32768) + in_l = -32768; + else if (in_l > 32767) + in_l = 32767; + + if (in_r < -32768) + in_r = -32768; + else if (in_r > 32767) + in_r = 32767; + sb->dsp.record_buffer[c_record&0xFFFF] = in_l; + sb->dsp.record_buffer[(c_record+1)&0xFFFF] = in_r; + } + + buffer[c] += (out_l << mixer->output_gain_L); + buffer[c + 1] += (out_r << mixer->output_gain_R); + } + sb->dsp.record_pos_write+=((len * sb->dsp.sb_freq) / 48000)*2; + sb->dsp.record_pos_write&=0xFFFF; + + sb->pos = 0; + sb->opl.pos = 0; + sb->dsp.pos = 0; +} +#ifdef SB_DSP_RECORD_DEBUG +int old_dsp_rec_pos=0; +int buf_written=0; +int last_crecord=0; +#endif static void sb_get_buffer_emu8k(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; int c; opl3_update2(&sb->opl); - sb_dsp_update(&sb->dsp); emu8k_update(&sb->emu8k); + sb_dsp_update(&sb->dsp); + const int dsp_rec_pos = sb->dsp.record_pos_write; for (c = 0; c < len * 2; c += 2) { + int32_t out_l, out_r, in_l, in_r; int c_emu8k = (((c/2) * 44100) / 48000)*2; - int32_t out_l, out_r; - out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 16); - out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 16) * (sb->opl_emu ? 47000 : 51000)) >> 16); + out_l = ((((sb->opl.buffer[c] * mixer->fm_l) >> 15) * (sb->opl_emu ? 47000 : 51000)) >> 16); + out_r = ((((sb->opl.buffer[c + 1] * mixer->fm_r) >> 15) * (sb->opl_emu ? 47000 : 51000)) >> 16); - out_l += ((sb->emu8k.buffer[c_emu8k] * mixer->fm_l) >> 16); - out_r += ((sb->emu8k.buffer[c_emu8k + 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; - out_r += (int)(((sb_iir(1, (float)sb->dsp.buffer[c + 1]) / 1.3) * mixer->voice_r) / 3) >> 16; - } - else - { - out_l += ((int32_t)(sb->dsp.buffer[c] * mixer->voice_l) / 3) >> 16; - out_r += ((int32_t)(sb->dsp.buffer[c + 1] * mixer->voice_r) / 3) >> 16; - } + out_l += ((sb->emu8k.buffer[c_emu8k] * mixer->fm_l) >> 15); + out_r += ((sb->emu8k.buffer[c_emu8k + 1] * mixer->fm_r) >> 15); - out_l = (out_l * mixer->master_l) >> 16; - out_r = (out_r * mixer->master_r) >> 16; + /*TODO: multi-recording mic with agc/+20db, cd and line in with channel inversion */ + 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 = (out_l * mixer->master_l) >> 15; + out_r = (out_r * mixer->master_r) >> 15; if (mixer->bass_l != 8 || mixer->bass_r != 8 || mixer->treble_l != 8 || mixer->treble_r != 8) { - // This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the cpu usage + /* This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the cpu usage */ if (mixer->bass_l>8) out_l += (int32_t)(low_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->bass_l]); if (mixer->bass_r>8) out_r += (int32_t)(low_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->bass_r]); if (mixer->treble_l>8) out_l += (int32_t)(high_iir(0, (float)out_l)*sb_bass_treble_4bits[mixer->treble_l]); if (mixer->treble_r>8) out_r += (int32_t)(high_iir(1, (float)out_r)*sb_bass_treble_4bits[mixer->treble_r]); - if (mixer->bass_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->bass_l] + low_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->bass_l])); - if (mixer->bass_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->bass_r] + low_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->bass_r])); - if (mixer->treble_l<8) out_l = (int32_t)((out_l )*sb_bass_treble_4bits[mixer->treble_l] + high_cut_iir(0, (float)out_l)*(1.0-sb_bass_treble_4bits[mixer->treble_l])); - if (mixer->treble_r<8) out_r = (int32_t)((out_r )*sb_bass_treble_4bits[mixer->treble_r] + high_cut_iir(1, (float)out_r)*(1.0-sb_bass_treble_4bits[mixer->treble_r])); + if (mixer->bass_l<8) out_l = (int32_t)(out_l *sb_bass_treble_4bits[mixer->bass_l] + low_cut_iir(0, (float)out_l)*(1.f-sb_bass_treble_4bits[mixer->bass_l])); + if (mixer->bass_r<8) out_r = (int32_t)(out_r *sb_bass_treble_4bits[mixer->bass_r] + low_cut_iir(1, (float)out_r)*(1.f-sb_bass_treble_4bits[mixer->bass_r])); + if (mixer->treble_l<8) out_l = (int32_t)(out_l *sb_bass_treble_4bits[mixer->treble_l] + high_cut_iir(0, (float)out_l)*(1.f-sb_bass_treble_4bits[mixer->treble_l])); + if (mixer->treble_r<8) out_r = (int32_t)(out_r *sb_bass_treble_4bits[mixer->treble_r] + high_cut_iir(1, (float)out_r)*(1.f-sb_bass_treble_4bits[mixer->treble_r])); } - - buffer[c] += out_l; - buffer[c + 1] += out_r; - } + if (sb->dsp.sb_enable_i) + { +// in_l += (mixer->input_selector_left&INPUT_CD_L) ? audio_cd_buffer[cd_read_pos+c_emu8k] : 0 + (mixer->input_selector_left&INPUT_CD_R) ? audio_cd_buffer[cd_read_pos+c_emu8k+1] : 0; +// in_r += (mixer->input_selector_right&INPUT_CD_L) ? audio_cd_buffer[cd_read_pos+c_emu8k]: 0 + (mixer->input_selector_right&INPUT_CD_R) ? audio_cd_buffer[cd_read_pos+c_emu8k+1] : 0; + int c_record = dsp_rec_pos; + c_record +=(((c/2) * sb->dsp.sb_freq) / 48000)*2; + #ifdef SB_DSP_RECORD_DEBUG + if (c_record > 0xFFFF && !buf_written) + { + if (!soundfsb) soundfsb=fopen("sound_sb.pcm","wb"); + fwrite(sb->dsp.record_buffer,2,0x10000,soundfsb); + old_dsp_rec_pos = dsp_rec_pos; + buf_written=1; + } + #endif + in_l <<= mixer->input_gain_L; + in_r <<= mixer->input_gain_R; + // Clip signal + if (in_l < -32768) + in_l = -32768; + else if (in_l > 32767) + in_l = 32767; + + if (in_r < -32768) + in_r = -32768; + else if (in_r > 32767) + in_r = 32767; + sb->dsp.record_buffer[c_record&0xFFFF] = in_l; + sb->dsp.record_buffer[(c_record+1)&0xFFFF] = in_r; + #ifdef SB_DSP_RECORD_DEBUG + if (c_record != last_crecord) + { + if (!soundfsbin) soundfsbin=fopen("sound_sb_in.pcm","wb"); + fwrite(&sb->dsp.record_buffer[c_record&0xFFFF],2,2,soundfsbin); + last_crecord=c_record; + } + #endif + } + + buffer[c] += (out_l << mixer->output_gain_L); + buffer[c + 1] += (out_r << mixer->output_gain_R); + } + #ifdef SB_DSP_RECORD_DEBUG + if (old_dsp_rec_pos > dsp_rec_pos) + { + buf_written=0; + old_dsp_rec_pos=dsp_rec_pos; + } + #endif + + sb->dsp.record_pos_write+=((len * sb->dsp.sb_freq) / 48000)*2; + sb->dsp.record_pos_write&=0xFFFF; sb->pos = 0; sb->opl.pos = 0; sb->dsp.pos = 0; sb->emu8k.pos = 0; } -void sb_pro_mixer_write(uint16_t addr, uint8_t val, void *p) + +void sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; if (!(addr & 1)) - mixer->index = val & 0xff; + { + mixer->index = val; + mixer->regs[0x01] = val; + } else { - mixer->regs[mixer->index] = val; - - mixer->master_l = sb_att[(mixer->regs[0x22] >> 4) | 0x11]; - mixer->master_r = sb_att[(mixer->regs[0x22] & 0xf) | 0x11]; - mixer->voice_l = sb_att[(mixer->regs[0x04] >> 4) | 0x11]; - mixer->voice_r = sb_att[(mixer->regs[0x04] & 0xf) | 0x11]; - mixer->fm_l = sb_att[(mixer->regs[0x26] >> 4) | 0x11]; - mixer->fm_r = sb_att[(mixer->regs[0x26] & 0xf) | 0x11]; - mixer->cd_l = sb_att[(mixer->regs[0x28] >> 4) | 0x11]; - mixer->cd_r = sb_att[(mixer->regs[0x28] & 0xf) | 0x11]; - mixer->filter = !(mixer->regs[0xe] & 0x20); - mixer->bass_l = mixer->bass_r = 8; - mixer->treble_l = mixer->treble_r = 8; - sound_set_cd_volume(((uint32_t)mixer->master_l * (uint32_t)mixer->cd_l) / 65535, - ((uint32_t)mixer->master_r * (uint32_t)mixer->cd_r) / 65535); -// pclog("%02X %02X %02X\n", mixer->regs[0x04], mixer->regs[0x22], mixer->regs[0x26]); -// pclog("Mixer - %04X %04X %04X %04X %04X %04X\n", mixer->master_l, mixer->master_r, mixer->voice_l, mixer->voice_r, mixer->fm_l, mixer->fm_r); - if (mixer->index == 0xe) - sb_dsp_set_stereo(&sb->dsp, val & 2); + if (mixer->index == 0) + { + /* Reset */ + mixer->regs[0x02] = 4 << 1; + mixer->regs[0x06] = 4 << 1; + mixer->regs[0x08] = 0 << 1; + /* changed default from -46dB to 0dB*/ + mixer->regs[0x0A] = 3 << 1; + } + else + { + mixer->regs[mixer->index] = val; + switch (mixer->index) + { + case 0x00: case 0x02: case 0x06: case 0x08: case 0x0A: + break; + + default: + pclog("sb_ct1335: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; + } + } + mixer->master = sb_att_4dbstep_3bits[(mixer->regs[0x02] >> 1)&0x7]; + mixer->fm = sb_att_4dbstep_3bits[(mixer->regs[0x06] >> 1)&0x7]; + mixer->cd = sb_att_4dbstep_3bits[(mixer->regs[0x08] >> 1)&0x7]; + mixer->voice = sb_att_7dbstep_2bits[(mixer->regs[0x0A] >> 1)&0x3]; + + sound_set_cd_volume(((uint32_t)mixer->master * (uint32_t)mixer->cd) / 65535, + ((uint32_t)mixer->master * (uint32_t)mixer->cd) / 65535); } } -uint8_t sb_pro_mixer_read(uint16_t addr, void *p) +uint8_t sb_ct1335_mixer_read(uint16_t addr, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; + + if (!(addr & 1)) + return mixer->index; + + switch (mixer->index) + { + case 0x00: case 0x02: case 0x06: case 0x08: case 0x0A: + return mixer->regs[mixer->index]; + default: + pclog("sb_ct1335: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; + } + + return 0xff; +} + +void sb_ct1335_mixer_reset(sb_t* sb) +{ + sb_ct1335_mixer_write(0x254,0,sb); + sb_ct1335_mixer_write(0x255,0,sb); +} + +void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + + if (!(addr & 1)) + { + mixer->index = val; + mixer->regs[0x01] = val; + } + else + { + if (mixer->index == 0) + { + /* Reset */ + mixer->regs[0x0A] = 0 << 1; + mixer->regs[0x0C] = (0 << 5) | (0 << 3) | (0 << 1); + mixer->regs[0x0E] = (0 << 5) | (0 << 1); + /* changed default from -11dB to 0dB */ + mixer->regs[0x04] = (7 << 5) | (7 << 1); + mixer->regs[0x22] = (7 << 5) | (7 << 1); + mixer->regs[0x26] = (7 << 5) | (7 << 1); + mixer->regs[0x28] = (0 << 5) | (0 << 1); + mixer->regs[0x2E] = (0 << 5) | (0 << 1); + sb_dsp_set_stereo(&sb->dsp, mixer->regs[0x0E] & 2); + } + else + { + mixer->regs[mixer->index] = val; + switch (mixer->index) + { + /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */ + case 0x02: case 0x06: + mixer->regs[mixer->index+0x20]=((val&0xE) << 4)||(val&0xE) << 4; + break; + + case 0x22: case 0x26: + mixer->regs[mixer->index-0x20]=(val&0xE); + break; + + /* More compatibility: SoundBlaster Pro selects register 020h for 030h, 022h for 032h, 026h for 036h,028h for 038h. */ + case 0x30: case 0x32: case 0x36: case 0x38: + mixer->regs[mixer->index-0x10]=(val&0xEE); + break; + + case 0x00: case 0x04: case 0x0a: case 0x0c: case 0x0e: + case 0x28: case 0x2e: + break; + + + default: + pclog("sb_ct1345: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; + } + } + + mixer->voice_l = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 5)&0x7]; + mixer->voice_r = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 1)&0x7]; + mixer->master_l = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 5)&0x7]; + mixer->master_r = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 1)&0x7]; + mixer->fm_l = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 5)&0x7]; + mixer->fm_r = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 1)&0x7]; + mixer->cd_l = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 5)&0x7]; + mixer->cd_r = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 1)&0x7]; + mixer->line_l = sb_att_4dbstep_3bits[(mixer->regs[0x2E] >> 5)&0x7]; + mixer->line_r = sb_att_4dbstep_3bits[(mixer->regs[0x2E] >> 1)&0x7]; + + mixer->mic = sb_att_7dbstep_2bits[(mixer->regs[0x0A] >> 1)&0x3]; + + mixer->output_filter = !(mixer->regs[0xE] & 0x20); + mixer->input_filter = !(mixer->regs[0xC] & 0x20); + mixer->in_filter_freq = ((mixer->regs[0xC] & 0x8) == 0) ? 3200 : 8800; + mixer->stereo = mixer->regs[0xE] & 2; + if (mixer->index == 0xE) + sb_dsp_set_stereo(&sb->dsp, val & 2); + + switch ((mixer->regs[0xc]&6)) + { + case 2: + mixer->input_selector = INPUT_CD_L|INPUT_CD_R; + break; + case 6: + mixer->input_selector = INPUT_LINE_L|INPUT_LINE_R; + break; + default: + mixer->input_selector = INPUT_MIC; + break; + } + + /* TODO: pcspeaker volume? Or is it not worth? */ + sound_set_cd_volume(((uint32_t)mixer->master_l * (uint32_t)mixer->cd_l) / 65535, + ((uint32_t)mixer->master_r * (uint32_t)mixer->cd_r) / 65535); + } +} + +uint8_t sb_ct1345_mixer_read(uint16_t addr, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; if (!(addr & 1)) return mixer->index; @@ -261,106 +597,251 @@ uint8_t sb_pro_mixer_read(uint16_t addr, void *p) switch (mixer->index) { case 0x00: case 0x04: case 0x0a: case 0x0c: case 0x0e: - case 0x22: case 0x26: case 0x28: case 0x2e: + case 0x22: case 0x26: case 0x28: case 0x2e: case 0x02: case 0x06: + case 0x30: case 0x32: case 0x36: case 0x38: return mixer->regs[mixer->index]; + + default: + pclog("sb_ct1345: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; } return 0xff; } +void sb_ct1345_mixer_reset(sb_t* sb) +{ + sb_ct1345_mixer_write(4,0,sb); + sb_ct1345_mixer_write(5,0,sb); +} -void sb_16_mixer_write(uint16_t addr, uint8_t val, void *p) +void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; if (!(addr & 1)) + { mixer->index = val; + } else { - mixer->regs[mixer->index] = val; + // TODO: and this? 001h: + /*DESCRIPTION + Contains previously selected register value. Mixer Data Register value + NOTES + * SoundBlaster 16 sets bit 7 if previous mixer index invalid. + * Status bytes initially 080h on startup for all but level bytes (SB16) + */ + + if (mixer->index == 0) + { + /* Reset */ + /* Changed defaults from -14dB to 0dB*/ + mixer->regs[0x30]=31 << 3; + mixer->regs[0x31]=31 << 3; + mixer->regs[0x32]=31 << 3; + mixer->regs[0x33]=31 << 3; + mixer->regs[0x34]=31 << 3; + mixer->regs[0x35]=31 << 3; + mixer->regs[0x36]=0 << 3; + mixer->regs[0x37]=0 << 3; + mixer->regs[0x38]=0 << 3; + mixer->regs[0x39]=0 << 3; + + mixer->regs[0x3A]=0 << 3; + mixer->regs[0x3B]=0 << 6; + mixer->regs[0x3C] = OUTPUT_MIC|OUTPUT_CD_R|OUTPUT_CD_L|OUTPUT_LINE_R|OUTPUT_LINE_L; + mixer->regs[0x3D] = INPUT_MIC|INPUT_CD_L|INPUT_LINE_L|INPUT_MIDI_L; + mixer->regs[0x3E] = INPUT_MIC|INPUT_CD_R|INPUT_LINE_R|INPUT_MIDI_R; + + mixer->regs[0x3F] = mixer->regs[0x40] = 0 << 6; + mixer->regs[0x41] = mixer->regs[0x42] = 0 << 6; + + mixer->regs[0x44] = mixer->regs[0x45] = 8 << 4; + mixer->regs[0x46] = mixer->regs[0x47] = 8 << 4; + + mixer->regs[0x43] = 0; + } + else + { + mixer->regs[mixer->index] = val; + } switch (mixer->index) { + /* SBPro compatibility. Copy values to sb16 registers. */ case 0x22: - mixer->regs[0x30] = ((mixer->regs[0x22] >> 4) | 0x11) << 3; - mixer->regs[0x31] = ((mixer->regs[0x22] & 0xf) | 0x11) << 3; + mixer->regs[0x30] = (mixer->regs[0x22] & 0xF0) | 0x8; + mixer->regs[0x31] = ((mixer->regs[0x22] & 0xf) << 4) | 0x8; break; case 0x04: - mixer->regs[0x32] = ((mixer->regs[0x04] >> 4) | 0x11) << 3; - mixer->regs[0x33] = ((mixer->regs[0x04] & 0xf) | 0x11) << 3; + mixer->regs[0x32] = (mixer->regs[0x04] & 0xF0) | 0x8; + mixer->regs[0x33] = ((mixer->regs[0x04] & 0xf) << 4) | 0x8; break; case 0x26: - mixer->regs[0x34] = ((mixer->regs[0x26] >> 4) | 0x11) << 3; - mixer->regs[0x35] = ((mixer->regs[0x26] & 0xf) | 0x11) << 3; + mixer->regs[0x34] = (mixer->regs[0x26] & 0xF0) | 0x8; + mixer->regs[0x35] = ((mixer->regs[0x26] & 0xf) << 4) | 0x8; break; case 0x28: - mixer->regs[0x36] = ((mixer->regs[0x28] >> 4) | 0x11) << 3; - mixer->regs[0x37] = ((mixer->regs[0x28] & 0xf) | 0x11) << 3; + mixer->regs[0x36] = (mixer->regs[0x28] & 0xF0) | 0x8; + mixer->regs[0x37] = ((mixer->regs[0x28] & 0xf) << 4) | 0x8; break; + case 0x2E: + mixer->regs[0x38] = (mixer->regs[0x2E] & 0xF0) | 0x8; + mixer->regs[0x39] = ((mixer->regs[0x2E] & 0xf) << 4) | 0x8; + break; + case 0x0A: + mixer->regs[0x3A] = (mixer->regs[0x0A]*3)+10; + break; + + /* + (DSP 4.xx feature) The Interrupt Setup register, addressed as register 80h on the Mixer register map, is used to configure or determine the Interrupt request line. The DMA setup register, addressed as register 81h on the Mixer register map, is used to configure or determine the DMA channels. + + Note: Registers 80h and 81h are Read-only for PnP boards. + */ case 0x80: - if (val & 1) sb->dsp.sb_irqnum = 2; - if (val & 2) sb->dsp.sb_irqnum = 5; - if (val & 4) sb->dsp.sb_irqnum = 7; - if (val & 8) sb->dsp.sb_irqnum = 10; + if (val & 1) sb_dsp_setirq(&sb->dsp,2); + if (val & 2) sb_dsp_setirq(&sb->dsp,5); + if (val & 4) sb_dsp_setirq(&sb->dsp,7); + if (val & 8) sb_dsp_setirq(&sb->dsp,10); + break; + + case 0x81: + /* The documentation is confusing. sounds as if multple dma8 channels could be set. */ + if (val & 1) sb_dsp_setdma8(&sb->dsp,0); + if (val & 2) sb_dsp_setdma8(&sb->dsp,1); + if (val & 8) sb_dsp_setdma8(&sb->dsp,3); + if (val & 0x20) sb_dsp_setdma16(&sb->dsp,5); + if (val & 0x40) sb_dsp_setdma16(&sb->dsp,6); + if (val & 0x80) sb_dsp_setdma16(&sb->dsp,7); break; } - mixer->master_l = sb_att[mixer->regs[0x30] >> 3]; - mixer->master_r = sb_att[mixer->regs[0x31] >> 3]; - mixer->voice_l = sb_att[mixer->regs[0x32] >> 3]; - mixer->voice_r = sb_att[mixer->regs[0x33] >> 3]; - mixer->fm_l = sb_att[mixer->regs[0x34] >> 3]; - mixer->fm_r = sb_att[mixer->regs[0x35] >> 3]; - mixer->cd_l = sb_att[mixer->regs[0x36] >> 3]; - mixer->cd_r = sb_att[mixer->regs[0x37] >> 3]; + + mixer->output_selector = mixer->regs[0x3C]; + mixer->input_selector_left = mixer->regs[0x3D]; + mixer->input_selector_right = mixer->regs[0x3E]; + + mixer->master_l = sb_att_2dbstep_5bits[mixer->regs[0x30] >> 3]; + mixer->master_r = sb_att_2dbstep_5bits[mixer->regs[0x31] >> 3]; + mixer->voice_l = sb_att_2dbstep_5bits[mixer->regs[0x32] >> 3]; + mixer->voice_r = sb_att_2dbstep_5bits[mixer->regs[0x33] >> 3]; + mixer->fm_l = sb_att_2dbstep_5bits[mixer->regs[0x34] >> 3]; + mixer->fm_r = sb_att_2dbstep_5bits[mixer->regs[0x35] >> 3]; + mixer->cd_l = (mixer->output_selector&OUTPUT_CD_L) ? sb_att_2dbstep_5bits[mixer->regs[0x36] >> 3] : 0; + mixer->cd_r = (mixer->output_selector&OUTPUT_CD_R) ? sb_att_2dbstep_5bits[mixer->regs[0x37] >> 3] : 0; + mixer->line_l = (mixer->output_selector&OUTPUT_LINE_L) ? sb_att_2dbstep_5bits[mixer->regs[0x38] >> 3] : 0; + mixer->line_r = (mixer->output_selector&OUTPUT_LINE_R) ? sb_att_2dbstep_5bits[mixer->regs[0x39] >> 3] : 0; + + mixer->mic = sb_att_2dbstep_5bits[mixer->regs[0x3A] >> 3]; + mixer->speaker = sb_att_2dbstep_5bits[mixer->regs[0x3B]*3 + 22]; + + + mixer->input_gain_L = (mixer->regs[0x3F] >> 6); + mixer->input_gain_R = (mixer->regs[0x40] >> 6); + mixer->output_gain_L = (mixer->regs[0x41] >> 6); + mixer->output_gain_R = (mixer->regs[0x42] >> 6); + mixer->bass_l = mixer->regs[0x46] >> 4; mixer->bass_r = mixer->regs[0x47] >> 4; mixer->treble_l = mixer->regs[0x44] >> 4; mixer->treble_r = mixer->regs[0x45] >> 4; - mixer->filter = 0; + + /*TODO: pcspeaker volume, with "output_selector" check? or better not? */ sound_set_cd_volume(((uint32_t)mixer->master_l * (uint32_t)mixer->cd_l) / 65535, ((uint32_t)mixer->master_r * (uint32_t)mixer->cd_r) / 65535); -// pclog("%02X %02X %02X %02X %02X %02X\n", mixer->regs[0x30], mixer->regs[0x31], mixer->regs[0x32], mixer->regs[0x33], mixer->regs[0x34], mixer->regs[0x35]); -// pclog("Mixer - %04X %04X %04X %04X %04X %04X\n", mixer->master_l, mixer->master_r, mixer->voice_l, mixer->voice_r, mixer->fm_l, mixer->fm_r); +// pclog("sb_ct1745: Received register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); } } -uint8_t sb_16_mixer_read(uint16_t addr, void *p) +uint8_t sb_ct1745_mixer_read(uint16_t addr, void *p) { sb_t *sb = (sb_t *)p; - sb_mixer_t *mixer = &sb->mixer; + sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; if (!(addr & 1)) return mixer->index; +// pclog("sb_ct1745: received register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + + if (mixer->index>=0x30 && mixer->index<=0x47) + { + return mixer->regs[mixer->index]; + } switch (mixer->index) { - case 0x40: return 0xff; + case 0x00: case 0x04: case 0x0A: case 0x22: case 0x26: case 0x28: case 0x2E: + return mixer->regs[mixer->index]; + + case 0x48: + // Undocumented. The Creative Windows Mixer calls this after calling 3C (input selector). even when writing. + // Also, the version I have (5.17) does not use the MIDI.L/R input selectors. it uses the volume to mute (Affecting the output, obviously) + return mixer->regs[mixer->index]; + case 0x80: + /*TODO: Unaffected by mixer reset or soft reboot. + * Enabling multiple bits enables multiple IRQs. + */ + switch (sb->dsp.sb_irqnum) { - case 2: return 1; /*IRQ 7*/ - case 5: return 2; /*IRQ 7*/ - case 7: return 4; /*IRQ 7*/ - case 10: return 8; /*IRQ 7*/ + case 2: return 1; + case 5: return 2; + case 7: return 4; + case 10: return 8; } break; + case 0x81: - return 0x22; /*DMA 1 and 5*/ + { + /* TODO: Unaffected by mixer reset or soft reboot. + * Enabling multiple 8 or 16-bit DMA bits enables multiple DMA channels. + * Disabling all 8-bit DMA channel bits disables 8-bit DMA requests, + including translated 16-bit DMA requests. + * Disabling all 16-bit DMA channel bits enables translation of 16-bit DMA + requests to 8-bit ones, using the selected 8-bit DMA channel.*/ + + uint8_t result=0; + switch (sb->dsp.sb_8_dmanum) + { + case 0: result |= 1; break; + case 1: result |= 2; break; + case 3: result |= 8; break; + } + switch (sb->dsp.sb_16_dmanum) + { + case 5: result |= 0x20; break; + case 6: result |= 0x40; break; + case 7: result |= 0x80; break; + } + return result; + } + + /* The Interrupt status register, addressed as register 82h on the Mixer register map, + is used by the ISR to determine whether the interrupt is meant for it or for some other ISR, + in which case it should chain to the previous routine. + */ case 0x82: - return ((sb->dsp.sb_irq8) ? 1 : 0) | ((sb->dsp.sb_irq16) ? 2 : 0); + /* 0 = none, 1 = digital 8bit or SBMIDI, 2 = digital 16bit, 4 = MPU-401 */ + /* 0x02000 DSP v4.04, 0x4000 DSP v4.05 0x8000 DSP v4.12. I haven't seen this making any difference, but I'm keeping it for now. */ + return ((sb->dsp.sb_irq8) ? 1 : 0) | ((sb->dsp.sb_irq16) ? 2 : 0) | 0x4000; + + /* TODO: creative drivers read and write on 0xFE and 0xFF. not sure what they are supposed to be. */ + + + default: + pclog("sb_ct1745: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + break; } - return mixer->regs[mixer->index]; + + return 0xff; } -void sb_mixer_init(sb_mixer_t *mixer) +void sb_ct1745_mixer_reset(sb_t* sb) { - mixer->master_l = mixer->master_r = 65535; - mixer->voice_l = mixer->voice_r = 65535; - mixer->fm_l = mixer->fm_r = 65535; - mixer->bass_l = mixer->bass_r = 8; - mixer->treble_l = mixer->treble_r = 8; - mixer->filter = 1; + sb_ct1745_mixer_write(4,0,sb); + sb_ct1745_mixer_write(5,0,sb); } + static uint16_t sb_mcv_addr[8] = {0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270}; uint8_t sb_mcv_read(int port, void *p) @@ -385,6 +866,7 @@ void sb_mcv_write(int port, uint8_t val, void *p) addr = sb_mcv_addr[sb->pos_regs[4] & 7]; io_removehandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_removehandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); + /* DSP I/O handler is activated in sb_dsp_setaddr */ sb_dsp_setaddr(&sb->dsp, 0); sb->pos_regs[port & 7] = val; @@ -395,6 +877,7 @@ void sb_mcv_write(int port, uint8_t val, void *p) io_sethandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); + /* DSP I/O handler is activated in sb_dsp_setaddr */ sb_dsp_setaddr(&sb->dsp, addr); } } @@ -424,7 +907,8 @@ void sb_pro_mcv_write(int port, uint8_t val, void *p) io_removehandler(addr+0, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_removehandler(addr+8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_removehandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); - io_removehandler(addr+4, 0x0002, sb_pro_mixer_read, NULL, NULL, sb_pro_mixer_write, NULL, NULL, sb); + io_removehandler(addr+4, 0x0002, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, sb); + /* DSP I/O handler is activated in sb_dsp_setaddr */ sb_dsp_setaddr(&sb->dsp, 0); sb->pos_regs[port & 7] = val; @@ -436,16 +920,20 @@ void sb_pro_mcv_write(int port, uint8_t val, void *p) io_sethandler(addr+0, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(addr+8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); - io_sethandler(addr+4, 0x0002, sb_pro_mixer_read, NULL, NULL, sb_pro_mixer_write, NULL, NULL, sb); - + io_sethandler(addr+4, 0x0002, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, sb); + /* DSP I/O handler is activated in sb_dsp_setaddr */ sb_dsp_setaddr(&sb->dsp, addr); } sb_dsp_setirq(&sb->dsp, sb_pro_mcv_irqs[(sb->pos_regs[5] >> 4) & 3]); sb_dsp_setdma8(&sb->dsp, sb->pos_regs[4] & 3); } - + void *sb_1_init() { + /*sb1/2 port mappings, 210h to 260h in 10h steps + 2x0 to 2x3 -> CMS chip + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip*/ sb_t *sb = malloc(sizeof(sb_t)); uint16_t addr = device_get_config_int("addr"); memset(sb, 0, sizeof(sb_t)); @@ -455,14 +943,19 @@ void *sb_1_init() sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); + /* CMS I/O handler is activated on the dedicated sound_cms module + DSP I/O handler is activated in sb_dsp_setaddr */ io_sethandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); - sound_add_handler(sb_get_buffer_opl2, sb); + sound_add_handler(sb_get_buffer_sb2, sb); return sb; } void *sb_15_init() { + /*sb1/2 port mappings, 210h to 260h in 10h steps + 2x0 to 2x3 -> CMS chip + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip*/ sb_t *sb = malloc(sizeof(sb_t)); uint16_t addr = device_get_config_int("addr"); memset(sb, 0, sizeof(sb_t)); @@ -472,15 +965,19 @@ void *sb_15_init() sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); + /* CMS I/O handler is activated on the dedicated sound_cms module + DSP I/O handler is activated in sb_dsp_setaddr */ io_sethandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); - sound_add_handler(sb_get_buffer_opl2, sb); + sound_add_handler(sb_get_buffer_sb2, sb); return sb; } void *sb_mcv_init() { + /*sb1/2 port mappings, 210h to 260h in 10h steps + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip*/ sb_t *sb = malloc(sizeof(sb_t)); memset(sb, 0, sizeof(sb_t)); @@ -489,8 +986,8 @@ void *sb_mcv_init() sb_dsp_setaddr(&sb->dsp, 0);//addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); - sound_add_handler(sb_get_buffer_opl2, sb); + sound_add_handler(sb_get_buffer_sb2, sb); + /* I/O handlers activated in sb_mcv_write */ mca_add(sb_mcv_read, sb_mcv_write, sb); sb->pos_regs[0] = 0x84; sb->pos_regs[1] = 0x50; @@ -498,6 +995,13 @@ void *sb_mcv_init() } void *sb_2_init() { + /*sb2 port mappings. 220h or 240h. + 2x0 to 2x3 -> CMS chip + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip + "CD version" also uses 250h or 260h for + 2x0 to 2x3 -> CDROM interface + 2x4 to 2x5 -> Mixer interface*/ sb_t *sb = malloc(sizeof(sb_t)); uint16_t addr = device_get_config_int("addr"); memset(sb, 0, sizeof(sb_t)); @@ -507,15 +1011,32 @@ void *sb_2_init() sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); + sb_ct1335_mixer_reset(sb); + /* CMS I/O handler is activated on the dedicated sound_cms module + DSP I/O handler is activated in sb_dsp_setaddr */ io_sethandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); - sound_add_handler(sb_get_buffer_opl2, sb); + + int mixer_addr = device_get_config_int("mixaddr"); + if (mixer_addr > 0) + { + io_sethandler(mixer_addr+4, 0x0002, sb_ct1335_mixer_read, NULL, NULL, sb_ct1335_mixer_write, NULL, NULL, sb); + sound_add_handler(sb_get_buffer_sb2_mixer, sb); + } + else + sound_add_handler(sb_get_buffer_sb2, sb); + return sb; } void *sb_pro_v1_init() { + /*sbpro port mappings. 220h or 240h. + 2x0 to 2x3 -> FM chip, Left and Right (9*2 voices) + 2x4 to 2x5 -> Mixer interface + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip (9 voices) + 2x0+10 to 2x0+13 CDROM interface.*/ sb_t *sb = malloc(sizeof(sb_t)); uint16_t addr = device_get_config_int("addr"); memset(sb, 0, sizeof(sb_t)); @@ -525,66 +1046,65 @@ void *sb_pro_v1_init() sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); + sb_ct1345_mixer_reset(sb); + /* DSP I/O handler is activated in sb_dsp_setaddr */ io_sethandler(addr+0, 0x0002, opl2_l_read, NULL, NULL, opl2_l_write, NULL, NULL, &sb->opl); io_sethandler(addr+2, 0x0002, opl2_r_read, NULL, NULL, opl2_r_write, NULL, NULL, &sb->opl); io_sethandler(addr+8, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); - io_sethandler(addr+4, 0x0002, sb_pro_mixer_read, NULL, NULL, sb_pro_mixer_write, NULL, NULL, sb); - sound_add_handler(sb_get_buffer_opl2, sb); - - sb->mixer.regs[0x22] = 0xff; - sb->mixer.regs[0x04] = 0xff; - sb->mixer.regs[0x26] = 0xff; - sb->mixer.regs[0xe] = 0; + io_sethandler(addr+4, 0x0002, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, sb); + sound_add_handler(sb_get_buffer_sbpro, sb); return sb; } void *sb_pro_v2_init() { + /*sbpro port mappings. 220h or 240h. + 2x0 to 2x3 -> FM chip (18 voices) + 2x4 to 2x5 -> Mixer interface + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip (9 voices) + 2x0+10 to 2x0+13 CDROM interface.*/ sb_t *sb = malloc(sizeof(sb_t)); - uint16_t addr = device_get_config_int("addr"); memset(sb, 0, sizeof(sb_t)); + uint16_t addr = device_get_config_int("addr"); sb->opl_emu = device_get_config_int("opl_emu"); opl3_init(&sb->opl, sb->opl_emu); sb_dsp_init(&sb->dsp, SBPRO2); sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); - sb_mixer_init(&sb->mixer); + sb_ct1345_mixer_reset(sb); + /* DSP I/O handler is activated in sb_dsp_setaddr */ io_sethandler(addr+0, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(addr+8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); - io_sethandler(addr+4, 0x0002, sb_pro_mixer_read, NULL, NULL, sb_pro_mixer_write, NULL, NULL, sb); - sound_add_handler(sb_get_buffer_opl3, sb); - - sb->mixer.regs[0x22] = 0xff; - sb->mixer.regs[0x04] = 0xff; - sb->mixer.regs[0x26] = 0xff; - sb->mixer.regs[0xe] = 0; + io_sethandler(addr+4, 0x0002, sb_ct1345_mixer_read, NULL, NULL, sb_ct1345_mixer_write, NULL, NULL, sb); + sound_add_handler(sb_get_buffer_sbpro, sb); return sb; } void *sb_pro_mcv_init() { + /*sbpro port mappings. 220h or 240h. + 2x0 to 2x3 -> FM chip, Left and Right (18 voices) + 2x4 to 2x5 -> Mixer interface + 2x6, 2xA, 2xC, 2xE -> DSP chip + 2x8, 2x9, 388 and 389 FM chip (9 voices)*/ sb_t *sb = malloc(sizeof(sb_t)); memset(sb, 0, sizeof(sb_t)); sb->opl_emu = device_get_config_int("opl_emu"); opl3_init(&sb->opl, sb->opl_emu); sb_dsp_init(&sb->dsp, SBPRO2); - sb_mixer_init(&sb->mixer); - io_sethandler(0x0388, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); - sound_add_handler(sb_get_buffer_opl3, sb); - - sb->mixer.regs[0x22] = 0xff; - sb->mixer.regs[0x04] = 0xff; - sb->mixer.regs[0x26] = 0xff; - sb->mixer.regs[0xe] = 0; + sb_ct1345_mixer_reset(sb); + /* I/O handlers activated in sb_mcv_write */ + sound_add_handler(sb_get_buffer_sbpro, sb); + /* I/O handlers activated in sb_pro_mcv_write */ mca_add(sb_pro_mcv_read, sb_pro_mcv_write, sb); sb->pos_regs[0] = 0x03; sb->pos_regs[1] = 0x51; @@ -597,31 +1117,20 @@ void *sb_16_init() sb_t *sb = malloc(sizeof(sb_t)); memset(sb, 0, sizeof(sb_t)); + uint16_t addr = device_get_config_int("addr"); sb->opl_emu = device_get_config_int("opl_emu"); opl3_init(&sb->opl, sb->opl_emu); 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); + sb_dsp_setaddr(&sb->dsp, addr); + // TODO: irq and dma options too? + sb_ct1745_mixer_reset(sb); + io_sethandler(addr, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); + io_sethandler(addr+8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0004, 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_get_buffer_opl3, sb); + io_sethandler(addr+4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, sb_ct1745_mixer_write, NULL, NULL, sb); + sound_add_handler(sb_get_buffer_sb16, sb); mpu401_uart_init(&sb->mpu, 0x330); - 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; } @@ -637,39 +1146,41 @@ void *sb_awe32_init() int onboard_ram = device_get_config_int("onboard_ram"); memset(sb, 0, sizeof(sb_t)); + uint16_t addr = device_get_config_int("addr"); + uint16_t emu_addr = device_get_config_int("emu_addr"); sb->opl_emu = device_get_config_int("opl_emu"); opl3_init(&sb->opl, sb->opl_emu); sb_dsp_init(&sb->dsp, SB16 + 1); - 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); + sb_dsp_setaddr(&sb->dsp, addr); + // TODO: irq and dma options too? + sb_ct1745_mixer_reset(sb); + io_sethandler(addr, 0x0004, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); + io_sethandler(addr+8, 0x0002, opl3_read, NULL, NULL, opl3_write, NULL, NULL, &sb->opl); io_sethandler(0x0388, 0x0004, 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); + io_sethandler(addr+4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_emu8k, sb); - mpu401_uart_init(&sb->mpu, 0x330); - emu8k_init(&sb->emu8k, onboard_ram); + mpu401_uart_init(&sb->mpu, 0x330); + emu8k_init(&sb->emu8k, emu_addr, onboard_ram); - 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; + sb_dsp_close(&sb->dsp); + #ifdef SB_DSP_RECORD_DEBUG + if (soundfsb != 0) + { + fclose(soundfsb); + soundfsb=0; + } + if (soundfsbin!= 0) + { + fclose(soundfsbin); + soundfsbin=0; + } + #endif free(sb); } @@ -680,7 +1191,7 @@ void sb_awe32_close(void *p) emu8k_close(&sb->emu8k); - free(sb); + sb_close(sb); } void sb_speed_changed(void *p) @@ -702,7 +1213,97 @@ static device_config_t sb_config[] = { .name = "addr", .description = "Address", - .type = CONFIG_BINARY, + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "0x210", + .value = 0x210 + }, + { + .description = "0x220", + .value = 0x220 + }, + { + .description = "0x230", + .value = 0x230 + }, + { + .description = "0x240", + .value = 0x240 + }, + { + .description = "0x250", + .value = 0x250 + }, + { + .description = "0x260", + .value = 0x260 + }, + { + .description = "" + } + }, + .default_int = 0x220 + }, + { + .name = "irq", + .description = "IRQ", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "IRQ 2", + .value = 2 + }, + { + .description = "IRQ 3", + .value = 3 + }, + { + .description = "IRQ 5", + .value = 5 + }, + { + .description = "IRQ 7", + .value = 7 + }, + { + .description = "" + } + }, + .default_int = 7 + }, + { + .name = "dma", + .description = "DMA", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "DMA 1", + .value = 1 + }, + { + .description = "DMA 3", + .value = 3 + }, + { + .description = "" + } + }, + .default_int = 1 + }, + { + .type = -1 + } +}; + +static device_config_t sb2_config[] = +{ + { + .name = "addr", + .description = "Address", .type = CONFIG_SELECTION, .selection = { @@ -720,6 +1321,30 @@ static device_config_t sb_config[] = }, .default_int = 0x220 }, + { + .name = "mixaddr", + .description = "Mixer Address", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "No mixer", + .value = 0 + }, + { + .description = "0x250", + .value = 0x250 + }, + { + .description = "0x260", + .value = 0x260 + }, + { + .description = "" + } + }, + .default_int = 0 + }, { .name = "irq", .description = "IRQ", @@ -829,7 +1454,6 @@ static device_config_t sb_pro_v1_config[] = { .name = "addr", .description = "Address", - .type = CONFIG_BINARY, .type = CONFIG_SELECTION, .selection = { @@ -905,7 +1529,6 @@ static device_config_t sb_pro_v2_config[] = { .name = "addr", .description = "Address", - .type = CONFIG_BINARY, .type = CONFIG_SELECTION, .selection = { @@ -1019,6 +1642,34 @@ static device_config_t sb_pro_mcv_config[] = static device_config_t sb_16_config[] = { + { + .name = "addr", + .description = "Address", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "0x220", + .value = 0x220 + }, + { + .description = "0x240", + .value = 0x240 + }, + { + .description = "0x260", + .value = 0x260 + }, + { + .description = "0x280", + .value = 0x280 + }, + { + .description = "" + } + }, + .default_int = 0x220 + }, { .name = "midi", .description = "MIDI out device", @@ -1050,7 +1701,62 @@ static device_config_t sb_16_config[] = static device_config_t sb_awe32_config[] = { { - .name = "midi", + .name = "addr", + .description = "Address", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "0x220", + .value = 0x220 + }, + { + .description = "0x240", + .value = 0x240 + }, + { + .description = "0x260", + .value = 0x260 + }, + { + .description = "0x280", + .value = 0x280 + }, + { + .description = "" + } + }, + .default_int = 0x220 + }, + { + .name = "emu_addr", + .description = "EMU8000 Address", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "0x620", + .value = 0x620 + }, + { + .description = "0x640", + .value = 0x640 + }, + { + .description = "0x660", + .value = 0x660 + }, + { + .description = "0x680", + .value = 0x680 + }, + { + .description = "" + } + }, + .default_int = 0x620 + }, + { .name = "midi", .description = "MIDI out device", .type = CONFIG_MIDI, .default_int = 0 @@ -1155,7 +1861,7 @@ device_t sb_2_device = sb_speed_changed, NULL, sb_add_status_info, - sb_config + sb2_config }; device_t sb_pro_v1_device = { @@ -1210,7 +1916,7 @@ device_t sb_awe32_device = "Sound Blaster AWE32", 0, sb_awe32_init, - sb_close, + sb_awe32_close, sb_awe32_available, sb_speed_changed, NULL, diff --git a/src/sound_sb_dsp.c b/src/sound_sb_dsp.c index b0dbe1d..6bb1639 100644 --- a/src/sound_sb_dsp.c +++ b/src/sound_sb_dsp.c @@ -16,10 +16,22 @@ #include "sound_sb_dsp.h" #include "timer.h" +/*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/ +#define SB_DSP_REC_SAFEFTY_MARGIN 4096 + void pollsb(void *p); void sb_poll_i(void *p); +//#define SB_DSP_RECORD_DEBUG +//#define SB_TEST_RECORDING_SAW +#ifdef SB_DSP_RECORD_DEBUG +FILE* soundf = 0/*NULL*/; +#endif + +#ifdef SB_TEST_RECORDING_SAW +int counttest; +#endif static int sbe2dat[4][9] = { { 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 }, @@ -133,9 +145,20 @@ void sb_dsp_reset(sb_dsp_t *dsp) dsp->sbreset = 0; dsp->sbenable = dsp->sb_enable_i = dsp->sb_count_i = 0; + dsp->record_pos_read=0; + dsp->record_pos_write=SB_DSP_REC_SAFEFTY_MARGIN; + picintc(1 << dsp->sb_irqnum); dsp->asp_data_len = 0; + + #ifdef SB_DSP_RECORD_DEBUG + if (soundf != 0) + { + fclose(soundf); + soundf=0; + } + #endif } void sb_doreset(sb_dsp_t *dsp) @@ -205,8 +228,8 @@ void sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len if (dsp->sb_8_enable && dsp->sb_8_output) dsp->sb_8_enable = 0; dsp->sb_16_output = 1; timer_process(); - dsp->sbenable = dsp->sb_16_enable; - timer_update_outstanding(); + dsp->sbenable = dsp->sb_16_enable; + timer_update_outstanding(); // pclog("Start 16-bit DMA addr %06X len %04X\n",dma16.ac[1]+(dma16.page[1]<<16),len); } } @@ -215,6 +238,13 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l { if (dma8) { +#ifdef SB_TEST_RECORDING_SAW + switch (dsp->sb_8_format) + { + case 00:case 20:counttest=0x80;break; + case 10:case 30:counttest=0;break; + } +#endif dsp->sb_8_length = len; dsp->sb_8_format = format; dsp->sb_8_autoinit = autoinit; @@ -229,6 +259,13 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l } else { +#ifdef SB_TEST_RECORDING_SAW + switch (dsp->sb_16_format) + { + case 00:case 20:counttest=0x8000;break; + case 10:case 30:counttest=0;break; + } +#endif dsp->sb_16_length = len; dsp->sb_16_format = format; dsp->sb_16_autoinit = autoinit; @@ -241,6 +278,16 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l timer_update_outstanding(); // pclog("Start 16-bit input DMA addr %06X len %04X\n",dma.ac[1]+(dma.page[1]<<16),len); } + memset(dsp->record_buffer,0,sizeof(dsp->record_buffer)); + + #ifdef SB_DSP_RECORD_DEBUG + if (soundf != 0) + { + fclose(soundf); + soundf=0; + } + #endif + } int sb_8_read_dma(sb_dsp_t *dsp) @@ -250,14 +297,23 @@ int sb_8_read_dma(sb_dsp_t *dsp) void sb_8_write_dma(sb_dsp_t *dsp, uint8_t val) { dma_channel_write(dsp->sb_8_dmanum, val); +#ifdef SB_DSP_RECORD_DEBUG + if (!soundf) soundf=fopen("sound_dsp.pcm","wb"); + fwrite(&val,1,1,soundf); +#endif } uint16_t sb_16_read_dma(sb_dsp_t *dsp) { - return dma_channel_read(5); + return dma_channel_read(dsp->sb_16_dmanum); } -void sb_16_write_dma(sb_dsp_t *dsp, uint16_t val) +int sb_16_write_dma(sb_dsp_t *dsp, uint16_t val) { - dma_channel_write(5, val); + int ret = dma_channel_write(dsp->sb_16_dmanum, val); +#ifdef SB_DSP_RECORD_DEBUG + if (!soundf) soundf=fopen("sound_dsp.pcm","wb"); + fwrite(&val,2,1,soundf); +#endif + return (ret == DMA_NODATA); } void sb_dsp_setirq(sb_dsp_t *dsp, int irq) @@ -270,6 +326,10 @@ void sb_dsp_setdma8(sb_dsp_t *dsp, int dma) dsp->sb_8_dmanum = dma; } +void sb_dsp_setdma16(sb_dsp_t *dsp, int dma) +{ + dsp->sb_16_dmanum = dma; +} void sb_exec_command(sb_dsp_t *dsp) { int temp,c; @@ -310,7 +370,19 @@ void sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_length--; break; case 0x20: /*8-bit direct input*/ - sb_add_data(dsp, 0); + sb_add_data(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8) ^0x80); + /*Due to the current implementation, I need to emulate a samplerate, even if this + * mode does not imply such samplerate. Position is increased in sb_poll_i*/ + if (dsp->sb_enable_i==0) + { + dsp->sb_timei = 256 - 22; + dsp->sblatchi = TIMER_USEC * 22; + temp = 1000000 / 22; + dsp->sb_freq = temp; + timer_process(); + dsp->sb_enable_i = 1; + timer_update_outstanding(); + } break; case 0x24: /*8-bit single cycle DMA input*/ sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); @@ -373,28 +445,30 @@ void sb_exec_command(sb_dsp_t *dsp) case 0x80: /*Pause DAC*/ dsp->sb_pausetime = dsp->sb_data[0] + (dsp->sb_data[1] << 8); // pclog("SB pause %04X\n",sb_pausetime); - timer_process(); + timer_process(); dsp->sbenable = 1; timer_update_outstanding(); break; case 0x90: /*High speed 8-bit autoinit DMA output*/ - if (dsp->sb_type < SB2) break; + if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break; sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen); break; case 0x91: /*High speed 8-bit single cycle DMA output*/ - if (dsp->sb_type < SB2) break; + if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break; sb_start_dma(dsp, 1, 0, 0, dsp->sb_8_autolen); break; case 0x98: /*High speed 8-bit autoinit DMA input*/ - if (dsp->sb_type < SB2) break; + if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break; sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_8_autolen); break; case 0x99: /*High speed 8-bit single cycle DMA input*/ - if (dsp->sb_type < SB2) break; + if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break; sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_8_autolen); break; case 0xA0: /*Set input mode to mono*/ case 0xA8: /*Set input mode to stereo*/ + if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break; + //TODO: Implement. 3.xx-only command. break; case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: /*16-bit DMA output*/ @@ -424,9 +498,17 @@ void sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_pause = 1; break; case 0xD1: /*Speaker on*/ + if (dsp->sb_type < SB15 ) + dsp->sb_8_pause = 1; + else if ( dsp->sb_type < SB16 ) + dsp->muted = 0; dsp->sb_speaker = 1; break; case 0xD3: /*Speaker off*/ + if (dsp->sb_type < SB15 ) + dsp->sb_8_pause = 1; + else if ( dsp->sb_type < SB16 ) + dsp->muted = 1; dsp->sb_speaker = 0; break; case 0xD4: /*Continue 8-bit DMA*/ @@ -481,6 +563,10 @@ void sb_exec_command(sb_dsp_t *dsp) // pclog("Trigger IRQ\n"); sb_irq(dsp, 1); break; + case 0xF3: /*Trigger 16-bit IRQ*/ +// pclog("Trigger IRQ\n"); + sb_irq(dsp, 0); + break; case 0xE7: /*???*/ case 0xFA: /*???*/ break; @@ -517,6 +603,22 @@ void sb_exec_command(sb_dsp_t *dsp) break; // default: // fatal("Exec bad SB command %02X\n",sb_command); + + + /*TODO: Some more data about the DSP registeres + * http://the.earth.li/~tfm/oldpage/sb_dsp.html + * http://www.synchrondata.com/pheaven/www/area19.htm + * http://www.dcee.net/Files/Programm/Sound/ + 0E3h DSP Copyright SBPro2??? + 0F0h Sine Generator SB + 0F1h DSP Auxiliary Status (Obsolete) SB-Pro2 + 0F2h IRQ Request, 8-bit SB + 0F3h IRQ Request, 16-bit SB16 + 0FBh DSP Status SB16 + 0FCh DSP Auxiliary Status SB16 + 0FDh DSP Command Status SB16 + */ + } } @@ -570,7 +672,6 @@ void sb_write(uint16_t a, uint8_t v, void *priv) uint8_t sb_read(uint16_t a, void *priv) { sb_dsp_t *dsp = (sb_dsp_t *)priv; -// if (a==0x224) output=1; // pclog("sb_read : Read soundblaster %04X %04X:%04X\n",a,CS,pc); switch (a & 0xf) { @@ -613,8 +714,10 @@ void sb_dsp_init(sb_dsp_t *dsp, int type) { dsp->sb_type = type; + // Default values. Use sb_dsp_setxxx() methods to change. dsp->sb_irqnum = 7; dsp->sb_8_dmanum = 1; + dsp->sb_16_dmanum = 5; sb_doreset(dsp); @@ -670,7 +773,7 @@ void pollsb(void *p) dsp->sbleftright = !dsp->sbleftright; } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; dsp->sb_8_length--; break; case 0x10: /*Mono signed*/ @@ -685,7 +788,7 @@ void pollsb(void *p) dsp->sbleftright = !dsp->sbleftright; } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; dsp->sb_8_length--; break; case 0x20: /*Stereo unsigned*/ @@ -737,7 +840,7 @@ void pollsb(void *p) dsp->sbleftright = !dsp->sbleftright; } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; case ADPCM_26: @@ -771,7 +874,7 @@ void pollsb(void *p) dsp->sbleftright = !dsp->sbleftright; } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; case ADPCM_2: @@ -801,7 +904,7 @@ void pollsb(void *p) dsp->sbleftright = !dsp->sbleftright; } else - dsp->sbdatl = dsp->sbdatr = dsp->sbdat; + dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; // default: @@ -839,7 +942,6 @@ void pollsb(void *p) dsp->sbdatr = sb_16_read_dma(dsp); dsp->sb_16_length -= 2; break; - // default: // fatal("Unrecognised SB 16-bit format %02X\n",sb_16_format); } @@ -867,32 +969,57 @@ void pollsb(void *p) void sb_poll_i(void *p) { sb_dsp_t *dsp = (sb_dsp_t *)p; - + int processed=0; dsp->sb_count_i += dsp->sblatchi; // pclog("PollSBi %i %i %i %i\n",sb_8_enable,sb_8_pause,sb_pausetime,sb_8_output); if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) { switch (dsp->sb_8_format) { - case 0x00: /*Mono unsigned*/ - sb_8_write_dma(dsp, 0x80); +#ifdef SB_TEST_RECORDING_SAW + case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/ + case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/ + sb_8_write_dma(dsp, counttest); + counttest+=0x10; + counttest&=0xFF; dsp->sb_8_length--; break; - case 0x10: /*Mono signed*/ - sb_8_write_dma(dsp, 0x00); + case 0x20: /*Unsigned stereo*/ + case 0x30: /*Signed stereo*/ + sb_8_write_dma(dsp, counttest); + sb_8_write_dma(dsp, counttest); + counttest+=0x10; + counttest&=0xFF; + dsp->sb_8_length -= 2; + break; +#else + case 0x00: /*Mono unsigned As the manual says, only the left channel is recorded*/ + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8) ^0x80); dsp->sb_8_length--; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; + break; + case 0x10: /*Mono signed As the manual says, only the left channel is recorded*/ + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8)); + dsp->sb_8_length--; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; break; case 0x20: /*Stereo unsigned*/ - sb_8_write_dma(dsp, 0x80); - sb_8_write_dma(dsp, 0x80); + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8)^0x80); + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read+1]>>8)^0x80); dsp->sb_8_length -= 2; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; break; case 0x30: /*Stereo signed*/ - sb_8_write_dma(dsp, 0x00); - sb_8_write_dma(dsp, 0x00); + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8)); + sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read+1]>>8)); dsp->sb_8_length -= 2; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; break; - +#endif // default: // fatal("Unrecognised SB 8-bit input format %02X\n",sb_8_format); } @@ -901,33 +1028,65 @@ void sb_poll_i(void *p) { // pclog("Input DMA over %i\n",sb_8_autoinit); if (dsp->sb_8_autoinit) dsp->sb_8_length = dsp->sb_8_autolen; - else dsp->sb_8_enable = dsp->sbenable = 0; + else dsp->sb_8_enable = dsp->sb_enable_i = 0; sb_irq(dsp, 1); } + processed=1; } if (dsp->sb_16_enable && !dsp->sb_16_pause && dsp->sb_pausetime < 0 && !dsp->sb_16_output) { switch (dsp->sb_16_format) { - case 0x00: /*Unsigned mono*/ - sb_16_write_dma(dsp, 0x8000); - dsp->sb_16_length--; - break; - case 0x10: /*Signed mono*/ - sb_16_write_dma(dsp, 0); +#ifdef SB_TEST_RECORDING_SAW + case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/ + case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/ + if (sb_16_write_dma(dsp, counttest)) + return; + counttest+=0x1000; + counttest&=0xFFFF; dsp->sb_16_length--; break; case 0x20: /*Unsigned stereo*/ - sb_16_write_dma(dsp, 0x8000); - sb_16_write_dma(dsp, 0x8000); + case 0x30: /*Signed stereo*/ + if (sb_16_write_dma(dsp, counttest)) + return; + sb_16_write_dma(dsp, counttest); + counttest+=0x1000; + counttest&=0xFFFF; dsp->sb_16_length -= 2; break; +#else + case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/ + if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]^0x8000)) + return; + dsp->sb_16_length--; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; + break; + case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/ + if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + return; + dsp->sb_16_length--; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; + break; + case 0x20: /*Unsigned stereo*/ + if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]^0x8000)) + return; + sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read+1]^0x8000); + dsp->sb_16_length -= 2; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; + break; case 0x30: /*Signed stereo*/ - sb_16_write_dma(dsp, 0); - sb_16_write_dma(dsp, 0); + if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read])) + return; + sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read+1]); dsp->sb_16_length -= 2; + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; break; - +#endif // default: // fatal("Unrecognised SB 16-bit input format %02X\n",sb_16_format); } @@ -936,20 +1095,43 @@ void sb_poll_i(void *p) { // pclog("16iDMA over %i\n",sb_16_autoinit); if (dsp->sb_16_autoinit) dsp->sb_16_length = dsp->sb_16_autolen; - else dsp->sb_16_enable = dsp->sbenable = 0; + else dsp->sb_16_enable = dsp->sb_enable_i = 0; sb_irq(dsp, 0); } + processed=1; + } + //Assume this is direct mode + if (!processed) + { + dsp->record_pos_read+=2; + dsp->record_pos_read&=0xFFFF; } } void sb_dsp_update(sb_dsp_t *dsp) { + /*this "if" implements two things: speaker on/off and buffer cleanup after stopping audio. */ + if (!dsp->sbenable || dsp->muted) + { + dsp->sbdatl=0; + dsp->sbdatr=0; + } for (; dsp->pos < sound_pos_global; dsp->pos++) { dsp->buffer[dsp->pos*2] = dsp->sbdatl; dsp->buffer[dsp->pos*2 + 1] = dsp->sbdatr; } } +void sb_dsp_close(sb_dsp_t *dsp) +{ + #ifdef SB_DSP_RECORD_DEBUG + if (soundf != 0) + { + fclose(soundf); + soundf=0; + } + #endif +} void sb_dsp_add_status_info(char *s, int max_len, sb_dsp_t *dsp) { diff --git a/src/sound_sb_dsp.h b/src/sound_sb_dsp.h index e945b40..3c100e5 100644 --- a/src/sound_sb_dsp.h +++ b/src/sound_sb_dsp.h @@ -5,11 +5,13 @@ typedef struct sb_dsp_t int sb_8_length, sb_8_format, sb_8_autoinit, sb_8_pause, sb_8_enable, sb_8_autolen, sb_8_output; int sb_8_dmanum; int sb_16_length, sb_16_format, sb_16_autoinit, sb_16_pause, sb_16_enable, sb_16_autolen, sb_16_output; + int sb_16_dmanum; int sb_pausetime; uint8_t sb_read_data[256]; int sb_read_wp, sb_read_rp; int sb_speaker; + int muted; int sb_data_stat; @@ -56,15 +58,20 @@ typedef struct sb_dsp_t int asp_data_len; int wb_time, wb_full; - + + int record_pos_read; + int record_pos_write; + int16_t record_buffer[0xFFFF]; int16_t buffer[MAXSOUNDBUFLEN * 2]; int pos; } sb_dsp_t; void sb_dsp_init(sb_dsp_t *dsp, int type); +void sb_dsp_close(sb_dsp_t *dsp); void sb_dsp_setirq(sb_dsp_t *dsp, int irq); void sb_dsp_setdma8(sb_dsp_t *dsp, int dma); +void sb_dsp_setdma16(sb_dsp_t *dsp, int dma); void sb_dsp_setaddr(sb_dsp_t *dsp, uint16_t addr); void sb_dsp_speed_changed(sb_dsp_t *dsp);