OSDN Git Service

[VM][COMMON_VM] Include IO:: class to common_vm.
[csp-qt/common_source_project-fm7.git] / source / src / vm / ld700.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2014.02.12-
6
7         [ Pioneer LD-700 ]
8 */
9
10 #define EVENT_ACK               0
11 #define EVENT_SOUND             1
12 #define EVENT_MIX               2
13
14 #define PHASE_IDLE              0
15 #define PHASE_HEADER_PULSE      1
16 #define PHASE_HEADER_SPACE      2
17 #define PHASE_BITS_PULSE        3
18 #define PHASE_BITS_SPACE        4
19
20 #define STATUS_EJECT            0
21 #define STATUS_STOP             1
22 #define STATUS_PLAY             2
23 #define STATUS_PAUSE            3
24
25 #define SEEK_CHAPTER            0x40
26 #define SEEK_FRAME              0x41
27 #define SEEK_WAIT               0x5f
28
29 #include "ld700.h"
30 #include "../fifo.h"
31 #if !defined(__GNUC__) && !defined(__CYGWIN__) && !defined(Q_OS_CYGWIN)
32 #define strnicmp(a,b,c) _strnicmp(a,b,c)
33 #endif
34 void LD700::initialize()
35 {
36         DEVICE::initialize();
37         prev_remote_signal = false;
38         prev_remote_time = 0;
39         command = num_bits = 0;
40         
41         status = STATUS_EJECT;
42         phase = PHASE_IDLE;
43         seek_mode = seek_num = 0;
44         accepted = false;
45         cur_frame_raw = 0;
46         wait_frame_raw = 0;
47         
48         prev_sound_signal = false;
49         sound_buffer_l = new FIFO(48000 * 4);
50         sound_buffer_r = new FIFO(48000 * 4);
51         signal_buffer = new FIFO(48000 * 4);
52         signal_buffer_ok = false;
53         sound_event_id = -1;
54         sound_sample_l = sound_sample_r = 0;
55         
56         mix_buffer_l = mix_buffer_r = NULL;
57         mix_buffer_ptr = mix_buffer_length = 0;
58         mix_buffer_ptr = mix_buffer_length = 0;
59         
60         register_frame_event(this);
61 }
62
63 void LD700::release()
64 {
65         if(mix_buffer_l != NULL) {
66                 free(mix_buffer_l);
67         }
68         if(mix_buffer_r != NULL) {
69                 free(mix_buffer_r);
70         }
71         sound_buffer_l->release();
72         delete sound_buffer_l;
73         sound_buffer_r->release();
74         delete sound_buffer_r;
75         signal_buffer->release();
76         delete signal_buffer;
77 }
78
79 void LD700::write_signal(int id, uint32_t data, uint32_t mask)
80 {
81         if(id == SIG_LD700_REMOTE) {
82                 bool signal = ((data & mask) != 0);
83                 if(prev_remote_signal != signal) {
84                         int usec = (int)get_passed_usec(prev_remote_time);
85                         prev_remote_time = get_current_clock();
86                         prev_remote_signal = signal;
87                         
88                         // from openmsx-0.10.0/src/laserdisc/
89                         switch(phase) {
90                         case PHASE_IDLE:
91                                 if(signal) {
92                                         touch_sound();
93                                         command = num_bits = 0;
94                                         phase = PHASE_HEADER_PULSE;
95                                 }
96                                 break;
97                         case PHASE_HEADER_PULSE:
98                                 touch_sound();
99                                 if(5800 <= usec && usec < 11200) {
100                                         phase = PHASE_HEADER_SPACE;
101                                 } else {
102                                         phase = PHASE_IDLE;
103                                 }
104                                 break;
105                         case PHASE_HEADER_SPACE:
106                                 touch_sound();
107                                 if(3400 <= usec && usec < 6200) {
108                                         phase = PHASE_BITS_PULSE;
109                                 } else {
110                                         phase = PHASE_IDLE;
111                                 }
112                                 break;
113                         case PHASE_BITS_PULSE:
114                                 touch_sound();
115                                 if(usec >= 380 && usec < 1070) {
116                                         phase = PHASE_BITS_SPACE;
117                                 } else {
118                                         phase = PHASE_IDLE;
119                                 }
120                                 break;
121                         case PHASE_BITS_SPACE:
122                                 touch_sound();
123                                 if(1260 <= usec && usec < 4720) {
124                                         // bit 1
125                                         command |= 1 << num_bits;
126                                 } else if(usec < 300 || usec >= 1065) {
127                                         // error
128                                         phase = PHASE_IDLE;
129                                         break;
130                                 }
131                                 if(++num_bits == 32) {
132                                         uint8_t custom      = ( command >>  0) & 0xff;
133                                         uint8_t custom_comp = (~command >>  8) & 0xff;
134                                         uint8_t code        = ( command >> 16) & 0xff;
135                                         uint8_t code_comp   = (~command >> 24) & 0xff;
136                                         if(custom == custom_comp && custom == 0xa8 && code == code_comp) {
137                                                 // command accepted
138                                                 accepted = true;
139                                         }
140                                         phase = PHASE_IDLE;
141                                 } else {
142                                         phase = PHASE_BITS_PULSE;
143                                 }
144                                 break;
145                         }
146                 }
147         } else if(id == SIG_LD700_MUTE_L) {
148                 touch_sound();
149                 sound_mute_l = ((data & mask) != 0);
150         } else if(id == SIG_LD700_MUTE_R) {
151                 touch_sound();
152                 sound_mute_r = ((data & mask) != 0);
153         }
154 }
155
156 uint32_t LD700::read_signal(int id)
157 {
158         return (status == STATUS_PLAY) ? 1 : 0;
159 }
160
161 void LD700::event_frame()
162 {
163         int prev_frame_raw = cur_frame_raw;
164         bool seek_done = false;
165         
166         cur_frame_raw = get_cur_frame_raw();
167         
168         if(accepted) {
169                 command = (command >> 16) & 0xff;
170                 this->out_debug_log(_T("---\n"), command);
171                 this->out_debug_log(_T("LD700: COMMAND=%02x\n"), command);
172                 switch(command) {
173                 case 0x00:
174                 case 0x01:
175                 case 0x02:
176                 case 0x03:
177                 case 0x04:
178                 case 0x05:
179                 case 0x06:
180                 case 0x07:
181                 case 0x08:
182                 case 0x09:
183                         if(status != STATUS_EJECT /*&& status != STATUS_STOP*/) {
184                                 seek_num = seek_num * 10 + command;
185                                 this->out_debug_log(_T("LD700: SEEK NUMBER=%d\n"), seek_num);
186                         }
187                         break;
188                 case 0x16:
189                         if(status != STATUS_EJECT) {
190                                 if(status == STATUS_STOP) {
191                                         //emu->close_laser_disc();
192                                 } else {
193                                         emu->stop_movie();
194                                         emu->set_cur_movie_frame(0, false);
195                                         set_status(STATUS_STOP);
196                                         this->out_debug_log(_T("LD700: STOP\n"));
197                                 }
198                         }
199                         break;
200                 case 0x17:
201                         if(status != STATUS_EJECT && status != STATUS_PLAY) {
202                                 emu->mute_video_dev(true, true);
203                                 emu->play_movie();
204                                 set_status(STATUS_PLAY);
205                                 this->out_debug_log(_T("LD700: PLAY\n"));
206                         }
207                         break;
208                 case 0x18:
209                         if(status != STATUS_EJECT /*&& status != STATUS_STOP*/) {
210                                 emu->pause_movie();
211                                 set_status(STATUS_PAUSE);
212                                 this->out_debug_log(_T("LD700: PAUSE\n"));
213                         }
214                         break;
215                 case 0x40:      // SEEK_CHAPTER
216                 case 0x41:      // SEEK_FRAME
217                 case 0x5f:      // SEEK_WAIT
218                         if(status != STATUS_EJECT /*&& status != STATUS_STOP*/) {
219                                 seek_mode = command;
220                                 seek_num = 0;
221                         }
222                         break;
223                 case 0x42:
224                         if(status != STATUS_EJECT /*&& status != STATUS_STOP*/) {
225                                 int tmp = seek_num, num[3];
226                                 bool flag = true;
227                                 memset(num, 0, sizeof(num));
228                                 
229                                 for(int i = 0; i < 3; i++) {
230                                         int n0 = tmp % 10;
231                                         tmp /= 10;
232                                         int n1 = tmp % 10;
233                                         tmp /= 10;
234                                         int n2 = tmp % 10;
235                                         tmp /= 10;
236                                         if(n0 == n1 && n0 == n2) {
237                                                 num[i] = n0;
238                                         } else {
239                                                 flag = false;
240                                                 break;
241                                         }
242                                 }
243                                 if(flag && (num[1] != 0 || num[2] != 0)) {
244                                         seek_num = num[0] + num[1] * 10 + num[2] * 100;
245                                 }
246                                 if(seek_mode == SEEK_WAIT) {
247                                         if(seek_num >= 101 && seek_num < 200) {
248                                                 wait_frame_raw = track_frame_raw[seek_num - 100];
249                                         } else {
250                                                 wait_frame_raw = (int)((double)seek_num / 29.97 * emu->get_movie_frame_rate() + 0.5);
251                                         }
252                                         this->out_debug_log(_T("LD700: WAIT FRAME=%d\n"), seek_num);
253                                 } else {
254                                         if(seek_mode == SEEK_CHAPTER) {
255                                                 this->out_debug_log(_T("LD700: SEEK TRACK=%d\n"), seek_num);
256                                                 set_cur_track(seek_num);
257                                         } else if(seek_mode == SEEK_FRAME) {
258                                                 this->out_debug_log(_T("LD700: SEEK FRAME=%d\n"), seek_num);
259                                                 set_cur_frame(seek_num, false);
260                                         }
261                                         if(status == STATUS_PAUSE) {
262                                                 emu->mute_video_dev(true, true);
263                                                 emu->play_movie();
264                                                 set_status(STATUS_PLAY);
265                                                 this->out_debug_log(_T("LD700: PLAY\n"));
266                                         }
267                                         seek_done = true;
268                                 }
269                                 seek_mode = 0;
270                         }
271                         break;
272                 case 0x45:
273                         if(status != STATUS_EJECT /*&& status != STATUS_STOP*/) {
274                                 seek_num = 0;
275                         }
276                         break;
277                 default:
278                         this->out_debug_log(_T("LaserDisc: Unknown Command %02X\n"), command);
279                 }
280                 accepted = false;
281                 set_ack(true);
282         }
283         
284         if(!seek_done && status == STATUS_PLAY) {
285                 if(wait_frame_raw != 0 && prev_frame_raw < wait_frame_raw && cur_frame_raw >= wait_frame_raw) {
286                         this->out_debug_log(_T("LD700: WAIT RAW FRAME=%d (%d)\n"), wait_frame_raw, cur_frame_raw);
287                         set_ack(true);
288                         wait_frame_raw = 0;
289                 }
290                 for(int i = 0; i < num_pauses; i++) {
291                         if(prev_frame_raw < pause_frame_raw[i] && cur_frame_raw >= pause_frame_raw[i]) {
292                                 emu->pause_movie();
293                                 set_status(STATUS_PAUSE);
294                                 this->out_debug_log(_T("LD700: PAUSE RAW FRAME=%d (%d->%d)\n"), pause_frame_raw[i], prev_frame_raw, cur_frame_raw);
295                                 break;
296                         }
297                 }
298         }
299 }
300
301 void LD700::event_callback(int event_id, int err)
302 {
303         if(event_id == EVENT_ACK) {
304                 set_ack(false);
305         } else if(event_id == EVENT_SOUND) {
306                 if(signal_buffer_ok) {
307                         int sample = signal_buffer->read();
308                         bool signal = sample > 100 ? true : sample < -100 ? false : prev_sound_signal;
309                         prev_sound_signal = signal;
310                         write_signals(&outputs_sound, signal ? 0xffffffff : 0);
311                 }
312                 sound_sample_l = sound_buffer_l->read();
313                 sound_sample_r = sound_buffer_r->read();
314         } else if(event_id == EVENT_MIX) {
315                 if(mix_buffer_ptr < mix_buffer_length) {
316                         mix_buffer_l[mix_buffer_ptr] = sound_mute_l ? 0 : sound_sample_l;
317                         mix_buffer_r[mix_buffer_ptr] = sound_mute_r ? 0 : sound_sample_r;
318                         mix_buffer_ptr++;
319                 }
320         }
321 }
322
323 void LD700::set_status(int value)
324 {
325         if(status != value) {
326                 if(value == STATUS_PLAY) {
327                         if(sound_event_id == -1) {
328                                 register_event(this, EVENT_SOUND, 1000000.0 / emu->get_movie_sound_rate(), true, &sound_event_id);
329                         }
330                         sound_buffer_l->clear();
331                         sound_buffer_r->clear();
332                         signal_buffer->clear();
333                         signal_buffer_ok = false;
334                 } else {
335                         if(sound_event_id != -1) {
336                                 cancel_event(this, sound_event_id);
337                                 sound_event_id = -1;
338                                 sound_sample_l = sound_sample_r = 0;
339                         }
340                 }
341                 write_signals(&outputs_exv, !(value == STATUS_EJECT || value == STATUS_STOP) ? 0xffffffff : 0);
342                 status = value;
343         }
344 }
345
346 void LD700::set_ack(bool value)
347 {
348         if(value) {
349                 register_event(this, EVENT_ACK, 46000, false, NULL);
350         }
351         write_signals(&outputs_ack, value ? 0xffffffff : 0);
352 }
353
354 void LD700::set_cur_frame(int frame, bool relative)
355 {
356         if(relative) {
357                 if(frame == 0) {
358                         return;
359                 }
360         } else {
361                 if(frame < 0) {
362                         return;
363                 }
364         }
365         bool sign = (frame >= 0);
366         frame = (int)((double)abs(frame) / 29.97 * emu->get_movie_frame_rate() + 0.5);
367         if(relative && frame == 0) {
368                 frame = 1;
369         }
370         emu->set_cur_movie_frame(sign ? frame : -frame, relative);
371         this->out_debug_log(_T("LD700: SEEK RAW FRAME=%d RELATIVE=%d\n"), sign ? frame : -frame, relative);
372 }
373
374 int LD700::get_cur_frame_raw()
375 {
376         return emu->get_cur_movie_frame();
377 }
378
379 void LD700::set_cur_track(int track)
380 {
381         if(track >= 0 && track <= num_tracks) {
382                 emu->set_cur_movie_frame(track_frame_raw[track], false);
383         }
384 }
385
386 void LD700::open_disc(const _TCHAR* file_path)
387 {
388         if(emu->open_movie_file(file_path)) {
389                 this->out_debug_log(_T("LD700: OPEN MOVIE PATH=%s\n"), file_path);
390                 
391                 // read LOCATION information
392                 num_tracks = -1;
393                 memset(track_frame_raw, 0, sizeof(track_frame_raw));
394                 num_pauses = 0;
395                 memset(pause_frame_raw, 0, sizeof(pause_frame_raw));
396                 
397                 if(check_file_extension(file_path, _T(".ogv"))) {
398                         FILEIO* fio = new FILEIO();
399                         if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
400                                 uint8_t buffer[0x1000+1];
401                                 fio->Fread(buffer, sizeof(buffer), 1);
402                                 fio->Fclose();
403                                 buffer[0x1000] = 0;
404                                 
405                                 for(int i = 0; i < 0x1000; i++) {
406                                         char *top = (char *)(buffer + i), tmp[128];
407                                         if(strnicmp(top, "chapter:", 8) == 0) {
408                                                 top += 8;
409                                                 for(int j = 0;;) {
410                                                         char c = *top++;
411                                                         if(c >= '0' && c <= '9') {
412                                                                 tmp[j++] = c;
413                                                         } else if(c != ' ') {
414                                                                 tmp[j] = '\0';
415                                                                 break;
416                                                         }
417                                                 }
418                                                 int track = atoi(tmp);
419                                                 for(int j = 0;;) {
420                                                         char c = *top++;
421                                                         if(c >= '0' && c <= '9') {
422                                                                 tmp[j++] = c;
423                                                         } else if(c != ' ') {
424                                                                 tmp[j] = '\0';
425                                                                 break;
426                                                         }
427                                                 }
428                                                 if(track >= 0 && track <= MAX_TRACKS) {
429                                                         if(track > num_tracks) {
430                                                                 num_tracks = track;
431                                                         }
432                                                         track_frame_raw[track] = atoi(tmp);
433                                                         this->out_debug_log(_T("LD700: TRACK %d: %d\n"), track, track_frame_raw[track]);
434                                                 }
435                                         } else if(strnicmp(top, "stop:", 5) == 0) {
436                                                 top += 5;
437                                                 for(int j = 0;;) {
438                                                         char c = *top++;
439                                                         if(c >= '0' && c <= '9') {
440                                                                 tmp[j++] = c;
441                                                         } else if(c != ' ') {
442                                                                 tmp[j] = '\0';
443                                                                 break;
444                                                         }
445                                                 }
446                                                 if(num_pauses < MAX_PAUSES) {
447                                                         pause_frame_raw[num_pauses] = atoi(tmp) > 300 ? atoi(tmp) : 285;
448                                                         this->out_debug_log(_T("LD700: PAUSE %d\n"), pause_frame_raw[num_pauses]);
449                                                         num_pauses++;
450                                                 }
451                                         } else if(strnicmp(top, "ENCODER=", 8) == 0) {
452                                                 break;
453                                         }
454                                 }
455                         }
456                         delete fio;
457                 } else {
458                         _TCHAR ini_path[_MAX_PATH];
459                         my_stprintf_s(ini_path, _MAX_PATH, _T("%s.ini"), get_file_path_without_extensiton(file_path));
460                         this->out_debug_log(_T("LD700: OPEN INI PATH=%s\n"), ini_path);
461                         
462                         for(int i = 0; i <= MAX_TRACKS; i++) {
463                                 int value = MyGetPrivateProfileInt(_T("Location"), create_string(_T("chapter%d"), i), -1, ini_path);
464                                 if(value < 0) {
465                                         break;
466                                 } else {
467                                         track_frame_raw[i] = value;
468                                         num_tracks = i;
469                                 }
470                         }
471                         for(int i = 0; i < MAX_PAUSES; i++) {
472                                 int value = MyGetPrivateProfileInt(_T("Location"), create_string(_T("stop%d"), i), -1, ini_path);
473                                 if(value < 0) {
474                                         break;
475                                 } else {
476                                         pause_frame_raw[num_pauses++] = value;
477                                 }
478                         }
479                 }
480                 for(int i = 1; i < num_tracks; i++) {
481                         if(track_frame_raw[i] == 0) {
482                                 track_frame_raw[i] = track_frame_raw[i - 1];
483                         }
484                 }
485                 set_status(STATUS_STOP);
486         } else {
487                 close_disc();
488         }
489 }
490
491 void LD700::close_disc()
492 {
493         emu->close_movie_file();
494         num_tracks = -1;
495         set_status(STATUS_EJECT);
496 }
497
498 bool LD700::is_disc_inserted()
499 {
500         return (status != STATUS_EJECT);
501 }
502
503 void LD700::initialize_sound(int rate, int samples)
504 {
505         mix_buffer_l = (int16_t *)malloc(samples * 2 * sizeof(int16_t));
506         mix_buffer_r = (int16_t *)malloc(samples * 2 * sizeof(int16_t));
507         mix_buffer_length = samples * 2;
508         register_event(this, EVENT_MIX, 1000000. / (double)rate, true, NULL);
509 }
510
511 void LD700::mix(int32_t* buffer, int cnt)
512 {
513         int16_t sample_l = 0, sample_r = 0;
514         for(int i = 0; i < cnt; i++) {
515                 if(i < mix_buffer_ptr) {
516                         sample_l = apply_volume(mix_buffer_l[i], volume_l);
517                         sample_r = apply_volume(mix_buffer_r[i], volume_r);
518                 }
519                 *buffer += sample_l;
520                 *buffer += sample_r;
521         }
522         if(cnt < mix_buffer_ptr) {
523                 memmove(mix_buffer_l, mix_buffer_l + cnt, (mix_buffer_ptr - cnt) * sizeof(int16_t));
524                 memmove(mix_buffer_r, mix_buffer_r + cnt, (mix_buffer_ptr - cnt) * sizeof(int16_t));
525                 mix_buffer_ptr -= cnt;
526         } else {
527                 mix_buffer_ptr = 0;
528         }
529 }
530
531 void LD700::set_volume(int ch, int decibel_l, int decibel_r)
532 {
533         volume_l = decibel_to_volume(decibel_l);
534         volume_r = decibel_to_volume(decibel_r);
535 }
536
537 void LD700::movie_sound_callback(uint8_t *buffer, long size)
538 {
539         if(status == STATUS_PLAY) {
540                 int16_t *buffer16 = (int16_t *)buffer;
541                 size /= 2;
542                 for(int i = 0; i < size; i += 2) {
543                         sound_buffer_l->write(buffer16[i]);
544                         sound_buffer_r->write(buffer16[i + 1]);
545                         signal_buffer->write(buffer16[i + 1]);
546                 }
547                 if(signal_buffer->count() >= emu->get_movie_sound_rate() / 2) {
548                         signal_buffer_ok = true;
549                 }
550         }
551 }