midi_manager_mac.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 <iostream>
8#include <string>
9
10#include "base/debug/trace_event.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/sys_string_conversions.h"
13#include <CoreAudio/HostTime.h>
14
15using base::IntToString;
16using base::SysCFStringRefToUTF8;
17using std::string;
18
19// NB: System MIDI types are pointer types in 32-bit and integer types in
20// 64-bit. Therefore, the initialization is the simplest one that satisfies both
21// (if possible).
22
23namespace media {
24
25MIDIManager* MIDIManager::Create() {
26  return new MIDIManagerMac();
27}
28
29MIDIManagerMac::MIDIManagerMac()
30    : midi_client_(0),
31      coremidi_input_(0),
32      coremidi_output_(0),
33      packet_list_(NULL),
34      midi_packet_(NULL) {
35}
36
37bool MIDIManagerMac::Initialize() {
38  TRACE_EVENT0("midi", "MIDIManagerMac::Initialize");
39
40  // CoreMIDI registration.
41  midi_client_ = 0;
42  OSStatus result = MIDIClientCreate(
43      CFSTR("Google Chrome"),
44      NULL,
45      NULL,
46      &midi_client_);
47
48  if (result != noErr)
49    return false;
50
51  coremidi_input_ = 0;
52
53  // Create input and output port.
54  result = MIDIInputPortCreate(
55      midi_client_,
56      CFSTR("MIDI Input"),
57      ReadMIDIDispatch,
58      this,
59      &coremidi_input_);
60  if (result != noErr)
61    return false;
62
63  result = MIDIOutputPortCreate(
64      midi_client_,
65      CFSTR("MIDI Output"),
66      &coremidi_output_);
67  if (result != noErr)
68    return false;
69
70  uint32 destination_count = MIDIGetNumberOfDestinations();
71  destinations_.resize(destination_count);
72
73  for (uint32 i = 0; i < destination_count ; i++) {
74    MIDIEndpointRef destination = MIDIGetDestination(i);
75
76    // Keep track of all destinations (known as outputs by the Web MIDI API).
77    // Cache to avoid any possible overhead in calling MIDIGetDestination().
78    destinations_[i] = destination;
79
80    MIDIPortInfo info = GetPortInfoFromEndpoint(destination);
81    AddOutputPort(info);
82  }
83
84  // Open connections from all sources.
85  uint32 source_count = MIDIGetNumberOfSources();
86
87  for (uint32 i = 0; i < source_count; ++i)  {
88    // Receive from all sources.
89    MIDIEndpointRef src = MIDIGetSource(i);
90    MIDIPortConnectSource(coremidi_input_, src, reinterpret_cast<void*>(src));
91
92    // Keep track of all sources (known as inputs in Web MIDI API terminology).
93    source_map_[src] = i;
94
95    MIDIPortInfo info = GetPortInfoFromEndpoint(src);
96    AddInputPort(info);
97  }
98
99  // TODO(crogers): Fix the memory management here!
100  packet_list_ = reinterpret_cast<MIDIPacketList*>(midi_buffer_);
101  midi_packet_ = MIDIPacketListInit(packet_list_);
102
103  return true;
104}
105
106MIDIManagerMac::~MIDIManagerMac() {
107  if (coremidi_input_)
108    MIDIPortDispose(coremidi_input_);
109  if (coremidi_output_)
110    MIDIPortDispose(coremidi_output_);
111}
112
113void MIDIManagerMac::ReadMIDIDispatch(const MIDIPacketList* packet_list,
114                                      void* read_proc_refcon,
115                                      void* src_conn_refcon) {
116  MIDIManagerMac* manager = static_cast<MIDIManagerMac*>(read_proc_refcon);
117#if __LP64__
118  MIDIEndpointRef source = reinterpret_cast<uintptr_t>(src_conn_refcon);
119#else
120  MIDIEndpointRef source = static_cast<MIDIEndpointRef>(src_conn_refcon);
121#endif
122
123  // Dispatch to class method.
124  manager->ReadMIDI(source, packet_list);
125}
126
127void MIDIManagerMac::ReadMIDI(MIDIEndpointRef source,
128                              const MIDIPacketList* packet_list) {
129  // Lookup the port index based on the source.
130  SourceMap::iterator j = source_map_.find(source);
131  if (j == source_map_.end())
132    return;
133  uint32 port_index = source_map_[source];
134
135  // Go through each packet and process separately.
136  for(size_t i = 0; i < packet_list->numPackets; i++) {
137    // Each packet contains MIDI data for one or more messages (like note-on).
138    const MIDIPacket &packet = packet_list->packet[i];
139    double timestamp_seconds = MIDITimeStampToSeconds(packet.timeStamp);
140
141    ReceiveMIDIData(
142        port_index,
143        packet.data,
144        packet.length,
145        timestamp_seconds);
146  }
147}
148
149void MIDIManagerMac::SendMIDIData(MIDIManagerClient* client,
150                                  uint32 port_index,
151                                  const std::vector<uint8>& data,
152                                  double timestamp) {
153  DCHECK(CurrentlyOnMIDISendThread());
154
155  // System Exclusive has already been filtered.
156  MIDITimeStamp coremidi_timestamp = SecondsToMIDITimeStamp(timestamp);
157
158  midi_packet_ = MIDIPacketListAdd(
159      packet_list_,
160      kMaxPacketListSize,
161      midi_packet_,
162      coremidi_timestamp,
163      data.size(),
164      &data[0]);
165
166  // Lookup the destination based on the port index.
167  if (static_cast<size_t>(port_index) >= destinations_.size())
168    return;
169
170  MIDIEndpointRef destination = destinations_[port_index];
171
172  MIDISend(coremidi_output_, destination, packet_list_);
173
174  // Re-initialize for next time.
175  midi_packet_ = MIDIPacketListInit(packet_list_);
176
177  client->AccumulateMIDIBytesSent(data.size());
178}
179
180MIDIPortInfo MIDIManagerMac::GetPortInfoFromEndpoint(
181    MIDIEndpointRef endpoint) {
182  SInt32 id_number = 0;
183  MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &id_number);
184  string id = IntToString(id_number);
185
186  CFStringRef manufacturer_ref = NULL;
187  MIDIObjectGetStringProperty(
188      endpoint, kMIDIPropertyManufacturer, &manufacturer_ref);
189  string manufacturer = SysCFStringRefToUTF8(manufacturer_ref);
190
191  CFStringRef name_ref = NULL;
192  MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &name_ref);
193  string name = SysCFStringRefToUTF8(name_ref);
194
195  SInt32 version_number = 0;
196  MIDIObjectGetIntegerProperty(
197      endpoint, kMIDIPropertyDriverVersion, &version_number);
198  string version = IntToString(version_number);
199
200  return MIDIPortInfo(id, manufacturer, name, version);
201}
202
203double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp) {
204  UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
205  return static_cast<double>(nanoseconds) / 1.0e9;
206}
207
208MIDITimeStamp MIDIManagerMac::SecondsToMIDITimeStamp(double seconds) {
209  UInt64 nanos = UInt64(seconds * 1.0e9);
210  return AudioConvertNanosToHostTime(nanos);
211}
212
213}  // namespace media
214