Remove old about scheme URL constants.
[chromium-blink-merge.git] / media / midi / midi_manager_mac.cc
bloba36d1debe13d61adfcc723c6952b88ddfa3ce85f
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/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;
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 bool MIDIManagerMac::Initialize() {
40 TRACE_EVENT0("midi", "MIDIManagerMac::Initialize");
42 // CoreMIDI registration.
43 midi_client_ = 0;
44 OSStatus result = MIDIClientCreate(
45 CFSTR("Google Chrome"),
46 NULL,
47 NULL,
48 &midi_client_);
50 if (result != noErr)
51 return false;
53 coremidi_input_ = 0;
55 // Create input and output port.
56 result = MIDIInputPortCreate(
57 midi_client_,
58 CFSTR("MIDI Input"),
59 ReadMIDIDispatch,
60 this,
61 &coremidi_input_);
62 if (result != noErr)
63 return false;
65 result = MIDIOutputPortCreate(
66 midi_client_,
67 CFSTR("MIDI Output"),
68 &coremidi_output_);
69 if (result != noErr)
70 return false;
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);
83 AddOutputPort(info);
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).
95 source_map_[src] = i;
97 MIDIPortInfo info = GetPortInfoFromEndpoint(src);
98 AddInputPort(info);
101 // TODO(crogers): Fix the memory management here!
102 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
103 midi_packet_ = MIDIPacketListInit(packet_list_);
105 return true;
108 void MIDIManagerMac::DispatchSendMIDIData(MIDIManagerClient* client,
109 uint32 port_index,
110 const std::vector<uint8>& data,
111 double timestamp) {
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(
117 FROM_HERE,
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.
124 send_thread_.Stop();
126 if (coremidi_input_)
127 MIDIPortDispose(coremidi_input_);
128 if (coremidi_output_)
129 MIDIPortDispose(coremidi_output_);
132 // static
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);
137 #if __LP64__
138 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
139 #else
140 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
141 #endif
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())
152 return;
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);
161 ReceiveMIDIData(
162 port_index,
163 packet.data,
164 packet.length,
165 timestamp_seconds);
169 void MIDIManagerMac::SendMIDIData(MIDIManagerClient* client,
170 uint32 port_index,
171 const std::vector<uint8>& data,
172 double timestamp) {
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(
179 packet_list_,
180 kMaxPacketListSize,
181 midi_packet_,
182 coremidi_timestamp,
183 data.size(),
184 &data[0]);
186 // Lookup the destination based on the port index.
187 if (static_cast<size_t>(port_index) >= destinations_.size())
188 return;
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());
200 // static
201 MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint(
202 MIDIEndpointRef endpoint) {
203 SInt32 id_number = 0;
204 MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
205 string id = IntToString(id_number);
207 string manufacturer;
208 CFStringRef manufacturer_ref = NULL;
209 OSStatus result = MIDIObjectGetStringProperty(
210 endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
211 if (result == noErr) {
212 manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
213 } else {
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 "
217 << result;
220 string name;
221 CFStringRef name_ref = NULL;
222 result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
223 if (result == noErr)
224 name = SysCFStringRefToUTF8(name_ref);
225 else
226 DLOG(WARNING) << "Failed to get kMIDIPropertyName with status " << result;
228 string version;
229 SInt32 version_number = 0;
230 result = MIDIObjectGetIntegerProperty(
231 endpoint, kMIDIPropertyDriverVersion, &version_number);
232 if (result == noErr) {
233 version = IntToString(version_number);
234 } else {
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 "
238 << result;
241 return MIDIPortInfo(id, manufacturer, name, version);
244 // static
245 double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
246 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
247 return static_cast<double>(nanoseconds) / 1.0e9;
250 // static
251 MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) {
252 UInt64 nanos = UInt64(seconds * 1.0e9);
253 return AudioConvertNanosToHostTime(nanos);
256 } // namespace media