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"
9 #include "base/debug/trace_event.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 bool MidiManagerMac::Initialize() {
40 TRACE_EVENT0("midi", "MidiManagerMac::Initialize");
42 // CoreMIDI registration.
44 OSStatus result = MIDIClientCreate(
45 CFSTR("Google Chrome"),
55 // Create input and output port.
56 result = MIDIInputPortCreate(
65 result = MIDIOutputPortCreate(
72 uint32 destination_count = MIDIGetNumberOfDestinations();
73 destinations_.resize(destination_count);
75 for (uint32 i = 0; i < destination_count ; i++) {
76 MIDIEndpointRef destination = MIDIGetDestination(i);
78 // Keep track of all destinations (known as outputs by the Web MIDI API).
79 // Cache to avoid any possible overhead in calling MIDIGetDestination().
80 destinations_[i] = destination;
82 MidiPortInfo info = GetPortInfoFromEndpoint(destination);
86 // Open connections from all sources.
87 uint32 source_count = MIDIGetNumberOfSources();
89 for (uint32 i = 0; i < source_count; ++i) {
90 // Receive from all sources.
91 MIDIEndpointRef src = MIDIGetSource(i);
92 MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
94 // Keep track of all sources (known as inputs in Web MIDI API terminology).
97 MidiPortInfo info = GetPortInfoFromEndpoint(src);
101 // TODO(toyoshim): Fix the memory management here!
102 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
103 midi_packet_ = MIDIPacketListInit(packet_list_);
108 void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
110 const std::vector<uint8>& data,
112 if (!send_thread_.IsRunning())
113 send_thread_.Start();
115 // OK to use base::Unretained(this) since we join to thread in dtor().
116 send_thread_.message_loop()->PostTask(
118 base::Bind(&MidiManagerMac::SendMidiData, base::Unretained(this),
119 client, port_index, data, timestamp));
122 MidiManagerMac::~MidiManagerMac() {
123 // Wait for the termination of |send_thread_| before disposing MIDI ports.
127 MIDIPortDispose(coremidi_input_);
128 if (coremidi_output_)
129 MIDIPortDispose(coremidi_output_);
133 void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
134 void* read_proc_refcon,
135 void* src_conn_refcon) {
136 MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon);
138 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
140 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
143 // Dispatch to class method.
144 manager->ReadMidi(source, packet_list);
147 void MidiManagerMac::ReadMidi(MIDIEndpointRef source,
148 const MIDIPacketList* packet_list) {
149 // Lookup the port index based on the source.
150 SourceMap::iterator j = source_map_.find(source);
151 if (j == source_map_.end())
153 uint32 port_index = source_map_[source];
155 // Go through each packet and process separately.
156 for (size_t i = 0; i < packet_list->numPackets; i++) {
157 // Each packet contains MIDI data for one or more messages (like note-on).
158 const MIDIPacket &packet = packet_list->packet[i];
159 double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp);
169 void MidiManagerMac::SendMidiData(MidiManagerClient* client,
171 const std::vector<uint8>& data,
173 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
175 // System Exclusive has already been filtered.
176 MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
178 midi_packet_ = MIDIPacketListAdd(
186 // Lookup the destination based on the port index.
187 if (static_cast<size_t>(port_index) >= destinations_.size())
190 MIDIEndpointRef destination = destinations_[port_index];
192 MIDISend(coremidi_output_, destination, packet_list_);
194 // Re-initialize for next time.
195 midi_packet_ = MIDIPacketListInit(packet_list_);
197 client->AccumulateMidiBytesSent(data.size());
201 MidiPortInfo MidiManagerMac::GetPortInfoFromEndpoint(
202 MIDIEndpointRef endpoint) {
203 SInt32 id_number = 0;
204 MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
205 string id = IntToString(id_number);
208 CFStringRef manufacturer_ref = NULL;
209 OSStatus result = MIDIObjectGetStringProperty(
210 endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
211 if (result == noErr) {
212 manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
214 // kMIDIPropertyManufacturer is not supported in IAC driver providing
215 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
216 DLOG(WARNING) << "Failed to get kMIDIPropertyManufacturer with status "
221 CFStringRef name_ref = NULL;
222 result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
224 name = SysCFStringRefToUTF8(name_ref);
226 DLOG(WARNING) << "Failed to get kMIDIPropertyName with status " << result;
229 SInt32 version_number = 0;
230 result = MIDIObjectGetIntegerProperty(
231 endpoint, kMIDIPropertyDriverVersion, &version_number);
232 if (result == noErr) {
233 version = IntToString(version_number);
235 // kMIDIPropertyDriverVersion is not supported in IAC driver providing
236 // endpoints, and the result will be kMIDIUnknownProperty (-10835).
237 DLOG(WARNING) << "Failed to get kMIDIPropertyDriverVersion with status "
241 return MidiPortInfo(id, manufacturer, name, version);
245 double MidiManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
246 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
247 return static_cast<double>(nanoseconds) / 1.0e9;
251 MIDITimeStamp MidiManagerMac::SecondsToMIDITimeStamp(double seconds) {
252 UInt64 nanos = UInt64(seconds * 1.0e9);
253 return AudioConvertNanosToHostTime(nanos);