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