1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found in the LICENSE file.
4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
5eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "media/midi/midi_manager_mac.h"
6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <string>
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
9010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/bind.h"
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/message_loop/message_loop.h"
11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_number_conversions.h"
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/sys_string_conversions.h"
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <CoreAudio/HostTime.h>
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing base::IntToString;
17eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing base::SysCFStringRefToUTF8;
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing std::string;
19eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// NB: System MIDI types are pointer types in 32-bit and integer types in
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// 64-bit. Therefore, the initialization is the simplest one that satisfies both
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// (if possible).
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace media {
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManager* MidiManager::Create() {
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new MidiManagerMac();
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManagerMac::MidiManagerMac()
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    : midi_client_(0),
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      coremidi_input_(0),
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      coremidi_output_(0),
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      packet_list_(NULL),
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_packet_(NULL),
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      send_thread_("MidiSendThread") {
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void MidiManagerMac::StartInitialization() {
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // CoreMIDI registration.
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  midi_client_ = 0;
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  OSStatus result =
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      MIDIClientCreate(CFSTR("Chrome"), NULL, NULL, &midi_client_);
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (result != noErr)
46010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  coremidi_input_ = 0;
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Create input and output port.
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  result = MIDIInputPortCreate(
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      midi_client_,
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      CFSTR("MIDI Input"),
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ReadMidiDispatch,
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      this,
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      &coremidi_input_);
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (result != noErr)
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  result = MIDIOutputPortCreate(
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      midi_client_,
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      CFSTR("MIDI Output"),
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      &coremidi_output_);
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (result != noErr)
65010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
67424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  uint32 destination_count = MIDIGetNumberOfDestinations();
683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  destinations_.resize(destination_count);
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
70424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  for (uint32 i = 0; i < destination_count ; i++) {
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIEndpointRef destination = MIDIGetDestination(i);
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Keep track of all destinations (known as outputs by the Web MIDI API).
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Cache to avoid any possible overhead in calling MIDIGetDestination().
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    destinations_[i] = destination;
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MidiPortInfo info = GetPortInfoFromEndpoint(destination);
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    AddOutputPort(info);
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Open connections from all sources.
82424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  uint32 source_count = MIDIGetNumberOfSources();
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
84424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  for (uint32 i = 0; i < source_count; ++i)  {
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Receive from all sources.
86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIEndpointRef src = MIDIGetSource(i);
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Keep track of all sources (known as inputs in Web MIDI API terminology).
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    source_map_[src] = i;
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MidiPortInfo info = GetPortInfoFromEndpoint(src);
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    AddInputPort(info);
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  midi_packet_ = MIDIPacketListInit(packet_list_);
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
99010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  CompleteInitialization(MIDI_OK);
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          uint32 port_index,
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          const std::vector<uint8>& data,
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          double timestamp) {
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!send_thread_.IsRunning())
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    send_thread_.Start();
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // OK to use base::Unretained(this) since we join to thread in dtor().
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  send_thread_.message_loop()->PostTask(
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FROM_HERE,
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::Bind(&MidiManagerMac::SendMidiData, base::Unretained(this),
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 client, port_index, data, timestamp));
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManagerMac::~MidiManagerMac() {
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Wait for the termination of |send_thread_| before disposing MIDI ports.
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  send_thread_.Stop();
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (coremidi_input_)
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIPortDispose(coremidi_input_);
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (coremidi_output_)
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIPortDispose(coremidi_output_);
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1268bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// static
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                      void* read_proc_refcon,
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                      void* src_conn_refcon) {
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon);
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#if __LP64__
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#else
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#endif
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Dispatch to class method.
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  manager->ReadMidi(source, packet_list);
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerMac::ReadMidi(MIDIEndpointRef source,
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                              const MIDIPacketList* packet_list) {
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Lookup the port index based on the source.
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SourceMap::iterator j = source_map_.find(source);
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (j == source_map_.end())
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
147424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  uint32 port_index = source_map_[source];
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Go through each packet and process separately.
1501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  for (size_t i = 0; i < packet_list->numPackets; i++) {
151eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Each packet contains MIDI data for one or more messages (like note-on).
152eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const MIDIPacket &packet = packet_list->packet[i];
153eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp);
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ReceiveMidiData(
156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        port_index,
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        packet.data,
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        packet.length,
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        timestamp_seconds);
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerMac::SendMidiData(MidiManagerClient* client,
164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                                  uint32 port_index,
165424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                                  const std::vector<uint8>& data,
166eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                  double timestamp) {
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
169a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // System Exclusive has already been filtered.
170eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  midi_packet_ = MIDIPacketListAdd(
173eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      packet_list_,
174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      kMaxPacketListSize,
175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      midi_packet_,
176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      coremidi_timestamp,
177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      data.size(),
178424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      &data[0]);
179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
180eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Lookup the destination based on the port index.
181424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (static_cast<size_t>(port_index) >= destinations_.size())
182eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return;
183eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
184eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDIEndpointRef destination = destinations_[port_index];
185eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
186eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDISend(coremidi_output_, destination, packet_list_);
187eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Re-initialize for next time.
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  midi_packet_ = MIDIPacketListInit(packet_list_);
190a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  client->AccumulateMidiBytesSent(data.size());
192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// static
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiPortInfo MidiManagerMac::GetPortInfoFromEndpoint(
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    MIDIEndpointRef endpoint) {
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SInt32 id_number = 0;
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  string id = IntToString(id_number);
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  string manufacturer;
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CFStringRef manufacturer_ref = NULL;
2031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  OSStatus result = MIDIObjectGetStringProperty(
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
2051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (result == noErr) {
2061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
2071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  } else {
2081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // kMIDIPropertyManufacturer is not supported in IAC driver providing
2091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // endpoints, and the result will be kMIDIUnknownProperty (-10835).
2101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    DLOG(WARNING) << "Failed to get kMIDIPropertyManufacturer with status "
2111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                  << result;
2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
213eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  string name;
215eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  CFStringRef name_ref = NULL;
2161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
2171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (result == noErr)
2181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    name = SysCFStringRefToUTF8(name_ref);
2191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  else
2201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    DLOG(WARNING) << "Failed to get kMIDIPropertyName with status " << result;
221eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  string version;
223eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  SInt32 version_number = 0;
2241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  result = MIDIObjectGetIntegerProperty(
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      endpoint, kMIDIPropertyDriverVersion, &version_number);
2261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (result == noErr) {
2271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    version = IntToString(version_number);
2281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  } else {
2291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // kMIDIPropertyDriverVersion is not supported in IAC driver providing
2301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // endpoints, and the result will be kMIDIUnknownProperty (-10835).
2311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    DLOG(WARNING) << "Failed to get kMIDIPropertyDriverVersion with status "
2321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                  << result;
2331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return MidiPortInfo(id, manufacturer, name, version);
236eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// static
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)double MidiManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return static_cast<double>(nanoseconds) / 1.0e9;
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2448bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// static
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MIDITimeStamp MidiManagerMac::SecondsToMIDITimeStamp(double seconds) {
246eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  UInt64 nanos = UInt64(seconds * 1.0e9);
247eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return AudioConvertNanosToHostTime(nanos);
248eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
250eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace media
251