summaryrefslogtreecommitdiffstats
path: root/media/midi/usb_midi_output_stream.cc
diff options
context:
space:
mode:
authoryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-22 10:39:41 +0000
committeryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-22 10:39:41 +0000
commit1e0f832b9b057a284de798e6c6acd1846821418a (patch)
treef7e231993175bfc7839060c44786df3ef8b5fb2d /media/midi/usb_midi_output_stream.cc
parente5f77dce153f6515f2a91ec5554869432a675539 (diff)
downloadchromium_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.cc187
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, &current, &data_to_send))
+ break;
+ } else if ((first_byte & kSysMessageBitMask) == kSysMessageBitPattern) {
+ if (first_byte & 0x08) {
+ // System Real-Time messages
+ PushSysRTMessage(data, &current, &data_to_send);
+ } else {
+ // System Common messages
+ if (!PushSysCommonMessage(data, &current, &data_to_send))
+ break;
+ }
+ } else if (first_byte & 0x80) {
+ if (!PushChannelMessage(data, &current, &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