blob: c1d68799e6611658acd8761f5d40443ea981fe96 [file] [log] [blame]
crogers@google.com27356e42013-06-22 04:03:03 +00001// 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 Toyoshima143f0fd2017-08-01 16:15:33 +00007#include <stddef.h>
8
toyoshim5c6fe4b2015-02-18 23:28:09 -08009#include <algorithm>
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000010#include <iterator>
11#include <string>
12
13#include <CoreAudio/HostTime.h>
crogers@google.com27356e42013-06-22 04:03:03 +000014
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000015#include "base/bind.h"
crogers@google.com27356e42013-06-22 04:03:03 +000016#include "base/strings/string_number_conversions.h"
17#include "base/strings/sys_string_conversions.h"
Patrick Monette54c53f42021-10-08 20:27:23 +000018#include "base/task/single_thread_task_runner_forward.h"
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000019#include "media/midi/midi_service.h"
20#include "media/midi/task_service.h"
crogers@google.com27356e42013-06-22 04:03:03 +000021
Raul Tambre16b3e732019-02-06 19:56:36 +000022using base::NumberToString;
crogers@google.com27356e42013-06-22 04:03:03 +000023using base::SysCFStringRefToUTF8;
toyoshimec2570a2016-10-21 02:15:27 -070024using midi::mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070025using midi::mojom::Result;
crogers@google.com27356e42013-06-22 04:03:03 +000026
avi@chromium.org66efc322013-06-25 17:40:55 +000027// 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
toyoshime147c5e2015-05-07 21:58:31 -070031namespace midi {
crogers@google.com27356e42013-06-22 04:03:03 +000032
toyoshimf96ff612014-10-13 22:56:18 -070033namespace {
34
toyoshime75675b2015-03-11 20:45:03 -070035// Maximum buffer size that CoreMIDI can handle for MIDIPacketList.
36const size_t kCoreMIDIMaxPacketListSize = 65536;
37// Pessimistic estimation on available data size of MIDIPacketList.
38const size_t kEstimatedMaxPacketDataSize = kCoreMIDIMaxPacketListSize / 2;
39
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000040enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000041 kSessionTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000042 kClientTaskRunner,
43};
44
Adithya Srinivasan33252732018-10-17 15:59:40 +000045mojom::PortInfo GetPortInfoFromEndpoint(MIDIEndpointRef endpoint) {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000046 std::string manufacturer;
toyoshimf96ff612014-10-13 22:56:18 -070047 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 Toyoshima143f0fd2017-08-01 16:15:33 +000059 std::string name;
toyoshimf96ff612014-10-13 22:56:18 -070060 CFStringRef name_ref = NULL;
agoode158180b2015-03-24 04:33:51 -070061 result = MIDIObjectGetStringProperty(endpoint, kMIDIPropertyDisplayName,
62 &name_ref);
toyoshimc9746982015-04-03 05:32:55 -070063 if (result == noErr) {
toyoshimf96ff612014-10-13 22:56:18 -070064 name = SysCFStringRefToUTF8(name_ref);
toyoshimc9746982015-04-03 05:32:55 -070065 } else {
agoode158180b2015-03-24 04:33:51 -070066 DLOG(WARNING) << "Failed to get kMIDIPropertyDisplayName with status "
67 << result;
toyoshimc9746982015-04-03 05:32:55 -070068 }
toyoshimf96ff612014-10-13 22:56:18 -070069
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +000070 std::string version;
toyoshimf96ff612014-10-13 22:56:18 -070071 SInt32 version_number = 0;
72 result = MIDIObjectGetIntegerProperty(
73 endpoint, kMIDIPropertyDriverVersion, &version_number);
74 if (result == noErr) {
Raul Tambre16b3e732019-02-06 19:56:36 +000075 version = NumberToString(version_number);
toyoshimf96ff612014-10-13 22:56:18 -070076 } 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 Toyoshima143f0fd2017-08-01 16:15:33 +000083 std::string id;
toyoshimc9746982015-04-03 05:32:55 -070084 SInt32 id_number = 0;
85 result = MIDIObjectGetIntegerProperty(
86 endpoint, kMIDIPropertyUniqueID, &id_number);
87 if (result == noErr) {
Raul Tambre16b3e732019-02-06 19:56:36 +000088 id = NumberToString(id_number);
toyoshimc9746982015-04-03 05:32:55 -070089 } 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
toyoshimec2570a2016-10-21 02:15:27 -070099 const PortState state = PortState::OPENED;
Adithya Srinivasan33252732018-10-17 15:59:40 +0000100 return mojom::PortInfo(id, manufacturer, name, version, state);
toyoshimf96ff612014-10-13 22:56:18 -0700101}
102
tzik925e2c62018-02-02 07:39:45 +0000103base::TimeTicks MIDITimeStampToTimeTicks(MIDITimeStamp timestamp) {
toyoshimf96ff612014-10-13 22:56:18 -0700104 UInt64 nanoseconds = AudioConvertHostTimeToNanos(timestamp);
Peter Kasting99947e52021-10-02 03:06:35 +0000105 return base::TimeTicks() + base::Nanoseconds(nanoseconds);
toyoshimf96ff612014-10-13 22:56:18 -0700106}
107
tzik925e2c62018-02-02 07:39:45 +0000108MIDITimeStamp TimeTicksToMIDITimeStamp(base::TimeTicks ticks) {
109 return AudioConvertNanosToHostTime(ticks.since_origin().InNanoseconds());
toyoshimf96ff612014-10-13 22:56:18 -0700110}
111
112} // namespace
113
toyoshimf4d61522017-02-10 02:03:32 -0800114MidiManager* MidiManager::Create(MidiService* service) {
115 return new MidiManagerMac(service);
crogers@google.com27356e42013-06-22 04:03:03 +0000116}
117
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000118MidiManagerMac::MidiManagerMac(MidiService* service) : MidiManager(service) {}
toyoshimc9f52132014-10-15 05:50:07 -0700119
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000120MidiManagerMac::~MidiManagerMac() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000121 if (!service()->task_service()->UnbindInstance())
122 return;
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000123
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000124 // Finalization steps should be implemented after the UnbindInstance() call.
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000125 // 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}
toyoshimb9c877f2015-10-12 23:11:38 -0700131
132void MidiManagerMac::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000133 if (!service()->task_service()->BindInstance())
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000134 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000135
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000136 service()->task_service()->PostBoundTask(
137 kClientTaskRunner, base::BindOnce(&MidiManagerMac::InitializeCoreMIDI,
138 base::Unretained(this)));
toyoshimb9c877f2015-10-12 23:11:38 -0700139}
140
toyoshimc9f52132014-10-15 05:50:07 -0700141void MidiManagerMac::DispatchSendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500142 uint32_t port_index,
143 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000144 base::TimeTicks timestamp) {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000145 service()->task_service()->PostBoundTask(
146 kClientTaskRunner,
147 base::BindOnce(&MidiManagerMac::SendMidiData, base::Unretained(this),
148 client, port_index, data, timestamp));
toyoshimc9f52132014-10-15 05:50:07 -0700149}
150
151void MidiManagerMac::InitializeCoreMIDI() {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000152 DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner));
toyoshimc9f52132014-10-15 05:50:07 -0700153
crogers@google.com27356e42013-06-22 04:03:03 +0000154 // CoreMIDI registration.
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000155 MIDIClientRef client = 0u;
156 OSStatus result = MIDIClientCreate(CFSTR("Chrome"), ReceiveMidiNotifyDispatch,
157 this, &client);
158 if (result != noErr || client == 0u)
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000159 return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR);
crogers@google.com27356e42013-06-22 04:03:03 +0000160
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000161 {
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 Toyoshima5a6e6a32018-09-27 11:20:52 +0000172 return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR);
crogers@google.com27356e42013-06-22 04:03:03 +0000173
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000174 result = MIDIOutputPortCreate(client, CFSTR("MIDI Output"), &midi_output_);
175 if (result != noErr || midi_output_ == 0u)
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000176 return CompleteCoreMIDIInitialization(Result::INITIALIZATION_ERROR);
crogers@google.com27356e42013-06-22 04:03:03 +0000177
toyoshime75675b2015-03-11 20:45:03 -0700178 // Following loop may miss some newly attached devices, but such device will
179 // be captured by ReceiveMidiNotifyDispatch callback.
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000180 destinations_.resize(MIDIGetNumberOfDestinations());
181 for (size_t i = 0u; i < destinations_.size(); ++i) {
crogers@google.com27356e42013-06-22 04:03:03 +0000182 MIDIEndpointRef destination = MIDIGetDestination(i);
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000183 DCHECK_NE(0u, destination);
crogers@google.com27356e42013-06-22 04:03:03 +0000184
185 // Keep track of all destinations (known as outputs by the Web MIDI API).
crogers@google.com27356e42013-06-22 04:03:03 +0000186 destinations_[i] = destination;
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000187 AddOutputPort(GetPortInfoFromEndpoint(destination));
crogers@google.com27356e42013-06-22 04:03:03 +0000188 }
toyoshime75675b2015-03-11 20:45:03 -0700189 // Allocate maximum size of buffer that CoreMIDI can handle.
190 midi_buffer_.resize(kCoreMIDIMaxPacketListSize);
crogers@google.com27356e42013-06-22 04:03:03 +0000191
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000192 // 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 Toyoshima5a6e6a32018-09-27 11:20:52 +0000206 CompleteCoreMIDIInitialization(Result::OK);
207}
208
209void MidiManagerMac::CompleteCoreMIDIInitialization(mojom::Result result) {
210 service()->task_service()->PostBoundTask(
211 kSessionTaskRunner,
212 base::BindOnce(&MidiManagerMac::CompleteInitialization,
213 base::Unretained(this), result));
crogers@google.com27356e42013-06-22 04:03:03 +0000214}
215
thakis@chromium.orgd898f702013-10-17 13:13:58 +0000216// static
toyoshim5c6fe4b2015-02-18 23:28:09 -0800217void MidiManagerMac::ReceiveMidiNotifyDispatch(const MIDINotification* message,
218 void* refcon) {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000219 // This callback function is invoked on |kClientTaskRunner|.
220 // |manager| should be valid because we can ensure |midi_client_| is still
221 // alive here.
toyoshim5c6fe4b2015-02-18 23:28:09 -0800222 MidiManagerMac* manager = static_cast<MidiManagerMac*>(refcon);
223 manager->ReceiveMidiNotify(message);
224}
225
226void MidiManagerMac::ReceiveMidiNotify(const MIDINotification* message) {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000227 DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner));
toyoshim5c6fe4b2015-02-18 23:28:09 -0800228
229 if (kMIDIMsgObjectAdded == message->messageID) {
toyoshime75675b2015-03-11 20:45:03 -0700230 // New device is going to be attached.
toyoshim5c6fe4b2015-02-18 23:28:09 -0800231 const MIDIObjectAddRemoveNotification* notification =
232 reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
233 MIDIEndpointRef endpoint =
234 static_cast<MIDIEndpointRef>(notification->child);
235 if (notification->childType == kMIDIObjectType_Source) {
toyoshime75675b2015-03-11 20:45:03 -0700236 // Attaching device is an input device.
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000237 auto it = std::find(sources_.begin(), sources_.end(), endpoint);
238 if (it == sources_.end()) {
Adithya Srinivasan33252732018-10-17 15:59:40 +0000239 mojom::PortInfo info = GetPortInfoFromEndpoint(endpoint);
240 // If the device disappears before finishing queries, mojom::PortInfo
toyoshimc9746982015-04-03 05:32:55 -0700241 // 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 Toyoshima143f0fd2017-08-01 16:15:33 +0000245 sources_.push_back(endpoint);
toyoshimc9746982015-04-03 05:32:55 -0700246 AddInputPort(info);
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000247 MIDIPortConnectSource(midi_input_, endpoint,
Takashi Toyoshimaf51e6ac2017-11-27 09:45:51 +0000248 reinterpret_cast<void*>(sources_.size() - 1));
toyoshimc9746982015-04-03 05:32:55 -0700249 }
toyoshim5c6fe4b2015-02-18 23:28:09 -0800250 } else {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000251 SetInputPortState(it - sources_.begin(), PortState::OPENED);
toyoshim5c6fe4b2015-02-18 23:28:09 -0800252 }
253 } else if (notification->childType == kMIDIObjectType_Destination) {
toyoshime75675b2015-03-11 20:45:03 -0700254 // Attaching device is an output device.
255 auto it = std::find(destinations_.begin(), destinations_.end(), endpoint);
256 if (it == destinations_.end()) {
Adithya Srinivasan33252732018-10-17 15:59:40 +0000257 mojom::PortInfo info = GetPortInfoFromEndpoint(endpoint);
toyoshimc9746982015-04-03 05:32:55 -0700258 // Skip cases that queries are not finished correctly.
259 if (!info.id.empty()) {
260 destinations_.push_back(endpoint);
261 AddOutputPort(info);
262 }
toyoshime75675b2015-03-11 20:45:03 -0700263 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700264 SetOutputPortState(it - destinations_.begin(), PortState::OPENED);
toyoshim5c6fe4b2015-02-18 23:28:09 -0800265 }
266 }
267 } else if (kMIDIMsgObjectRemoved == message->messageID) {
toyoshime75675b2015-03-11 20:45:03 -0700268 // Existing device is going to be detached.
toyoshim5c6fe4b2015-02-18 23:28:09 -0800269 const MIDIObjectAddRemoveNotification* notification =
270 reinterpret_cast<const MIDIObjectAddRemoveNotification*>(message);
271 MIDIEndpointRef endpoint =
272 static_cast<MIDIEndpointRef>(notification->child);
273 if (notification->childType == kMIDIObjectType_Source) {
toyoshime75675b2015-03-11 20:45:03 -0700274 // Detaching device is an input device.
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000275 auto it = std::find(sources_.begin(), sources_.end(), endpoint);
276 if (it != sources_.end())
277 SetInputPortState(it - sources_.begin(), PortState::DISCONNECTED);
toyoshim5c6fe4b2015-02-18 23:28:09 -0800278 } else if (notification->childType == kMIDIObjectType_Destination) {
toyoshime75675b2015-03-11 20:45:03 -0700279 // Detaching device is an output device.
280 auto it = std::find(destinations_.begin(), destinations_.end(), endpoint);
281 if (it != destinations_.end())
toyoshimec2570a2016-10-21 02:15:27 -0700282 SetOutputPortState(it - destinations_.begin(), PortState::DISCONNECTED);
toyoshim5c6fe4b2015-02-18 23:28:09 -0800283 }
284 }
285}
286
287// static
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000288void MidiManagerMac::ReadMidiDispatch(const MIDIPacketList* packet_list,
crogers@google.com27356e42013-06-22 04:03:03 +0000289 void* read_proc_refcon,
290 void* src_conn_refcon) {
toyoshimc9f52132014-10-15 05:50:07 -0700291 // This method is called on a separate high-priority thread owned by CoreMIDI.
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000292 // |manager| should be valid because we can ensure |midi_client_| is still
293 // alive here.
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000294 MidiManagerMac* manager = static_cast<MidiManagerMac*>(read_proc_refcon);
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000295 DCHECK(manager);
296 uint32_t port_index = reinterpret_cast<uintptr_t>(src_conn_refcon);
crogers@google.com27356e42013-06-22 04:03:03 +0000297
298 // Go through each packet and process separately.
toyoshime1b081e2015-03-01 22:38:52 -0800299 const MIDIPacket* packet = &packet_list->packet[0];
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000300 for (size_t i = 0u; i < packet_list->numPackets; i++) {
crogers@google.com27356e42013-06-22 04:03:03 +0000301 // Each packet contains MIDI data for one or more messages (like note-on).
tzik925e2c62018-02-02 07:39:45 +0000302 base::TimeTicks timestamp = MIDITimeStampToTimeTicks(packet->timeStamp);
crogers@google.com27356e42013-06-22 04:03:03 +0000303
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000304 manager->ReceiveMidiData(port_index, packet->data, packet->length,
tzik925e2c62018-02-02 07:39:45 +0000305 timestamp);
toyoshime1b081e2015-03-01 22:38:52 -0800306
307 packet = MIDIPacketNext(packet);
crogers@google.com27356e42013-06-22 04:03:03 +0000308 }
309}
310
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +0000311void MidiManagerMac::SendMidiData(MidiManagerClient* client,
Avi Drissman3528fd02015-12-18 20:11:31 -0500312 uint32_t port_index,
313 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000314 base::TimeTicks timestamp) {
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000315 DCHECK(service()->task_service()->IsOnTaskRunner(kClientTaskRunner));
toyoshim@chromium.orgae6ad362013-08-27 15:30:20 +0000316
crogers@google.com27356e42013-06-22 04:03:03 +0000317 // Lookup the destination based on the port index.
toyoshim@chromium.orgae6ad362013-08-27 15:30:20 +0000318 if (static_cast<size_t>(port_index) >= destinations_.size())
crogers@google.com27356e42013-06-22 04:03:03 +0000319 return;
tzik925e2c62018-02-02 07:39:45 +0000320 MIDITimeStamp coremidi_timestamp = TimeTicksToMIDITimeStamp(timestamp);
crogers@google.com27356e42013-06-22 04:03:03 +0000321 MIDIEndpointRef destination = destinations_[port_index];
322
toyoshime75675b2015-03-11 20:45:03 -0700323 size_t send_size;
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000324 for (size_t sent_size = 0u; sent_size < data.size(); sent_size += send_size) {
toyoshime75675b2015-03-11 20:45:03 -0700325 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.com27356e42013-06-22 04:03:03 +0000342
Takashi Toyoshima143f0fd2017-08-01 16:15:33 +0000343 MIDISend(midi_output_, destination, packet_list);
toyoshime75675b2015-03-11 20:45:03 -0700344 }
crogers@google.com542a43a2013-07-31 05:16:49 +0000345
ochang39a6dbc2015-12-07 18:36:08 -0800346 AccumulateMidiBytesSent(client, data.size());
crogers@google.com27356e42013-06-22 04:03:03 +0000347}
348
toyoshime147c5e2015-05-07 21:58:31 -0700349} // namespace midi