OSDN Git Service

[VM][PC9801][SOUND] Playable PCM of PC-9801-86.
authorK.Ohta <whatisthis.sowhat@gmail.com>
Fri, 29 Mar 2019 11:44:25 +0000 (20:44 +0900)
committerK.Ohta <whatisthis.sowhat@gmail.com>
Fri, 29 Mar 2019 11:44:25 +0000 (20:44 +0900)
source/src/vm/pc9801/fmsound.cpp
source/src/vm/pc9801/fmsound.h
source/src/vm/pc9801/pc9801.cpp
source/src/vm/pc9801/pc9801.h

index 438085f..831c551 100644 (file)
@@ -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);
index 99d5a61..42bfc36 100644 (file)
@@ -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)
index e6e6aa0..03ac5c9 100644 (file)
@@ -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) {
index 577d40a..e6a8eea 100644 (file)
 #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
 #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