openMSX
MidiInWindows.cc
Go to the documentation of this file.
1 #if defined(_WIN32)
2 #include "MidiInWindows.hh"
3 #include "MidiInConnector.hh"
4 #include "PluggingController.hh"
5 #include "PlugException.hh"
6 #include "EventDistributor.hh"
7 #include "Scheduler.hh"
8 #include "one_of.hh"
9 #include "serialize.hh"
10 #include <cstring>
11 #include <cerrno>
12 #include "Midi_w32.hh"
13 #ifndef WIN32_LEAN_AND_MEAN
14 #define WIN32_LEAN_AND_MEAN
15 #endif
16 #include <windows.h>
17 #include <mmsystem.h>
18 #include <memory>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 
22 using std::string;
23 
24 namespace openmsx {
25 
26 void MidiInWindows::registerAll(EventDistributor& eventDistributor,
27  Scheduler& scheduler,
28  PluggingController& controller)
29 {
30  w32_midiInInit();
31  unsigned devnum = w32_midiInGetVFNsNum();
32  for (unsigned i = 0 ; i <devnum; ++i) {
33  controller.registerPluggable(std::make_unique<MidiInWindows>(
34  eventDistributor, scheduler, i));
35  }
36 }
37 
38 
39 MidiInWindows::MidiInWindows(EventDistributor& eventDistributor_,
40  Scheduler& scheduler_, unsigned num)
41  : eventDistributor(eventDistributor_), scheduler(scheduler_)
42  , devIdx(unsigned(-1))
43 {
44  name = w32_midiInGetVFN(num);
45  desc = w32_midiInGetRDN(num);
46 
47  eventDistributor.registerEventListener(OPENMSX_MIDI_IN_WINDOWS_EVENT, *this);
48 }
49 
50 MidiInWindows::~MidiInWindows()
51 {
52  eventDistributor.unregisterEventListener(OPENMSX_MIDI_IN_WINDOWS_EVENT, *this);
53 
54  //w32_midiInClean(); // TODO
55 }
56 
57 // Pluggable
58 void MidiInWindows::plugHelper(Connector& connector_, EmuTime::param /*time*/)
59 {
60  auto& midiConnector = static_cast<MidiInConnector&>(connector_);
61  midiConnector.setDataBits(SerialDataInterface::DATA_8); // 8 data bits
62  midiConnector.setStopBits(SerialDataInterface::STOP_1); // 1 stop bit
63  midiConnector.setParityBit(false, SerialDataInterface::EVEN); // no parity
64 
65  setConnector(&connector_); // base class will do this in a moment,
66  // but thread already needs it
67 
68  {
69  std::unique_lock<std::mutex> threadIdLock(threadIdMutex);
70  thread = std::thread([this]() { run(); });
71  threadIdCond.wait(threadIdLock);
72  }
73  {
74  std::lock_guard<std::mutex> devIdxLock(devIdxMutex);
75  devIdx = w32_midiInOpen(name.c_str(), threadId);
76  }
77  devIdxCond.notify_all();
78  if (devIdx == unsigned(-1)) {
79  throw PlugException("Failed to open " + name);
80  }
81 }
82 
83 void MidiInWindows::unplugHelper(EmuTime::param /*time*/)
84 {
85  assert(devIdx != unsigned(-1));
86  w32_midiInClose(devIdx);
87  devIdx = unsigned(-1);
88  thread.join();
89 }
90 
91 const string& MidiInWindows::getName() const
92 {
93  return name;
94 }
95 
96 std::string_view MidiInWindows::getDescription() const
97 {
98  return desc;
99 }
100 
101 void MidiInWindows::procLongMsg(LPMIDIHDR p)
102 {
103  if (p->dwBytesRecorded) {
104  {
105  std::lock_guard<std::mutex> lock(queueMutex);
106  for (unsigned i = 0; i < p->dwBytesRecorded; ++i) {
107  queue.push_back(p->lpData[i]);
108  }
109  }
110  eventDistributor.distributeEvent(
111  std::make_shared<SimpleEvent>(OPENMSX_MIDI_IN_WINDOWS_EVENT));
112  }
113 }
114 
115 void MidiInWindows::procShortMsg(DWORD param)
116 {
117  int num;
118  switch (param & 0xF0) {
119  case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0:
120  num = 3; break;
121  case 0xC0: case 0xD0:
122  num = 2; break;
123  default:
124  num = 1; break;
125  }
126  std::lock_guard<std::mutex> lock(queueMutex);
127  while (num--) {
128  queue.push_back(param & 0xFF);
129  param >>= 8;
130  }
131  eventDistributor.distributeEvent(
132  std::make_shared<SimpleEvent>(OPENMSX_MIDI_IN_WINDOWS_EVENT));
133 }
134 
135 void MidiInWindows::run()
136 {
137  assert(isPluggedIn());
138 
139  {
140  std::lock_guard<std::mutex> threadIdLock(threadIdMutex);
141  threadId = GetCurrentThreadId();
142  }
143 
144  {
145  std::unique_lock<std::mutex> devIdxLock(devIdxMutex);
146  threadIdCond.notify_all();
147  devIdxCond.wait(devIdxLock);
148  }
149 
150  bool fexit = devIdx == unsigned(-1);
151  while (!fexit) {
152  MSG msg;
153  if (GetMessage(&msg, nullptr, 0, 0) == one_of(0, -1)) {
154  break;
155  }
156  switch (msg.message) {
157  case MM_MIM_OPEN:
158  break;
159  case MM_MIM_DATA:
160  case MM_MIM_MOREDATA:
161  procShortMsg(DWORD(msg.lParam));
162  break;
163  case MM_MIM_LONGDATA:
164  procLongMsg(reinterpret_cast<LPMIDIHDR>(msg.lParam));
165  break;
166  case MM_MIM_ERROR:
167  case MM_MIM_LONGERROR:
168  case MM_MIM_CLOSE:
169  fexit = true;
170  break;
171  default:
172  break;
173  }
174  }
175  threadId = 0;
176 }
177 
178 // MidiInDevice
179 void MidiInWindows::signal(EmuTime::param time)
180 {
181  auto* conn = static_cast<MidiInConnector*>(getConnector());
182  if (!conn->acceptsData()) {
183  std::lock_guard<std::mutex> lock(queueMutex);
184  queue.clear();
185  return;
186  }
187  if (!conn->ready()) return;
188 
189  byte data;
190  {
191  std::lock_guard<std::mutex> lock(queueMutex);
192  if (queue.empty()) return;
193  data = queue.pop_front();
194  }
195  conn->recvByte(data, time);
196 }
197 
198 // EventListener
199 int MidiInWindows::signalEvent(const std::shared_ptr<const Event>& /*event*/)
200 {
201  if (isPluggedIn()) {
202  signal(scheduler.getCurrentTime());
203  } else {
204  std::lock_guard<std::mutex> lock(queueMutex);
205  queue.clear();
206  }
207  return 0;
208 }
209 
210 template<typename Archive>
211 void MidiInWindows::serialize(Archive& /*ar*/, unsigned /*version*/)
212 {
213  // don't restore this after loadstate
214 }
215 INSTANTIATE_SERIALIZE_METHODS(MidiInWindows);
216 REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInWindows, "MidiInWindows");
217 
218 } // namespace openmsx
219 
220 #endif // defined(_WIN32)
one_of.hh
MidiInWindows.hh
serialize.hh
PlugException.hh
PluggingController.hh
openmsx::OPENMSX_MIDI_IN_WINDOWS_EVENT
@ OPENMSX_MIDI_IN_WINDOWS_EVENT
Definition: Event.hh:75
MidiInConnector.hh
openmsx::MidiInConnector
MidiInConnector
Definition: MidiInConnector.cc:37
openmsx::Keys::getName
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:742
one_of
Definition: one_of.hh:7
openmsx::SerialDataInterface::DATA_8
@ DATA_8
Definition: SerialDataInterface.hh:13
EventDistributor.hh
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:982
openmsx::SerialDataInterface::STOP_1
@ STOP_1
Definition: SerialDataInterface.hh:16
Scheduler.hh
openmsx::Scheduler
Scheduler
Definition: Scheduler.cc:132
Midi_w32.hh
openmsx::serialize
void serialize(Archive &ar, T &t, unsigned version)
Definition: serialize_core.hh:41
openmsx
This file implemented 3 utility functions:
Definition: Autofire.cc:5
openmsx::SerialDataInterface::EVEN
@ EVEN
Definition: SerialDataInterface.hh:20
openmsx::REGISTER_POLYMORPHIC_INITIALIZER
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer")
openmsx::Connector
Connector
Definition: Connector.cc:83