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