openMSX
MidiInCoreMIDI.cc
Go to the documentation of this file.
1#if defined(__APPLE__)
2
3#include "MidiInCoreMIDI.hh"
4#include "MidiInConnector.hh"
6#include "PlugException.hh"
7#include "EventDistributor.hh"
8#include "Scheduler.hh"
9#include "serialize.hh"
10#include "StringOp.hh"
11#include "xrange.hh"
12#include <mach/mach_time.h>
13#include <memory>
14
15
16namespace openmsx {
17
18// MidiInCoreMIDI ===========================================================
19
20void MidiInCoreMIDI::registerAll(EventDistributor& eventDistributor,
21 Scheduler& scheduler,
22 PluggingController& controller)
23{
24 for (auto i : xrange(MIDIGetNumberOfSources())) {
25 if (MIDIEndpointRef endpoint = MIDIGetSource(i)) {
26 controller.registerPluggable(std::make_unique<MidiInCoreMIDI>(
27 eventDistributor, scheduler, endpoint));
28 }
29 }
30}
31
32MidiInCoreMIDI::MidiInCoreMIDI(EventDistributor& eventDistributor_,
33 Scheduler& scheduler_, MIDIEndpointRef endpoint_)
34 : eventDistributor(eventDistributor_)
35 , scheduler(scheduler_)
36 , endpoint(endpoint_)
37{
38 // Get a user-presentable name for the endpoint.
39 CFStringRef midiDeviceName;
40 OSStatus status = MIDIObjectGetStringProperty(
41 endpoint, kMIDIPropertyDisplayName, &midiDeviceName);
42 if (status) {
43 status = MIDIObjectGetStringProperty(
44 endpoint, kMIDIPropertyName, &midiDeviceName);
45 }
46 if (status) {
47 name = "Nameless endpoint";
48 } else {
49 name = strCat(StringOp::fromCFString(midiDeviceName), " IN");
50 CFRelease(midiDeviceName);
51 }
52
53 eventDistributor.registerEventListener(
54 EventType::MIDI_IN_COREMIDI, *this);
55}
56
57MidiInCoreMIDI::~MidiInCoreMIDI()
58{
59 eventDistributor.unregisterEventListener(
60 EventType::MIDI_IN_COREMIDI, *this);
61}
62
63void MidiInCoreMIDI::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/)
64{
65 // Create client.
66 if (OSStatus status = MIDIClientCreate(
67 CFSTR("openMSX"), nullptr, nullptr, &client)) {
68 throw PlugException("Failed to create MIDI client (", status, ')');
69 }
70 // Create input port.
71 if (OSStatus status = MIDIInputPortCreate(
72 client, CFSTR("Input"), sendPacketList, this, &port)) {
73 MIDIClientDispose(client);
74 client = 0;
75 throw PlugException("Failed to create MIDI port (", status, ')');
76 }
77
78 MIDIPortConnectSource(port, endpoint, nullptr);
79}
80
81void MidiInCoreMIDI::unplugHelper(EmuTime::param /*time*/)
82{
83 // Dispose of the client; this automatically disposes of the port as well.
84 if (OSStatus status = MIDIClientDispose(client)) {
85 fprintf(stderr, "Failed to dispose of MIDI client (%d)\n", int(status));
86 }
87 port = 0;
88 client = 0;
89}
90
91std::string_view MidiInCoreMIDI::getName() const
92{
93 return name;
94}
95
96std::string_view MidiInCoreMIDI::getDescription() const
97{
98 return "Receives MIDI events from an existing CoreMIDI source.";
99}
100
101void MidiInCoreMIDI::sendPacketList(const MIDIPacketList *packetList,
102 void *readProcRefCon, void *srcConnRefCon)
103{
104 static_cast<MidiInCoreMIDI*>(readProcRefCon)
105 ->sendPacketList(packetList, srcConnRefCon);
106}
107
108void MidiInCoreMIDI::sendPacketList(const MIDIPacketList *packetList,
109 void * /*srcConnRefCon*/) {
110 {
111 std::lock_guard<std::mutex> lock(mutex);
112 const MIDIPacket* packet = &packetList->packet[0];
113 repeat(packetList->numPackets, [&] {
114 for (auto j : xrange(packet->length)) {
115 queue.push_back(packet->data[j]);
116 }
117 packet = MIDIPacketNext(packet);
118 });
119 }
120 eventDistributor.distributeEvent(
121 Event::create<MidiInCoreMidiEvent>());
122}
123
124// MidiInDevice
125void MidiInCoreMIDI::signal(EmuTime::param time)
126{
127 auto connector = static_cast<MidiInConnector*>(getConnector());
128 if (!connector->acceptsData()) {
129 std::lock_guard<std::mutex> lock(mutex);
130 queue.clear();
131 return;
132 }
133 if (!connector->ready()) {
134 return;
135 }
136
137 byte data;
138 {
139 std::lock_guard<std::mutex> lock(mutex);
140 if (queue.empty()) return;
141 data = queue.pop_front();
142 }
143 connector->recvByte(data, time);
144}
145
146// EventListener
147int MidiInCoreMIDI::signalEvent(const Event& /*event*/)
148{
149 if (isPluggedIn()) {
150 signal(scheduler.getCurrentTime());
151 } else {
152 std::lock_guard<std::mutex> lock(mutex);
153 queue.clear();
154 }
155 return 0;
156}
157
158template<typename Archive>
159void MidiInCoreMIDI::serialize(Archive& /*ar*/, unsigned /*version*/)
160{
161}
162INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDI);
163REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDI, "MidiInCoreMIDI");
164
165
166// MidiInCoreMIDIVirtual ====================================================
167
168MidiInCoreMIDIVirtual::MidiInCoreMIDIVirtual(EventDistributor& eventDistributor_,
169 Scheduler& scheduler_)
170 : eventDistributor(eventDistributor_)
171 , scheduler(scheduler_)
172 , client(0)
173 , endpoint(0)
174{
175 eventDistributor.registerEventListener(
176 EventType::MIDI_IN_COREMIDI_VIRTUAL, *this);
177}
178
179MidiInCoreMIDIVirtual::~MidiInCoreMIDIVirtual()
180{
181 eventDistributor.unregisterEventListener(
182 EventType::MIDI_IN_COREMIDI_VIRTUAL, *this);
183}
184
185void MidiInCoreMIDIVirtual::plugHelper(Connector& /*connector*/,
186 EmuTime::param /*time*/)
187{
188 // Create client.
189 if (OSStatus status = MIDIClientCreate(CFSTR("openMSX"),
190 nullptr, nullptr, &client)) {
191 throw PlugException("Failed to create MIDI client (", status, ')');
192 }
193 // Create endpoint.
194 if (OSStatus status = MIDIDestinationCreate(client, CFSTR("openMSX"),
195 sendPacketList, this,
196 &endpoint)) {
197 MIDIClientDispose(client);
198 throw PlugException("Failed to create MIDI endpoint (", status, ')');
199 }
200}
201
202void MidiInCoreMIDIVirtual::unplugHelper(EmuTime::param /*time*/)
203{
204 if (OSStatus status = MIDIEndpointDispose(endpoint)) {
205 fprintf(stderr, "Failed to dispose of MIDI port (%d)\n", int(status));
206 }
207 endpoint = 0;
208 if (OSStatus status = MIDIClientDispose(client)) {
209 fprintf(stderr, "Failed to dispose of MIDI client (%d)\n", int(status));
210 }
211 client = 0;
212}
213
214std::string_view MidiInCoreMIDIVirtual::getName() const
215{
216 return "Virtual IN";
217}
218
219std::string_view MidiInCoreMIDIVirtual::getDescription() const
220{
221 return "Sends MIDI events from a newly created CoreMIDI virtual source.";
222}
223
224void MidiInCoreMIDIVirtual::sendPacketList(const MIDIPacketList *packetList,
225 void *readProcRefCon,
226 void *srcConnRefCon)
227{
228 static_cast<MidiInCoreMIDIVirtual*>(readProcRefCon)
229 ->sendPacketList(packetList, srcConnRefCon);
230}
231
232void MidiInCoreMIDIVirtual::sendPacketList(const MIDIPacketList *packetList,
233 void * /*srcConnRefCon*/)
234{
235 {
236 std::lock_guard<std::mutex> lock(mutex);
237 const MIDIPacket* packet = &packetList->packet[0];
238 repeat(packetList->numPackets, [&] {
239 for (auto j : xrange(packet->length)) {
240 queue.push_back(packet->data[j]);
241 }
242 packet = MIDIPacketNext(packet);
243 });
244 }
245 eventDistributor.distributeEvent(
246 Event::create<MidiInCoreMidiVirtualEvent>());
247}
248
249// MidiInDevice
250void MidiInCoreMIDIVirtual::signal(EmuTime::param time)
251{
252 auto connector = static_cast<MidiInConnector*>(getConnector());
253 if (!connector->acceptsData()) {
254 std::lock_guard<std::mutex> lock(mutex);
255 queue.clear();
256 return;
257 }
258 if (!connector->ready()) {
259 return;
260 }
261
262 byte data;
263 {
264 std::lock_guard<std::mutex> lock(mutex);
265 if (queue.empty()) return;
266 data = queue.pop_front();
267 }
268 connector->recvByte(data, time);
269}
270
271// EventListener
272int MidiInCoreMIDIVirtual::signalEvent(const Event& /*event*/)
273{
274 if (isPluggedIn()) {
275 signal(scheduler.getCurrentTime());
276 } else {
277 std::lock_guard<std::mutex> lock(mutex);
278 queue.clear();
279 }
280 return 0;
281}
282
283template<typename Archive>
284void MidiInCoreMIDIVirtual::serialize(Archive& /*ar*/, unsigned /*version*/)
285{
286}
287INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDIVirtual);
288REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDIVirtual, "MidiInCoreMIDIVirtual");
289
290} // namespace openmsx
291
292#endif // defined(__APPLE__)
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)
std::string strCat(Ts &&...ts)
Definition: strCat.hh:542
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:147
constexpr auto xrange(T e)
Definition: xrange.hh:132