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