diff options
author | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-22 10:39:41 +0000 |
---|---|---|
committer | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-22 10:39:41 +0000 |
commit | 1e0f832b9b057a284de798e6c6acd1846821418a (patch) | |
tree | f7e231993175bfc7839060c44786df3ef8b5fb2d /media/midi/usb_midi_output_stream.cc | |
parent | e5f77dce153f6515f2a91ec5554869432a675539 (diff) | |
download | chromium_src-1e0f832b9b057a284de798e6c6acd1846821418a.zip chromium_src-1e0f832b9b057a284de798e6c6acd1846821418a.tar.gz chromium_src-1e0f832b9b057a284de798e6c6acd1846821418a.tar.bz2 |
[WebMIDI] Introduce UsbMidi{Input, Output}Stream.
UsbMidiInputStream converts USB-MIDI messages to MIDI messages.
UsbMidiOutputStream converts MIDI MIDI messages to USB-MIDI messages.
BUG=303596
R=toyoshim@chromium.org
Review URL: https://codereview.chromium.org/107513012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@246277 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/midi/usb_midi_output_stream.cc')
-rw-r--r-- | media/midi/usb_midi_output_stream.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/media/midi/usb_midi_output_stream.cc b/media/midi/usb_midi_output_stream.cc new file mode 100644 index 0000000..1aef282 --- /dev/null +++ b/media/midi/usb_midi_output_stream.cc @@ -0,0 +1,187 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/midi/usb_midi_output_stream.h" + +#include "base/logging.h" +#include "media/midi/midi_message_util.h" +#include "media/midi/usb_midi_device.h" + +namespace media { + +UsbMidiOutputStream::UsbMidiOutputStream(const UsbMidiJack& jack) + : jack_(jack), pending_size_(0), is_sending_sysex_(false) {} + +void UsbMidiOutputStream::Send(const std::vector<uint8>& data) { + // To prevent link errors caused by DCHECK_*. + const size_t kPacketContentSize = UsbMidiOutputStream::kPacketContentSize; + DCHECK_LT(jack_.cable_number, 16u); + + std::vector<uint8> data_to_send; + size_t current = 0; + size_t size = GetSize(data); + while (current < size) { + uint8 first_byte = Get(data, current); + if (first_byte == kSysExByte || is_sending_sysex_) { + // System Exclusive messages + if (!PushSysExMessage(data, ¤t, &data_to_send)) + break; + } else if ((first_byte & kSysMessageBitMask) == kSysMessageBitPattern) { + if (first_byte & 0x08) { + // System Real-Time messages + PushSysRTMessage(data, ¤t, &data_to_send); + } else { + // System Common messages + if (!PushSysCommonMessage(data, ¤t, &data_to_send)) + break; + } + } else if (first_byte & 0x80) { + if (!PushChannelMessage(data, ¤t, &data_to_send)) + break; + } else { + // Unknown messages + DVLOG(1) << "Unknown byte: " << static_cast<unsigned int>(first_byte); + ++current; + } + } + + if (data_to_send.size() > 0) + jack_.device->Send(jack_.endpoint_number(), data_to_send); + + DCHECK_LE(current, size); + DCHECK_LE(size - current, kPacketContentSize); + // Note that this can be a self-copying and the iteration order is important. + for (size_t i = current; i < size; ++i) + pending_data_[i - current] = Get(data, i); + pending_size_ = size - current; +} + +size_t UsbMidiOutputStream::GetSize(const std::vector<uint8>& data) const { + return data.size() + pending_size_; +} + +uint8_t UsbMidiOutputStream::Get(const std::vector<uint8>& data, + size_t index) const { + DCHECK_LT(index, GetSize(data)); + if (index < pending_size_) + return pending_data_[index]; + return data[index - pending_size_]; +} + +bool UsbMidiOutputStream::PushSysExMessage(const std::vector<uint8>& data, + size_t* current, + std::vector<uint8>* data_to_send) { + size_t index = *current; + size_t message_size = 0; + const size_t kMessageSizeMax = 3; + uint8 message[kMessageSizeMax] = {}; + + while (index < GetSize(data)) { + if (message_size == kMessageSizeMax) { + // We can't find the end-of-message mark in the three bytes. + *current = index; + data_to_send->push_back((jack_.cable_number << 4) | 0x4); + data_to_send->insert(data_to_send->end(), + message, + message + arraysize(message)); + is_sending_sysex_ = true; + return true; + } + uint8 byte = Get(data, index); + if ((byte & kSysRTMessageBitMask) == kSysRTMessageBitPattern) { + // System Real-Time messages interleaved in a SysEx message + PushSysRTMessage(data, &index, data_to_send); + continue; + } + + message[message_size] = byte; + ++message_size; + if (byte == kEndOfSysExByte) { + uint8 code_index = message_size + 0x4; + DCHECK(code_index == 0x5 || code_index == 0x6 || code_index == 0x7); + data_to_send->push_back((jack_.cable_number << 4) | code_index); + data_to_send->insert(data_to_send->end(), + message, + message + arraysize(message)); + *current = index + 1; + is_sending_sysex_ = false; + return true; + } + ++index; + } + return false; +} + +bool UsbMidiOutputStream::PushSysCommonMessage( + const std::vector<uint8>& data, + size_t* current, + std::vector<uint8>* data_to_send) { + size_t index = *current; + uint8 first_byte = Get(data, index); + DCHECK_LE(0xf1, first_byte); + DCHECK_LE(first_byte, 0xf7); + const size_t message_size_table[8] = { + 0, 2, 3, 2, 1, 1, 1, 0, + }; + size_t message_size = message_size_table[first_byte & 0x0f]; + DCHECK_NE(0u, message_size); + DCHECK_LE(message_size, 3u); + + if (GetSize(data) < index + message_size) { + // The message is incomplete. + return false; + } + + uint8 code_index = message_size == 1 ? 0x5 : static_cast<uint8>(message_size); + data_to_send->push_back((jack_.cable_number << 4) | code_index); + for (size_t i = index; i < index + 3; ++i) + data_to_send->push_back(i < index + message_size ? Get(data, i) : 0); + *current += message_size; + return true; +} + +void UsbMidiOutputStream::PushSysRTMessage(const std::vector<uint8>& data, + size_t* current, + std::vector<uint8>* data_to_send) { + size_t index = *current; + uint8 first_byte = Get(data, index); + DCHECK_LE(0xf8, first_byte); + DCHECK_LE(first_byte, 0xff); + + data_to_send->push_back((jack_.cable_number << 4) | 0x5); + data_to_send->push_back(first_byte); + data_to_send->push_back(0); + data_to_send->push_back(0); + *current += 1; +} + +bool UsbMidiOutputStream::PushChannelMessage(const std::vector<uint8>& data, + size_t* current, + std::vector<uint8>* data_to_send) { + size_t index = *current; + uint8 first_byte = Get(data, index); + DCHECK_LE(0x80, (first_byte & 0xf0)); + DCHECK_LE((first_byte & 0xf0), 0xe0); + + const size_t message_size_table[8] = { + 3, 3, 3, 3, 2, 3, 3, 0, + }; + uint8 code_index = first_byte >> 4; + size_t message_size = message_size_table[code_index & 0x7]; + DCHECK_NE(0u, message_size); + DCHECK_LE(message_size, 3u); + + if (GetSize(data) < index + message_size) { + // The message is incomplete. + return false; + } + + data_to_send->push_back((jack_.cable_number << 4) | code_index); + for (size_t i = index; i < index + 3; ++i) + data_to_send->push_back(i < index + message_size ? Get(data, i) : 0); + *current += message_size; + return true; +} + +} // namespace media |