From 69aa499d736a0fbbaa6539129792717759aefca1 Mon Sep 17 00:00:00 2001 From: "K.Ohta" Date: Fri, 29 Mar 2019 20:44:25 +0900 Subject: [PATCH] [VM][PC9801][SOUND] Playable PCM of PC-9801-86. --- source/src/vm/pc9801/fmsound.cpp | 308 +++++++++++++++++++++++++++++++++------ source/src/vm/pc9801/fmsound.h | 10 ++ source/src/vm/pc9801/pc9801.cpp | 6 + source/src/vm/pc9801/pc9801.h | 12 +- 4 files changed, 285 insertions(+), 51 deletions(-) diff --git a/source/src/vm/pc9801/fmsound.cpp b/source/src/vm/pc9801/fmsound.cpp index 438085fcd..831c551c9 100644 --- a/source/src/vm/pc9801/fmsound.cpp +++ b/source/src/vm/pc9801/fmsound.cpp @@ -79,13 +79,12 @@ void FMSOUND::reset() // Will move to initialize()? fifo_enabled = false; pcm_freq = 44100; - pcm_da_intleft = 256 << 7; + pcm_da_intleft = 128; // Q: Is clear FIFO? lrclock = false; fifo_direction = true; // PLAY fifo_int_status = false; fifo_int_flag = false; - fifo_reset_req = false; pcm_is_16bit = false; pcm_l_enabled = true; pcm_r_enabled = true; @@ -96,7 +95,7 @@ void FMSOUND::reset() play_wptr = 0; memset(play_pool, 0x00, sizeof(int32_t) * play_bufsize); - + mix_mod = 0; if(event_pcm >= 0) { cancel_event(this, event_pcm); event_pcm = -1; @@ -108,8 +107,9 @@ void FMSOUND::check_fifo_position() { #ifdef _PC98_HAVE_86PCM if(pcm_fifo != NULL) { - if(pcm_da_intleft == pcm_fifo->count()) { - if(fifo_int_flag) { + if(pcm_da_intleft >= pcm_fifo->count()) { + if(fifo_int_flag && !(fifo_int_status)) { + //out_debug_log("PCM INTERRUPT at %d\n", pcm_da_intleft); fifo_int_status = true; write_signals(&outputs_int_pcm, 0xffffffff); } @@ -121,8 +121,182 @@ void FMSOUND::check_fifo_position() void FMSOUND::mix(int32_t* buffer, int cnt) { int ncount = 0; - int32_t rsample, lsample; + int32_t sample_l, sample_r; + int32_t lastvol_l, lastvol_r; #ifdef _PC98_HAVE_86PCM + int lptr = play_rptr; + if(lptr >= play_bufsize) lptr = 0; + int rptr; + + if(((pcm_l_enabled) || (pcm_r_enabled)) && (play_w_remain > 0) && (fifo_direction) && (fifo_enabled)) { // Play + if(pcm_freq == sample_rate) { + int32_t* p = buffer; + if((pcm_l_enabled) && (pcm_r_enabled)) { + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + } else if(pcm_l_enabled) { + rptr = lptr; + sample_l = play_pool[lptr]; + sample_r = 0; + } else if(pcm_r_enabled) { + rptr = lptr; + sample_r = play_pool[rptr]; + sample_l = 0; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + for(int i = 0; i < cnt; i++) { + p[0] += lastvol_l; + p[1] += lastvol_r; + play_w_remain -= (((pcm_l_enabled) && (pcm_r_enabled)) ? 2 : 1); + if(play_w_remain > 0) { + if((pcm_l_enabled) && (pcm_r_enabled)) { + lptr += 2; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + } else if(pcm_l_enabled) { + lptr++; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr; + sample_l = play_pool[lptr]; + sample_r = 0; + } else if(pcm_r_enabled) { + lptr++; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr; + sample_r = play_pool[rptr]; + sample_l = 0; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + } else { + play_w_remain = 0; + } + p += 2; + play_rptr = lptr; + } + } else if(pcm_freq < sample_rate) { + int32_t* p = buffer; + if((pcm_l_enabled) && (pcm_r_enabled)) { + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + } else if(pcm_l_enabled) { + rptr = lptr; + sample_l = play_pool[lptr]; + sample_r = 0; + } else if(pcm_r_enabled) { + rptr = lptr; + sample_r = play_pool[rptr]; + sample_l = 0; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + for(int i = 0; i < cnt; i++) { + p[0] += lastvol_l; + p[1] += lastvol_r; + mix_mod = mix_mod + pcm_freq; + if(mix_mod >= sample_rate) { + mix_mod -= sample_rate; + //play_w_remain -= (((pcm_l_enabled) && (pcm_r_enabled)) ? 2 : 1); + if(play_w_remain > 0) { + if((pcm_l_enabled) && (pcm_r_enabled)) { + lptr += 2; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + play_w_remain -= 2; + } else if(pcm_r_enabled) { + lptr++; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr; + sample_r = play_pool[rptr]; + play_w_remain -= 1; + } else if(pcm_l_enabled) { + lptr++; + if(lptr >= play_bufsize) lptr = 0; + rptr = lptr; + sample_l = play_pool[lptr]; + play_w_remain -= 1; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + } else { + play_w_remain = 0; + } + } + p += 2; + play_rptr = lptr; + } + } else if(pcm_freq > sample_rate) { + int32_t* p = buffer; + int min_skip = pcm_freq / sample_rate; + if((pcm_l_enabled) && (pcm_r_enabled)) { + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + } else if(pcm_l_enabled) { + rptr = lptr; + sample_l = play_pool[lptr]; + sample_r = 0; + } else if(pcm_r_enabled) { + rptr = lptr; + sample_r = play_pool[rptr]; + sample_l = 0; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + int inc_factor; + for(int i = 0; i < cnt; i++) { + p[0] += lastvol_l; + p[1] += lastvol_r; + inc_factor = 0; + mix_mod = mix_mod + (sample_rate * min_skip); + if(mix_mod >= pcm_freq) { + mix_mod -= pcm_freq; + inc_factor = 1; + } + play_w_remain -= (((pcm_l_enabled) && (pcm_r_enabled)) ? ((min_skip + inc_factor) * 2) : (min_skip + inc_factor)); + if(play_w_remain > 0) { + if((pcm_l_enabled) && (pcm_r_enabled)) { + lptr += ((min_skip + inc_factor) * 2); + if(lptr >= play_bufsize) lptr = lptr - play_bufsize; + rptr = lptr + 1; + if(rptr >= play_bufsize) rptr = 0; + sample_l = play_pool[lptr]; + sample_r = play_pool[rptr]; + } else if(pcm_l_enabled) { + lptr += (min_skip + inc_factor); + if(lptr >= play_bufsize) lptr = lptr - play_bufsize; + rptr = lptr; + sample_l = play_pool[lptr]; + sample_r = 0; + } else if(pcm_r_enabled) { + lptr += (min_skip + inc_factor); + if(lptr >= play_bufsize) lptr = lptr - play_bufsize; + rptr = lptr; + sample_r = play_pool[rptr]; + sample_l = 0; + } + lastvol_l = apply_volume(sample_l, volume_l); + lastvol_r = apply_volume(sample_r, volume_r); + } else { + play_w_remain = 0; + } + p += 2; + play_rptr = lptr; + } + } + } //* ToDo: Mix PCM #endif } @@ -133,26 +307,34 @@ void FMSOUND::event_callback(int id, int err) case EVENT_PCM: #ifdef _PC98_HAVE_86PCM lrclock = !lrclock; - if((pcm_fifo != NULL) && (lrclock)) { + if((pcm_fifo != NULL)) { if(fifo_direction) { // Play pair16_t data; data.w = 0; - if(fifo_enabled) { - if(pcm_is_16bit) { - data.b.h = (uint8_t)(pcm_fifo->read()); - check_fifo_position(); - data.b.l = (uint8_t)(pcm_fifo->read()); - check_fifo_position(); - } else { - data.b.h = (uint8_t)(pcm_fifo->read()); - check_fifo_position(); - data.b.l = 0x00; + if(!(pcm_fifo->empty())) { + if(fifo_enabled) { + if(pcm_is_16bit) { + if(((lrclock) && (pcm_l_enabled)) || (!(lrclock) && (pcm_r_enabled))) { + data.b.h = (uint8_t)(pcm_fifo->read()); + data.b.l = (uint8_t)(pcm_fifo->read()); + check_fifo_position(); + } + } else { + if(((lrclock) && (pcm_l_enabled)) || (!(lrclock) && (pcm_r_enabled))) { + data.b.h = (uint8_t)(pcm_fifo->read()); + check_fifo_position(); + data.b.l = 0x00; + } + } } + if(((lrclock) && (pcm_l_enabled)) || (!(lrclock) && (pcm_r_enabled))) { + pcm_data.sd = (int32_t)(data.sw >> 1); + play_pool[play_wptr++] = pcm_data.sd; + } + if(play_wptr >= play_bufsize) play_wptr = 0; + play_w_remain++; + if(play_w_remain >= play_bufsize) play_w_remain = play_bufsize; } - pcm_data.sd = (int32_t)(data.sw); - play_pool[play_wptr++] = pcm_data.sd; - if(play_wptr >= play_bufsize) play_wptr = 0; - play_w_remain++; } else { // ToDo: Record pcm_data.sd = 0; if(pcm_is_16bit) { @@ -213,19 +395,32 @@ void FMSOUND::write_io8(uint32_t addr, uint32_t data) break; case 0xa468: { - fifo_enabled = ((data & 0x80) != 0); - fifo_direction = ((data & 0x40) == 0); // '1' = recording, '0' = playing. - fifo_int_flag = ((data & 0x20) != 0); - if((data & 0x10) == 0) { + if((pcm_ctrl_reg & 0x80) != (data & 0x80)) { + fifo_enabled = ((data & 0x80) != 0); + //out_debug_log("FIFO : %s\n", (fifo_enabled) ? "ENABLED" : "DISABLED"); + } + if((pcm_ctrl_reg & 0x40) != (data & 0x40)) { + fifo_direction = ((data & 0x40) == 0); // '1' = recording, '0' = playing. + //out_debug_log("Update PCM FIFO TYPE : %s\n", (fifo_direction) ? "PLAY" : "REC"); + } + if((pcm_ctrl_reg & 0x20) != (data & 0x20)) { + fifo_int_flag = ((data & 0x20) != 0); + //out_debug_log("Update PCM FIFO INT-MASK : %s\n", (fifo_int_flag) ? "YES" : "NO"); + } + if(((pcm_ctrl_reg & 0x10) != 0) && ((data & 0x10) == 0)) { + //out_debug_log("PCM CLEAR INTr\n"); write_signals(&outputs_int_pcm, 0x00000000); fifo_int_status = false; } - if((data & 0x08) != 0) { - fifo_reset_req = true; - } else { - if(fifo_reset_req) { + if((pcm_ctrl_reg & 0x08) != (data & 0x08)) { + if((data & 0x08) != 0) { + //out_debug_log("PCM RESET \n"); + play_w_remain = 0; + play_rptr = 0; + play_wptr = 0; + memset(play_pool, 0x00, sizeof(int32_t) * play_bufsize); + mix_mod = 0; pcm_fifo->clear(); - fifo_reset_req = false; } } uint32_t freq; @@ -255,15 +450,19 @@ void FMSOUND::write_io8(uint32_t addr, uint32_t data) freq = 4130; break; } - if(event_pcm >= 0) { - cancel_event(this, event_pcm); - event_pcm = -1; + if(freq != pcm_freq) { + if(event_pcm >= 0) { + cancel_event(this, event_pcm); + event_pcm = -1; + } + //out_debug_log("Update PCM FREQ=%d\n", freq); + lrclock = false; + pcm_freq = freq; + mix_mod = 0; } - if(fifo_enabled) { - register_event(this, EVENT_PCM, 1.0e6 / ((double)freq * 2.0), true, &event_pcm); + if(event_pcm < 0) { + register_event(this, EVENT_PCM, 1.0e6 / (double)(freq * 2), true, &event_pcm); } - lrclock = true; - pcm_freq = freq; } pcm_ctrl_reg = data; break; @@ -273,15 +472,22 @@ void FMSOUND::write_io8(uint32_t addr, uint32_t data) pcm_l_enabled = ((data & 0x20) != 0); pcm_r_enabled = ((data & 0x10) != 0); pcm_da_reg = data; + //out_debug_log("Update PCM TYPE: %s / %s%s\n", (pcm_is_16bit) ? "16bit" : "8bit", (pcm_l_enabled) ? "L" : " ", (pcm_r_enabled) ? "R" : " "); } else { - pcm_da_intleft = (int)(((uint32_t)data) << 7); + if((data & 0xff) == 0xff) { + pcm_da_intleft = 0x7ffc; + } else { + pcm_da_intleft = (int)(((uint32_t)((data & 0xff) + 1)) << 7); + } } case 0xa46c: if(pcm_fifo != NULL) { - if((fifo_enabled) && (fifo_direction)) { + //out_debug_log("FIFO DATA OUT %02x", data); + //if((fifo_enabled) && (fifo_direction)) { pcm_fifo->write(data); - check_fifo_position(); - } + //out_debug_log("FIFO DATA OUT %02x", data); + //check_fifo_position(); + //} } break; #endif @@ -344,17 +550,29 @@ uint32_t FMSOUND::read_io8(uint32_t addr) return pcm_da_reg; break; case 0xa46c: - if((fifo_enabled) && !(fifo_direction)) { + //if((fifo_enabled) && !(fifo_direction)) { uint8_t data = pcm_fifo->read(); - check_fifo_position(); + // check_fifo_position(); return (uint32_t)data; - } + //} break; #endif } return 0xff; } +void FMSOUND::set_volume(int ch, int decibel_l, int decibel_r) +{ + volume_l = decibel_to_volume(decibel_l); + volume_r = decibel_to_volume(decibel_r); +} + +void FMSOUND::initialize_sound(int rate, int samples) +{ + sample_rate = rate; + sample_samples = samples; +} + #define STATE_VERSION 2 bool FMSOUND::process_state(FILEIO* state_fio, bool loading) @@ -382,7 +600,6 @@ bool FMSOUND::process_state(FILEIO* state_fio, bool loading) state_fio->StateValue(fifo_direction); state_fio->StateValue(fifo_int_flag); state_fio->StateValue(fifo_int_status); - state_fio->StateValue(fifo_reset_req); state_fio->StateValue(lrclock); state_fio->StateValue(pcm_is_16bit); @@ -393,6 +610,7 @@ bool FMSOUND::process_state(FILEIO* state_fio, bool loading) state_fio->StateValue(play_w_remain); state_fio->StateValue(play_rptr); state_fio->StateValue(play_wptr); + state_fio->StateValue(mix_mod); state_fio->StateValue(event_pcm); state_fio->StateArray(play_pool, sizeof(play_pool), 1); diff --git a/source/src/vm/pc9801/fmsound.h b/source/src/vm/pc9801/fmsound.h index 99d5a61e4..42bfc362e 100644 --- a/source/src/vm/pc9801/fmsound.h +++ b/source/src/vm/pc9801/fmsound.h @@ -63,6 +63,8 @@ private: bool pcm_r_enabled; int pcm_da_intleft; + int mix_mod; + int play_bufsize; int play_w_remain; int play_rptr; @@ -71,6 +73,12 @@ private: int event_pcm; #endif + + int sample_rate; + int sample_samples; + int volume_r; + int volume_l; + void check_fifo_position(); public: FMSOUND(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) @@ -93,6 +101,8 @@ public: bool process_state(FILEIO* state_fio, bool loading); void mix(int32_t* buffer, int cnt); void event_callback(int id, int err); + void set_volume(int ch, int decibel_l, int decibel_r); + void initialize_sound(int rate, int samples); // unique function void set_context_opn(DEVICE* device) diff --git a/source/src/vm/pc9801/pc9801.cpp b/source/src/vm/pc9801/pc9801.cpp index e6e6aa0b7..03ac5c94f 100644 --- a/source/src/vm/pc9801/pc9801.cpp +++ b/source/src/vm/pc9801/pc9801.cpp @@ -371,6 +371,7 @@ VM::VM(EMU* parent_emu) : VM_TEMPLATE(parent_emu) event->set_context_sound(beep); if(sound_type == 0 || sound_type == 1) { event->set_context_sound(opn); + event->set_context_sound(fmsound); } else if(sound_type == 2 || sound_type == 3) { event->set_context_sound(tms3631); } @@ -1268,6 +1269,7 @@ void VM::initialize_sound(int rate, int samples) } else { opn->initialize_sound(rate, 3993624, samples, 0, 0); } + fmsound->initialize_sound(rate, samples); } else if(sound_type == 2 || sound_type == 3) { tms3631->initialize_sound(rate, 8000); } @@ -1326,6 +1328,10 @@ void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r) if(sound_type == 0 || sound_type == 1) { opn->set_volume(3, decibel_l, decibel_r); } + } else if(ch-- == 0) { + if(sound_type == 0 || sound_type == 1) { + fmsound->set_volume(0, decibel_l, decibel_r); + } #endif } else if(ch-- == 0) { if(sound_type == 2 || sound_type == 3) { diff --git a/source/src/vm/pc9801/pc9801.h b/source/src/vm/pc9801/pc9801.h index 577d40af3..e6a8eeaf4 100644 --- a/source/src/vm/pc9801/pc9801.h +++ b/source/src/vm/pc9801/pc9801.h @@ -307,17 +307,17 @@ #define USE_SOUND_TYPE 5 #if defined(_PC98DO) || defined(_PC98DOPLUS) #if defined(SUPPORT_PC98_OPNA) && defined(SUPPORT_PC88_OPNA) -#define USE_SOUND_VOLUME (4 + 1 + 1 + 4 + 1 + 1) +#define USE_SOUND_VOLUME (4 + 1 + 1 + 1 + 4 + 1 + 1) #elif defined(SUPPORT_PC98_OPNA) && !defined(SUPPORT_PC88_OPNA) -#define USE_SOUND_VOLUME (4 + 1 + 1 + 2 + 1 + 1) +#define USE_SOUND_VOLUME (4 + 1 + 1 + 1 + 2 + 1 + 1) #elif !defined(SUPPORT_PC98_OPNA) && defined(SUPPORT_PC88_OPNA) -#define USE_SOUND_VOLUME (2 + 1 + 1 + 4 + 1 + 1) +#define USE_SOUND_VOLUME (2 + 1 + 1 + 1 + 4 + 1 + 1) #elif !defined(SUPPORT_PC98_OPNA) && !defined(SUPPORT_PC88_OPNA) -#define USE_SOUND_VOLUME (2 + 1 + 1 + 2 + 1 + 1) +#define USE_SOUND_VOLUME (2 + 1 + 1 + 1 + 2 + 1 + 1) #endif #else #if defined(SUPPORT_PC98_OPNA) -#define USE_SOUND_VOLUME (4 + 1 + 1 + 1) +#define USE_SOUND_VOLUME (4 + 1 + 1 + 1 + 1) #else #define USE_SOUND_VOLUME (2 + 1 + 1 + 1) #endif @@ -346,7 +346,7 @@ #ifdef USE_SOUND_VOLUME static const _TCHAR *sound_device_caption[] = { #if defined(SUPPORT_PC98_OPNA) - _T("OPNA (FM)"), _T("OPNA (PSG)"), _T("OPNA (ADPCM)"), _T("OPNA (Rhythm)"), + _T("OPNA (FM)"), _T("OPNA (PSG)"), _T("OPNA (ADPCM)"), _T("OPNA (Rhythm)"), _T("PCM(PC-9801-86)"), #else _T("OPN (FM)"), _T("OPN (PSG)"), #endif -- 2.11.0