OXYGENHUB
◆ main openmsx / src / cassette / CassettePlayerCommand.cc
8.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "CassettePlayerCommand.hh"
2
3
#include "CassettePlayer.hh"
4
5
#include "CommandController.hh"
6
#include "Scheduler.hh"
7
#include "StateChangeDistributor.hh"
8
9
namespace openmsx {
10
11
using namespace std::literals;
12
13
CassettePlayerCommand::CassettePlayerCommand(
14
		CassettePlayer* cassettePlayer_,
15
		CommandController& commandController_,
16
		StateChangeDistributor& stateChangeDistributor_,
17
		Scheduler& scheduler_)
18
	: RecordedCommand(commandController_, stateChangeDistributor_,
19
	                  scheduler_, "cassetteplayer")
20
	, cassettePlayer(cassettePlayer_)
21
{
22
}
23
24
void CassettePlayerCommand::execute(
25
	std::span<const TclObject> tokens, TclObject& result, EmuTime time)
26
{
27
	if (!cassettePlayer) {
28
		throw CommandException("There is no cassetteplayer available.");
29
	}
30
31
	auto stopRecording = [&] {
32
		if (cassettePlayer->getState() == CassettePlayer::State::RECORD) {
33
			try {
34
				cassettePlayer->playTape(cassettePlayer->getImageName(), time);
35
				return true;
36
			} catch (MSXException& e) {
37
				throw CommandException(std::move(e).getMessage());
38
			}
39
		}
40
		return false; // was not recording
41
	};
42
43
	if (tokens.size() == 1) {
44
		// Returning Tcl lists here, similar to the disk commands in
45
		// DiskChanger
46
		TclObject options = makeTclList(cassettePlayer->getStateString());
47
		result.addListElement(tmpStrCat(getName(), ':'),
48
		                      cassettePlayer->getImageName().getResolved(),
49
		                      options);
50
51
	} else if (tokens[1] == "new") {
52
		std::string_view prefix = "openmsx";
53
		std::string filename = FileOperations::parseCommandFileArgument(
54
			(tokens.size() == 3) ? tokens[2].getString() : std::string{},
55
			CassettePlayer::TAPE_RECORDING_DIR, prefix, CassettePlayer::TAPE_RECORDING_EXTENSION);
56
		cassettePlayer->recordTape(Filename(filename), time);
57
		result = tmpStrCat(
58
			"Created new cassette image file: ", filename,
59
			", inserted it and set recording mode.");
60
61
	} else if (tokens[1] == "insert" && tokens.size() == 3) {
62
		try {
63
			result = "Changing tape";
64
			Filename filename(tokens[2].getString(), userFileContext());
65
			cassettePlayer->playTape(filename, time);
66
		} catch (MSXException& e) {
67
			throw CommandException(std::move(e).getMessage());
68
		}
69
70
	} else if (tokens[1] == "motorcontrol" && tokens.size() == 3) {
71
		if (tokens[2] == "on") {
72
			cassettePlayer->setMotorControl(true, time);
73
			result = "Motor control enabled.";
74
		} else if (tokens[2] == "off") {
75
			cassettePlayer->setMotorControl(false, time);
76
			result = "Motor control disabled.";
77
		} else {
78
			throw SyntaxError();
79
		}
80
81
	} else if (tokens[1] == "setpos" && tokens.size() == 3) {
82
		stopRecording();
83
		cassettePlayer->setTapePos(time, tokens[2].getDouble(getInterpreter()));
84
85
	} else if (tokens.size() != 2) {
86
		throw SyntaxError();
87
88
	} else if (tokens[1] == "motorcontrol") {
89
		result = tmpStrCat("Motor control is ",
90
		                (cassettePlayer->motorControl ? "on"sv : "off"sv));
91
92
	} else if (tokens[1] == "record") {
93
			result = "TODO: implement this... (sorry)";
94
95
	} else if (tokens[1] == "play") {
96
		if (stopRecording()) {
97
			result = "Play mode set, rewinding tape.";
98
		} else if (cassettePlayer->getState() == CassettePlayer::State::STOP) {
99
			throw CommandException("No tape inserted or tape at end!");
100
		} else {
101
			// PLAY mode
102
			result = "Already in play mode.";
103
		}
104
105
	} else if (tokens[1] == "eject") {
106
		result = "Tape ejected";
107
		cassettePlayer->removeTape(time);
108
109
	} else if (tokens[1] == "rewind") {
110
		bool stopped = stopRecording();
111
		cassettePlayer->rewind(time);
112
		result = strCat((stopped ? "First stopping recording... "sv : ""sv),
113
		                "Tape rewound"sv);
114
115
	} else if (tokens[1] == "getpos") {
116
		result = cassettePlayer->getTapePos(time);
117
118
	} else if (tokens[1] == "getlength") {
119
		result = cassettePlayer->getTapeLength(time);
120
121
	} else {
122
		try {
123
			result = "Changing tape";
124
			Filename filename(tokens[1].getString(), userFileContext());
125
			cassettePlayer->playTape(filename, time);
126
		} catch (MSXException& e) {
127
			throw CommandException(std::move(e).getMessage());
128
		}
129
	}
130
	//if (!cassettePlayer->getConnector()) {
131
	//	cassettePlayer->cliComm.printWarning("Cassette player not plugged in.");
132
	//}
133
}
134
135
std::string CassettePlayerCommand::help(std::span<const TclObject> tokens) const
136
{
137
	std::string helpText;
138
	if (tokens.size() >= 2) {
139
		if (tokens[1] == "eject") {
140
			helpText =
141
			    "Well, just eject the cassette from the cassette "
142
			    "player/recorder!";
143
		} else if (tokens[1] == "rewind") {
144
			helpText =
145
			    "Indeed, rewind the tape that is currently in the "
146
			    "cassette player/recorder...";
147
		} else if (tokens[1] == "motorcontrol") {
148
			helpText =
149
			    "Setting this to 'off' is equivalent to "
150
			    "disconnecting the black remote plug from the "
151
			    "cassette player: it makes the cassette player "
152
			    "run (if in play mode); the motor signal from the "
153
			    "MSX will be ignored. Normally this is set to "
154
			    "'on': the cassetteplayer obeys the motor control "
155
			    "signal from the MSX.";
156
		} else if (tokens[1] == "play") {
157
			helpText =
158
			    "Go to play mode. Only useful if you were in "
159
			    "record mode (which is currently the only other "
160
			    "mode available).";
161
		} else if (tokens[1] == "new") {
162
			helpText =
163
			    "Create a new cassette image. If the file name is "
164
			    "omitted, one will be generated in the default "
165
			    "directory for tape recordings. Implies going to "
166
			    "record mode (why else do you want a new cassette "
167
			    "image?).";
168
		} else if (tokens[1] == "insert") {
169
			helpText =
170
			    "Inserts the specified cassette image into the "
171
			    "cassette player, rewinds it and switches to play "
172
			    "mode.";
173
		} else if (tokens[1] == "record") {
174
			helpText =
175
			    "Go to record mode. NOT IMPLEMENTED YET. Will be "
176
			    "used to be able to resume recording to an "
177
			    "existing cassette image, previously inserted with "
178
			    "the insert command.";
179
		} else if (tokens[1] == "getpos") {
180
			helpText =
181
			    "Return the position of the tape, in seconds from "
182
			    "the beginning of the tape.";
183
		} else if (tokens[1] == "setpos") {
184
			helpText =
185
			    "Wind the tape to the given position, in seconds from "
186
			    "the beginning of the tape.";
187
		} else if (tokens[1] == "getlength") {
188
			helpText =
189
			    "Return the length of the tape in seconds.";
190
		}
191
	} else {
192
		helpText =
193
		    "cassetteplayer eject             "
194
		    ": remove tape from virtual player\n"
195
		    "cassetteplayer rewind            "
196
		    ": rewind tape in virtual player\n"
197
		    "cassetteplayer motorcontrol      "
198
		    ": enables or disables motor control (remote)\n"
199
		    "cassetteplayer play              "
200
		    ": change to play mode (default)\n"
201
		    "cassetteplayer record            "
202
		    ": change to record mode (NOT IMPLEMENTED YET)\n"
203
		    "cassetteplayer new [<filename>]  "
204
		    ": create and insert new tape image file and go to record mode\n"
205
		    "cassetteplayer insert <filename> "
206
		    ": insert (a different) tape file\n"
207
		    "cassetteplayer getpos            "
208
		    ": query the position of the tape\n"
209
		    "cassetteplayer setpos <new-pos>  "
210
		    ": wind the tape to the given position\n"
211
		    "cassetteplayer getlength         "
212
		    ": query the total length of the tape\n"
213
		    "cassetteplayer <filename>        "
214
		    ": insert (a different) tape file\n";
215
	}
216
	return helpText;
217
}
218
219
void CassettePlayerCommand::tabCompletion(std::vector<std::string>& tokens) const
220
{
221
	using namespace std::literals;
222
	if (tokens.size() == 2) {
223
		static constexpr std::array cmds = {
224
			"eject"sv, "rewind"sv, "motorcontrol"sv, "insert"sv, "new"sv,
225
			"play"sv, "getpos"sv, "setpos"sv, "getlength"sv,
226
			//"record"sv,
227
		};
228
		completeFileName(tokens, userFileContext(), cmds);
229
	} else if ((tokens.size() == 3) && (tokens[1] == "insert")) {
230
		completeFileName(tokens, userFileContext());
231
	} else if ((tokens.size() == 3) && (tokens[1] == "motorcontrol")) {
232
		static constexpr std::array extra = {"on"sv, "off"sv};
233
		completeString(tokens, extra);
234
	}
235
}
236
237
bool CassettePlayerCommand::needRecord(std::span<const TclObject> tokens) const
238
{
239
	return tokens.size() > 1;
240
}
241
242
} // namespace openmsx