Temporarily re-enabling SizeAfterPrefChange test with traces (this time for Linux...
[chromium-blink-merge.git] / media / midi / midi_manager_mac.cc
blobc1302e6c80238a6415d0494d9b1af8658bf52805
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 <string>
9 #include "base/bind.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;
18 using std::string;
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
22 // (if possible).
24 namespace media {
26 MidiManager* MidiManager::Create() {
27 return new MidiManagerMac();
30 MidiManagerMac::MidiManagerMac()
31 : midi_client_(0),
32 coremidi_input_(0),
33 coremidi_output_(0),
34 packet_list_(NULL),
35 midi_packet_(NULL),
36 send_thread_("MidiSendThread") {
39 void MidiManagerMac::StartInitialization() {
40 // CoreMIDI registration.
41 midi_client_ = 0;
42 OSStatus result =
43 MIDIClientCreate(CFSTR("Chrome"), NULL, NULL, &midi_client_);
45 if (result != noErr)
46 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
48 coremidi_input_ = 0;
50 // Create input and output port.
51 result = MIDIInputPortCreate(
52 midi_client_,
53 CFSTR("MIDI Input"),
54 ReadMidiDispatch,
55 this,
56 &coremidi_input_);
57 if (result != noErr)
58 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
60 result = MIDIOutputPortCreate(
61 midi_client_,
62 CFSTR("MIDI Output"),
63 &coremidi_output_);
64 if (result != noErr)
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);
78 AddOutputPort(info);
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).
90 source_map_[src] = i;
92 MidiPortInfo info = GetPortInfoFromEndpoint(src);
93 AddInputPort(info);
96 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
97 midi_packet_ = MIDIPacketListInit(packet_list_);
99 CompleteInitialization(MIDI_OK);
102 void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
103 uint32 port_index,
104 const std::vector<uint8>& data,
105 double timestamp) {
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(
111 FROM_HERE,
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.
118 send_thread_.Stop();
120 if (coremidi_input_)
121 MIDIPortDispose(coremidi_input_);
122 if (coremidi_output_)
123 MIDIPortDispose(coremidi_output_);
126 // static
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);
131 #if __LP64__
132 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
133 #else
134 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
135 #endif
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())
146 return;
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);
155 ReceiveMidiData(
156 port_index,
157 packet.data,
158 packet.length,
159 timestamp_seconds);
163 void MidiManagerMac::SendMidiData(MidiManagerClient* client,
164 uint32 port_index,
165 const std::vector<uint8>& data,
166 double timestamp) {
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(
173 packet_list_,
174 kMaxPacketListSize,
175 midi_packet_,
176 coremidi_timestamp,
177 data.size(),
178 &data[0]);
180 // Lookup the destination based on the port index.
181 if (static_cast<size_t>(port_index) >= destinations_.size())
182 return;
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());
194 // static
195 MidiPortInfo MidiManagerMac::GetPortInfoFromEndpoint(
196 MIDIEndpointRef endpoint) {
197 SInt32 id_number = 0;
198 MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
199 string id = IntToString(id_number);
201 string manufacturer;
202 CFStringRef manufacturer_ref = NULL;
203 OSStatus result = MIDIObjectGetStringProperty(
204 endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
205 if (result == noErr) {
206 manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
207 } else {
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 "
211 << result;
214 string name;
215 CFStringRef name_ref = NULL;
216 result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
217 if (result == noErr)
218 name = SysCFStringRefToUTF8(name_ref);
219 else
220 DLOG(WARNING) << "Failed to get kMIDIPropertyName with status " << result;
222 string version;
223 SInt32 version_number = 0;
224 result = MIDIObjectGetIntegerProperty(
225 endpoint, kMIDIPropertyDriverVersion, &version_number);
226 if (result == noErr) {
227 version = IntToString(version_number);
228 } else {
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 "
232 << result;
235 return MidiPortInfo(id, manufacturer, name, version);
238 // static
239 double MidiManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
240 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
241 return static_cast<double>(nanoseconds) / 1.0e9;
244 // static
245 MIDITimeStamp MidiManagerMac::SecondsToMIDITimeStamp(double seconds) {
246 UInt64 nanos = UInt64(seconds * 1.0e9);
247 return AudioConvertNanosToHostTime(nanos);
250 } // namespace media