openMSX
MidiInReader.cc
Go to the documentation of this file.
1 #include "MidiInReader.hh"
2 #include "MidiInConnector.hh"
3 #include "PlugException.hh"
4 #include "EventDistributor.hh"
5 #include "Scheduler.hh"
6 #include "FileOperations.hh"
7 #include "serialize.hh"
8 #include <cstdio>
9 #include <cerrno>
10 #include <cstring>
11 
12 using std::string;
13 
14 namespace openmsx {
15 
17  Scheduler& scheduler_,
18  CommandController& commandController)
19  : eventDistributor(eventDistributor_), scheduler(scheduler_)
20  , readFilenameSetting(
21  commandController, "midi-in-readfilename",
22  "filename of the file where the MIDI input is read from",
23  "/dev/midi")
24 {
25  eventDistributor.registerEventListener(OPENMSX_MIDI_IN_READER_EVENT, *this);
26 }
27 
29 {
31 }
32 
33 // Pluggable
34 void MidiInReader::plugHelper(Connector& connector_, EmuTime::param /*time*/)
35 {
36  file = FileOperations::openFile(string(readFilenameSetting.getString()), "rb");
37  if (!file) {
38  throw PlugException("Failed to open input: ", strerror(errno));
39  }
40 
41  auto& midiConnector = static_cast<MidiInConnector&>(connector_);
42  midiConnector.setDataBits(SerialDataInterface::DATA_8); // 8 data bits
43  midiConnector.setStopBits(SerialDataInterface::STOP_1); // 1 stop bit
44  midiConnector.setParityBit(false, SerialDataInterface::EVEN); // no parity
45 
46  setConnector(&connector_); // base class will do this in a moment,
47  // but thread already needs it
48  thread = std::thread([this]() { run(); });
49 }
50 
51 void MidiInReader::unplugHelper(EmuTime::param /*time*/)
52 {
53  poller.abort();
54  thread.join();
55  file.reset();
56 }
57 
58 const string& MidiInReader::getName() const
59 {
60  static const string name("midi-in-reader");
61  return name;
62 }
63 
64 std::string_view MidiInReader::getDescription() const
65 {
66  return "MIDI in file reader. Sends data from an input file to the "
67  "MIDI port it is connected to. The filename is set with "
68  "the 'midi-in-readfilename' setting.";
69 }
70 
71 
72 void MidiInReader::run()
73 {
74  byte buf;
75  if (!file) return;
76  while (true) {
77 #ifndef _WIN32
78  if (poller.poll(fileno(file.get()))) {
79  break;
80  }
81 #endif
82  size_t num = fread(&buf, 1, 1, file.get());
83  if (poller.aborted()) {
84  break;
85  }
86  if (num != 1) {
87  continue;
88  }
89  assert(isPluggedIn());
90 
91  {
92  std::lock_guard<std::mutex> lock(mutex);
93  queue.push_back(buf);
94  }
95  eventDistributor.distributeEvent(
96  std::make_shared<SimpleEvent>(OPENMSX_MIDI_IN_READER_EVENT));
97  }
98 }
99 
100 // MidiInDevice
101 void MidiInReader::signal(EmuTime::param time)
102 {
103  auto* conn = static_cast<MidiInConnector*>(getConnector());
104  if (!conn->acceptsData()) {
105  std::lock_guard<std::mutex> lock(mutex);
106  queue.clear();
107  return;
108  }
109  if (!conn->ready()) {
110  return;
111  }
112 
113  byte data;
114  {
115  std::lock_guard<std::mutex> lock(mutex);
116  if (queue.empty()) return;
117  data = queue.pop_front();
118  }
119  conn->recvByte(data, time);
120 }
121 
122 // EventListener
123 int MidiInReader::signalEvent(const std::shared_ptr<const Event>& /*event*/)
124 {
125  if (isPluggedIn()) {
126  signal(scheduler.getCurrentTime());
127  } else {
128  std::lock_guard<std::mutex> lock(mutex);
129  queue.clear();
130  }
131  return 0;
132 }
133 
134 
135 template<typename Archive>
136 void MidiInReader::serialize(Archive& /*ar*/, unsigned /*version*/)
137 {
138  // don't try to resume a previous logfile (see PrinterPortLogger)
139 }
142 
143 } // namespace openmsx
void abort()
Aborts a poll in progress and any future poll attempts.
Definition: Poller.cc:31
MidiInReader(EventDistributor &eventDistributor, Scheduler &scheduler, CommandController &commandController)
Definition: MidiInReader.cc:16
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
Represents something you can plug devices into.
Definition: Connector.hh:20
~MidiInReader() override
Definition: MidiInReader.cc:28
void signal(EmuTime::param time) override
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
Definition: Pluggable.hh:43
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
virtual void setDataBits(DataBits bits)=0
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void setConnector(Connector *conn)
Definition: Pluggable.hh:58
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
Definition: Pluggable.hh:49
void unplugHelper(EmuTime::param time) override
Definition: MidiInReader.cc:51
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void distributeEvent(const EventPtr &event)
Schedule the given event for delivery.
bool empty() const
EmuTime::param getCurrentTime() const
Get the current scheduler time.
Definition: Scheduler.cc:85
void serialize(Archive &ar, unsigned version)
bool poll(int fd)
Waits for an event to occur on the given file descriptor.
Definition: Poller.cc:43
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
bool aborted()
Returns true iff abort() was called.
Definition: Poller.hh:28
const std::string & getName() const override
Name used to identify this pluggable.
Definition: MidiInReader.cc:58
std::string_view getDescription() const override
Description for this pluggable.
Definition: MidiInReader.cc:64
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
void plugHelper(Connector &connector, EmuTime::param time) override
Definition: MidiInReader.cc:34
FILE_t openFile(const std::string &filename, const std::string &mode)
Call fopen() in a platform-independent manner.
void push_back(U &&u)
std::string_view getString() const noexcept
Thrown when a plug action fails.