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