OSDN Git Service

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