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