blob: 2b4494a8db2ce8bcfd3deaf8e07c06d9a9ad40ac [file] [log] [blame]
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +00001// Copyright 2014 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/usb_midi_output_stream.h"
6
Lei Zhang21a44532021-05-26 19:26:48 +00007#include "base/cxx17_backports.h"
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +00008#include "base/logging.h"
toyoshimbb2750c2016-10-20 05:13:24 -07009#include "media/midi/message_util.h"
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000010#include "media/midi/usb_midi_device.h"
11
toyoshime147c5e2015-05-07 21:58:31 -070012namespace midi {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000013
14UsbMidiOutputStream::UsbMidiOutputStream(const UsbMidiJack& jack)
15 : jack_(jack), pending_size_(0), is_sending_sysex_(false) {}
16
Avi Drissman3528fd02015-12-18 20:11:31 -050017void UsbMidiOutputStream::Send(const std::vector<uint8_t>& data) {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000018 DCHECK_LT(jack_.cable_number, 16u);
19
Avi Drissman3528fd02015-12-18 20:11:31 -050020 std::vector<uint8_t> data_to_send;
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000021 size_t current = 0;
22 size_t size = GetSize(data);
23 while (current < size) {
Avi Drissman3528fd02015-12-18 20:11:31 -050024 uint8_t first_byte = Get(data, current);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000025 if (first_byte == kSysExByte || is_sending_sysex_) {
26 // System Exclusive messages
27 if (!PushSysExMessage(data, &current, &data_to_send))
28 break;
29 } else if ((first_byte & kSysMessageBitMask) == kSysMessageBitPattern) {
30 if (first_byte & 0x08) {
31 // System Real-Time messages
32 PushSysRTMessage(data, &current, &data_to_send);
33 } else {
34 // System Common messages
35 if (!PushSysCommonMessage(data, &current, &data_to_send))
36 break;
37 }
38 } else if (first_byte & 0x80) {
39 if (!PushChannelMessage(data, &current, &data_to_send))
40 break;
41 } else {
42 // Unknown messages
43 DVLOG(1) << "Unknown byte: " << static_cast<unsigned int>(first_byte);
44 ++current;
45 }
46 }
47
48 if (data_to_send.size() > 0)
49 jack_.device->Send(jack_.endpoint_number(), data_to_send);
50
51 DCHECK_LE(current, size);
52 DCHECK_LE(size - current, kPacketContentSize);
53 // Note that this can be a self-copying and the iteration order is important.
54 for (size_t i = current; i < size; ++i)
55 pending_data_[i - current] = Get(data, i);
56 pending_size_ = size - current;
57}
58
Avi Drissman3528fd02015-12-18 20:11:31 -050059size_t UsbMidiOutputStream::GetSize(const std::vector<uint8_t>& data) const {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000060 return data.size() + pending_size_;
61}
62
Avi Drissman3528fd02015-12-18 20:11:31 -050063uint8_t UsbMidiOutputStream::Get(const std::vector<uint8_t>& data,
64 size_t index) const {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000065 DCHECK_LT(index, GetSize(data));
66 if (index < pending_size_)
67 return pending_data_[index];
68 return data[index - pending_size_];
69}
70
Avi Drissman3528fd02015-12-18 20:11:31 -050071bool UsbMidiOutputStream::PushSysExMessage(const std::vector<uint8_t>& data,
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000072 size_t* current,
Avi Drissman3528fd02015-12-18 20:11:31 -050073 std::vector<uint8_t>* data_to_send) {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000074 size_t index = *current;
75 size_t message_size = 0;
76 const size_t kMessageSizeMax = 3;
Avi Drissman3528fd02015-12-18 20:11:31 -050077 uint8_t message[kMessageSizeMax] = {};
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000078
79 while (index < GetSize(data)) {
80 if (message_size == kMessageSizeMax) {
81 // We can't find the end-of-message mark in the three bytes.
82 *current = index;
83 data_to_send->push_back((jack_.cable_number << 4) | 0x4);
Avi Drissman2c637192018-12-25 20:26:39 +000084 data_to_send->insert(data_to_send->end(), message,
85 message + base::size(message));
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000086 is_sending_sysex_ = true;
87 return true;
88 }
Avi Drissman3528fd02015-12-18 20:11:31 -050089 uint8_t byte = Get(data, index);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +000090 if ((byte & kSysRTMessageBitMask) == kSysRTMessageBitPattern) {
91 // System Real-Time messages interleaved in a SysEx message
92 PushSysRTMessage(data, &index, data_to_send);
93 continue;
94 }
95
96 message[message_size] = byte;
97 ++message_size;
98 if (byte == kEndOfSysExByte) {
Avi Drissman3528fd02015-12-18 20:11:31 -050099 uint8_t code_index = static_cast<uint8_t>(message_size) + 0x4;
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000100 DCHECK(code_index == 0x5 || code_index == 0x6 || code_index == 0x7);
101 data_to_send->push_back((jack_.cable_number << 4) | code_index);
Avi Drissman2c637192018-12-25 20:26:39 +0000102 data_to_send->insert(data_to_send->end(), message,
103 message + base::size(message));
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000104 *current = index + 1;
105 is_sending_sysex_ = false;
106 return true;
107 }
108 ++index;
109 }
110 return false;
111}
112
113bool UsbMidiOutputStream::PushSysCommonMessage(
Avi Drissman3528fd02015-12-18 20:11:31 -0500114 const std::vector<uint8_t>& data,
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000115 size_t* current,
Avi Drissman3528fd02015-12-18 20:11:31 -0500116 std::vector<uint8_t>* data_to_send) {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000117 size_t index = *current;
Avi Drissman3528fd02015-12-18 20:11:31 -0500118 uint8_t first_byte = Get(data, index);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000119 DCHECK_LE(0xf1, first_byte);
120 DCHECK_LE(first_byte, 0xf7);
yhirano86ce9052014-11-03 18:58:10 -0800121 DCHECK_EQ(0xf0, first_byte & 0xf8);
122 // There are only 6 message types (0xf1 - 0xf7), so the table size is 8.
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000123 const size_t message_size_table[8] = {
124 0, 2, 3, 2, 1, 1, 1, 0,
125 };
yhirano86ce9052014-11-03 18:58:10 -0800126 size_t message_size = message_size_table[first_byte & 0x07];
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000127 DCHECK_NE(0u, message_size);
128 DCHECK_LE(message_size, 3u);
129
130 if (GetSize(data) < index + message_size) {
131 // The message is incomplete.
132 return false;
133 }
134
Avi Drissman3528fd02015-12-18 20:11:31 -0500135 uint8_t code_index =
136 message_size == 1 ? 0x5 : static_cast<uint8_t>(message_size);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000137 data_to_send->push_back((jack_.cable_number << 4) | code_index);
138 for (size_t i = index; i < index + 3; ++i)
139 data_to_send->push_back(i < index + message_size ? Get(data, i) : 0);
140 *current += message_size;
141 return true;
142}
143
Avi Drissman3528fd02015-12-18 20:11:31 -0500144void UsbMidiOutputStream::PushSysRTMessage(const std::vector<uint8_t>& data,
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000145 size_t* current,
Avi Drissman3528fd02015-12-18 20:11:31 -0500146 std::vector<uint8_t>* data_to_send) {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000147 size_t index = *current;
Avi Drissman3528fd02015-12-18 20:11:31 -0500148 uint8_t first_byte = Get(data, index);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000149 DCHECK_LE(0xf8, first_byte);
150 DCHECK_LE(first_byte, 0xff);
151
152 data_to_send->push_back((jack_.cable_number << 4) | 0x5);
153 data_to_send->push_back(first_byte);
154 data_to_send->push_back(0);
155 data_to_send->push_back(0);
156 *current += 1;
157}
158
Avi Drissman3528fd02015-12-18 20:11:31 -0500159bool UsbMidiOutputStream::PushChannelMessage(
160 const std::vector<uint8_t>& data,
161 size_t* current,
162 std::vector<uint8_t>* data_to_send) {
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000163 size_t index = *current;
Avi Drissman3528fd02015-12-18 20:11:31 -0500164 uint8_t first_byte = Get(data, index);
yhirano86ce9052014-11-03 18:58:10 -0800165
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000166 DCHECK_LE(0x80, (first_byte & 0xf0));
167 DCHECK_LE((first_byte & 0xf0), 0xe0);
yhirano86ce9052014-11-03 18:58:10 -0800168 // There are only 7 message types (0x8-0xe in the higher four bits), so the
169 // table size is 8.
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000170 const size_t message_size_table[8] = {
171 3, 3, 3, 3, 2, 3, 3, 0,
172 };
Avi Drissman3528fd02015-12-18 20:11:31 -0500173 uint8_t code_index = first_byte >> 4;
yhirano86ce9052014-11-03 18:58:10 -0800174 DCHECK_LE(0x08, code_index);
175 DCHECK_LE(code_index, 0x0e);
yhirano@chromium.org0e3c3ea2014-01-22 10:39:41 +0000176 size_t message_size = message_size_table[code_index & 0x7];
177 DCHECK_NE(0u, message_size);
178 DCHECK_LE(message_size, 3u);
179
180 if (GetSize(data) < index + message_size) {
181 // The message is incomplete.
182 return false;
183 }
184
185 data_to_send->push_back((jack_.cable_number << 4) | code_index);
186 for (size_t i = index; i < index + 3; ++i)
187 data_to_send->push_back(i < index + message_size ? Get(data, i) : 0);
188 *current += message_size;
189 return true;
190}
191
toyoshime147c5e2015-05-07 21:58:31 -0700192} // namespace midi