crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 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 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 9 | #include <algorithm> |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 10 | #include <iterator> |
| 11 | #include <string> |
| 12 | |
| 13 | #include <CoreAudio/HostTime.h> |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 14 | |
toyoshim@chromium.org | 51c7f53 | 2014-05-01 17:17:32 +0000 | [diff] [blame] | 15 | #include "base/bind.h" |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 16 | #include "base/strings/string_number_conversions.h" |
| 17 | #include "base/strings/sys_string_conversions.h" |
Patrick Monette | 54c53f4 | 2021-10-08 20:27:23 +0000 | [diff] [blame^] | 18 | #include "base/task/single_thread_task_runner_forward.h" |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 19 | #include "media/midi/midi_service.h" |
| 20 | #include "media/midi/task_service.h" |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 21 | |
Raul Tambre | 16b3e73 | 2019-02-06 19:56:36 +0000 | [diff] [blame] | 22 | using base::NumberToString; |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 23 | using base::SysCFStringRefToUTF8; |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 24 | using midi::mojom::PortState; |
toyoshim | 2f3a48f | 2016-10-17 01:54:13 -0700 | [diff] [blame] | 25 | using midi::mojom::Result; |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 26 | |
avi@chromium.org | 66efc32 | 2013-06-25 17:40:55 +0000 | [diff] [blame] | 27 | // NB: System MIDI types are pointer types in 32-bit and integer types in |
| 28 | // 64-bit. Therefore, the initialization is the simplest one that satisfies both |
| 29 | // (if possible). |
| 30 | |
toyoshim | e147c5e | 2015-05-07 21:58:31 -0700 | [diff] [blame] | 31 | namespace midi { |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 32 | |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 33 | namespace { |
| 34 | |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 35 | // Maximum buffer size that CoreMIDI can handle for MIDIPacketList. |
| 36 | const size_t kCoreMIDIMaxPacketListSize = 65536; |
| 37 | // Pessimistic estimation on available data size of MIDIPacketList. |
| 38 | const size_t kEstimatedMaxPacketDataSize = kCoreMIDIMaxPacketListSize / 2; |
| 39 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 40 | enum { |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 41 | kSessionTaskRunner = TaskService::kDefaultRunnerId, |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 42 | kClientTaskRunner, |
| 43 | }; |
| 44 | |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 45 | mojom::PortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 46 | std::string manufacturer; |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 47 | CFStringRef manufacturer_ref = NULL; |
| 48 | OSStatus result = MIDIObjectGetStringProperty( |
| 49 | endpoint, kMIDIPropertyManufacturer, &manufacturer_ref); |
| 50 | if (result == noErr) { |
| 51 | manufacturer = SysCFStringRefToUTF8(manufacturer_ref); |
| 52 | } else { |
| 53 | // kMIDIPropertyManufacturer is not supported in IAC driver providing |
| 54 | // endpoints, and the result will be kMIDIUnknownProperty (-10835). |
| 55 | DLOG(WARNING) << "Failed to get kMIDIPropertyManufacturer with status " |
| 56 | << result; |
| 57 | } |
| 58 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 59 | std::string name; |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 60 | CFStringRef name_ref = NULL; |
agoode | 158180b | 2015-03-24 04:33:51 -0700 | [diff] [blame] | 61 | result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyDisplayName, |
| 62 | &name_ref); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 63 | if (result == noErr) { |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 64 | name = SysCFStringRefToUTF8(name_ref); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 65 | } else { |
agoode | 158180b | 2015-03-24 04:33:51 -0700 | [diff] [blame] | 66 | DLOG(WARNING) << "Failed to get kMIDIPropertyDisplayName with status " |
| 67 | << result; |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 68 | } |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 69 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 70 | std::string version; |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 71 | SInt32 version_number = 0; |
| 72 | result = MIDIObjectGetIntegerProperty( |
| 73 | endpoint, kMIDIPropertyDriverVersion, &version_number); |
| 74 | if (result == noErr) { |
Raul Tambre | 16b3e73 | 2019-02-06 19:56:36 +0000 | [diff] [blame] | 75 | version = NumberToString(version_number); |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 76 | } else { |
| 77 | // kMIDIPropertyDriverVersion is not supported in IAC driver providing |
| 78 | // endpoints, and the result will be kMIDIUnknownProperty (-10835). |
| 79 | DLOG(WARNING) << "Failed to get kMIDIPropertyDriverVersion with status " |
| 80 | << result; |
| 81 | } |
| 82 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 83 | std::string id; |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 84 | SInt32 id_number = 0; |
| 85 | result = MIDIObjectGetIntegerProperty( |
| 86 | endpoint, kMIDIPropertyUniqueID, &id_number); |
| 87 | if (result == noErr) { |
Raul Tambre | 16b3e73 | 2019-02-06 19:56:36 +0000 | [diff] [blame] | 88 | id = NumberToString(id_number); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 89 | } else { |
| 90 | // On connecting some devices, e.g., nano KONTROL2, unknown endpoints |
| 91 | // appear and disappear quickly and they fail on queries. |
| 92 | // Let's ignore such ghost devices. |
| 93 | // Same problems will happen if the device is disconnected before finishing |
| 94 | // all queries. |
| 95 | DLOG(WARNING) << "Failed to get kMIDIPropertyUniqueID with status " |
| 96 | << result; |
| 97 | } |
| 98 | |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 99 | const PortState state = PortState::OPENED; |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 100 | return mojom::PortInfo(id, manufacturer, name, version, state); |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 101 | } |
| 102 | |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 103 | base::TimeTicks MIDITimeStampToTimeTicks(MIDITimeStamp timestamp) { |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 104 | UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp); |
Peter Kasting | 99947e5 | 2021-10-02 03:06:35 +0000 | [diff] [blame] | 105 | return base::TimeTicks() + base::Nanoseconds(nanoseconds); |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 106 | } |
| 107 | |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 108 | MIDITimeStamp TimeTicksToMIDITimeStamp(base::TimeTicks ticks) { |
| 109 | return AudioConvertNanosToHostTime(ticks.since_origin().InNanoseconds()); |
toyoshim | f96ff61 | 2014-10-13 22:56:18 -0700 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | } // namespace |
| 113 | |
toyoshim | f4d6152 | 2017-02-10 02:03:32 -0800 | [diff] [blame] | 114 | MidiManager* MidiManager::Create(MidiService* service) { |
| 115 | return new MidiManagerMac(service); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 116 | } |
| 117 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 118 | MidiManagerMac::MidiManagerMac(MidiService* service) : MidiManager(service) {} |
toyoshim | c9f5213 | 2014-10-15 05:50:07 -0700 | [diff] [blame] | 119 | |
Takashi Toyoshima | e8240ab | 2018-10-03 09:30:11 +0000 | [diff] [blame] | 120 | MidiManagerMac::~MidiManagerMac() { |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 121 | if (!service()->task_service()->UnbindInstance()) |
| 122 | return; |
Takashi Toyoshima | e8240ab | 2018-10-03 09:30:11 +0000 | [diff] [blame] | 123 | |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 124 | // Finalization steps should be implemented after the UnbindInstance() call. |
Takashi Toyoshima | e8240ab | 2018-10-03 09:30:11 +0000 | [diff] [blame] | 125 | // Do not need to dispose |coremidi_input_| and |coremidi_output_| explicitly. |
| 126 | // CoreMIDI automatically disposes them on the client disposal. |
| 127 | base::AutoLock lock(midi_client_lock_); |
| 128 | if (midi_client_) |
| 129 | MIDIClientDispose(midi_client_); |
| 130 | } |
toyoshim | b9c877f | 2015-10-12 23:11:38 -0700 | [diff] [blame] | 131 | |
| 132 | void MidiManagerMac::StartInitialization() { |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 133 | if (!service()->task_service()->BindInstance()) |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 134 | return CompleteInitialization(Result::INITIALIZATION_ERROR); |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 135 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 136 | service()->task_service()->PostBoundTask( |
| 137 | kClientTaskRunner, base::BindOnce(&MidiManagerMac::InitializeCoreMIDI, |
| 138 | base::Unretained(this))); |
toyoshim | b9c877f | 2015-10-12 23:11:38 -0700 | [diff] [blame] | 139 | } |
| 140 | |
toyoshim | c9f5213 | 2014-10-15 05:50:07 -0700 | [diff] [blame] | 141 | void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client, |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 142 | uint32_t port_index, |
| 143 | const std::vector<uint8_t>& data, |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 144 | base::TimeTicks timestamp) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 145 | service()->task_service()->PostBoundTask( |
| 146 | kClientTaskRunner, |
| 147 | base::BindOnce(&MidiManagerMac::SendMidiData, base::Unretained(this), |
| 148 | client, port_index, data, timestamp)); |
toyoshim | c9f5213 | 2014-10-15 05:50:07 -0700 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | void MidiManagerMac::InitializeCoreMIDI() { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 152 | DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner)); |
toyoshim | c9f5213 | 2014-10-15 05:50:07 -0700 | [diff] [blame] | 153 | |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 154 | // CoreMIDI registration. |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 155 | MIDIClientRef client = 0u; |
| 156 | OSStatus result = MIDIClientCreate(CFSTR("Chrome"), ReceiveMidiNotifyDispatch, |
| 157 | this, &client); |
| 158 | if (result != noErr || client == 0u) |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 159 | return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 160 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 161 | { |
| 162 | base::AutoLock lock(midi_client_lock_); |
| 163 | midi_client_ = client; |
| 164 | } |
| 165 | |
| 166 | // Create input and output port. These MIDIPortRef references are not needed |
| 167 | // to be disposed explicitly. CoreMIDI automatically disposes them on the |
| 168 | // client disposal. |
| 169 | result = MIDIInputPortCreate(client, CFSTR("MIDI Input"), ReadMidiDispatch, |
| 170 | this, &midi_input_); |
| 171 | if (result != noErr || midi_input_ == 0u) |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 172 | return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 173 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 174 | result = MIDIOutputPortCreate(client, CFSTR("MIDI Output"), &midi_output_); |
| 175 | if (result != noErr || midi_output_ == 0u) |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 176 | return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 177 | |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 178 | // Following loop may miss some newly attached devices, but such device will |
| 179 | // be captured by ReceiveMidiNotifyDispatch callback. |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 180 | destinations_.resize(MIDIGetNumberOfDestinations()); |
| 181 | for (size_t i = 0u; i < destinations_.size(); ++i) { |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 182 | MIDIEndpointRef destination = MIDIGetDestination(i); |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 183 | DCHECK_NE(0u, destination); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 184 | |
| 185 | // Keep track of all destinations (known as outputs by the Web MIDI API). |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 186 | destinations_[i] = destination; |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 187 | AddOutputPort(GetPortInfoFromEndpoint(destination)); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 188 | } |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 189 | // Allocate maximum size of buffer that CoreMIDI can handle. |
| 190 | midi_buffer_.resize(kCoreMIDIMaxPacketListSize); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 191 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 192 | // Open connections from all sources. This loop also may miss new devices. |
| 193 | sources_.resize(MIDIGetNumberOfSources()); |
| 194 | for (size_t i = 0u; i < sources_.size(); ++i) { |
| 195 | MIDIEndpointRef source = MIDIGetSource(i); |
| 196 | DCHECK_NE(0u, source); |
| 197 | |
| 198 | // Keep track of all sources (known as inputs by the Web MIDI API). |
| 199 | sources_[i] = source; |
| 200 | AddInputPort(GetPortInfoFromEndpoint(source)); |
| 201 | } |
| 202 | // Start listening. |
| 203 | for (size_t i = 0u; i < sources_.size(); ++i) |
| 204 | MIDIPortConnectSource(midi_input_, sources_[i], reinterpret_cast<void*>(i)); |
| 205 | |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 206 | CompleteCoreMIDIInitialization(Result::OK); |
| 207 | } |
| 208 | |
| 209 | void MidiManagerMac::CompleteCoreMIDIInitialization(mojom::Result result) { |
| 210 | service()->task_service()->PostBoundTask( |
| 211 | kSessionTaskRunner, |
| 212 | base::BindOnce(&MidiManagerMac::CompleteInitialization, |
| 213 | base::Unretained(this), result)); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 214 | } |
| 215 | |
thakis@chromium.org | d898f70 | 2013-10-17 13:13:58 +0000 | [diff] [blame] | 216 | // static |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 217 | void MidiManagerMac::ReceiveMidiNotifyDispatch(const MIDINotification* message, |
| 218 | void* refcon) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 219 | // This callback function is invoked on |kClientTaskRunner|. |
| 220 | // |manager| should be valid because we can ensure |midi_client_| is still |
| 221 | // alive here. |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 222 | MidiManagerMac* manager = static_cast<MidiManagerMac*>(refcon); |
| 223 | manager->ReceiveMidiNotify(message); |
| 224 | } |
| 225 | |
| 226 | void MidiManagerMac::ReceiveMidiNotify(const MIDINotification* message) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 227 | DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner)); |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 228 | |
| 229 | if (kMIDIMsgObjectAdded == message->messageID) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 230 | // New device is going to be attached. |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 231 | const MIDIObjectAddRemoveNotification* notification = |
| 232 | reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message); |
| 233 | MIDIEndpointRef endpoint = |
| 234 | static_cast<MIDIEndpointRef>(notification->child); |
| 235 | if (notification->childType == kMIDIObjectType_Source) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 236 | // Attaching device is an input device. |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 237 | auto it = std::find(sources_.begin(), sources_.end(), endpoint); |
| 238 | if (it == sources_.end()) { |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 239 | mojom::PortInfo info = GetPortInfoFromEndpoint(endpoint); |
| 240 | // If the device disappears before finishing queries, mojom::PortInfo |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 241 | // becomes incomplete. Skip and do not cache such information here. |
| 242 | // On kMIDIMsgObjectRemoved, the entry will be ignored because it |
| 243 | // will not be found in the pool. |
| 244 | if (!info.id.empty()) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 245 | sources_.push_back(endpoint); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 246 | AddInputPort(info); |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 247 | MIDIPortConnectSource(midi_input_, endpoint, |
Takashi Toyoshima | f51e6ac | 2017-11-27 09:45:51 +0000 | [diff] [blame] | 248 | reinterpret_cast<void*>(sources_.size() - 1)); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 249 | } |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 250 | } else { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 251 | SetInputPortState(it - sources_.begin(), PortState::OPENED); |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 252 | } |
| 253 | } else if (notification->childType == kMIDIObjectType_Destination) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 254 | // Attaching device is an output device. |
| 255 | auto it = std::find(destinations_.begin(), destinations_.end(), endpoint); |
| 256 | if (it == destinations_.end()) { |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 257 | mojom::PortInfo info = GetPortInfoFromEndpoint(endpoint); |
toyoshim | c974698 | 2015-04-03 05:32:55 -0700 | [diff] [blame] | 258 | // Skip cases that queries are not finished correctly. |
| 259 | if (!info.id.empty()) { |
| 260 | destinations_.push_back(endpoint); |
| 261 | AddOutputPort(info); |
| 262 | } |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 263 | } else { |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 264 | SetOutputPortState(it - destinations_.begin(), PortState::OPENED); |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 265 | } |
| 266 | } |
| 267 | } else if (kMIDIMsgObjectRemoved == message->messageID) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 268 | // Existing device is going to be detached. |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 269 | const MIDIObjectAddRemoveNotification* notification = |
| 270 | reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message); |
| 271 | MIDIEndpointRef endpoint = |
| 272 | static_cast<MIDIEndpointRef>(notification->child); |
| 273 | if (notification->childType == kMIDIObjectType_Source) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 274 | // Detaching device is an input device. |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 275 | auto it = std::find(sources_.begin(), sources_.end(), endpoint); |
| 276 | if (it != sources_.end()) |
| 277 | SetInputPortState(it - sources_.begin(), PortState::DISCONNECTED); |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 278 | } else if (notification->childType == kMIDIObjectType_Destination) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 279 | // Detaching device is an output device. |
| 280 | auto it = std::find(destinations_.begin(), destinations_.end(), endpoint); |
| 281 | if (it != destinations_.end()) |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 282 | SetOutputPortState(it - destinations_.begin(), PortState::DISCONNECTED); |
toyoshim | 5c6fe4b | 2015-02-18 23:28:09 -0800 | [diff] [blame] | 283 | } |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | // static |
toyoshim@chromium.org | c82e66e | 2014-02-04 07:05:47 +0000 | [diff] [blame] | 288 | void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list, |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 289 | void* read_proc_refcon, |
| 290 | void* src_conn_refcon) { |
toyoshim | c9f5213 | 2014-10-15 05:50:07 -0700 | [diff] [blame] | 291 | // This method is called on a separate high-priority thread owned by CoreMIDI. |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 292 | // |manager| should be valid because we can ensure |midi_client_| is still |
| 293 | // alive here. |
toyoshim@chromium.org | c82e66e | 2014-02-04 07:05:47 +0000 | [diff] [blame] | 294 | MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon); |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 295 | DCHECK(manager); |
| 296 | uint32_t port_index = reinterpret_cast<uintptr_t>(src_conn_refcon); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 297 | |
| 298 | // Go through each packet and process separately. |
toyoshim | e1b081e | 2015-03-01 22:38:52 -0800 | [diff] [blame] | 299 | const MIDIPacket* packet = &packet_list->packet[0]; |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 300 | for (size_t i = 0u; i < packet_list->numPackets; i++) { |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 301 | // Each packet contains MIDI data for one or more messages (like note-on). |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 302 | base::TimeTicks timestamp = MIDITimeStampToTimeTicks(packet->timeStamp); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 303 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 304 | manager->ReceiveMidiData(port_index, packet->data, packet->length, |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 305 | timestamp); |
toyoshim | e1b081e | 2015-03-01 22:38:52 -0800 | [diff] [blame] | 306 | |
| 307 | packet = MIDIPacketNext(packet); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 308 | } |
| 309 | } |
| 310 | |
toyoshim@chromium.org | c82e66e | 2014-02-04 07:05:47 +0000 | [diff] [blame] | 311 | void MidiManagerMac::SendMidiData(MidiManagerClient* client, |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 312 | uint32_t port_index, |
| 313 | const std::vector<uint8_t>& data, |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 314 | base::TimeTicks timestamp) { |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 315 | DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner)); |
toyoshim@chromium.org | ae6ad36 | 2013-08-27 15:30:20 +0000 | [diff] [blame] | 316 | |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 317 | // Lookup the destination based on the port index. |
toyoshim@chromium.org | ae6ad36 | 2013-08-27 15:30:20 +0000 | [diff] [blame] | 318 | if (static_cast<size_t>(port_index) >= destinations_.size()) |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 319 | return; |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 320 | MIDITimeStamp coremidi_timestamp = TimeTicksToMIDITimeStamp(timestamp); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 321 | MIDIEndpointRef destination = destinations_[port_index]; |
| 322 | |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 323 | size_t send_size; |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 324 | for (size_t sent_size = 0u; sent_size < data.size(); sent_size += send_size) { |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 325 | MIDIPacketList* packet_list = |
| 326 | reinterpret_cast<MIDIPacketList*>(midi_buffer_.data()); |
| 327 | MIDIPacket* midi_packet = MIDIPacketListInit(packet_list); |
| 328 | // Limit the maximum payload size to kEstimatedMaxPacketDataSize that is |
| 329 | // half of midi_buffer data size. MIDIPacketList and MIDIPacket consume |
| 330 | // extra buffer areas for meta information, and available size is smaller |
| 331 | // than buffer size. Here, we simply assume that at least half size is |
| 332 | // available for data payload. |
| 333 | send_size = std::min(data.size() - sent_size, kEstimatedMaxPacketDataSize); |
| 334 | midi_packet = MIDIPacketListAdd( |
| 335 | packet_list, |
| 336 | kCoreMIDIMaxPacketListSize, |
| 337 | midi_packet, |
| 338 | coremidi_timestamp, |
| 339 | send_size, |
| 340 | &data[sent_size]); |
| 341 | DCHECK(midi_packet); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 342 | |
Takashi Toyoshima | 143f0fd | 2017-08-01 16:15:33 +0000 | [diff] [blame] | 343 | MIDISend(midi_output_, destination, packet_list); |
toyoshim | e75675b | 2015-03-11 20:45:03 -0700 | [diff] [blame] | 344 | } |
crogers@google.com | 542a43a | 2013-07-31 05:16:49 +0000 | [diff] [blame] | 345 | |
ochang | 39a6dbc | 2015-12-07 18:36:08 -0800 | [diff] [blame] | 346 | AccumulateMidiBytesSent(client, data.size()); |
crogers@google.com | 27356e4 | 2013-06-22 04:03:03 +0000 | [diff] [blame] | 347 | } |
| 348 | |
toyoshim | e147c5e | 2015-05-07 21:58:31 -0700 | [diff] [blame] | 349 | } // namespace midi |