[Android] Always move bookmarks at the end of child list
[chromium-blink-merge.git] / media / midi / midi_manager_mac.cc
blobe9d56fb13add478a8c79e963981b485cabbc8174
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 namespace {
28 MidiPortInfo GetPortInfoFromEndpoint(
29 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 return MidiPortInfo(id, manufacturer, name, version);
71 double MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
72 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
73 return static_cast<double>(nanoseconds) / 1.0e9;
76 MIDITimeStamp SecondsToMIDITimeStamp(double seconds) {
77 UInt64 nanos = UInt64(seconds * 1.0e9);
78 return AudioConvertNanosToHostTime(nanos);
81 } // namespace
83 MidiManager* MidiManager::Create() {
84 return new MidiManagerMac();
87 MidiManagerMac::MidiManagerMac()
88 : midi_client_(0),
89 coremidi_input_(0),
90 coremidi_output_(0),
91 packet_list_(NULL),
92 midi_packet_(NULL),
93 client_thread_("MidiClientThread"),
94 shutdown_(false) {
97 MidiManagerMac::~MidiManagerMac() {
98 // Wait for the termination of |client_thread_| before disposing MIDI ports.
99 shutdown_ = true;
100 client_thread_.Stop();
102 if (coremidi_input_)
103 MIDIPortDispose(coremidi_input_);
104 if (coremidi_output_)
105 MIDIPortDispose(coremidi_output_);
106 if (midi_client_)
107 MIDIClientDispose(midi_client_);
110 void MidiManagerMac::StartInitialization() {
111 // MIDIClient should be created on |client_thread_| to receive CoreMIDI event
112 // notifications.
113 RunOnClientThread(
114 base::Bind(&MidiManagerMac::InitializeCoreMIDI, base::Unretained(this)));
117 void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
118 uint32 port_index,
119 const std::vector<uint8>& data,
120 double timestamp) {
121 RunOnClientThread(
122 base::Bind(&MidiManagerMac::SendMidiData,
123 base::Unretained(this), client, port_index, data, timestamp));
126 void MidiManagerMac::RunOnClientThread(const base::Closure& closure) {
127 if (shutdown_)
128 return;
130 if (!client_thread_.IsRunning())
131 client_thread_.Start();
133 client_thread_.message_loop()->PostTask(FROM_HERE, closure);
136 void MidiManagerMac::InitializeCoreMIDI() {
137 DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
139 // CoreMIDI registration.
140 // TODO(toyoshim): Set MIDINotifyProc to receive CoreMIDI event notifications.
141 midi_client_ = 0;
142 OSStatus result =
143 MIDIClientCreate(CFSTR("Chrome"), NULL, NULL, &midi_client_);
145 if (result != noErr)
146 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
148 coremidi_input_ = 0;
150 // Create input and output port.
151 result = MIDIInputPortCreate(
152 midi_client_,
153 CFSTR("MIDI Input"),
154 ReadMidiDispatch,
155 this,
156 &coremidi_input_);
157 if (result != noErr)
158 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
160 result = MIDIOutputPortCreate(
161 midi_client_,
162 CFSTR("MIDI Output"),
163 &coremidi_output_);
164 if (result != noErr)
165 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
167 uint32 destination_count = MIDIGetNumberOfDestinations();
168 destinations_.resize(destination_count);
170 for (uint32 i = 0; i < destination_count ; i++) {
171 MIDIEndpointRef destination = MIDIGetDestination(i);
173 // Keep track of all destinations (known as outputs by the Web MIDI API).
174 // Cache to avoid any possible overhead in calling MIDIGetDestination().
175 destinations_[i] = destination;
177 MidiPortInfo info = GetPortInfoFromEndpoint(destination);
178 AddOutputPort(info);
181 // Open connections from all sources.
182 uint32 source_count = MIDIGetNumberOfSources();
184 for (uint32 i = 0; i < source_count; ++i) {
185 // Receive from all sources.
186 MIDIEndpointRef src = MIDIGetSource(i);
187 MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
189 // Keep track of all sources (known as inputs in Web MIDI API terminology).
190 source_map_[src] = i;
192 MidiPortInfo info = GetPortInfoFromEndpoint(src);
193 AddInputPort(info);
196 packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
197 midi_packet_ = MIDIPacketListInit(packet_list_);
199 CompleteInitialization(MIDI_OK);
202 // static
203 void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
204 void* read_proc_refcon,
205 void* src_conn_refcon) {
206 // This method is called on a separate high-priority thread owned by CoreMIDI.
208 MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon);
209 #if __LP64__
210 MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
211 #else
212 MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
213 #endif
215 // Dispatch to class method.
216 manager->ReadMidi(source, packet_list);
219 void MidiManagerMac::ReadMidi(MIDIEndpointRef source,
220 const MIDIPacketList* packet_list) {
221 // This method is called from ReadMidiDispatch() and runs on a separate
222 // high-priority thread owned by CoreMIDI.
224 // Lookup the port index based on the source.
225 SourceMap::iterator j = source_map_.find(source);
226 if (j == source_map_.end())
227 return;
228 // This is safe since MidiManagerMac does not remove any existing
229 // MIDIEndpointRef, and the order is saved.
230 uint32 port_index = source_map_[source];
232 // Go through each packet and process separately.
233 for (size_t i = 0; i < packet_list->numPackets; i++) {
234 // Each packet contains MIDI data for one or more messages (like note-on).
235 const MIDIPacket &packet = packet_list->packet[i];
236 double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp);
238 ReceiveMidiData(
239 port_index,
240 packet.data,
241 packet.length,
242 timestamp_seconds);
246 void MidiManagerMac::SendMidiData(MidiManagerClient* client,
247 uint32 port_index,
248 const std::vector<uint8>& data,
249 double timestamp) {
250 DCHECK(client_thread_.message_loop_proxy()->BelongsToCurrentThread());
252 // System Exclusive has already been filtered.
253 MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
255 midi_packet_ = MIDIPacketListAdd(
256 packet_list_,
257 kMaxPacketListSize,
258 midi_packet_,
259 coremidi_timestamp,
260 data.size(),
261 &data[0]);
263 // Lookup the destination based on the port index.
264 if (static_cast<size_t>(port_index) >= destinations_.size())
265 return;
267 MIDIEndpointRef destination = destinations_[port_index];
269 MIDISend(coremidi_output_, destination, packet_list_);
271 // Re-initialize for next time.
272 midi_packet_ = MIDIPacketListInit(packet_list_);
274 client->AccumulateMidiBytesSent(data.size());
277 } // namespace media