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::scoped_lock 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(MidiInCoreMidiEvent());
121}
122
123// MidiInDevice
124void MidiInCoreMIDI::signal(EmuTime::param time)
125{
126 auto connector = static_cast<MidiInConnector*>(getConnector());
127 if (!connector->acceptsData()) {
128 std::scoped_lock lock(mutex);
129 queue.clear();
130 return;
131 }
132 if (!connector->ready()) {
133 return;
134 }
135
136 byte data;
137 {
138 std::scoped_lock lock(mutex);
139 if (queue.empty()) return;
140 data = queue.pop_front();
141 }
142 connector->recvByte(data, time);
143}
144
145// EventListener
146bool MidiInCoreMIDI::signalEvent(const Event& /*event*/)
147{
148 if (isPluggedIn()) {
149 signal(scheduler.getCurrentTime());
150 } else {
151 std::scoped_lock lock(mutex);
152 queue.clear();
153 }
154 return false;
155}
156
157template<typename Archive>
158void MidiInCoreMIDI::serialize(Archive& /*ar*/, unsigned /*version*/)
159{
160}
161INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDI);
162REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDI, "MidiInCoreMIDI");
163
164
165// MidiInCoreMIDIVirtual ====================================================
166
167MidiInCoreMIDIVirtual::MidiInCoreMIDIVirtual(EventDistributor& eventDistributor_,
168 Scheduler& scheduler_)
169 : eventDistributor(eventDistributor_)
170 , scheduler(scheduler_)
171 , client(0)
172 , endpoint(0)
173{
174 eventDistributor.registerEventListener(
175 EventType::MIDI_IN_COREMIDI_VIRTUAL, *this);
176}
177
178MidiInCoreMIDIVirtual::~MidiInCoreMIDIVirtual()
179{
180 eventDistributor.unregisterEventListener(
181 EventType::MIDI_IN_COREMIDI_VIRTUAL, *this);
182}
183
184void MidiInCoreMIDIVirtual::plugHelper(Connector& /*connector*/,
185 EmuTime::param /*time*/)
186{
187 // Create client.
188 if (OSStatus status = MIDIClientCreate(CFSTR("openMSX"),
189 nullptr, nullptr, &client)) {
190 throw PlugException("Failed to create MIDI client (", status, ')');
191 }
192 // Create endpoint.
193 if (OSStatus status = MIDIDestinationCreate(client, CFSTR("openMSX"),
194 sendPacketList, this,
195 &endpoint)) {
196 MIDIClientDispose(client);
197 throw PlugException("Failed to create MIDI endpoint (", status, ')');
198 }
199}
200
201void MidiInCoreMIDIVirtual::unplugHelper(EmuTime::param /*time*/)
202{
203 if (OSStatus status = MIDIEndpointDispose(endpoint)) {
204 fprintf(stderr, "Failed to dispose of MIDI port (%d)\n", int(status));
205 }
206 endpoint = 0;
207 if (OSStatus status = MIDIClientDispose(client)) {
208 fprintf(stderr, "Failed to dispose of MIDI client (%d)\n", int(status));
209 }
210 client = 0;
211}
212
213std::string_view MidiInCoreMIDIVirtual::getName() const
214{
215 return "Virtual IN";
216}
217
218std::string_view MidiInCoreMIDIVirtual::getDescription() const
219{
220 return "Sends MIDI events from a newly created CoreMIDI virtual source.";
221}
222
223void MidiInCoreMIDIVirtual::sendPacketList(const MIDIPacketList *packetList,
224 void *readProcRefCon,
225 void *srcConnRefCon)
226{
227 static_cast<MidiInCoreMIDIVirtual*>(readProcRefCon)
228 ->sendPacketList(packetList, srcConnRefCon);
229}
230
231void MidiInCoreMIDIVirtual::sendPacketList(const MIDIPacketList *packetList,
232 void * /*srcConnRefCon*/)
233{
234 {
235 std::scoped_lock lock(mutex);
236 const MIDIPacket* packet = &packetList->packet[0];
237 repeat(packetList->numPackets, [&] {
238 for (auto j : xrange(packet->length)) {
239 queue.push_back(packet->data[j]);
240 }
241 packet = MIDIPacketNext(packet);
242 });
243 }
244 eventDistributor.distributeEvent(MidiInCoreMidiVirtualEvent());
245}
246
247// MidiInDevice
248void MidiInCoreMIDIVirtual::signal(EmuTime::param time)
249{
250 auto connector = static_cast<MidiInConnector*>(getConnector());
251 if (!connector->acceptsData()) {
252 std::scoped_lock lock(mutex);
253 queue.clear();
254 return;
255 }
256 if (!connector->ready()) {
257 return;
258 }
259
260 byte data;
261 {
262 std::scoped_lock lock(mutex);
263 if (queue.empty()) return;
264 data = queue.pop_front();
265 }
266 connector->recvByte(data, time);
267}
268
269// EventListener
270bool MidiInCoreMIDIVirtual::signalEvent(const Event& /*event*/)
271{
272 if (isPluggedIn()) {
273 signal(scheduler.getCurrentTime());
274 } else {
275 std::scoped_lock lock(mutex);
276 queue.clear();
277 }
278 return false;
279}
280
281template<typename Archive>
282void MidiInCoreMIDIVirtual::serialize(Archive& /*ar*/, unsigned /*version*/)
283{
284}
285INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDIVirtual);
286REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDIVirtual, "MidiInCoreMIDIVirtual");
287
288} // namespace openmsx
289
290#endif // defined(__APPLE__)
This file implemented 3 utility functions:
Definition Autofire.cc:11
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)
std::string strCat()
Definition strCat.hh:703
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