1 // 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 7 #include "base/logging.h" 8 #include "media/midi/midi_message_util.h" 9 #include "media/midi/usb_midi_device.h" 10 11 namespace media { 12 13 UsbMidiOutputStream::UsbMidiOutputStream(const UsbMidiJack& jack) 14 : jack_(jack), pending_size_(0), is_sending_sysex_(false) {} 15 16 void UsbMidiOutputStream::Send(const std::vector<uint8>& data) { 17 // To prevent link errors caused by DCHECK_*. 18 const size_t kPacketContentSize = UsbMidiOutputStream::kPacketContentSize; 19 DCHECK_LT(jack_.cable_number, 16u); 20 21 std::vector<uint8> data_to_send; 22 size_t current = 0; 23 size_t size = GetSize(data); 24 while (current < size) { 25 uint8 first_byte = Get(data, current); 26 if (first_byte == kSysExByte || is_sending_sysex_) { 27 // System Exclusive messages 28 if (!PushSysExMessage(data, ¤t, &data_to_send)) 29 break; 30 } else if ((first_byte & kSysMessageBitMask) == kSysMessageBitPattern) { 31 if (first_byte & 0x08) { 32 // System Real-Time messages 33 PushSysRTMessage(data, ¤t, &data_to_send); 34 } else { 35 // System Common messages 36 if (!PushSysCommonMessage(data, ¤t, &data_to_send)) 37 break; 38 } 39 } else if (first_byte & 0x80) { 40 if (!PushChannelMessage(data, ¤t, &data_to_send)) 41 break; 42 } else { 43 // Unknown messages 44 DVLOG(1) << "Unknown byte: " << static_cast<unsigned int>(first_byte); 45 ++current; 46 } 47 } 48 49 if (data_to_send.size() > 0) 50 jack_.device->Send(jack_.endpoint_number(), data_to_send); 51 52 DCHECK_LE(current, size); 53 DCHECK_LE(size - current, kPacketContentSize); 54 // Note that this can be a self-copying and the iteration order is important. 55 for (size_t i = current; i < size; ++i) 56 pending_data_[i - current] = Get(data, i); 57 pending_size_ = size - current; 58 } 59 60 size_t UsbMidiOutputStream::GetSize(const std::vector<uint8>& data) const { 61 return data.size() + pending_size_; 62 } 63 64 uint8_t UsbMidiOutputStream::Get(const std::vector<uint8>& data, 65 size_t index) const { 66 DCHECK_LT(index, GetSize(data)); 67 if (index < pending_size_) 68 return pending_data_[index]; 69 return data[index - pending_size_]; 70 } 71 72 bool UsbMidiOutputStream::PushSysExMessage(const std::vector<uint8>& data, 73 size_t* current, 74 std::vector<uint8>* data_to_send) { 75 size_t index = *current; 76 size_t message_size = 0; 77 const size_t kMessageSizeMax = 3; 78 uint8 message[kMessageSizeMax] = {}; 79 80 while (index < GetSize(data)) { 81 if (message_size == kMessageSizeMax) { 82 // We can't find the end-of-message mark in the three bytes. 83 *current = index; 84 data_to_send->push_back((jack_.cable_number << 4) | 0x4); 85 data_to_send->insert(data_to_send->end(), 86 message, 87 message + arraysize(message)); 88 is_sending_sysex_ = true; 89 return true; 90 } 91 uint8 byte = Get(data, index); 92 if ((byte & kSysRTMessageBitMask) == kSysRTMessageBitPattern) { 93 // System Real-Time messages interleaved in a SysEx message 94 PushSysRTMessage(data, &index, data_to_send); 95 continue; 96 } 97 98 message[message_size] = byte; 99 ++message_size; 100 if (byte == kEndOfSysExByte) { 101 uint8 code_index = message_size + 0x4; 102 DCHECK(code_index == 0x5 || code_index == 0x6 || code_index == 0x7); 103 data_to_send->push_back((jack_.cable_number << 4) | code_index); 104 data_to_send->insert(data_to_send->end(), 105 message, 106 message + arraysize(message)); 107 *current = index + 1; 108 is_sending_sysex_ = false; 109 return true; 110 } 111 ++index; 112 } 113 return false; 114 } 115 116 bool UsbMidiOutputStream::PushSysCommonMessage( 117 const std::vector<uint8>& data, 118 size_t* current, 119 std::vector<uint8>* data_to_send) { 120 size_t index = *current; 121 uint8 first_byte = Get(data, index); 122 DCHECK_LE(0xf1, first_byte); 123 DCHECK_LE(first_byte, 0xf7); 124 const size_t message_size_table[8] = { 125 0, 2, 3, 2, 1, 1, 1, 0, 126 }; 127 size_t message_size = message_size_table[first_byte & 0x0f]; 128 DCHECK_NE(0u, message_size); 129 DCHECK_LE(message_size, 3u); 130 131 if (GetSize(data) < index + message_size) { 132 // The message is incomplete. 133 return false; 134 } 135 136 uint8 code_index = message_size == 1 ? 0x5 : static_cast<uint8>(message_size); 137 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 144 void UsbMidiOutputStream::PushSysRTMessage(const std::vector<uint8>& data, 145 size_t* current, 146 std::vector<uint8>* data_to_send) { 147 size_t index = *current; 148 uint8 first_byte = Get(data, index); 149 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 159 bool UsbMidiOutputStream::PushChannelMessage(const std::vector<uint8>& data, 160 size_t* current, 161 std::vector<uint8>* data_to_send) { 162 size_t index = *current; 163 uint8 first_byte = Get(data, index); 164 DCHECK_LE(0x80, (first_byte & 0xf0)); 165 DCHECK_LE((first_byte & 0xf0), 0xe0); 166 167 const size_t message_size_table[8] = { 168 3, 3, 3, 3, 2, 3, 3, 0, 169 }; 170 uint8 code_index = first_byte >> 4; 171 size_t message_size = message_size_table[code_index & 0x7]; 172 DCHECK_NE(0u, message_size); 173 DCHECK_LE(message_size, 3u); 174 175 if (GetSize(data) < index + message_size) { 176 // The message is incomplete. 177 return false; 178 } 179 180 data_to_send->push_back((jack_.cable_number << 4) | code_index); 181 for (size_t i = index; i < index + 3; ++i) 182 data_to_send->push_back(i < index + message_size ? Get(data, i) : 0); 183 *current += message_size; 184 return true; 185 } 186 187 } // namespace media 188