OXYGENHUB
◆ main openmsx / src / laserdisc / LaserdiscPlayer.hh
6.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef LASERDISCPLAYER_HH
2
#define LASERDISCPLAYER_HH
3
4
#include "OggReader.hh"
5
6
#include "BooleanSetting.hh"
7
#include "DynamicClock.hh"
8
#include "EmuTime.hh"
9
#include "EventListener.hh"
10
#include "Filename.hh"
11
#include "MSXMotherBoard.hh"
12
#include "RecordedCommand.hh"
13
#include "ResampledSoundDevice.hh"
14
#include "Schedulable.hh"
15
#include "ThrottleManager.hh"
16
#include "VideoSystemChangeListener.hh"
17
18
#include "outer.hh"
19
20
#include <cstdint>
21
#include <memory>
22
#include <optional>
23
24
namespace openmsx {
25
26
class PioneerLDControl;
27
class HardwareConfig;
28
class LDRenderer;
29
class RawFrame;
30
31
class LaserdiscPlayer final : public ResampledSoundDevice
32
                            , private EventListener
33
                            , private VideoSystemChangeListener
34
                            , public MediaProvider
35
{
36
public:
37
	LaserdiscPlayer(HardwareConfig& hwConf, PioneerLDControl& ldControl);
38
	~LaserdiscPlayer();
39
40
	// Called from CassettePort
41
	[[nodiscard]] int16_t readSample(EmuTime time);
42
43
	// Called from PioneerLDControl
44
	void setMuting(bool left, bool right, EmuTime time);
45
	[[nodiscard]] bool extAck(EmuTime /*time*/) const { return ack; }
46
	void extControl(bool bit, EmuTime time);
47
	[[nodiscard]] const RawFrame* getRawFrame() const;
48
49
	template<typename Archive>
50
	void serialize(Archive& ar, unsigned version);
51
52
	// video interface
53
	[[nodiscard]] MSXMotherBoard& getMotherBoard() { return motherBoard; }
54
55
	// MediaInfoProvider
56
	void getMediaInfo(TclObject& result) override;
57
	void setMedia(const TclObject& info, EmuTime time) override;
58
59
	enum class RemoteState : uint8_t {
60
		IDLE,
61
		HEADER_PULSE,
62
		NEC_HEADER_SPACE,
63
		NEC_BITS_PULSE,
64
		NEC_BITS_SPACE,
65
	};
66
67
	enum class PlayerState : uint8_t {
68
		STOPPED,
69
		PLAYING,
70
		MULTI_SPEED,
71
		PAUSED,
72
		STILL
73
	};
74
75
	enum class SeekState : uint8_t {
76
		NONE,
77
		CHAPTER,
78
		FRAME,
79
		WAIT,
80
	};
81
82
	enum class StereoMode : uint8_t {
83
		LEFT,
84
		RIGHT,
85
		STEREO
86
	};
87
88
	enum class RemoteProtocol : uint8_t {
89
		NONE,
90
		NEC,
91
	};
92
private:
93
	std::string getStateString() const;
94
	void setImageName(std::string newImage, EmuTime time);
95
	[[nodiscard]] const Filename& getImageName() const { return oggImage; }
96
	void autoRun();
97
98
	/** Laserdisc player commands
99
	  */
100
	void play(EmuTime time);
101
	void pause(EmuTime time);
102
	void stop(EmuTime time);
103
	void eject(EmuTime time);
104
	void seekFrame(size_t frame, EmuTime time);
105
	void stepFrame(bool forwards);
106
	void seekChapter(int chapter, EmuTime time);
107
108
	// Control from MSX
109
110
	/** Is video output being generated?
111
	  */
112
	void scheduleDisplayStart(EmuTime time);
113
	[[nodiscard]] bool isVideoOutputAvailable(EmuTime time);
114
	void remoteButtonNEC(uint8_t code, EmuTime time);
115
	void submitRemote(RemoteProtocol protocol, uint8_t code);
116
	void setAck(EmuTime time, int wait);
117
	[[nodiscard]] size_t getCurrentSample(EmuTime time);
118
	void createRenderer();
119
120
	// SoundDevice
121
	void generateChannels(std::span<float*> buffers, unsigned num) override;
122
	bool updateBuffer(size_t length, float* buffer,
123
	                  EmuTime time) override;
124
	[[nodiscard]] float getAmplificationFactorImpl() const override;
125
126
	// Schedulable
127
	struct SyncAck final : public Schedulable {
128
		friend class LaserdiscPlayer;
129
		explicit SyncAck(Scheduler& s) : Schedulable(s) {}
130
		void executeUntil(EmuTime time) override {
131
			auto& player = OUTER(LaserdiscPlayer, syncAck);
132
			player.execSyncAck(time);
133
		}
134
	} syncAck;
135
	struct SyncOdd final : public Schedulable {
136
		friend class LaserdiscPlayer;
137
		explicit SyncOdd(Scheduler& s) : Schedulable(s) {}
138
		void executeUntil(EmuTime time) override {
139
			auto& player = OUTER(LaserdiscPlayer, syncOdd);
140
			player.execSyncFrame(time, true);
141
		}
142
	} syncOdd;
143
	struct SyncEven final : public Schedulable {
144
		friend class LaserdiscPlayer;
145
		explicit SyncEven(Scheduler& s) : Schedulable(s) {}
146
		void executeUntil(EmuTime time) override {
147
			auto& player = OUTER(LaserdiscPlayer, syncEven);
148
			player.execSyncFrame(time, false);
149
		}
150
	} syncEven;
151
152
	void execSyncAck(EmuTime time);
153
	void execSyncFrame(EmuTime time, bool odd);
154
	[[nodiscard]] EmuTime getCurrentTime() const { return syncAck.getCurrentTime(); }
155
156
	// EventListener
157
	bool signalEvent(const Event& event) override;
158
159
	// VideoSystemChangeListener interface:
160
	void preVideoSystemChange() noexcept override;
161
	void postVideoSystemChange() noexcept override;
162
163
	MSXMotherBoard& motherBoard;
164
	PioneerLDControl& ldControl;
165
166
	struct Command final : RecordedCommand {
167
		Command(CommandController& commandController,
168
		        StateChangeDistributor& stateChangeDistributor,
169
		        Scheduler& scheduler);
170
		void execute(std::span<const TclObject> tokens, TclObject& result,
171
			     EmuTime time) override;
172
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
173
		void tabCompletion(std::vector<std::string>& tokens) const override;
174
	} laserdiscCommand;
175
176
	std::optional<OggReader> video;
177
	Filename oggImage;
178
	std::unique_ptr<LDRenderer> renderer;
179
180
	void nextFrame(EmuTime time);
181
	void setFrameStep();
182
	size_t currentFrame;
183
	int frameStep;
184
185
	// Audio state
186
	DynamicClock sampleClock{EmuTime::zero()};
187
	EmuTime start = EmuTime::zero();
188
	size_t playingFromSample;
189
	size_t lastPlayedSample;
190
	bool muteLeft = false, muteRight = false;
191
	StereoMode stereoMode;
192
193
	// Ext Control
194
	RemoteState remoteState = RemoteState::IDLE;
195
	EmuTime remoteLastEdge = EmuTime::zero();
196
	unsigned remoteBitNr;
197
	unsigned remoteBits;
198
	bool remoteLastBit = false;
199
	RemoteProtocol remoteProtocol = RemoteProtocol::NONE;
200
	uint8_t remoteCode;
201
	bool remoteExecuteDelayed;
202
	// Number of v-blank since code was sent
203
	int remoteVblanksBack;
204
205
	/* We need to maintain some state for seeking */
206
	SeekState seekState;
207
208
	/* frame the MSX has requested to wait for */
209
	size_t waitFrame;
210
211
	// pause playing back on reaching wait frame
212
	bool stillOnWaitFrame;
213
214
	/* The specific frame or chapter we are seeking to */
215
	int seekNum;
216
217
	// For ack
218
	bool ack = false;
219
220
	// State of the video itself
221
	bool seeking = false;
222
223
	PlayerState playerState = PlayerState::STOPPED;
224
225
	enum PlayingSpeed : int8_t {
226
		SPEED_STEP3 = -5,	// Each frame is repeated 90 times
227
		SPEED_STEP1 = -4,	// Each frame is repeated 30 times
228
		SPEED_1IN16 = -3,	// Each frame is repeated 16 times
229
		SPEED_1IN8 = -2,	// Each frame is repeated 8 times
230
		SPEED_1IN4 = -1,	// Each frame is repeated 4 times
231
		SPEED_1IN2 = 0,
232
		SPEED_X1 = 1,
233
		SPEED_X2 = 2,
234
		SPEED_X3 = 3
235
	};
236
	int playingSpeed;
237
238
	// Loading indicator
239
	BooleanSetting autoRunSetting;
240
	LoadingIndicator loadingIndicator;
241
	int sampleReads = 0;
242
};
243
244
SERIALIZE_CLASS_VERSION(LaserdiscPlayer, 4);
245
246
} // namespace openmsx
247
248
#endif