12#include <mach/mach_time.h>
20void MidiInCoreMIDI::registerAll(EventDistributor& eventDistributor,
22 PluggingController& controller)
24 for (
auto i :
xrange(MIDIGetNumberOfSources())) {
25 if (MIDIEndpointRef endpoint = MIDIGetSource(i)) {
26 controller.registerPluggable(std::make_unique<MidiInCoreMIDI>(
27 eventDistributor, scheduler, endpoint));
32MidiInCoreMIDI::MidiInCoreMIDI(EventDistributor& eventDistributor_,
33 Scheduler& scheduler_, MIDIEndpointRef endpoint_)
34 : eventDistributor(eventDistributor_)
35 , scheduler(scheduler_)
39 CFStringRef midiDeviceName;
40 OSStatus status = MIDIObjectGetStringProperty(
41 endpoint, kMIDIPropertyDisplayName, &midiDeviceName);
43 status = MIDIObjectGetStringProperty(
44 endpoint, kMIDIPropertyName, &midiDeviceName);
47 name =
"Nameless endpoint";
49 name =
strCat(StringOp::fromCFString(midiDeviceName),
" IN");
50 CFRelease(midiDeviceName);
53 eventDistributor.registerEventListener(
54 EventType::MIDI_IN_COREMIDI, *
this);
57MidiInCoreMIDI::~MidiInCoreMIDI()
59 eventDistributor.unregisterEventListener(
60 EventType::MIDI_IN_COREMIDI, *
this);
63void MidiInCoreMIDI::plugHelper(Connector& , EmuTime::param )
66 if (OSStatus status = MIDIClientCreate(
67 CFSTR(
"openMSX"),
nullptr,
nullptr, &client)) {
68 throw PlugException(
"Failed to create MIDI client (", status,
')');
71 if (OSStatus status = MIDIInputPortCreate(
72 client, CFSTR(
"Input"), sendPacketList,
this, &port)) {
73 MIDIClientDispose(client);
75 throw PlugException(
"Failed to create MIDI port (", status,
')');
78 MIDIPortConnectSource(port, endpoint,
nullptr);
81void MidiInCoreMIDI::unplugHelper(EmuTime::param )
84 if (OSStatus status = MIDIClientDispose(client)) {
85 fprintf(stderr,
"Failed to dispose of MIDI client (%d)\n",
int(status));
91std::string_view MidiInCoreMIDI::getName()
const
96std::string_view MidiInCoreMIDI::getDescription()
const
98 return "Receives MIDI events from an existing CoreMIDI source.";
101void MidiInCoreMIDI::sendPacketList(
const MIDIPacketList *packetList,
102 void *readProcRefCon,
void *srcConnRefCon)
104 static_cast<MidiInCoreMIDI*
>(readProcRefCon)
105 ->sendPacketList(packetList, srcConnRefCon);
108void MidiInCoreMIDI::sendPacketList(
const MIDIPacketList *packetList,
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]);
117 packet = MIDIPacketNext(packet);
120 eventDistributor.distributeEvent(MidiInCoreMidiEvent());
124void MidiInCoreMIDI::signal(EmuTime::param time)
127 if (!connector->acceptsData()) {
128 std::scoped_lock lock(mutex);
132 if (!connector->ready()) {
138 std::scoped_lock lock(mutex);
139 if (queue.empty())
return;
140 data = queue.pop_front();
142 connector->recvByte(data, time);
146bool MidiInCoreMIDI::signalEvent(
const Event& )
149 signal(scheduler.getCurrentTime());
151 std::scoped_lock lock(mutex);
157template<
typename Archive>
158void MidiInCoreMIDI::serialize(Archive& ,
unsigned )
167MidiInCoreMIDIVirtual::MidiInCoreMIDIVirtual(EventDistributor& eventDistributor_,
168 Scheduler& scheduler_)
169 : eventDistributor(eventDistributor_)
170 , scheduler(scheduler_)
174 eventDistributor.registerEventListener(
175 EventType::MIDI_IN_COREMIDI_VIRTUAL, *
this);
178MidiInCoreMIDIVirtual::~MidiInCoreMIDIVirtual()
180 eventDistributor.unregisterEventListener(
181 EventType::MIDI_IN_COREMIDI_VIRTUAL, *
this);
184void MidiInCoreMIDIVirtual::plugHelper(Connector& ,
188 if (OSStatus status = MIDIClientCreate(CFSTR(
"openMSX"),
189 nullptr,
nullptr, &client)) {
190 throw PlugException(
"Failed to create MIDI client (", status,
')');
193 if (OSStatus status = MIDIDestinationCreate(client, CFSTR(
"openMSX"),
194 sendPacketList,
this,
196 MIDIClientDispose(client);
197 throw PlugException(
"Failed to create MIDI endpoint (", status,
')');
201void MidiInCoreMIDIVirtual::unplugHelper(EmuTime::param )
203 if (OSStatus status = MIDIEndpointDispose(endpoint)) {
204 fprintf(stderr,
"Failed to dispose of MIDI port (%d)\n",
int(status));
207 if (OSStatus status = MIDIClientDispose(client)) {
208 fprintf(stderr,
"Failed to dispose of MIDI client (%d)\n",
int(status));
213std::string_view MidiInCoreMIDIVirtual::getName()
const
218std::string_view MidiInCoreMIDIVirtual::getDescription()
const
220 return "Sends MIDI events from a newly created CoreMIDI virtual source.";
223void MidiInCoreMIDIVirtual::sendPacketList(
const MIDIPacketList *packetList,
224 void *readProcRefCon,
227 static_cast<MidiInCoreMIDIVirtual*
>(readProcRefCon)
228 ->sendPacketList(packetList, srcConnRefCon);
231void MidiInCoreMIDIVirtual::sendPacketList(
const MIDIPacketList *packetList,
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]);
241 packet = MIDIPacketNext(packet);
244 eventDistributor.distributeEvent(MidiInCoreMidiVirtualEvent());
248void MidiInCoreMIDIVirtual::signal(EmuTime::param time)
251 if (!connector->acceptsData()) {
252 std::scoped_lock lock(mutex);
256 if (!connector->ready()) {
262 std::scoped_lock lock(mutex);
263 if (queue.empty())
return;
264 data = queue.pop_front();
266 connector->recvByte(data, time);
270bool MidiInCoreMIDIVirtual::signalEvent(
const Event& )
273 signal(scheduler.getCurrentTime());
275 std::scoped_lock lock(mutex);
281template<
typename Archive>
282void MidiInCoreMIDIVirtual::serialize(Archive& ,
unsigned )
This file implemented 3 utility functions:
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
constexpr auto xrange(T e)