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"
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>
15 using base::IntToString
;
16 using base::SysCFStringRefToUTF8
;
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
25 MIDIManager
* MIDIManager::Create() {
26 return new MIDIManagerMac();
29 MIDIManagerMac::MIDIManagerMac()
37 bool MIDIManagerMac::Initialize() {
38 TRACE_EVENT0("midi", "MIDIManagerMac::Initialize");
40 // CoreMIDI registration.
42 OSStatus result
= MIDIClientCreate(
43 CFSTR("Google Chrome"),
53 // Create input and output port.
54 result
= MIDIInputPortCreate(
63 result
= MIDIOutputPortCreate(
70 int destination_count
= MIDIGetNumberOfDestinations();
71 destinations_
.reserve(destination_count
);
73 for (int i
= 0; i
< destination_count
; i
++) {
74 MIDIEndpointRef destination
= MIDIGetDestination(i
);
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
;
80 MIDIPortInfo info
= GetPortInfoFromEndpoint(destination
);
84 // Open connections from all sources.
85 int source_count
= MIDIGetNumberOfSources();
87 for (int 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
));
92 // Keep track of all sources (known as inputs in Web MIDI API terminology).
95 MIDIPortInfo info
= GetPortInfoFromEndpoint(src
);
99 // TODO(crogers): Fix the memory management here!
100 packet_list_
= reinterpret_cast<MIDIPacketList
*>(midi_buffer_
);
101 midi_packet_
= MIDIPacketListInit(packet_list_
);
106 MIDIManagerMac::~MIDIManagerMac() {
108 MIDIPortDispose(coremidi_input_
);
109 if (coremidi_output_
)
110 MIDIPortDispose(coremidi_output_
);
113 void 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
);
118 MIDIEndpointRef source
= reinterpret_cast<uintptr_t>(src_conn_refcon
);
120 MIDIEndpointRef source
= static_cast<MIDIEndpointRef
>(src_conn_refcon
);
123 // Dispatch to class method.
124 manager
->ReadMidi(source
, packet_list
);
127 void 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())
133 int port_index
= source_map_
[source
];
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
);
149 void MIDIManagerMac::SendMIDIData(int port_index
,
153 // TODO(crogers): Filter out sysex.
155 MIDITimeStamp coremidi_timestamp
= SecondsToMIDITimeStamp(timestamp
);
157 midi_packet_
= MIDIPacketListAdd(
165 // Lookup the destination based on the port index.
166 // TODO(crogers): re-factor |port_index| to use unsigned
167 // to avoid the need for this check.
168 if (port_index
< 0 ||
169 static_cast<size_t>(port_index
) >= destinations_
.size())
172 MIDIEndpointRef destination
= destinations_
[port_index
];
174 MIDISend(coremidi_output_
, destination
, packet_list_
);
176 // Re-initialize for next time.
177 midi_packet_
= MIDIPacketListInit(packet_list_
);
180 MIDIPortInfo
MIDIManagerMac::GetPortInfoFromEndpoint(
181 MIDIEndpointRef endpoint
) {
182 SInt32 id_number
= 0;
183 MIDIObjectGetIntegerProperty(endpoint
, kMIDIPropertyUniqueID
, &id_number
);
184 string id
= IntToString(id_number
);
186 CFStringRef manufacturer_ref
= NULL
;
187 MIDIObjectGetStringProperty(
188 endpoint
, kMIDIPropertyManufacturer
, &manufacturer_ref
);
189 string manufacturer
= SysCFStringRefToUTF8(manufacturer_ref
);
191 CFStringRef name_ref
= NULL
;
192 MIDIObjectGetStringProperty(endpoint
, kMIDIPropertyName
, &name_ref
);
193 string name
= SysCFStringRefToUTF8(name_ref
);
195 SInt32 version_number
= 0;
196 MIDIObjectGetIntegerProperty(
197 endpoint
, kMIDIPropertyDriverVersion
, &version_number
);
198 string version
= IntToString(version_number
);
200 return MIDIPortInfo(id
, manufacturer
, name
, version
);
203 double MIDIManagerMac::MIDITimeStampToSeconds(MIDITimeStamp timestamp
) {
204 UInt64 nanoseconds
= AudioConvertHostTimeToNanos(timestamp
);
205 return static_cast<double>(nanoseconds
) / 1.0e9
;
208 MIDITimeStamp
MIDIManagerMac::SecondsToMIDITimeStamp(double seconds
) {
209 UInt64 nanos
= UInt64(seconds
* 1.0e9
);
210 return AudioConvertNanosToHostTime(nanos
);