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