openMSX
MidiOutCoreMIDI.cc
Go to the documentation of this file.
1 #if defined(__APPLE__)
2 
3 #include "MidiOutCoreMIDI.hh"
4 #include "PluggingController.hh"
5 #include "PlugException.hh"
6 #include "serialize.hh"
7 #include "openmsx.hh"
8 #include "StringOp.hh"
9 
10 #include <mach/mach_time.h>
11 #include <cassert>
12 #include <memory>
13 
14 
15 namespace openmsx {
16 
17 // MidiOutMessageBuffer ======================================================
18 
19 void MidiOutMessageBuffer::recvMessage(
20  const std::vector<uint8_t>& message, EmuTime::param /*time*/)
21 {
22  // TODO: It would be better to schedule events based on EmuTime.
23  MIDITimeStamp abstime = mach_absolute_time();
24 
25  MIDIPacketList packetList;
26  MIDIPacket *curPacket = MIDIPacketListInit(&packetList);
27  curPacket = MIDIPacketListAdd(&packetList, sizeof(packetList),
28  curPacket, abstime, message.size(), message.data());
29  if (!curPacket) {
30  fprintf(stderr, "Failed to package MIDI data\n");
31  } else if (OSStatus status = sendPacketList(&packetList)) {
32  fprintf(stderr, "Failed to send MIDI data (%d)\n", int(status));
33  } else {
34  //fprintf(stderr, "MIDI send OK: %02X\n", value);
35  }
36 }
37 
38 
39 // MidiOutCoreMIDI ===========================================================
40 
41 void MidiOutCoreMIDI::registerAll(PluggingController& controller)
42 {
43  for (auto i : xrange(MIDIGetNumberOfDestinations())) {
44  if (MIDIEndpointRef endpoint = MIDIGetDestination(i)) {
45  controller.registerPluggable(
46  std::make_unique<MidiOutCoreMIDI>(endpoint));
47  }
48  }
49 }
50 
51 MidiOutCoreMIDI::MidiOutCoreMIDI(MIDIEndpointRef endpoint_)
52  : endpoint(endpoint_)
53 {
54  // Get a user-presentable name for the endpoint.
55  CFStringRef midiDeviceName;
56  OSStatus status = MIDIObjectGetStringProperty(
57  endpoint, kMIDIPropertyDisplayName, &midiDeviceName);
58  if (status) {
59  status = MIDIObjectGetStringProperty(
60  endpoint, kMIDIPropertyName, &midiDeviceName);
61  }
62  if (status) {
63  name = "Nameless endpoint";
64  } else {
65  name = strCat(StringOp::fromCFString(midiDeviceName), " OUT");
66  CFRelease(midiDeviceName);
67  }
68 }
69 
70 void MidiOutCoreMIDI::plugHelper(Connector& /*connector*/,
71  EmuTime::param /*time*/)
72 {
73  // Create client.
74  if (OSStatus status = MIDIClientCreate(CFSTR("openMSX"), nullptr, nullptr, &client)) {
75  throw PlugException("Failed to create MIDI client (", status, ')');
76  }
77  // Create output port.
78  if (OSStatus status = MIDIOutputPortCreate(client, CFSTR("Output"), &port)) {
79  MIDIClientDispose(client);
80  client = 0;
81  throw PlugException("Failed to create MIDI port (", status, ')');
82  }
83 }
84 
85 void MidiOutCoreMIDI::unplugHelper(EmuTime::param /*time*/)
86 {
87  clearBuffer();
88 
89  // Dispose of the client; this automatically disposes of the port as well.
90  if (OSStatus status = MIDIClientDispose(client)) {
91  fprintf(stderr, "Failed to dispose of MIDI client (%d)\n", int(status));
92  }
93  port = 0;
94  client = 0;
95 }
96 
97 std::string_view MidiOutCoreMIDI::getName() const
98 {
99  return name;
100 }
101 
102 std::string_view MidiOutCoreMIDI::getDescription() const
103 {
104  return "Sends MIDI events to an existing CoreMIDI destination.";
105 }
106 
107 OSStatus MidiOutCoreMIDI::sendPacketList(MIDIPacketList *myPacketList)
108 {
109  return MIDISend(port, endpoint, myPacketList);
110 }
111 
112 template<typename Archive>
113 void MidiOutCoreMIDI::serialize(Archive& /*ar*/, unsigned /*version*/)
114 {
115 }
116 INSTANTIATE_SERIALIZE_METHODS(MidiOutCoreMIDI);
117 REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiOutCoreMIDI, "MidiOutCoreMIDI");
118 
119 
120 // MidiOutCoreMIDIVirtual ====================================================
121 
122 MidiOutCoreMIDIVirtual:: MidiOutCoreMIDIVirtual()
123  : client(0)
124  , endpoint(0)
125 {
126 }
127 
128 void MidiOutCoreMIDIVirtual::plugHelper(Connector& /*connector*/,
129  EmuTime::param /*time*/)
130 {
131  // Create client.
132  if (OSStatus status = MIDIClientCreate(CFSTR("openMSX"), nullptr, nullptr, &client)) {
133  throw PlugException("Failed to create MIDI client (", status, ')');
134  }
135  // Create endpoint.
136  if (OSStatus status = MIDISourceCreate(client, CFSTR("openMSX"), &endpoint)) {
137  MIDIClientDispose(client);
138  throw PlugException("Failed to create MIDI endpoint (", status, ')');
139  }
140 }
141 
142 void MidiOutCoreMIDIVirtual::unplugHelper(EmuTime::param /*time*/)
143 {
144  clearBuffer();
145 
146  if (OSStatus status = MIDIEndpointDispose(endpoint)) {
147  fprintf(stderr, "Failed to dispose of MIDI port (%d)\n", int(status));
148  }
149  endpoint = 0;
150  if (OSStatus status = MIDIClientDispose(client)) {
151  fprintf(stderr, "Failed to dispose of MIDI client (%d)\n", int(status));
152  }
153  client = 0;
154 }
155 
156 std::string_view MidiOutCoreMIDIVirtual::getName() const
157 {
158  return "Virtual OUT";
159 }
160 
161 std::string_view MidiOutCoreMIDIVirtual::getDescription() const
162 {
163  return "Sends MIDI events from a newly created CoreMIDI virtual source.";
164 }
165 
166 OSStatus MidiOutCoreMIDIVirtual::sendPacketList(MIDIPacketList *myPacketList)
167 {
168  return MIDIReceived(endpoint, myPacketList);
169 }
170 
171 template<typename Archive>
172 void MidiOutCoreMIDIVirtual::serialize(Archive& /*ar*/, unsigned /*version*/)
173 {
174 }
175 INSTANTIATE_SERIALIZE_METHODS(MidiOutCoreMIDIVirtual);
176 REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiOutCoreMIDIVirtual, "MidiOutCoreMIDIVirtual");
177 
178 } // namespace openmsx
179 
180 #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
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 auto xrange(T e)
Definition: xrange.hh:155