From 26d42adb1eba8b2b63af1f20a706f9a6e4bc95ff Mon Sep 17 00:00:00 2001 From: OBattler Date: Mon, 19 Oct 2020 01:02:40 +0200 Subject: [PATCH] CD-ROM CUE indexes are now respected for skip even if prestart is 0 (fixes CUE+BIN images that use indexes for pregap), CD Audio attenuation is now done per the specification, and proper sound card filtering and volume calculation now applies to CD Audio. --- src/cdrom/cdrom_image_backend.c | 3 +- src/include/86box/snd_ad1848.h | 4 +- src/include/86box/sound.h | 2 + src/sound/snd_ad1848.c | 28 ++++++----- src/sound/snd_audiopci.c | 27 ++++++++--- src/sound/snd_sb.c | 84 +++++++++++++++++++++++++++++---- src/sound/sound.c | 53 ++++++++++++++------- 7 files changed, 155 insertions(+), 46 deletions(-) diff --git a/src/cdrom/cdrom_image_backend.c b/src/cdrom/cdrom_image_backend.c index 4552ddc00..96f60ed61 100644 --- a/src/cdrom/cdrom_image_backend.c +++ b/src/cdrom/cdrom_image_backend.c @@ -718,7 +718,8 @@ cdi_add_track(cd_img_t *cdi, track_t *cur, uint64_t *shift, uint64_t prestart, u uint64_t skip, temp; track_t *prev = NULL; - if (prestart > 0) { + /* Skip *MUST* be calculated even if prestart is 0. */ + if (prestart >= 0) { if (prestart > cur->start) return 0; skip = cur->start - prestart; diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index 9c71f084b..82a7721f3 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -14,7 +14,9 @@ typedef struct ad1848_t int count; int16_t out_l, out_r; - + + uint32_t cd_vol_l, cd_vol_r; + int enable; int irq, dma; diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index a47d97191..a1fdb4b42 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -45,6 +45,8 @@ extern int sound_card_current; extern void sound_add_handler(void (*get_buffer)(int32_t *buffer, \ int len, void *p), void *p); +extern void sound_set_cd_audio_filter(void (*filter)(int channel, \ + float *buffer, void *p), void *p); extern int sound_card_available(int card); extern char *sound_card_getname(int card); diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 4953da1a0..7aa699b16 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -56,7 +56,6 @@ void ad1848_write(uint16_t addr, uint8_t val, void *p) { ad1848_t *ad1848 = (ad1848_t *)p; double freq; - uint32_t new_cd_vol_l, new_cd_vol_r; switch (addr & 3) { case 0: /*Index*/ @@ -126,18 +125,13 @@ void ad1848_write(uint16_t addr, uint8_t val, void *p) if (ad1848->type == AD1848_TYPE_CS4231) { /* TODO: configure CD volume for CS4248/AD1848 too */ if (ad1848->regs[0x12] & 0x80) - new_cd_vol_l = 0; + ad1848->cd_vol_l = 0; else - new_cd_vol_l = ad1848_vols_5bits_aux_gain[ad1848->regs[0x12] & 0x1f]; + ad1848->cd_vol_l = ad1848_vols_5bits_aux_gain[ad1848->regs[0x12] & 0x1f]; if (ad1848->regs[0x13] & 0x80) - new_cd_vol_r = 0; + ad1848->cd_vol_r = 0; else - new_cd_vol_r = ad1848_vols_5bits_aux_gain[ad1848->regs[0x13] & 0x1f]; - - /* Apparently there is no master volume to modulate here - (The windows mixer just adjusts all registers at the same - time when the master slider is adjusted) */ - sound_set_cd_volume(new_cd_vol_l, new_cd_vol_r); + ad1848->cd_vol_r = ad1848_vols_5bits_aux_gain[ad1848->regs[0x13] & 0x1f]; } break; case 2: @@ -223,10 +217,20 @@ static void ad1848_poll(void *p) else { ad1848->out_l = ad1848->out_r = 0; - sound_set_cd_volume(0, 0); + ad1848->cd_vol_l = ad1848->cd_vol_r = 0; } } +static void ad1848_filter_cd_audio(int channel, float *buffer, void *p) +{ + ad1848_t *ad1848 = (ad1848_t *)p; + int32_t c; + uint32_t volume = channel ? ad1848->cd_vol_r : ad1848->cd_vol_l; + + c = (((int32_t) buffer) * volume) >> 16; + *buffer = (float) c; +} + void ad1848_init(ad1848_t *ad1848, int type) { int c; @@ -294,4 +298,6 @@ void ad1848_init(ad1848_t *ad1848, int type) ad1848->type = type; timer_add(&ad1848->timer_count, ad1848_poll, ad1848, 0); + + sound_set_cd_audio_filter(ad1848_filter_cd_audio, ad1848); } diff --git a/src/sound/snd_audiopci.c b/src/sound/snd_audiopci.c index 250de39d1..e070e1dc5 100644 --- a/src/sound/snd_audiopci.c +++ b/src/sound/snd_audiopci.c @@ -611,12 +611,6 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p) es1371->master_vol_r = codec_attn[0x1f - (val & 0x1f)]; } break; - case 0x12: /*CD volume*/ - if (val & 0x8000) - sound_set_cd_volume(0, 0); - else - sound_set_cd_volume(codec_attn[0x1f - ((val >> 8) & 0x1f)] * 2, codec_attn[0x1f - (val & 0x1f)] * 2); - break; } } break; @@ -1145,7 +1139,7 @@ static void es1371_update(es1371_t *es1371) l = (es1371->dac[0].out_l * es1371->dac[0].vol_l) >> 12; l += ((es1371->dac[1].out_l * es1371->dac[1].vol_l) >> 12); r = (es1371->dac[0].out_r * es1371->dac[0].vol_r) >> 12; - r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12); + r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12); l >>= 1; r >>= 1; @@ -1270,6 +1264,24 @@ static void es1371_get_buffer(int32_t *buffer, int len, void *p) es1371->pos = 0; } +static void es1371_filter_cd_audio(int channel, float *buffer, void *p) +{ + es1371_t *es1371 = (es1371_t *)p; + int32_t c; + int32_t volume, val = (int32_t) (uint32_t) es1371->codec_regs[0x12]; + int master = channel ? es1371->master_vol_r : es1371->master_vol_l; + + if (val & 0x8000) + volume = 0; + else + volume = channel ? codec_attn[0x1f - (val & 0x1f)] : codec_attn[0x1f - ((val >> 8) & 0x1f)]; + + c = (((int32_t) buffer) * volume) >> 15; + c = (c * master) >> 15; + + *buffer = (float) c; +} + static inline double sinc(double x) { return sin(M_PI * x) / (M_PI * x); @@ -1312,6 +1324,7 @@ static void *es1371_init(const device_t *info) memset(es1371, 0, sizeof(es1371_t)); sound_add_handler(es1371_get_buffer, es1371); + sound_set_cd_audio_filter(es1371_filter_cd_audio, es1371); es1371->card = pci_add_card(info->local ? PCI_ADD_SOUND : PCI_ADD_NORMAL, es1371_pci_read, es1371_pci_write, es1371); diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 38d96cd61..37e09a5e1 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -123,6 +123,14 @@ static void sb_get_buffer_sb2(int32_t *buffer, int len, void *p) sb->dsp.pos = 0; } +static void sb2_filter_cd_audio(int channel, float *buffer, void *p) +{ + int32_t c; + + c = (int32_t)(((sb_iir(0, *buffer) / 1.3) * 65536) / 3) >> 16; + *buffer = (float) c; +} + static void sb_get_buffer_sb2_mixer(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; @@ -160,6 +168,17 @@ static void sb_get_buffer_sb2_mixer(int32_t *buffer, int len, void *p) sb->dsp.pos = 0; } +static void sb2_mixer_filter_cd_audio(int channel, float *buffer, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; + int32_t c; + + c = (int32_t)(((sb_iir(0, *buffer) / 1.3) * mixer->voice) / 3) >> 15; + c = (c * mixer->master) >> 15; + *buffer = (float) c; +} + void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; @@ -227,6 +246,22 @@ void sb_get_buffer_sbpro(int32_t *buffer, int len, void *p) sb->dsp.pos = 0; } +static void sbpro_filter_cd_audio(int channel, float *buffer, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + int32_t c; + int32_t voice = channel ? mixer->voice_r : mixer->voice_l; + int32_t master = channel ? mixer->master_r : mixer->master_l; + + if (mixer->output_filter) + c = (int32_t)(((sb_iir(channel, *buffer) / 1.3) * voice) / 3) >> 15; + else + c = ((int32_t)(((int32_t) *buffer) * voice) / 3) >> 15; + c = (c * master) >> 15; + *buffer = (float) c; +} + static void sb_get_buffer_sb16(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; @@ -308,6 +343,32 @@ int old_dsp_rec_pos=0; int buf_written=0; int last_crecord=0; #endif + +static void sb16_awe32_filter_cd_audio(int channel, float *buffer, void *p) +{ + sb_t *sb = (sb_t *)p; + sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + int32_t c; + int32_t voice = channel ? mixer->voice_r : mixer->voice_l; + int32_t master = channel ? mixer->master_r : mixer->master_l; + int32_t bass = channel ? mixer->bass_r : mixer->bass_l; + int32_t treble = channel ? mixer->treble_r : mixer->treble_l; + int32_t output_gain = channel ? mixer->output_gain_R : mixer->output_gain_L; + + c = ((int32_t)(low_fir_sb16(channel, *buffer) * voice) / 3) >> 15; + c = (c * master) >> 15; + + if (bass != 8 || treble != 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 (bass>8) c += (int32_t)(low_iir(channel, (float)c)*sb_bass_treble_4bits[bass]); + if (treble>8) c += (int32_t)(high_iir(channel, (float)c)*sb_bass_treble_4bits[treble]); + if (bass<8) c = (int32_t)((c )*sb_bass_treble_4bits[bass] + low_cut_iir(channel, (float)c)*(1.f-sb_bass_treble_4bits[bass])); + if (treble<8) c = (int32_t)((c )*sb_bass_treble_4bits[treble] + high_cut_iir(channel, (float)c)*(1.f-sb_bass_treble_4bits[treble])); + } + + *buffer = (float) (c << output_gain); +} + static void sb_get_buffer_emu8k(int32_t *buffer, int len, void *p) { sb_t *sb = (sb_t *)p; @@ -460,9 +521,6 @@ void sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *p) 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) / 32768, - ((uint32_t)mixer->master * (uint32_t)mixer->cd) / 32768); } } @@ -582,8 +640,6 @@ void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *p) } /* TODO: pcspeaker volume? Or is it not worth? */ - sound_set_cd_volume(((uint32_t)mixer->master_l * (uint32_t)mixer->cd_l) / 32768, - ((uint32_t)mixer->master_r * (uint32_t)mixer->cd_r) / 32768); } } @@ -785,8 +841,6 @@ void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *p) mixer->treble_r = mixer->regs[0x45] >> 4; /*TODO: pcspeaker volume, with "output_selector" check? or better not? */ - sound_set_cd_volume(((uint32_t)mixer->master_l * (uint32_t)mixer->cd_l) / 32768, - ((uint32_t)mixer->master_r * (uint32_t)mixer->cd_r) / 32768); sb_log("sb_ct1745: Received register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); } } @@ -1070,6 +1124,7 @@ void *sb_1_init(const device_t *info) io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); } sound_add_handler(sb_get_buffer_sb2, sb); + sound_set_cd_audio_filter(sb2_filter_cd_audio, sb); if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &sb->dsp); @@ -1100,6 +1155,7 @@ void *sb_15_init(const device_t *info) io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &sb->opl); } sound_add_handler(sb_get_buffer_sb2, sb); + sound_set_cd_audio_filter(sb2_filter_cd_audio, sb); if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &sb->dsp); @@ -1123,6 +1179,7 @@ void *sb_mcv_init(const device_t *info) sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); sb_dsp_setdma8(&sb->dsp, device_get_config_int("dma")); sound_add_handler(sb_get_buffer_sb2, sb); + sound_set_cd_audio_filter(sb2_filter_cd_audio, sb); /* I/O handlers activated in sb_mcv_write */ mca_add(sb_mcv_read, sb_mcv_write, sb_mcv_feedb, NULL, sb); sb->pos_regs[0] = 0x84; @@ -1174,9 +1231,11 @@ void *sb_2_init(const device_t *info) { 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); + sound_set_cd_audio_filter(sb2_mixer_filter_cd_audio, sb); + } else { + sound_add_handler(sb_get_buffer_sb2, sb); + sound_set_cd_audio_filter(sb2_filter_cd_audio, sb); + } if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &sb->dsp); @@ -1252,6 +1311,7 @@ void *sb_pro_v1_init(const device_t *info) } 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); + sound_set_cd_audio_filter(sbpro_filter_cd_audio, sb); if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &sb->dsp); @@ -1287,6 +1347,7 @@ void *sb_pro_v2_init(const device_t *info) } 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); + sound_set_cd_audio_filter(sbpro_filter_cd_audio, sb); if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &sb->dsp); @@ -1310,6 +1371,7 @@ void *sb_pro_mcv_init(const device_t *info) sb_ct1345_mixer_reset(sb); /* I/O handlers activated in sb_mcv_write */ sound_add_handler(sb_get_buffer_sbpro, sb); + sound_set_cd_audio_filter(sbpro_filter_cd_audio, sb); /* I/O handlers activated in sb_pro_mcv_write */ mca_add(sb_pro_mcv_read, sb_pro_mcv_write, sb_mcv_feedb, NULL, sb); @@ -1345,6 +1407,7 @@ void *sb_16_init(const device_t *info) } 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); + sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (mpu_addr) { sb->mpu = (mpu_t *) malloc(sizeof(mpu_t)); memset(sb->mpu, 0, sizeof(mpu_t)); @@ -1391,6 +1454,7 @@ void *sb_awe32_init(const device_t *info) } 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); + sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (mpu_addr) { sb->mpu = (mpu_t *) malloc(sizeof(mpu_t)); memset(sb->mpu, 0, sizeof(mpu_t)); diff --git a/src/sound/sound.c b/src/sound/sound.c index d240d60c9..a119a7959 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -16,6 +16,7 @@ * Copyright 2008-2020 Sarah Walker. * Copyright 2016-2020 Miran Grca. */ +#include #include #include #include @@ -76,6 +77,9 @@ static int cd_buf_update = CD_BUFLEN / SOUNDBUFLEN; static volatile int cdaudioon = 0; static int cd_thread_enable = 0; +static void (*filter_cd_audio)(int channel, float *buffer, void *p) = NULL; +static void *filter_cd_audio_p = NULL; + static const SOUND_CARD sound_cards[] = { @@ -241,8 +245,20 @@ sound_cd_thread(void *param) audio_vol_r = 255.0; } - audio_vol_l /= 511.0; - audio_vol_r /= 511.0; + /* Calculate attenuation per the specification. */ + if (audio_vol_l >= 255.0) + audio_vol_l = 1.0; + else if (audio_vol_l > 0.0) + audio_vol_l = (48.0 + (20.0 * log(audio_vol_l / 256.0))) / 48.0; + else + audio_vol_l = 0.0; + + if (audio_vol_r >= 255.0) + audio_vol_r = 1.0; + else if (audio_vol_r > 0.0) + audio_vol_r = (48.0 + (20.0 * log(audio_vol_r / 256.0))) / 48.0; + else + audio_vol_r = 0.0; if (cdrom[i].get_channel) { channel_select[0] = cdrom[i].get_channel(cdrom[i].priv, 0); @@ -256,16 +272,6 @@ sound_cd_thread(void *param) /*Apply ATAPI channel select*/ cd_buffer_temp[0] = cd_buffer_temp[1] = 0.0; -#if 0 - if (channel_select[0] & 1) - cd_buffer_temp[0] += ((float) cd_buffer[i][c]) * audio_vol_l; - if (channel_select[0] & 2) - cd_buffer_temp[1] += ((float) cd_buffer[i][c]) * audio_vol_l; - if (channel_select[1] & 1) - cd_buffer_temp[0] += ((float) cd_buffer[i][c + 1]) * audio_vol_r; - if (channel_select[1] & 2) - cd_buffer_temp[1] += ((float) cd_buffer[i][c + 1]) * audio_vol_r; -#else if ((audio_vol_l != 0.0) && (channel_select[0] != 0)) { if (channel_select[0] & 1) cd_buffer_temp[0] += ((float) cd_buffer[i][c]); /* Channel 0 => Port 0 */ @@ -283,11 +289,12 @@ sound_cd_thread(void *param) cd_buffer_temp[1] *= audio_vol_r; /* Multiply Port 1 by Port 1 volume */ } -#endif - /*Apply sound card CD volume*/ - cd_buffer_temp[0] *= ((float) cd_vol_l) / 32768.0; - cd_buffer_temp[1] *= ((float) cd_vol_r) / 32768.0; + /* Apply sound card CD volume and filters */ + if (filter_cd_audio != NULL) { + filter_cd_audio(0, &(cd_buffer_temp[0]), filter_cd_audio_p); + filter_cd_audio(1, &(cd_buffer_temp[0]), filter_cd_audio_p); + } if (sound_is_float) { cd_out_buffer[c] += (cd_buffer_temp[0] / 32768.0); @@ -376,6 +383,16 @@ sound_add_handler(void (*get_buffer)(int32_t *buffer, int len, void *p), void *p } +void +sound_set_cd_audio_filter(void (*filter)(int channel, float *buffer, void *p), void *p) +{ + if ((filter_cd_audio != NULL) || (filter == NULL)) { + filter_cd_audio = filter; + filter_cd_audio_p = p; + } +} + + void sound_poll(void *priv) { @@ -442,6 +459,10 @@ sound_reset(void) timer_add(&sound_poll_timer, sound_poll, NULL, 1); sound_handlers_num = 0; + memset(sound_handlers, 0x00, 8 * sizeof(sound_handler_t)); + + filter_cd_audio = NULL; + filter_cd_audio_p = NULL; sound_set_cd_volume(65535, 65535); }