[Ozone-Gbm] Explicitly crash if trying software rendering on GBM
[chromium-blink-merge.git] / media / midi / midi_manager_mac.cc
blob0843ff3bca455e07c8b5973452146d1647c01303
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/midi/midi_manager_mac.h"
7 #include <algorithm>
8 #include <string>
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/sys_string_conversions.h"
15 #include <CoreAudio/HostTime.h>
17 using base::IntToString;
18 using base::SysCFStringRefToUTF8;
19 using std::string;
21 // NB: System MIDI types are pointer types in 32-bit and integer types in
22 // 64-bit. Therefore, the initialization is the simplest one that satisfies both
23 // (if possible).
25 namespace media {
27 namespace {
29 MidiPortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint) {
30 SInt32 id_number = 0;
31 MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
32 string id = IntToString(id_number);
34 string manufacturer;
35 CFStringRef manufacturer_ref = NULL;
36 OSStatus result = MIDIObjectGetStringProperty(
37 endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
38 if (result == noErr) {
39 manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
40 } else {
41 // kMIDIPropertyManufacturer is not supported in IAC driver providing
42 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
43 DLOG(WARNING) << "Failed to get kMIDIPropertyManufacturer with status "
44 << result;
47 string name;
48 CFStringRef name_ref = NULL;
49 result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
50 if (result == noErr)
51 name = SysCFStringRefToUTF8(name_ref);
52 else
53 DLOG(WARNING) << "Failed to get kMIDIPropertyName with status " << result;
55 string version;
56 SInt32 version_number = 0;
57 result = MIDIObjectGetIntegerProperty(
58 endpoint, kMIDIPropertyDriverVersion, &version_number);
59 if (result == noErr) {
60 version = IntToString(version_number);
61 } else {
62 // kMIDIPropertyDriverVersion is not supported in IAC driver providing
63 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
64 DLOG(WARNING) << "Failed to get kMIDIPropertyDriverVersion with status "
65 << result;
68 const MidiPortState state = MIDI_PORT_OPENED;
69 return MidiPortInfo(id, manufacturer, name, version, state);
72 double MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
73 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
74 return static_cast<double>(nanoseconds) / 1.0e9;
77 MIDITimeStamp SecondsToMIDITimeStamp(double seconds) {
78 UInt64 nanos = UInt64(seconds * 1.0e9);
79 return AudioConvertNanosToHostTime(nanos);
82 } // namespace
84 MidiManager* MidiManager::Create() {
85 return new MidiManagerMac();
88 MidiManagerMac::MidiManagerMac()
89 : midi_client_(0),
90 coremidi_input_(0),
91 coremidi_output_(0),
92 packet_list_(NULL),
93 midi_packet_(NULL),
94 client_thread_("MidiClientThread"),
95 shutdown_(false) {
98 MidiManagerMac::~MidiManagerMac() {
99 // Wait for the termination of |client_thread_| before disposing MIDI ports.
100 shutdown_ = true;
101 client_thread_.Stop();
103 if (coremidi_input_)
104 MIDIPortDispose(coremidi_input_);
105 if (coremidi_output_)
106 MIDIPortDispose(coremidi_output_);
107 if (midi_client_)
108 MIDIClientDispose(midi_client_);
111 void MidiManagerMac::StartInitialization() {
112 // MIDIClient should be created on |client_thread_| to receive CoreMIDI event
113 // notifications.
114 RunOnClientThread(
115 base::Bind(&MidiManagerMac::InitializeCoreMIDI, base::Unretained(this)));
118 void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
119 uint32 port_index,
120 const std::vector<uint8>& data,
121 double timestamp) {
122 RunOnClientThread(
123 base::Bind(&MidiManagerMac::SendMidiData,
124 base::Unretained(this), client, port_index, data, timestamp));
127 void MidiManagerMac::RunOnClientThread(const base::Closure& closure) {
128 if (shutdown_)
129 return;
131 if (!client_thread_.IsRunning())
132 client_thread_.Start();
134 client_thread_.message_loop()->PostTask(FROM_HERE, closure);
137 void MidiManagerMac::InitializeCoreMIDI() {
138 DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
140 // CoreMIDI registration.
141 midi_client_ = 0;
142 OSStatus result =
143 MIDIClientCreate(CFSTR("Chrome"), ReceiveMidiNotifyDispatch, this,
144 &midi_client_);
146 if (result != noErr)
147 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
149 coremidi_input_ = 0;
151 // Create input and output port.
152 result = MIDIInputPortCreate(
153 midi_client_,
154 CFSTR("MIDI Input"),
155 ReadMidiDispatch,
156 this,
157 &coremidi_input_);
158 if (result != noErr)
159 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
161 result = MIDIOutputPortCreate(
162 midi_client_,
163 CFSTR("MIDI Output"),
164 &coremidi_output_);
165 if (result != noErr)
166 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
168 uint32 destination_count = MIDIGetNumberOfDestinations();
169 destinations_.resize(destination_count);
171 for (uint32 i = 0; i < destination_count ; i++) {
172 MIDIEndpointRef destination = MIDIGetDestination(i);
174 // Keep track of all destinations (known as outputs by the Web MIDI API).
175 // Cache to avoid any possible overhead in calling MIDIGetDestination().
176 destinations_[i] = destination;
178 MidiPortInfo info = GetPortInfoFromEndpoint(destination);
179 AddOutputPort(info);
182 // Open connections from all sources.
183 uint32 source_count = MIDIGetNumberOfSources();
185 for (uint32 i = 0; i < source_count; ++i) {
186 // Receive from all sources.
187 MIDIEndpointRef src = MIDIGetSource(i);
188 MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
190 // Keep track of all sources (known as inputs in Web MIDI API terminology).
191 source_map_[src] = i;
193 MidiPortInfo info = GetPortInfoFromEndpoint(src);
194 AddInputPort(info);
197 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
198 midi_packet_ = MIDIPacketListInit(packet_list_);
200 CompleteInitialization(MIDI_OK);
203 // static
204 void MidiManagerMac::ReceiveMidiNotifyDispatch(const MIDINotification* message,
205 void* refcon) {
206 MidiManagerMac* manager = static_cast<MidiManagerMac*>(refcon);
207 manager->ReceiveMidiNotify(message);
210 void MidiManagerMac::ReceiveMidiNotify(const MIDINotification* message) {
211 DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
213 if (kMIDIMsgObjectAdded == message->messageID) {
214 const MIDIObjectAddRemoveNotification* notification =
215 reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
216 MIDIEndpointRef endpoint =
217 static_cast<MIDIEndpointRef>(notification->child);
218 if (notification->childType == kMIDIObjectType_Source) {
219 SourceMap::iterator it = source_map_.find(endpoint);
220 if (it == source_map_.end()) {
221 uint32 index = source_map_.size();
222 source_map_[endpoint] = index;
223 MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
224 AddInputPort(info);
225 MIDIPortConnectSource(
226 coremidi_input_, endpoint, reinterpret_cast<void*>(endpoint));
227 } else {
228 uint32 index = it->second;
229 SetInputPortState(index, MIDI_PORT_OPENED);
231 } else if (notification->childType == kMIDIObjectType_Destination) {
232 auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
233 if (i != destinations_.end()) {
234 SetOutputPortState(i - destinations_.begin(), MIDI_PORT_OPENED);
235 } else {
236 destinations_.push_back(endpoint);
237 MidiPortInfo info = GetPortInfoFromEndpoint(endpoint);
238 AddOutputPort(info);
241 } else if (kMIDIMsgObjectRemoved == message->messageID) {
242 const MIDIObjectAddRemoveNotification* notification =
243 reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
244 MIDIEndpointRef endpoint =
245 static_cast<MIDIEndpointRef>(notification->child);
246 if (notification->childType == kMIDIObjectType_Source) {
247 SourceMap::iterator it = source_map_.find(endpoint);
248 if (it != source_map_.end()) {
249 uint32 index = it->second;
250 SetInputPortState(index, MIDI_PORT_DISCONNECTED);
252 } else if (notification->childType == kMIDIObjectType_Destination) {
253 auto i = std::find(destinations_.begin(), destinations_.end(), endpoint);
254 if (i != destinations_.end())
255 SetOutputPortState(i - destinations_.begin(), MIDI_PORT_DISCONNECTED);
260 // static
261 void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
262 void* read_proc_refcon,
263 void* src_conn_refcon) {
264 // This method is called on a separate high-priority thread owned by CoreMIDI.
266 MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon);
267 #if __LP64__
268 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
269 #else
270 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
271 #endif
273 // Dispatch to class method.
274 manager->ReadMidi(source, packet_list);
277 void MidiManagerMac::ReadMidi(MIDIEndpointRef source,
278 const MIDIPacketList* packet_list) {
279 // This method is called from ReadMidiDispatch() and runs on a separate
280 // high-priority thread owned by CoreMIDI.
282 // Lookup the port index based on the source.
283 SourceMap::iterator j = source_map_.find(source);
284 if (j == source_map_.end())
285 return;
286 // This is safe since MidiManagerMac does not remove any existing
287 // MIDIEndpointRef, and the order is saved.
288 uint32 port_index = source_map_[source];
290 // Go through each packet and process separately.
291 const MIDIPacket* packet = &packet_list->packet[0];
292 for (size_t i = 0; i < packet_list->numPackets; i++) {
293 // Each packet contains MIDI data for one or more messages (like note-on).
294 double timestamp_seconds = MIDITimeStampToSeconds(packet->timeStamp);
296 ReceiveMidiData(
297 port_index,
298 packet->data,
299 packet->length,
300 timestamp_seconds);
302 packet = MIDIPacketNext(packet);
306 void MidiManagerMac::SendMidiData(MidiManagerClient* client,
307 uint32 port_index,
308 const std::vector<uint8>& data,
309 double timestamp) {
310 DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
312 // System Exclusive has already been filtered.
313 MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
315 midi_packet_ = MIDIPacketListAdd(
316 packet_list_,
317 kMaxPacketListSize,
318 midi_packet_,
319 coremidi_timestamp,
320 data.size(),
321 &data[0]);
323 // Lookup the destination based on the port index.
324 if (static_cast<size_t>(port_index) >= destinations_.size())
325 return;
327 MIDIEndpointRef destination = destinations_[port_index];
329 MIDISend(coremidi_output_, destination, packet_list_);
331 // Re-initialize for next time.
332 midi_packet_ = MIDIPacketListInit(packet_list_);
334 client->AccumulateMidiBytesSent(data.size());
337 } // namespace media