openMSX
MidiInCoreMIDI.cc
Go to the documentation of this file.
1 #if defined(__APPLE__)
2 
3 #include "MidiInCoreMIDI.hh"
4 #include "MidiInConnector.hh"
5 #include "PluggingController.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 
16 namespace openmsx {
17 
18 // MidiInCoreMIDI ===========================================================
19 
20 void 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 
32 MidiInCoreMIDI::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(
55 }
56 
57 MidiInCoreMIDI::~MidiInCoreMIDI()
58 {
59  eventDistributor.unregisterEventListener(
61 }
62 
63 void 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 
81 void 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 
91 std::string_view MidiInCoreMIDI::getName() const
92 {
93  return name;
94 }
95 
96 std::string_view MidiInCoreMIDI::getDescription() const
97 {
98  return "Receives MIDI events from an existing CoreMIDI source.";
99 }
100 
101 void MidiInCoreMIDI::sendPacketList(const MIDIPacketList *packetList,
102  void *readProcRefCon, void *srcConnRefCon)
103 {
104  static_cast<MidiInCoreMIDI*>(readProcRefCon)
105  ->sendPacketList(packetList, srcConnRefCon);
106 }
107 
108 void 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  std::make_shared<SimpleEvent>(OPENMSX_MIDI_IN_COREMIDI_EVENT));
122 }
123 
124 // MidiInDevice
125 void 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
147 int MidiInCoreMIDI::signalEvent(const std::shared_ptr<const Event>& /*event*/) noexcept
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 
158 template<typename Archive>
159 void MidiInCoreMIDI::serialize(Archive& /*ar*/, unsigned /*version*/)
160 {
161 }
162 INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDI);
163 REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDI, "MidiInCoreMIDI");
164 
165 
166 // MidiInCoreMIDIVirtual ====================================================
167 
168 MidiInCoreMIDIVirtual::MidiInCoreMIDIVirtual(EventDistributor& eventDistributor_,
169  Scheduler& scheduler_)
170  : eventDistributor(eventDistributor_)
171  , scheduler(scheduler_)
172  , client(0)
173  , endpoint(0)
174 {
175  eventDistributor.registerEventListener(
177 }
178 
179 MidiInCoreMIDIVirtual::~MidiInCoreMIDIVirtual()
180 {
181  eventDistributor.unregisterEventListener(
183 }
184 
185 void 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 
202 void 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 
214 std::string_view MidiInCoreMIDIVirtual::getName() const
215 {
216  return "Virtual IN";
217 }
218 
219 std::string_view MidiInCoreMIDIVirtual::getDescription() const
220 {
221  return "Sends MIDI events from a newly created CoreMIDI virtual source.";
222 }
223 
224 void MidiInCoreMIDIVirtual::sendPacketList(const MIDIPacketList *packetList,
225  void *readProcRefCon,
226  void *srcConnRefCon)
227 {
228  static_cast<MidiInCoreMIDIVirtual*>(readProcRefCon)
229  ->sendPacketList(packetList, srcConnRefCon);
230 }
231 
232 void 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  std::make_shared<SimpleEvent>(OPENMSX_MIDI_IN_COREMIDI_VIRTUAL_EVENT));
247 }
248 
249 // MidiInDevice
250 void 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
272 int MidiInCoreMIDIVirtual::signalEvent(
273  const std::shared_ptr<const Event>& /*event*/) noexcept
274 {
275  if (isPluggedIn()) {
276  signal(scheduler.getCurrentTime());
277  } else {
278  std::lock_guard<std::mutex> lock(mutex);
279  queue.clear();
280  }
281  return 0;
282 }
283 
284 template<typename Archive>
285 void MidiInCoreMIDIVirtual::serialize(Archive& /*ar*/, unsigned /*version*/)
286 {
287 }
288 INSTANTIATE_SERIALIZE_METHODS(MidiInCoreMIDIVirtual);
289 REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInCoreMIDIVirtual, "MidiInCoreMIDIVirtual");
290 
291 } // namespace openmsx
292 
293 #endif // defined(__APPLE__)
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:742
This file implemented 3 utility functions:
Definition: Autofire.cc:5
@ OPENMSX_MIDI_IN_COREMIDI_VIRTUAL_EVENT
Definition: Event.hh:74
@ OPENMSX_MIDI_IN_COREMIDI_EVENT
Definition: Event.hh:73
void serialize(Archive &ar, T &t, unsigned version)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)
std::string strCat(Ts &&...ts)
Definition: strCat.hh:591
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:170
constexpr auto xrange(T e)
Definition: xrange.hh:155