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