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<std::mutex> threadIdLock(threadIdMutex);
68 thread = std::thread([this]() { run(); });
69 threadIdCond.wait(threadIdLock);
70 }
71 {
72 std::lock_guard<std::mutex> 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::lock_guard<std::mutex> lock(queueMutex);
104 for (auto i : xrange(p->dwBytesRecorded)) {
105 queue.push_back(p->lpData[i]);
106 }
107 }
108 eventDistributor.distributeEvent(
109 Event::create<MidiInWindowsEvent>());
110 }
111}
112
113void MidiInWindows::procShortMsg(DWORD param)
114{
115 int num;
116 switch (param & 0xF0) {
117 case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0:
118 num = 3; break;
119 case 0xC0: case 0xD0:
120 num = 2; break;
121 default:
122 num = 1; break;
123 }
124 std::lock_guard<std::mutex> lock(queueMutex);
125 while (num--) {
126 queue.push_back(param & 0xFF);
127 param >>= 8;
128 }
129 eventDistributor.distributeEvent(
130 Event::create<MidiInWindowsEvent>());
131}
132
133void MidiInWindows::run()
134{
135 assert(isPluggedIn());
136
137 {
138 std::lock_guard<std::mutex> threadIdLock(threadIdMutex);
139 threadId = GetCurrentThreadId();
140 }
141
142 {
143 std::unique_lock<std::mutex> devIdxLock(devIdxMutex);
144 threadIdCond.notify_all();
145 devIdxCond.wait(devIdxLock);
146 }
147
148 bool fexit = devIdx == unsigned(-1);
149 while (!fexit) {
150 MSG msg;
151 if (GetMessage(&msg, nullptr, 0, 0) == one_of(0, -1)) {
152 break;
153 }
154 switch (msg.message) {
155 case MM_MIM_OPEN:
156 break;
157 case MM_MIM_DATA:
158 case MM_MIM_MOREDATA:
159 procShortMsg(DWORD(msg.lParam));
160 break;
161 case MM_MIM_LONGDATA:
162 procLongMsg(reinterpret_cast<LPMIDIHDR>(msg.lParam));
163 break;
164 case MM_MIM_ERROR:
165 case MM_MIM_LONGERROR:
166 case MM_MIM_CLOSE:
167 fexit = true;
168 break;
169 default:
170 break;
171 }
172 }
173 threadId = 0;
174}
175
176// MidiInDevice
177void MidiInWindows::signal(EmuTime::param time)
178{
179 auto* conn = static_cast<MidiInConnector*>(getConnector());
180 if (!conn->acceptsData()) {
181 std::lock_guard<std::mutex> lock(queueMutex);
182 queue.clear();
183 return;
184 }
185 if (!conn->ready()) return;
186
187 byte data;
188 {
189 std::lock_guard<std::mutex> lock(queueMutex);
190 if (queue.empty()) return;
191 data = queue.pop_front();
192 }
193 conn->recvByte(data, time);
194}
195
196// EventListener
197int MidiInWindows::signalEvent(const Event& /*event*/)
198{
199 if (isPluggedIn()) {
200 signal(scheduler.getCurrentTime());
201 } else {
202 std::lock_guard<std::mutex> lock(queueMutex);
203 queue.clear();
204 }
205 return 0;
206}
207
208template<typename Archive>
209void MidiInWindows::serialize(Archive& /*ar*/, unsigned /*version*/)
210{
211 // don't restore this after loadstate
212}
213INSTANTIATE_SERIALIZE_METHODS(MidiInWindows);
214REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInWindows, "MidiInWindows");
215
216} // namespace openmsx
217
218#endif // defined(_WIN32)
Definition: one_of.hh:7
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:730
This file implemented 3 utility functions:
Definition: Autofire.cc:9
void serialize(Archive &ar, T &t, unsigned version)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)
constexpr auto xrange(T e)
Definition: xrange.hh:132