Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / media / midi / midi_manager_mac.cc
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.
4
5 #include "media/midi/midi_manager_mac.h"
6
7 #include <string>
8
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"
13
14 #include <CoreAudio/HostTime.h>
15
16 using base::IntToString;
17 using base::SysCFStringRefToUTF8;
18 using std::string;
19
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).
23
24 namespace media {
25
26 MidiManager* MidiManager::Create() {
27   return new MidiManagerMac();
28 }
29
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") {
37 }
38
39 bool MidiManagerMac::Initialize() {
40   TRACE_EVENT0("midi", "MidiManagerMac::Initialize");
41
42   // CoreMIDI registration.
43   midi_client_ = 0;
44   OSStatus result = MIDIClientCreate(
45       CFSTR("Google Chrome"),
46       NULL,
47       NULL,
48       &midi_client_);
49
50   if (result != noErr)
51     return false;
52
53   coremidi_input_ = 0;
54
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;
64
65   result = MIDIOutputPortCreate(
66       midi_client_,
67       CFSTR("MIDI Output"),
68       &coremidi_output_);
69   if (result != noErr)
70     return false;
71
72   uint32 destination_count = MIDIGetNumberOfDestinations();
73   destinations_.resize(destination_count);
74
75   for (uint32 i = 0; i < destination_count ; i++) {
76     MIDIEndpointRef destination = MIDIGetDestination(i);
77
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;
81
82     MidiPortInfo info = GetPortInfoFromEndpoint(destination);
83     AddOutputPort(info);
84   }
85
86   // Open connections from all sources.
87   uint32 source_count = MIDIGetNumberOfSources();
88
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));
93
94     // Keep track of all sources (known as inputs in Web MIDI API terminology).
95     source_map_[src] = i;
96
97     MidiPortInfo info = GetPortInfoFromEndpoint(src);
98     AddInputPort(info);
99   }
100
101   // TODO(toyoshim): Fix the memory management here!
102   packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
103   midi_packet_ = MIDIPacketListInit(packet_list_);
104
105   return true;
106 }
107
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();
114
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));
120 }
121
122 MidiManagerMac::~MidiManagerMac() {
123   // Wait for the termination of |send_thread_| before disposing MIDI ports.
124   send_thread_.Stop();
125
126   if (coremidi_input_)
127     MIDIPortDispose(coremidi_input_);
128   if (coremidi_output_)
129     MIDIPortDispose(coremidi_output_);
130 }
131
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
142
143   // Dispatch to class method.
144   manager->ReadMidi(source, packet_list);
145 }
146
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];
154
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);
160
161     ReceiveMidiData(
162         port_index,
163         packet.data,
164         packet.length,
165         timestamp_seconds);
166   }
167 }
168
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());
174
175   // System Exclusive has already been filtered.
176   MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
177
178   midi_packet_ = MIDIPacketListAdd(
179       packet_list_,
180       kMaxPacketListSize,
181       midi_packet_,
182       coremidi_timestamp,
183       data.size(),
184       &data[0]);
185
186   // Lookup the destination based on the port index.
187   if (static_cast<size_t>(port_index) >= destinations_.size())
188     return;
189
190   MIDIEndpointRef destination = destinations_[port_index];
191
192   MIDISend(coremidi_output_, destination, packet_list_);
193
194   // Re-initialize for next time.
195   midi_packet_ = MIDIPacketListInit(packet_list_);
196
197   client->AccumulateMidiBytesSent(data.size());
198 }
199
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);
206
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;
218   }
219
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;
227
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;
239   }
240
241   return MidiPortInfo(id, manufacturer, name, version);
242 }
243
244 // static
245 double MidiManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
246   UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
247   return static_cast<double>(nanoseconds) / 1.0e9;
248 }
249
250 // static
251 MIDITimeStamp MidiManagerMac::SecondsToMIDITimeStamp(double seconds) {
252   UInt64 nanos = UInt64(seconds * 1.0e9);
253   return AudioConvertNanosToHostTime(nanos);
254 }
255
256 }  // namespace media