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"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/sys_string_conversions.h"
14 #include <CoreAudio/HostTime.h>
16 using base::IntToString
;
17 using base::SysCFStringRefToUTF8
;
20 // NB: System MIDI types are pointer types in 32-bit and integer types in
21 // 64-bit. Therefore, the initialization is the simplest one that satisfies both
26 MidiManager
* MidiManager::Create() {
27 return new MidiManagerMac();
30 MidiManagerMac::MidiManagerMac()
36 send_thread_("MidiSendThread") {
39 void MidiManagerMac::StartInitialization() {
40 // CoreMIDI registration.
43 MIDIClientCreate(CFSTR("Chrome"), NULL
, NULL
, &midi_client_
);
46 return CompleteInitialization(MIDI_INITIALIZATION_ERROR
);
50 // Create input and output port.
51 result
= MIDIInputPortCreate(
58 return CompleteInitialization(MIDI_INITIALIZATION_ERROR
);
60 result
= MIDIOutputPortCreate(
65 return CompleteInitialization(MIDI_INITIALIZATION_ERROR
);
67 uint32 destination_count
= MIDIGetNumberOfDestinations();
68 destinations_
.resize(destination_count
);
70 for (uint32 i
= 0; i
< destination_count
; i
++) {
71 MIDIEndpointRef destination
= MIDIGetDestination(i
);
73 // Keep track of all destinations (known as outputs by the Web MIDI API).
74 // Cache to avoid any possible overhead in calling MIDIGetDestination().
75 destinations_
[i
] = destination
;
77 MidiPortInfo info
= GetPortInfoFromEndpoint(destination
);
81 // Open connections from all sources.
82 uint32 source_count
= MIDIGetNumberOfSources();
84 for (uint32 i
= 0; i
< source_count
; ++i
) {
85 // Receive from all sources.
86 MIDIEndpointRef src
= MIDIGetSource(i
);
87 MIDIPortConnectSource(coremidi_input_
, src
, reinterpret_cast<void*>(src
));
89 // Keep track of all sources (known as inputs in Web MIDI API terminology).
92 MidiPortInfo info
= GetPortInfoFromEndpoint(src
);
96 packet_list_
= reinterpret_cast<MIDIPacketList
*>(midi_buffer_
);
97 midi_packet_
= MIDIPacketListInit(packet_list_
);
99 CompleteInitialization(MIDI_OK
);
102 void MidiManagerMac::DispatchSendMidiData(MidiManagerClient
* client
,
104 const std::vector
<uint8
>& data
,
106 if (!send_thread_
.IsRunning())
107 send_thread_
.Start();
109 // OK to use base::Unretained(this) since we join to thread in dtor().
110 send_thread_
.message_loop()->PostTask(
112 base::Bind(&MidiManagerMac::SendMidiData
, base::Unretained(this),
113 client
, port_index
, data
, timestamp
));
116 MidiManagerMac::~MidiManagerMac() {
117 // Wait for the termination of |send_thread_| before disposing MIDI ports.
121 MIDIPortDispose(coremidi_input_
);
122 if (coremidi_output_
)
123 MIDIPortDispose(coremidi_output_
);
127 void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList
* packet_list
,
128 void* read_proc_refcon
,
129 void* src_conn_refcon
) {
130 MidiManagerMac
* manager
= static_cast<MidiManagerMac
*>(read_proc_refcon
);
132 MIDIEndpointRef source
= reinterpret_cast<uintptr_t>(src_conn_refcon
);
134 MIDIEndpointRef source
= static_cast<MIDIEndpointRef
>(src_conn_refcon
);
137 // Dispatch to class method.
138 manager
->ReadMidi(source
, packet_list
);
141 void MidiManagerMac::ReadMidi(MIDIEndpointRef source
,
142 const MIDIPacketList
* packet_list
) {
143 // Lookup the port index based on the source.
144 SourceMap::iterator j
= source_map_
.find(source
);
145 if (j
== source_map_
.end())
147 uint32 port_index
= source_map_
[source
];
149 // Go through each packet and process separately.
150 for (size_t i
= 0; i
< packet_list
->numPackets
; i
++) {
151 // Each packet contains MIDI data for one or more messages (like note-on).
152 const MIDIPacket
&packet
= packet_list
->packet
[i
];
153 double timestamp_seconds
= MIDITimeStampToSeconds(packet
.timeStamp
);
163 void MidiManagerMac::SendMidiData(MidiManagerClient
* client
,
165 const std::vector
<uint8
>& data
,
167 DCHECK(send_thread_
.message_loop_proxy()->BelongsToCurrentThread());
169 // System Exclusive has already been filtered.
170 MIDITimeStamp coremidi_timestamp
= SecondsToMIDITimeStamp(timestamp
);
172 midi_packet_
= MIDIPacketListAdd(
180 // Lookup the destination based on the port index.
181 if (static_cast<size_t>(port_index
) >= destinations_
.size())
184 MIDIEndpointRef destination
= destinations_
[port_index
];
186 MIDISend(coremidi_output_
, destination
, packet_list_
);
188 // Re-initialize for next time.
189 midi_packet_
= MIDIPacketListInit(packet_list_
);
191 client
->AccumulateMidiBytesSent(data
.size());
195 MidiPortInfo
MidiManagerMac::GetPortInfoFromEndpoint(
196 MIDIEndpointRef endpoint
) {
197 SInt32 id_number
= 0;
198 MIDIObjectGetIntegerProperty(endpoint
, kMIDIPropertyUniqueID
, &id_number
);
199 string id
= IntToString(id_number
);
202 CFStringRef manufacturer_ref
= NULL
;
203 OSStatus result
= MIDIObjectGetStringProperty(
204 endpoint
, kMIDIPropertyManufacturer
, &manufacturer_ref
);
205 if (result
== noErr
) {
206 manufacturer
= SysCFStringRefToUTF8(manufacturer_ref
);
208 // kMIDIPropertyManufacturer is not supported in IAC driver providing
209 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
210 DLOG(WARNING
) << "Failed to get kMIDIPropertyManufacturer with status "
215 CFStringRef name_ref
= NULL
;
216 result
= MIDIObjectGetStringProperty(endpoint
, kMIDIPropertyName
, &name_ref
);
218 name
= SysCFStringRefToUTF8(name_ref
);
220 DLOG(WARNING
) << "Failed to get kMIDIPropertyName with status " << result
;
223 SInt32 version_number
= 0;
224 result
= MIDIObjectGetIntegerProperty(
225 endpoint
, kMIDIPropertyDriverVersion
, &version_number
);
226 if (result
== noErr
) {
227 version
= IntToString(version_number
);
229 // kMIDIPropertyDriverVersion is not supported in IAC driver providing
230 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
231 DLOG(WARNING
) << "Failed to get kMIDIPropertyDriverVersion with status "
235 return MidiPortInfo(id
, manufacturer
, name
, version
);
239 double MidiManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp
) {
240 UInt64 nanoseconds
= AudioConvertHostTimeToNanos(timestamp
);
241 return static_cast<double>(nanoseconds
) / 1.0e9
;
245 MIDITimeStamp
MidiManagerMac::SecondsToMIDITimeStamp(double seconds
) {
246 UInt64 nanos
= UInt64(seconds
* 1.0e9
);
247 return AudioConvertNanosToHostTime(nanos
);