26 using std::unique_ptr;
41 virtual string execute(
const vector<string>& tokens,
43 virtual string help(
const vector<string>& tokens)
const;
54 scheduler,
"laserdiscplayer")
55 , laserdiscPlayer(laserdiscPlayer_)
62 if (tokens.size() == 1) {
70 }
else if (tokens.size() == 2 && tokens[1] ==
"eject") {
71 result +=
"Ejecting laserdisc.";
72 laserdiscPlayer.eject(time);
73 }
else if (tokens.size() == 3 && tokens[1] ==
"insert") {
75 result +=
"Changing laserdisc.";
76 laserdiscPlayer.setImageName(tokens[2], time);
88 if (tokens.size() >= 2) {
89 if (tokens[1] ==
"insert") {
90 return "Inserts the specfied laserdisc image into "
91 "the laserdisc player.";
92 }
else if (tokens[1] ==
"eject") {
93 return "Eject the laserdisc.";
96 return "laserdiscplayer insert <filename> "
97 ": insert a (different) laserdisc image\n"
98 "laserdiscplayer eject "
99 ": eject the laserdisc\n";
104 if (tokens.size() == 2) {
105 static const char*
const extra[] = {
"eject",
"insert" };
107 }
else if (tokens.size() == 3 && tokens[1] ==
"insert") {
125 "Laserdisc Player", 1, true)
126 ,
Schedulable(hwConf.getMotherBoard().getScheduler())
127 , motherBoard(hwConf.getMotherBoard())
128 , ldcontrol(ldcontrol_)
130 motherBoard.getCommandController(),
131 motherBoard.getStateChangeDistributor(),
132 motherBoard.getScheduler(),
138 , remoteState(REMOTE_IDLE)
139 , remoteLastEdge(
EmuTime::zero)
140 , remoteLastBit(false)
141 , remoteProtocol(IR_NONE)
144 , playerState(PLAYER_STOPPED)
146 motherBoard.getCommandController(),
"autorunlaserdisc",
147 "automatically try to run Laserdisc", true))
149 motherBoard.getReactor().getGlobalSettings().getThrottleManager()))
197 if (remoteLastBit == bit)
return;
202 remoteLastEdge = time;
205 switch (remoteState) {
208 remoteBits = remoteBitNr = 0;
213 if (5800 <= usec && usec < 11200) {
220 if (3400 <= usec && usec < 6200) {
227 if (usec >= 380 && usec < 1070) {
234 if (1260 <= usec && usec < 4720) {
236 remoteBits |= 1 << remoteBitNr;
237 }
else if (usec < 300 || usec >= 1065) {
246 if (++remoteBitNr == 32) {
247 byte custom = ( remoteBits >> 0) & 0xff;
248 byte customCompl = (~remoteBits >> 8) & 0xff;
249 byte code = ( remoteBits >> 16) & 0xff;
250 byte codeCompl = (~remoteBits >> 24) & 0xff;
251 if (custom == customCompl &&
254 submitRemote(
IR_NEC, code);
265 void LaserdiscPlayer::submitRemote(RemoteProtocol protocol,
unsigned code)
267 PRT_DEBUG(
"Laserdisc::submitRemote(" << std::hex << protocol <<
", "
272 if (protocol != remoteProtocol || code != remoteCode ||
273 (protocol ==
IR_NEC && (code == 0x42 || code == 0x17))) {
274 remoteProtocol = protocol;
276 remoteVblanksBack = 0;
277 remoteExecuteDelayed =
true;
279 PRT_DEBUG(
"Laserdisc::remote ignored after " << std::dec
280 << remoteVblanksBack <<
" vblanks");
281 remoteVblanksBack = 0;
282 remoteExecuteDelayed =
false;
288 return renderer->getRawFrame();
293 PRT_DEBUG(
"Laserdisc::Lowering ACK for " << std::dec << wait <<
"ms");
304 void LaserdiscPlayer::remoteButtonNEC(
unsigned code,
EmuTime::param time)
309 case 0x47: f =
"C+";
break;
310 case 0x46: f =
"C-";
break;
311 case 0x43: f =
"D+";
break;
312 case 0x4b: f =
"L+";
break;
313 case 0x49: f =
"L-";
break;
314 case 0x4a: f =
"L@";
break;
315 case 0x58: f =
"M+";
break;
316 case 0x55: f =
"M-";
break;
317 case 0x17: f =
"P+";
break;
318 case 0x16: f =
"P@";
break;
319 case 0x18: f =
"P/";
break;
320 case 0x54: f =
"S+";
break;
321 case 0x50: f =
"S-";
break;
322 case 0x45: f =
"X+";
break;
323 case 0x41: f =
'F';
break;
324 case 0x40: f =
'C';
break;
325 case 0x42: f =
"END";
break;
326 case 0x00: f =
'0';
break;
327 case 0x01: f =
'1';
break;
328 case 0x02: f =
'2';
break;
329 case 0x03: f =
'3';
break;
330 case 0x04: f =
'4';
break;
331 case 0x05: f =
'5';
break;
332 case 0x06: f =
'6';
break;
333 case 0x07: f =
'7';
break;
334 case 0x08: f =
'8';
break;
335 case 0x09: f =
'9';
break;
336 case 0x5f: f =
"WAIT FRAME";
break;
344 PRT_DEBUG(
"LaserdiscPlayer::remote " << f);
346 PRT_DEBUG(
"LaserdiscPlayer::remote unknown " << std::hex << code);
355 if (code == 0x49 || code == 0x4a || code == 0x4b) {
375 "ejecting laserdisc");
387 bool nonseekack =
true;
393 stillOnWaitFrame =
false;
403 nonseekack = video->chapter(0) != 0;
415 seekNum = seekNum * 10 + code;
421 seekFrame(seekNum % 100000, time);
426 seekChapter(seekNum % 100, time);
431 waitFrame = seekNum % 100000;
432 if (waitFrame >= 101 && waitFrame < 200) {
433 auto frame = video->chapter(
434 int(waitFrame - 100));
435 if (frame) waitFrame = frame;
437 PRT_DEBUG(
"Wait frame set to " << std::dec <<
446 if (seekState !=
SEEK_NONE && seekNum != 0) {
472 stillOnWaitFrame =
true;
483 "The Laserdisc player received a command to "
484 "play backwards (M-). This is currently not "
493 if (playingSpeed >= SPEED_STEP1) {
499 if (playingSpeed <= SPEED_X2) {
506 "The Laserdisc player received an unknown "
520 void LaserdiscPlayer::executeUntil(
EmuTime::param time,
int userdata)
539 if (!video.get() || video->getFrameRate() != 60)
544 (currentFrame > video->getFrames())) {
548 if (RawFrame* rawFrame = renderer->getRawFrame()) {
549 renderer->frameStart(time);
551 if (isVideoOutputAvailable(time)) {
552 auto frame = currentFrame;
553 if (video->getFrameRate() == 60) {
555 if (userdata == ODD_FRAME) {
560 video->getFrameNo(*rawFrame, frame);
562 if (userdata == EVEN_FRAME) {
566 renderer->drawBlank(0, 128, 196);
568 renderer->frameEnd();
572 loadingIndicator->update(seeking || sampleReads > 500);
575 if (userdata == EVEN_FRAME) {
576 scheduleDisplayStart(time);
580 if (userdata == EVEN_FRAME || userdata == ODD_FRAME) {
583 if (remoteProtocol ==
IR_NEC) {
584 if (remoteExecuteDelayed) {
585 remoteButtonNEC(remoteCode, time);
588 if (++remoteVblanksBack > 6) {
592 remoteExecuteDelayed =
false;
596 void LaserdiscPlayer::setFrameStep()
598 switch (playingSpeed) {
627 if (waitFrame && waitFrame == currentFrame) {
628 PRT_DEBUG(
"LaserdiscPlayer: wait frame " << std::dec <<
629 waitFrame <<
" reached");
635 if (stillOnWaitFrame) {
636 playingFromSample = getCurrentSample(time);
638 stillOnWaitFrame =
false;
647 switch (playingSpeed) {
665 && video->stopFrame(currentFrame)) {
666 PRT_DEBUG(
"LaserdiscPlayer: stopFrame " << std::dec <<
667 currentFrame <<
" reached");
669 playingFromSample = getCurrentSample(time);
674 void LaserdiscPlayer::setImageName(
const string& newImage,
EmuTime::param time)
677 oggImage =
Filename(newImage, UserFileContext());
678 video = make_unique<OggReader>(oggImage, motherBoard.
getMSXCliComm());
680 unsigned inputRate = video->getSampleRate();
681 sampleClock.
setFreq(inputRate);
688 const Filename& LaserdiscPlayer::getImageName()
const
693 int LaserdiscPlayer::signalEvent(
const std::shared_ptr<const Event>& event)
701 void LaserdiscPlayer::autoRun()
703 if (!autoRunSetting->getValue()) {
707 string var =
"::auto_run_ld_counter";
709 "if ![info exists " + var +
"] { set " + var +
" 0 }\n"
711 "after time 2 \"if $" + var +
"==\\$" + var +
" { "
712 "type 1CALLLD\\\\r }\"";
716 }
catch (CommandException& e) {
718 "Error executing loading instruction for AutoRun: " +
719 e.getMessage() +
"\n Please report a bug.");
723 void LaserdiscPlayer::generateChannels(
int** buffers,
unsigned num)
726 (muteLeft && muteRight)) {
727 buffers[0] =
nullptr;
732 size_t currentSample;
737 unsigned len = duration.getTicksAt(video->getSampleRate());
739 buffers[0] =
nullptr;
743 for (; pos < len; ++pos) {
744 buffers[0][pos * 2 + 0] = 0;
745 buffers[0][pos * 2 + 1] = 0;
748 currentSample = playingFromSample;
750 currentSample = getCurrentSample(start);
753 unsigned drift = video->getSampleRate() / 30;
755 if (currentSample > (lastPlayedSample + drift) ||
756 (currentSample + drift) < lastPlayedSample) {
757 PRT_DEBUG(
"Laserdisc audio drift: " << std::dec <<
758 lastPlayedSample <<
' ' << currentSample);
759 lastPlayedSample = currentSample;
762 int left = stereoMode ==
RIGHT ? 1 : 0;
763 int right = stereoMode ==
LEFT ? 0 : 1;
766 const AudioFragment* audio = video->getAudio(lastPlayedSample);
770 buffers[0] =
nullptr;
772 }
else for (; pos < num; ++pos) {
773 buffers[0][pos * 2 + 0] = 0;
774 buffers[0][pos * 2 + 1] = 0;
777 auto offset = unsigned(lastPlayedSample - audio->position);
778 unsigned len = std::min(audio->length -
offset, num - pos);
781 for (
unsigned i = 0; i < len; ++i, ++pos) {
782 buffers[0][pos * 2 + 0] = muteLeft ? 0 :
783 int(audio->pcm[left][
offset + i] * 65536.f);
784 buffers[0][pos * 2 + 1] = muteRight ? 0 :
785 int(audio->pcm[right][
offset + i] * 65536.f);
788 lastPlayedSample += len;
793 bool LaserdiscPlayer::updateBuffer(
unsigned length,
int* buffer,
804 PRT_DEBUG(
"Laserdisc::setMuting L:" << (left ?
"on" :
"off")
805 <<
" R:" << (right ?
"on" :
"off"));
825 lastPlayedSample = 0;
826 playingFromSample = 0;
835 playingSpeed = SPEED_1IN4;
843 playingFromSample = (currentFrame - 1ll) * 1001ll *
844 video->getSampleRate() / 30000ll;
858 switch(playerState) {
861 return playingFromSample;
863 return playingFromSample + sampleClock.
getTicksTill(time);
875 playingFromSample = getCurrentSample(time);
877 playingFromSample = (currentFrame - 1ll) * 1001ll *
878 video->getSampleRate() / 30000ll;
906 void LaserdiscPlayer::stepFrame(
bool forwards)
908 bool needseek =
false;
914 if (currentFrame < video->getFrames()) {
918 if (currentFrame > 1) {
926 int64_t samplePos = (currentFrame - 1ll) * 1001ll *
927 video->getSampleRate() / 30000ll;
928 playingFromSample = samplePos;
931 if (video->getFrameRate() == 60)
932 video->seek(currentFrame * 2, samplePos);
934 video->seek(currentFrame, samplePos);
938 void LaserdiscPlayer::seekFrame(
size_t toframe,
EmuTime::param time)
941 PRT_DEBUG(
"Laserdisc::SeekFrame " << std::dec << toframe);
944 PRT_DEBUG(
"FIXME: seek command while still seeking");
954 if (toframe > video->getFrames()) {
955 toframe = video->getFrames();
965 auto dist = abs(int64_t(toframe) - int64_t(currentFrame));
969 seektime = dist + 300;
971 seektime = 1800 + dist / 12;
974 int64_t samplePos = (toframe - 1ll) * 1001ll *
975 video->getSampleRate() / 30000ll;
977 if (video->getFrameRate() == 60)
978 video->seek(toframe * 2, samplePos);
980 video->seek(toframe, samplePos);
983 playingFromSample = samplePos;
984 currentFrame = toframe;
990 setAck(time, seektime);
995 void LaserdiscPlayer::seekChapter(
int chapter,
EmuTime::param time)
999 auto frameno = video->chapter(chapter);
1003 seekFrame(frameno, time);
1015 auto sample = getCurrentSample(time);
1018 int channel = stereoMode ==
LEFT ? 0 : 1;
1019 return int(audio->pcm[channel][sample - audio->position]
1026 bool LaserdiscPlayer::isVideoOutputAvailable(
EmuTime::param time)
1031 switch (playerState) {
1035 videoOut = !seeking;
1046 void LaserdiscPlayer::preVideoSystemChange()
1051 void LaserdiscPlayer::postVideoSystemChange()
1056 void LaserdiscPlayer::createRenderer()
1062 static enum_string<LaserdiscPlayer::RemoteState> RemoteStateInfo[] = {
1071 static enum_string<LaserdiscPlayer::PlayerState> PlayerStateInfo[] = {
1080 static enum_string<LaserdiscPlayer::SeekState> SeekStateInfo[] = {
1088 static enum_string<LaserdiscPlayer::StereoMode> StereoModeInfo[] = {
1095 static enum_string<LaserdiscPlayer::RemoteProtocol> RemoteProtocolInfo[] = {
1104 template<
typename Archive>
1108 ar.serialize(
"RemoteState", remoteState);
1110 ar.serialize(
"RemoteBitNr", remoteBitNr);
1111 ar.serialize(
"RemoteBits", remoteBits);
1112 if (ar.versionBelow(version, 3)) {
1113 assert(ar.isLoader());
1117 ar.serialize(
"RemoteLastBit", remoteLastBit);
1118 ar.serialize(
"RemoteLastEdge", remoteLastEdge);
1119 ar.serialize(
"RemoteProtocol", remoteProtocol);
1120 if (remoteProtocol !=
IR_NONE) {
1121 ar.serialize(
"RemoteCode", remoteCode);
1122 if (ar.versionBelow(version, 3)) {
1123 assert(ar.isLoader());
1126 ar.serialize(
"RemoteExecuteDelayed", remoteExecuteDelayed);
1127 ar.serialize(
"RemoteVblanksBack", remoteVblanksBack);
1131 ar.serialize(
"OggImage", oggImage);
1132 if (ar.isLoader()) {
1134 if (!oggImage.empty()) {
1140 ar.serialize(
"PlayerState", playerState);
1144 ar.serialize(
"SeekState", seekState);
1146 ar.serialize(
"SeekNum", seekNum);
1148 ar.serialize(
"seeking", seeking);
1151 ar.serialize(
"WaitFrame", waitFrame);
1154 if (ar.versionAtLeast(version, 2)) {
1155 ar.serialize(
"StillOnWaitFrame", stillOnWaitFrame);
1158 ar.serialize(
"ACK", ack);
1159 ar.serialize(
"PlayingSpeed", playingSpeed);
1162 ar.serialize(
"CurrentFrame", currentFrame);
1164 ar.serialize(
"FrameStep", frameStep);
1168 ar.serialize(
"StereoMode", stereoMode);
1169 ar.serialize(
"FromSample", playingFromSample);
1170 ar.serialize(
"SampleClock", sampleClock);
1172 if (ar.isLoader()) {
1174 if (video->getSampleRate() != sampleClock.
getFreq()) {
1175 uint64_t pos = playingFromSample;
1177 pos *= video->getSampleRate();
1180 playingFromSample = pos;
1181 sampleClock.
setFreq(video->getSampleRate());
1185 if (video->getFrameRate() == 60)
1186 video->seek(currentFrame * 2, sample);
1188 video->seek(currentFrame, sample);
1189 lastPlayedSample = sample;
1193 ar.template serializeBase<Schedulable>(*this);
1195 if (ar.isLoader()) {