Commit 1e0f832b authored by yhirano@chromium.org's avatar yhirano@chromium.org

[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
parent e5f77dce
......@@ -35,9 +35,6 @@ const size_t kMaxInFlightBytes = 10 * 1024 * 1024; // 10 MB.
// how many bytes will be sent before reporting back to the renderer.
const size_t kAcknowledgementThresholdBytes = 1024 * 1024; // 1 MB.
const uint8 kSysExMessage = 0xf0;
const uint8 kEndOfSysExMessage = 0xf7;
bool IsDataByte(uint8 data) {
return (data & 0x80) == 0;
}
......@@ -48,6 +45,9 @@ bool IsSystemRealTimeMessage(uint8 data) {
} // namespace
using media::kSysExByte;
using media::kEndOfSysExByte;
MIDIHost::MIDIHost(int renderer_process_id, media::MIDIManager* midi_manager)
: renderer_process_id_(renderer_process_id),
has_sys_ex_permission_(false),
......@@ -119,7 +119,7 @@ void MIDIHost::OnSendData(uint32 port,
// in JavaScript. The actual permission check for security purposes
// happens here in the browser process.
if (!has_sys_ex_permission_ &&
(std::find(data.begin(), data.end(), kSysExMessage) != data.end())) {
std::find(data.begin(), data.end(), kSysExByte) != data.end()) {
RecordAction(base::UserMetricsAction("BadMessageTerminate_MIDI"));
BadMessageReceived();
return;
......@@ -162,7 +162,7 @@ void MIDIHost::ReceiveMIDIData(
// MIDI devices may send a system exclusive messages even if the renderer
// doesn't have a permission to receive it. Don't kill the renderer as
// OnSendData() does.
if (message[0] == kSysExMessage && !has_sys_ex_permission_)
if (message[0] == kSysExByte && !has_sys_ex_permission_)
continue;
// Send to the renderer.
......@@ -204,13 +204,13 @@ bool MIDIHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
continue; // Found data byte as expected.
}
if (in_sysex) {
if (data[i] == kEndOfSysExMessage)
if (data[i] == kEndOfSysExByte)
in_sysex = false;
else if (!IsDataByte(current))
return false; // Error: |current| should have been data byte.
continue; // Found data byte as expected.
}
if (current == kSysExMessage) {
if (current == kSysExByte) {
in_sysex = true;
continue; // Found SysEX
}
......
......@@ -427,7 +427,12 @@
'midi/midi_port_info.h',
'midi/usb_midi_descriptor_parser.cc',
'midi/usb_midi_descriptor_parser.h',
'midi/usb_midi_device.h',
'midi/usb_midi_input_stream.cc',
'midi/usb_midi_input_stream.h',
'midi/usb_midi_jack.h',
'midi/usb_midi_output_stream.cc',
'midi/usb_midi_output_stream.h',
'video/capture/android/video_capture_device_android.cc',
'video/capture/android/video_capture_device_android.h',
'video/capture/fake_video_capture_device.cc',
......@@ -1019,6 +1024,8 @@
'midi/midi_message_queue_unittest.cc',
'midi/midi_message_util_unittest.cc',
'midi/usb_midi_descriptor_parser_unittest.cc',
'midi/usb_midi_input_stream_unittest.cc',
'midi/usb_midi_output_stream_unittest.cc',
'video/capture/video_capture_device_unittest.cc',
'webm/cluster_builder.cc',
'webm/cluster_builder.h',
......
......@@ -20,6 +20,14 @@ namespace media {
// - the End of System Exclusive message.
MEDIA_EXPORT size_t GetMIDIMessageLength(uint8 status_byte);
const uint8 kSysExByte = 0xf0;
const uint8 kEndOfSysExByte = 0xf7;
const uint8 kSysMessageBitMask = 0xf0;
const uint8 kSysMessageBitPattern = 0xf0;
const uint8 kSysRTMessageBitMask = 0xf8;
const uint8 kSysRTMessageBitPattern = 0xf8;
} // namespace media
#endif // MEDIA_MIDI_MIDI_MESSAGE_UTIL_H_
// 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.
#ifndef MEDIA_MIDI_USB_MIDI_DEVICE_H_
#define MEDIA_MIDI_USB_MIDI_DEVICE_H_
#include <vector>
#include "base/basictypes.h"
#include "media/base/media_export.h"
namespace media {
// UsbMidiDevice represents a USB-MIDI device.
// This is an interface class and each platform-dependent implementation class
// will be a derived class.
class MEDIA_EXPORT UsbMidiDevice {
public:
virtual ~UsbMidiDevice() {}
// Returns the descriptor of this device.
virtual std::vector<uint8> GetDescriptor() = 0;
// Sends |data| to the given USB endpoint of this device.
virtual void Send(int endpoint_number, const std::vector<uint8>& data) = 0;
};
} // namespace media
#endif // MEDIA_MIDI_USB_MIDI_DEVICE_H_
// 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_input_stream.h"
#include <string.h>
#include <map>
#include <vector>
#include "base/logging.h"
#include "media/midi/usb_midi_device.h"
#include "media/midi/usb_midi_jack.h"
namespace media {
UsbMidiInputStream::JackUniqueKey::JackUniqueKey(UsbMidiDevice* device,
int endpoint_number,
int cable_number)
: device(device),
endpoint_number(endpoint_number),
cable_number(cable_number) {}
bool UsbMidiInputStream::JackUniqueKey::operator==(
const JackUniqueKey& that) const {
return device == that.device &&
endpoint_number == that.endpoint_number &&
cable_number == that.cable_number;
}
bool UsbMidiInputStream::JackUniqueKey::operator<(
const JackUniqueKey& that) const {
if (device != that.device)
return device < that.device;
if (endpoint_number != that.endpoint_number)
return endpoint_number < that.endpoint_number;
return cable_number < that.cable_number;
}
UsbMidiInputStream::UsbMidiInputStream(const std::vector<UsbMidiJack>& jacks,
Delegate* delegate)
: delegate_(delegate) {
for (size_t i = 0; i < jacks.size(); ++i) {
jack_dictionary_.insert(
std::make_pair(JackUniqueKey(jacks[i].device,
jacks[i].endpoint_number(),
jacks[i].cable_number),
i));
}
}
UsbMidiInputStream::~UsbMidiInputStream() {}
void UsbMidiInputStream::OnReceivedData(UsbMidiDevice* device,
int endpoint_number,
const uint8* data,
size_t size,
double timestamp) {
DCHECK_EQ(0u, size % kPacketSize);
size_t current = 0;
while (current + kPacketSize <= size) {
ProcessOnePacket(device, endpoint_number, &data[current], timestamp);
current += kPacketSize;
}
}
void UsbMidiInputStream::ProcessOnePacket(UsbMidiDevice* device,
int endpoint_number,
const uint8* packet,
double timestamp) {
// The first 4 bytes of the packet is accessible here.
uint8 code_index = packet[0] & 0x0f;
uint8 cable_number = packet[0] >> 4;
const size_t packet_size_table[16] = {
0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1,
};
size_t packet_size = packet_size_table[code_index];
if (packet_size == 0) {
// These CINs are reserved. Ignore them.
DVLOG(1) << "code index number (" << code_index << ") arrives "
<< "but it is reserved.";
return;
}
std::map<JackUniqueKey, size_t>::const_iterator it =
jack_dictionary_.find(JackUniqueKey(device,
endpoint_number,
cable_number));
if (it != jack_dictionary_.end())
delegate_->OnReceivedData(it->second, &packet[1], packet_size, timestamp);
}
} // namespace media
// 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.
#ifndef MEDIA_MIDI_USB_MIDI_INPUT_STREAM_H_
#define MEDIA_MIDI_USB_MIDI_INPUT_STREAM_H_
#include <map>
#include <vector>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "media/base/media_export.h"
#include "media/midi/usb_midi_jack.h"
namespace media {
class UsbMidiDevice;
// UsbMidiInputStream converts USB-MIDI data to MIDI data.
// See "USB Device Class Definition for MIDI Devices" Release 1.0,
// Section 4 "USB-MIDI Event Packets" for details.
class MEDIA_EXPORT UsbMidiInputStream {
public:
class Delegate {
public:
virtual ~Delegate() {}
// This function is called when some data arrives to a USB-MIDI jack.
// An input USB-MIDI jack corresponds to an input MIDIPortInfo.
virtual void OnReceivedData(size_t jack_index,
const uint8* data,
size_t size,
double timestamp) = 0;
};
UsbMidiInputStream(const std::vector<UsbMidiJack>& jacks,
Delegate* delegate);
~UsbMidiInputStream();
// This function should be called when some data arrives to a USB-MIDI
// endpoint. This function converts the data to MIDI data and call
// |delegate->OnReceivedData| with it.
// |size| must be a multiple of |kPacketSize|.
void OnReceivedData(UsbMidiDevice* device,
int endpoint_number,
const uint8* data,
size_t size,
double timestamp);
private:
static const size_t kPacketSize = 4;
struct JackUniqueKey {
JackUniqueKey(UsbMidiDevice* device, int endpoint_number, int cable_number);
bool operator==(const JackUniqueKey& that) const;
bool operator<(const JackUniqueKey& that) const;
UsbMidiDevice* device;
int endpoint_number;
int cable_number;
};
// Processes a USB-MIDI Event Packet.
// The first |kPacketSize| bytes of |packet| must be accessible.
void ProcessOnePacket(UsbMidiDevice* device,
int endpoint_number,
const uint8* packet,
double timestamp);
// A map from UsbMidiJack to its index in |jacks_|.
std::map<JackUniqueKey, size_t> jack_dictionary_;
// Not owned
Delegate* delegate_;
DISALLOW_COPY_AND_ASSIGN(UsbMidiInputStream);
};
} // namespace media
#endif // MEDIA_MIDI_USB_MIDI_INPUT_STREAM_H_
// 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_input_stream.h"
#include <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/strings/stringprintf.h"
#include "media/midi/usb_midi_device.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
class TestUsbMidiDevice : public UsbMidiDevice {
public:
TestUsbMidiDevice() {}
virtual ~TestUsbMidiDevice() {}
virtual std::vector<uint8> GetDescriptor() OVERRIDE {
return std::vector<uint8>();
}
virtual void Send(int endpoint_number,
const std::vector<uint8>& data) OVERRIDE {}
private:
DISALLOW_COPY_AND_ASSIGN(TestUsbMidiDevice);
};
class MockDelegate : public UsbMidiInputStream::Delegate {
public:
MockDelegate() {}
virtual ~MockDelegate() {}
virtual void OnReceivedData(size_t jack_index,
const uint8* data,
size_t size,
double timestamp) OVERRIDE {
for (size_t i = 0; i < size; ++i)
received_data_ += base::StringPrintf("0x%02x ", data[i]);
received_data_ += "\n";
}
const std::string& received_data() const { return received_data_; }
private:
std::string received_data_;
DISALLOW_COPY_AND_ASSIGN(MockDelegate);
};
class UsbMidiInputStreamTest : public ::testing::Test {
protected:
UsbMidiInputStreamTest() {
std::vector<UsbMidiJack> jacks;
jacks.push_back(UsbMidiJack(&device1_,
84, // jack_id
4, // cable_number
135)); // endpoint_address
jacks.push_back(UsbMidiJack(&device2_,
85,
5,
137));
jacks.push_back(UsbMidiJack(&device2_,
84,
4,
135));
jacks.push_back(UsbMidiJack(&device1_,
85,
5,
135));
stream_.reset(new UsbMidiInputStream(jacks, &delegate_));
}
TestUsbMidiDevice device1_;
TestUsbMidiDevice device2_;
MockDelegate delegate_;
scoped_ptr<UsbMidiInputStream> stream_;
private:
DISALLOW_COPY_AND_ASSIGN(UsbMidiInputStreamTest);
};
TEST_F(UsbMidiInputStreamTest, UnknownMessage) {
uint8 data[] = {
0x40, 0xff, 0xff, 0xff,
0x41, 0xff, 0xff, 0xff,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 0);
EXPECT_EQ("", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, SystemCommonMessage) {
uint8 data[] = {
0x45, 0xf8, 0x00, 0x00,
0x42, 0xf3, 0x22, 0x00,
0x43, 0xf2, 0x33, 0x44,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 0);
EXPECT_EQ("0xf8 \n"
"0xf3 0x22 \n"
"0xf2 0x33 0x44 \n", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, SystemExclusiveMessage) {
uint8 data[] = {
0x44, 0xf0, 0x11, 0x22,
0x45, 0xf7, 0x00, 0x00,
0x46, 0xf0, 0xf7, 0x00,
0x47, 0xf0, 0x33, 0xf7,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 0);
EXPECT_EQ("0xf0 0x11 0x22 \n"
"0xf7 \n"
"0xf0 0xf7 \n"
"0xf0 0x33 0xf7 \n", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, ChannelMessage) {
uint8 data[] = {
0x48, 0x80, 0x11, 0x22,
0x49, 0x90, 0x33, 0x44,
0x4a, 0xa0, 0x55, 0x66,
0x4b, 0xb0, 0x77, 0x88,
0x4c, 0xc0, 0x99, 0x00,
0x4d, 0xd0, 0xaa, 0x00,
0x4e, 0xe0, 0xbb, 0xcc,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 0);
EXPECT_EQ("0x80 0x11 0x22 \n"
"0x90 0x33 0x44 \n"
"0xa0 0x55 0x66 \n"
"0xb0 0x77 0x88 \n"
"0xc0 0x99 \n"
"0xd0 0xaa \n"
"0xe0 0xbb 0xcc \n", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, SingleByteMessage) {
uint8 data[] = {
0x4f, 0xf8, 0x00, 0x00,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 0);
EXPECT_EQ("0xf8 \n", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, DispatchForMultipleCables) {
uint8 data[] = {
0x4f, 0xf8, 0x00, 0x00,
0x5f, 0xfa, 0x00, 0x00,
0x6f, 0xfb, 0x00, 0x00,
};
stream_->OnReceivedData(&device1_, 7, data, arraysize(data), 99);
EXPECT_EQ("0xf8 \n0xfa \n", delegate_.received_data());
}
TEST_F(UsbMidiInputStreamTest, DispatchForDevice2) {
uint8 data[] = { 0x4f, 0xf8, 0x00, 0x00 };
stream_->OnReceivedData(&device2_, 7, data, arraysize(data), 99);
EXPECT_EQ("0xf8 \n", delegate_.received_data());
}
} // namespace
} // namespace media
// 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
// 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.
#ifndef MEDIA_MIDI_USB_MIDI_OUTPUT_STREAM_H_
#define MEDIA_MIDI_USB_MIDI_OUTPUT_STREAM_H_
#include <vector>
#include "base/basictypes.h"
#include "media/base/media_export.h"
#include "media/midi/usb_midi_jack.h"
namespace media {
// UsbMidiOutputStream converts MIDI data to USB-MIDI data.
// See "USB Device Class Definition for MIDI Devices" Release 1.0,
// Section 4 "USB-MIDI Event Packets" for details.
class MEDIA_EXPORT UsbMidiOutputStream {
public:
explicit UsbMidiOutputStream(const UsbMidiJack& jack);
// Converts |data| to USB-MIDI data and send it to the jack.
void Send(const std::vector<uint8>& data);
private:
size_t GetSize(const std::vector<uint8>& data) const;
uint8_t Get(const std::vector<uint8>& data, size_t index) const;
bool PushSysExMessage(const std::vector<uint8>& data,
size_t* current,
std::vector<uint8>* data_to_send);
bool PushSysCommonMessage(const std::vector<uint8>& data,
size_t* current,
std::vector<uint8>* data_to_send);
void PushSysRTMessage(const std::vector<uint8>& data,
size_t* current,
std::vector<uint8>* data_to_send);
bool PushChannelMessage(const std::vector<uint8>& data,
size_t* current,
std::vector<uint8>* data_to_send);
static const size_t kPacketContentSize = 3;
UsbMidiJack jack_;
size_t pending_size_;
uint8 pending_data_[kPacketContentSize];
bool is_sending_sysex_;
DISALLOW_COPY_AND_ASSIGN(UsbMidiOutputStream);
};
} // namespace media
#endif // MEDIA_MIDI_USB_MIDI_OUTPUT_STREAM_H_
// 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 <string>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/strings/stringprintf.h"
#include "media/midi/usb_midi_device.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
template<typename T, size_t N>
std::vector<T> ToVector(const T((&array)[N])) {
return std::vector<T>(array, array + N);
}
class MockUsbMidiDevice : public UsbMidiDevice {
public:
MockUsbMidiDevice() {}
virtual ~MockUsbMidiDevice() {}
virtual std::vector<uint8> GetDescriptor() OVERRIDE {
return std::vector<uint8>();
}
virtual void Send(int endpoint_number, const std::vector<uint8>& data)
OVERRIDE {
for (size_t i = 0; i < data.size(); ++i) {
log_ += base::StringPrintf("0x%02x ", data[i]);
}
log_ += base::StringPrintf("(endpoint = %d)\n", endpoint_number);
}
const std::string& log() const { return log_; }
void ClearLog() { log_ = ""; }
private:
std::string log_;
DISALLOW_COPY_AND_ASSIGN(MockUsbMidiDevice);
};
class UsbMidiOutputStreamTest : public ::testing::Test {
protected:
UsbMidiOutputStreamTest() {
UsbMidiJack jack(&device_, 1, 2, 4);
stream_.reset(new UsbMidiOutputStream(jack));
}
MockUsbMidiDevice device_;
scoped_ptr<UsbMidiOutputStream> stream_;
private:
DISALLOW_COPY_AND_ASSIGN(UsbMidiOutputStreamTest);
};
TEST_F(UsbMidiOutputStreamTest, SendEmpty) {
stream_->Send(std::vector<uint8>());
EXPECT_EQ("", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendNoteOn) {
uint8 data[] = { 0x90, 0x45, 0x7f};
stream_->Send(ToVector(data));
EXPECT_EQ("0x29 0x90 0x45 0x7f (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendNoteOnPending) {
stream_->Send(std::vector<uint8>(1, 0x90));
stream_->Send(std::vector<uint8>(1, 0x45));
EXPECT_EQ("", device_.log());
stream_->Send(std::vector<uint8>(1, 0x7f));
EXPECT_EQ("0x29 0x90 0x45 0x7f (endpoint = 4)\n", device_.log());
device_.ClearLog();
stream_->Send(std::vector<uint8>(1, 0x90));
stream_->Send(std::vector<uint8>(1, 0x45));
EXPECT_EQ("", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendNoteOnBurst) {
uint8 data1[] = { 0x90, };
uint8 data2[] = { 0x45, 0x7f, 0x90, 0x45, 0x71, 0x90, 0x45, 0x72, 0x90, };
stream_->Send(ToVector(data1));
stream_->Send(ToVector(data2));
EXPECT_EQ("0x29 0x90 0x45 0x7f "
"0x29 0x90 0x45 0x71 "
"0x29 0x90 0x45 0x72 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendNoteOff) {
uint8 data[] = { 0x80, 0x33, 0x44, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x28 0x80 0x33 0x44 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendPolyphonicKeyPress) {
uint8 data[] = { 0xa0, 0x33, 0x44, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x2a 0xa0 0x33 0x44 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendControlChange) {
uint8 data[] = { 0xb7, 0x33, 0x44, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x2b 0xb7 0x33 0x44 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendProgramChange) {
uint8 data[] = { 0xc2, 0x33, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x2c 0xc2 0x33 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendChannelPressure) {
uint8 data[] = { 0xd1, 0x33, 0x44, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x2d 0xd1 0x33 0x44 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendPitchWheelChange) {
uint8 data[] = { 0xe4, 0x33, 0x44, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x2e 0xe4 0x33 0x44 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendTwoByteSysEx) {
uint8 data[] = { 0xf0, 0xf7, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x26 0xf0 0xf7 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendThreeByteSysEx) {
uint8 data[] = { 0xf0, 0x4f, 0xf7, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x27 0xf0 0x4f 0xf7 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendFourByteSysEx) {
uint8 data[] = { 0xf0, 0x00, 0x01, 0xf7, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x24 0xf0 0x00 0x01 "
"0x25 0xf7 0x00 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendFiveByteSysEx) {
uint8 data[] = { 0xf0, 0x00, 0x01, 0x02, 0xf7, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x24 0xf0 0x00 0x01 "
"0x26 0x02 0xf7 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendSixByteSysEx) {
uint8 data[] = { 0xf0, 0x00, 0x01, 0x02, 0x03, 0xf7, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x24 0xf0 0x00 0x01 "
"0x27 0x02 0x03 0xf7 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendPendingSysEx) {
uint8 data1[] = { 0xf0, 0x33, };
uint8 data2[] = { 0x44, 0x55, 0x66, };
uint8 data3[] = { 0x77, 0x88, 0x99, 0xf7, };
stream_->Send(ToVector(data1));
EXPECT_EQ("", device_.log());
stream_->Send(ToVector(data2));
EXPECT_EQ("0x24 0xf0 0x33 0x44 (endpoint = 4)\n", device_.log());
device_.ClearLog();
stream_->Send(ToVector(data3));
EXPECT_EQ("0x24 0x55 0x66 0x77 0x27 0x88 0x99 0xf7 (endpoint = 4)\n",
device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendNoteOnAfterSysEx) {
uint8 data[] = { 0xf0, 0x00, 0x01, 0x02, 0x03, 0xf7, 0x90, 0x44, 0x33, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x24 0xf0 0x00 0x01 "
"0x27 0x02 0x03 0xf7 "
"0x29 0x90 0x44 0x33 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendTimeCodeQuarterFrame) {
uint8 data[] = { 0xf1, 0x22, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x22 0xf1 0x22 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendSongPositionPointer) {
uint8 data[] = { 0xf2, 0x22, 0x33, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x23 0xf2 0x22 0x33 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendSongSelect) {
uint8 data[] = { 0xf3, 0x22, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x22 0xf3 0x22 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, TuneRequest) {
uint8 data[] = { 0xf6, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x25 0xf6 0x00 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendSongPositionPointerPending) {
uint8 data1[] = { 0xf2, 0x22, };
uint8 data2[] = { 0x33, };
stream_->Send(ToVector(data1));
EXPECT_EQ("", device_.log());
stream_->Send(ToVector(data2));
EXPECT_EQ("0x23 0xf2 0x22 0x33 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendRealTimeMessages) {
uint8 data[] = { 0xf8, 0xfa, 0xfb, 0xfc, 0xfe, 0xff, };
stream_->Send(ToVector(data));
EXPECT_EQ("0x25 0xf8 0x00 0x00 "
"0x25 0xfa 0x00 0x00 "
"0x25 0xfb 0x00 0x00 "
"0x25 0xfc 0x00 0x00 "
"0x25 0xfe 0x00 0x00 "
"0x25 0xff 0x00 0x00 (endpoint = 4)\n", device_.log());
}
TEST_F(UsbMidiOutputStreamTest, SendRealTimeInSysExMessage) {
uint8 data[] = {
0xf0, 0x00, 0x01, 0x02,
0xf8, 0xfa,
0x03, 0xf7,
};
stream_->Send(ToVector(data));
EXPECT_EQ("0x24 0xf0 0x00 0x01 "
"0x25 0xf8 0x00 0x00 "
"0x25 0xfa 0x00 0x00 "
"0x27 0x02 0x03 0xf7 (endpoint = 4)\n", device_.log());
}
} // namespace
} // namespace media
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment