summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 09:27:41 +0000
committeryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-29 09:27:41 +0000
commita2a4a77c398d65391bd3f46ac90ce79718cf3823 (patch)
treed8bac8b65a9dd148b77ec1d35915c1c1af2d0cd4
parent976aa18fbf2a925265dac5ff524f9e33cd572973 (diff)
downloadchromium_src-a2a4a77c398d65391bd3f46ac90ce79718cf3823.zip
chromium_src-a2a4a77c398d65391bd3f46ac90ce79718cf3823.tar.gz
chromium_src-a2a4a77c398d65391bd3f46ac90ce79718cf3823.tar.bz2
[WebMIDI] Introduce MidiManagerUsb
Introduce MidiManagerUsb, a MIDIManager subclass for USB-MIDI. Android port will use this class. BUG=303596 R=toyoshim@chromium.org Review URL: https://codereview.chromium.org/107163008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247665 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/media.gyp7
-rw-r--r--media/midi/midi_manager_usb.cc119
-rw-r--r--media/midi/midi_manager_usb.h83
-rw-r--r--media/midi/midi_manager_usb_unittest.cc318
-rw-r--r--media/midi/usb_midi_descriptor_parser_unittest.cc6
-rw-r--r--media/midi/usb_midi_device.h38
-rw-r--r--media/midi/usb_midi_input_stream.cc13
-rw-r--r--media/midi/usb_midi_input_stream.h25
-rw-r--r--media/midi/usb_midi_jack.h8
-rw-r--r--media/midi/usb_midi_output_stream.h2
10 files changed, 599 insertions, 20 deletions
diff --git a/media/media.gyp b/media/media.gyp
index 7da5c58..8630999 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -417,12 +417,14 @@
'midi/midi_manager_alsa.h',
'midi/midi_manager_mac.cc',
'midi/midi_manager_mac.h',
+ 'midi/midi_manager_usb.cc',
+ 'midi/midi_manager_usb.h',
+ 'midi/midi_manager_win.cc',
+ 'midi/midi_manager_win.h',
'midi/midi_message_queue.cc',
'midi/midi_message_queue.h',
'midi/midi_message_util.cc',
'midi/midi_message_util.h',
- 'midi/midi_manager_win.cc',
- 'midi/midi_manager_win.h',
'midi/midi_port_info.cc',
'midi/midi_port_info.h',
'midi/usb_midi_descriptor_parser.cc',
@@ -1022,6 +1024,7 @@
'filters/video_frame_painter_unittest.cc',
'filters/video_frame_stream_unittest.cc',
'filters/video_renderer_impl_unittest.cc',
+ 'midi/midi_manager_usb_unittest.cc',
'midi/midi_message_queue_unittest.cc',
'midi/midi_message_util_unittest.cc',
'midi/usb_midi_descriptor_parser_unittest.cc',
diff --git a/media/midi/midi_manager_usb.cc b/media/midi/midi_manager_usb.cc
new file mode 100644
index 0000000..c240c4a
--- /dev/null
+++ b/media/midi/midi_manager_usb.cc
@@ -0,0 +1,119 @@
+// 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/midi_manager_usb.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "media/midi/usb_midi_descriptor_parser.h"
+#include "media/midi/usb_midi_device.h"
+#include "media/midi/usb_midi_input_stream.h"
+#include "media/midi/usb_midi_jack.h"
+#include "media/midi/usb_midi_output_stream.h"
+
+namespace media {
+
+namespace {
+
+// Noop callback for (sync) Initialize.
+// TODO(yhirano): This function should go away when
+// MIDIManager::Initialize() becomes asynchronous.
+void Noop(bool result) {
+}
+
+} // namespace
+
+MidiManagerUsb::MidiManagerUsb(scoped_ptr<UsbMidiDevice::Factory> factory)
+ : device_factory_(factory.Pass()) {
+}
+
+MidiManagerUsb::~MidiManagerUsb() {
+}
+
+bool MidiManagerUsb::Initialize() {
+ Initialize(base::Bind(Noop));
+ return true;
+}
+
+void MidiManagerUsb::Initialize(base::Callback<void(bool result)> callback) {
+ initialize_callback_ = callback;
+ // This is safe because EnumerateDevices cancels the operation on destruction.
+ device_factory_->EnumerateDevices(
+ this,
+ base::Bind(&MidiManagerUsb::OnEnumerateDevicesDone,
+ base::Unretained(this)));
+}
+
+void MidiManagerUsb::DispatchSendMIDIData(MIDIManagerClient* client,
+ uint32_t port_index,
+ const std::vector<uint8>& data,
+ double timestamp) {
+ DCHECK_LT(port_index, output_streams_.size());
+ output_streams_[port_index]->Send(data);
+ client->AccumulateMIDIBytesSent(data.size());
+}
+
+void MidiManagerUsb::ReceiveUsbMidiData(UsbMidiDevice* device,
+ int endpoint_number,
+ const uint8* data,
+ size_t size,
+ double timestamp) {
+ if (!input_stream_)
+ return;
+ input_stream_->OnReceivedData(device,
+ endpoint_number,
+ data,
+ size,
+ timestamp);
+}
+
+void MidiManagerUsb::OnReceivedData(size_t jack_index,
+ const uint8* data,
+ size_t size,
+ double timestamp) {
+ ReceiveMIDIData(jack_index, data, size, timestamp);
+}
+
+
+void MidiManagerUsb::OnEnumerateDevicesDone(bool result,
+ UsbMidiDevice::Devices* devices) {
+ if (!result) {
+ initialize_callback_.Run(false);
+ return;
+ }
+ devices->swap(devices_);
+ for (size_t i = 0; i < devices_.size(); ++i) {
+ UsbMidiDescriptorParser parser;
+ std::vector<uint8> descriptor = devices_[i]->GetDescriptor();
+ const uint8* data = descriptor.size() > 0 ? &descriptor[0] : NULL;
+ std::vector<UsbMidiJack> jacks;
+ bool parse_result = parser.Parse(devices_[i],
+ data,
+ descriptor.size(),
+ &jacks);
+ if (!parse_result) {
+ initialize_callback_.Run(false);
+ return;
+ }
+ std::vector<UsbMidiJack> input_jacks;
+ for (size_t j = 0; j < jacks.size(); ++j) {
+ if (jacks[j].direction() == UsbMidiJack::DIRECTION_OUT) {
+ output_streams_.push_back(new UsbMidiOutputStream(jacks[j]));
+ // TODO(yhirano): Set appropriate properties.
+ AddOutputPort(MIDIPortInfo());
+ } else {
+ DCHECK_EQ(jacks[j].direction(), UsbMidiJack::DIRECTION_IN);
+ input_jacks.push_back(jacks[j]);
+ // TODO(yhirano): Set appropriate properties.
+ AddInputPort(MIDIPortInfo());
+ }
+ }
+ input_stream_.reset(new UsbMidiInputStream(input_jacks, this));
+ }
+ initialize_callback_.Run(true);
+}
+
+} // namespace media
diff --git a/media/midi/midi_manager_usb.h b/media/midi/midi_manager_usb.h
new file mode 100644
index 0000000..27a9aa7
--- /dev/null
+++ b/media/midi/midi_manager_usb.h
@@ -0,0 +1,83 @@
+// 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_MIDI_MANAGER_USB_H_
+#define MEDIA_MIDI_MIDI_MANAGER_USB_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/media_export.h"
+#include "media/midi/midi_manager.h"
+#include "media/midi/usb_midi_device.h"
+#include "media/midi/usb_midi_input_stream.h"
+#include "media/midi/usb_midi_jack.h"
+#include "media/midi/usb_midi_output_stream.h"
+
+namespace media {
+
+// MIDIManager for USB-MIDI.
+class MEDIA_EXPORT MidiManagerUsb : public MIDIManager,
+ public UsbMidiDeviceDelegate,
+ public UsbMidiInputStream::Delegate {
+ public:
+ explicit MidiManagerUsb(scoped_ptr<UsbMidiDevice::Factory> device_factory);
+ virtual ~MidiManagerUsb();
+
+ // MIDIManager implementation.
+ virtual bool Initialize() OVERRIDE;
+ virtual void DispatchSendMIDIData(MIDIManagerClient* client,
+ uint32 port_index,
+ const std::vector<uint8>& data,
+ double timestamp) OVERRIDE;
+
+ // UsbMidiDeviceDelegate implementation.
+ virtual void ReceiveUsbMidiData(UsbMidiDevice* device,
+ int endpoint_number,
+ const uint8* data,
+ size_t size,
+ double timestamp) OVERRIDE;
+
+ // UsbMidiInputStream::Delegate implementation.
+ virtual void OnReceivedData(size_t jack_index,
+ const uint8* data,
+ size_t size,
+ double timestamp) OVERRIDE;
+
+ const ScopedVector<UsbMidiOutputStream>& output_streams() const {
+ return output_streams_;
+ }
+ const UsbMidiInputStream* input_stream() const { return input_stream_.get(); }
+
+ // Initializes this object.
+ // When the initialization finishes, |callback| will be called with the
+ // result.
+ // When this factory is destroyed during the operation, the operation
+ // will be canceled silently (i.e. |callback| will not be called).
+ void Initialize(base::Callback<void(bool result)> callback);
+
+ private:
+ void OnEnumerateDevicesDone(bool result, UsbMidiDevice::Devices* devices);
+
+ scoped_ptr<UsbMidiDevice::Factory> device_factory_;
+ ScopedVector<UsbMidiDevice> devices_;
+ ScopedVector<UsbMidiOutputStream> output_streams_;
+ scoped_ptr<UsbMidiInputStream> input_stream_;
+
+ base::Callback<void(bool result)> initialize_callback_;
+
+ // A map from <endpoint_number, cable_number> to the index of input jacks.
+ base::hash_map<std::pair<int, int>, size_t> input_jack_dictionary_;
+
+ DISALLOW_COPY_AND_ASSIGN(MidiManagerUsb);
+};
+
+} // namespace media
+
+#endif // MEDIA_MIDI_MIDI_MANAGER_USB_H_
diff --git a/media/midi/midi_manager_usb_unittest.cc b/media/midi/midi_manager_usb_unittest.cc
new file mode 100644
index 0000000..1eb779d
--- /dev/null
+++ b/media/midi/midi_manager_usb_unittest.cc
@@ -0,0 +1,318 @@
+// 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/midi_manager_usb.h"
+
+#include <string>
+
+#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 Logger {
+ public:
+ Logger() {}
+ ~Logger() {}
+
+ void AddLog(const std::string& message) { log_ += message; }
+ std::string TakeLog() {
+ std::string result;
+ result.swap(log_);
+ return result;
+ }
+
+ private:
+ std::string log_;
+
+ DISALLOW_COPY_AND_ASSIGN(Logger);
+};
+
+class FakeUsbMidiDevice : public UsbMidiDevice {
+ public:
+ explicit FakeUsbMidiDevice(Logger* logger) : logger_(logger) {}
+ virtual ~FakeUsbMidiDevice() {}
+
+ virtual std::vector<uint8> GetDescriptor() OVERRIDE {
+ logger_->AddLog("UsbMidiDevice::GetDescriptor\n");
+ return descriptor_;
+ }
+
+ virtual void Send(int endpoint_number,
+ const std::vector<uint8>& data) OVERRIDE {
+ logger_->AddLog("UsbMidiDevice::Send ");
+ logger_->AddLog(base::StringPrintf("endpoint = %d data =",
+ endpoint_number));
+ for (size_t i = 0; i < data.size(); ++i)
+ logger_->AddLog(base::StringPrintf(" 0x%02x", data[i]));
+ logger_->AddLog("\n");
+ }
+
+ void SetDescriptor(const std::vector<uint8> descriptor) {
+ descriptor_ = descriptor;
+ }
+
+ private:
+ std::vector<uint8> descriptor_;
+ Logger* logger_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeUsbMidiDevice);
+};
+
+class FakeMidiManagerClient : public MIDIManagerClient {
+ public:
+ explicit FakeMidiManagerClient(Logger* logger) : logger_(logger) {}
+ virtual ~FakeMidiManagerClient() {}
+
+ virtual void ReceiveMIDIData(uint32 port_index,
+ const uint8* data,
+ size_t size,
+ double timestamp) OVERRIDE {
+ logger_->AddLog("MIDIManagerClient::ReceiveMIDIData ");
+ logger_->AddLog(base::StringPrintf("port_index = %d data =", port_index));
+ for (size_t i = 0; i < size; ++i)
+ logger_->AddLog(base::StringPrintf(" 0x%02x", data[i]));
+ logger_->AddLog("\n");
+ }
+
+ virtual void AccumulateMIDIBytesSent(size_t size) OVERRIDE {
+ logger_->AddLog("MIDIManagerClient::AccumulateMIDIBytesSent ");
+ // Windows has no "%zu".
+ logger_->AddLog(base::StringPrintf("size = %u\n",
+ static_cast<unsigned>(size)));
+ }
+
+ private:
+ Logger* logger_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
+};
+
+class TestUsbMidiDeviceFactory : public UsbMidiDevice::Factory {
+ public:
+ TestUsbMidiDeviceFactory() {}
+ virtual ~TestUsbMidiDeviceFactory() {}
+ virtual void EnumerateDevices(UsbMidiDeviceDelegate* device,
+ Callback callback) OVERRIDE {
+ callback_ = callback;
+ }
+
+ Callback callback_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestUsbMidiDeviceFactory);
+};
+
+class MidiManagerUsbTest : public ::testing::Test {
+ public:
+ MidiManagerUsbTest()
+ : initialize_callback_run_(false), initialize_result_(false) {
+ scoped_ptr<TestUsbMidiDeviceFactory> factory(new TestUsbMidiDeviceFactory);
+ factory_ = factory.get();
+ manager_.reset(
+ new MidiManagerUsb(factory.PassAs<UsbMidiDevice::Factory>()));
+ }
+ virtual ~MidiManagerUsbTest() {
+ std::string leftover_logs = logger_.TakeLog();
+ if (!leftover_logs.empty()) {
+ ADD_FAILURE() << "Log should be empty: " << leftover_logs;
+ }
+ }
+
+ protected:
+ void Initialize() {
+ manager_->Initialize(base::Bind(&MidiManagerUsbTest::OnInitializeDone,
+ base::Unretained(this)));
+ }
+
+ void OnInitializeDone(bool result) {
+ initialize_callback_run_ = true;
+ initialize_result_ = result;
+ }
+
+ bool initialize_callback_run_;
+ bool initialize_result_;
+
+ scoped_ptr<MidiManagerUsb> manager_;
+ // Owned by manager_.
+ TestUsbMidiDeviceFactory* factory_;
+ Logger logger_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MidiManagerUsbTest);
+};
+
+
+TEST_F(MidiManagerUsbTest, Initialize) {
+ scoped_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
+ uint8 descriptor[] = {
+ 0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a,
+ 0x2d, 0x75, 0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02,
+ 0x75, 0x00, 0x02, 0x01, 0x00, 0x80, 0x30, 0x09, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x09, 0x24, 0x01, 0x00,
+ 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01, 0x00, 0x02,
+ 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
+ 0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02,
+ 0x01, 0x03, 0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09,
+ 0x24, 0x03, 0x01, 0x07, 0x01, 0x06, 0x01, 0x00, 0x09, 0x24,
+ 0x03, 0x02, 0x04, 0x01, 0x02, 0x01, 0x00, 0x09, 0x24, 0x03,
+ 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05, 0x02, 0x02,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
+ 0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x25, 0x01, 0x01, 0x07,
+ };
+ device->SetDescriptor(ToVector(descriptor));
+
+ Initialize();
+ ScopedVector<UsbMidiDevice> devices;
+ devices.push_back(device.release());
+ EXPECT_FALSE(initialize_callback_run_);
+ factory_->callback_.Run(true, &devices);
+ EXPECT_TRUE(initialize_callback_run_);
+ EXPECT_TRUE(initialize_result_);
+
+ ASSERT_EQ(1u, manager_->input_ports().size());
+ ASSERT_EQ(2u, manager_->output_ports().size());
+ ASSERT_TRUE(manager_->input_stream());
+ std::vector<UsbMidiInputStream::JackUniqueKey> keys =
+ manager_->input_stream()->RegisteredJackKeysForTesting();
+ ASSERT_EQ(2u, manager_->output_streams().size());
+ EXPECT_EQ(2u, manager_->output_streams()[0]->jack().jack_id);
+ EXPECT_EQ(3u, manager_->output_streams()[1]->jack().jack_id);
+ ASSERT_EQ(1u, keys.size());
+ EXPECT_EQ(2, keys[0].endpoint_number);
+
+ EXPECT_EQ("UsbMidiDevice::GetDescriptor\n", logger_.TakeLog());
+}
+
+TEST_F(MidiManagerUsbTest, InitializeFail) {
+ Initialize();
+
+ EXPECT_FALSE(initialize_callback_run_);
+ factory_->callback_.Run(false, NULL);
+ EXPECT_TRUE(initialize_callback_run_);
+ EXPECT_FALSE(initialize_result_);
+}
+
+TEST_F(MidiManagerUsbTest, InitializeFailBecauseOfInvalidDescriptor) {
+ scoped_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
+ uint8 descriptor[] = {0x04};
+ device->SetDescriptor(ToVector(descriptor));
+
+ Initialize();
+ ScopedVector<UsbMidiDevice> devices;
+ devices.push_back(device.release());
+ EXPECT_FALSE(initialize_callback_run_);
+ factory_->callback_.Run(true, &devices);
+ EXPECT_TRUE(initialize_callback_run_);
+ EXPECT_FALSE(initialize_result_);
+ EXPECT_EQ("UsbMidiDevice::GetDescriptor\n", logger_.TakeLog());
+}
+
+TEST_F(MidiManagerUsbTest, Send) {
+ scoped_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
+ FakeMidiManagerClient client(&logger_);
+ uint8 descriptor[] = {
+ 0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a,
+ 0x2d, 0x75, 0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02,
+ 0x75, 0x00, 0x02, 0x01, 0x00, 0x80, 0x30, 0x09, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x09, 0x24, 0x01, 0x00,
+ 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01, 0x00, 0x02,
+ 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
+ 0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02,
+ 0x01, 0x03, 0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09,
+ 0x24, 0x03, 0x01, 0x07, 0x01, 0x06, 0x01, 0x00, 0x09, 0x24,
+ 0x03, 0x02, 0x04, 0x01, 0x02, 0x01, 0x00, 0x09, 0x24, 0x03,
+ 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05, 0x02, 0x02,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
+ 0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x25, 0x01, 0x01, 0x07,
+ };
+
+ device->SetDescriptor(ToVector(descriptor));
+ uint8 data[] = {
+ 0x90, 0x45, 0x7f,
+ 0xf0, 0x00, 0x01, 0xf7,
+ };
+
+ Initialize();
+ ScopedVector<UsbMidiDevice> devices;
+ devices.push_back(device.release());
+ EXPECT_FALSE(initialize_callback_run_);
+ factory_->callback_.Run(true, &devices);
+ ASSERT_TRUE(initialize_callback_run_);
+ ASSERT_TRUE(initialize_result_);
+ ASSERT_EQ(2u, manager_->output_streams().size());
+
+ manager_->DispatchSendMIDIData(&client, 1, ToVector(data), 0);
+ EXPECT_EQ("UsbMidiDevice::GetDescriptor\n"
+ "UsbMidiDevice::Send endpoint = 2 data = "
+ "0x19 0x90 0x45 0x7f "
+ "0x14 0xf0 0x00 0x01 "
+ "0x15 0xf7 0x00 0x00\n"
+ "MIDIManagerClient::AccumulateMIDIBytesSent size = 7\n",
+ logger_.TakeLog());
+}
+
+TEST_F(MidiManagerUsbTest, Receive) {
+ scoped_ptr<FakeUsbMidiDevice> device(new FakeUsbMidiDevice(&logger_));
+ FakeMidiManagerClient client(&logger_);
+ uint8 descriptor[] = {
+ 0x12, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x08, 0x86, 0x1a,
+ 0x2d, 0x75, 0x54, 0x02, 0x00, 0x02, 0x00, 0x01, 0x09, 0x02,
+ 0x75, 0x00, 0x02, 0x01, 0x00, 0x80, 0x30, 0x09, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x09, 0x24, 0x01, 0x00,
+ 0x01, 0x09, 0x00, 0x01, 0x01, 0x09, 0x04, 0x01, 0x00, 0x02,
+ 0x01, 0x03, 0x00, 0x00, 0x07, 0x24, 0x01, 0x00, 0x01, 0x51,
+ 0x00, 0x06, 0x24, 0x02, 0x01, 0x02, 0x00, 0x06, 0x24, 0x02,
+ 0x01, 0x03, 0x00, 0x06, 0x24, 0x02, 0x02, 0x06, 0x00, 0x09,
+ 0x24, 0x03, 0x01, 0x07, 0x01, 0x06, 0x01, 0x00, 0x09, 0x24,
+ 0x03, 0x02, 0x04, 0x01, 0x02, 0x01, 0x00, 0x09, 0x24, 0x03,
+ 0x02, 0x05, 0x01, 0x03, 0x01, 0x00, 0x09, 0x05, 0x02, 0x02,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x06, 0x25, 0x01, 0x02, 0x02,
+ 0x03, 0x09, 0x05, 0x82, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x25, 0x01, 0x01, 0x07,
+ };
+
+ device->SetDescriptor(ToVector(descriptor));
+ uint8 data[] = {
+ 0x09, 0x90, 0x45, 0x7f,
+ 0x04, 0xf0, 0x00, 0x01,
+ 0x49, 0x90, 0x88, 0x99, // This data should be ignored (CN = 4).
+ 0x05, 0xf7, 0x00, 0x00,
+ };
+
+ Initialize();
+ ScopedVector<UsbMidiDevice> devices;
+ UsbMidiDevice* device_raw = device.get();
+ devices.push_back(device.release());
+ EXPECT_FALSE(initialize_callback_run_);
+ factory_->callback_.Run(true, &devices);
+ ASSERT_TRUE(initialize_callback_run_);
+ ASSERT_TRUE(initialize_result_);
+
+ manager_->StartSession(&client);
+ manager_->ReceiveUsbMidiData(device_raw, 2, data, arraysize(data), 0);
+ manager_->EndSession(&client);
+
+ EXPECT_EQ("UsbMidiDevice::GetDescriptor\n"
+ "MIDIManagerClient::ReceiveMIDIData port_index = 0 "
+ "data = 0x90 0x45 0x7f\n"
+ "MIDIManagerClient::ReceiveMIDIData port_index = 0 "
+ "data = 0xf0 0x00 0x01\n"
+ "MIDIManagerClient::ReceiveMIDIData port_index = 0 data = 0xf7\n",
+ logger_.TakeLog());
+}
+
+} // namespace
+
+} // namespace media
diff --git a/media/midi/usb_midi_descriptor_parser_unittest.cc b/media/midi/usb_midi_descriptor_parser_unittest.cc
index fe6b3b2..cd6203b 100644
--- a/media/midi/usb_midi_descriptor_parser_unittest.cc
+++ b/media/midi/usb_midi_descriptor_parser_unittest.cc
@@ -80,19 +80,19 @@ TEST(UsbMidiDescriptorParserTest, Parse) {
EXPECT_EQ(2u, jacks[0].jack_id);
EXPECT_EQ(0u, jacks[0].cable_number);
EXPECT_EQ(2u, jacks[0].endpoint_number());
- EXPECT_EQ(UsbMidiJack::OUT, jacks[0].direction());
+ EXPECT_EQ(UsbMidiJack::DIRECTION_OUT, jacks[0].direction());
EXPECT_EQ(NULL, jacks[0].device);
EXPECT_EQ(3u, jacks[1].jack_id);
EXPECT_EQ(1u, jacks[1].cable_number);
EXPECT_EQ(2u, jacks[1].endpoint_number());
- EXPECT_EQ(UsbMidiJack::OUT, jacks[1].direction());
+ EXPECT_EQ(UsbMidiJack::DIRECTION_OUT, jacks[1].direction());
EXPECT_EQ(NULL, jacks[1].device);
EXPECT_EQ(7u, jacks[2].jack_id);
EXPECT_EQ(0u, jacks[2].cable_number);
EXPECT_EQ(2u, jacks[2].endpoint_number());
- EXPECT_EQ(UsbMidiJack::IN, jacks[2].direction());
+ EXPECT_EQ(UsbMidiJack::DIRECTION_IN, jacks[2].direction());
EXPECT_EQ(NULL, jacks[2].device);
}
diff --git a/media/midi/usb_midi_device.h b/media/midi/usb_midi_device.h
index 3f29d64..59f129d 100644
--- a/media/midi/usb_midi_device.h
+++ b/media/midi/usb_midi_device.h
@@ -8,15 +8,53 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
#include "media/base/media_export.h"
namespace media {
+class UsbMidiDevice;
+
+// Delegate class for UsbMidiDevice.
+// Each method is called when an corresponding event arrives at the device.
+class MEDIA_EXPORT UsbMidiDeviceDelegate {
+ public:
+ virtual ~UsbMidiDeviceDelegate() {}
+
+ // Called when USB-MIDI data arrives at |device|.
+ virtual void ReceiveUsbMidiData(UsbMidiDevice* device,
+ int endpoint_number,
+ const uint8* data,
+ size_t size,
+ double timestamp) = 0;
+};
+
// 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:
+ typedef ScopedVector<UsbMidiDevice> Devices;
+
+ // Factory class for USB-MIDI devices.
+ // Each concrete implementation will find and create devices
+ // in platform-dependent way.
+ class Factory {
+ public:
+ typedef base::Callback<void(bool result, Devices* devices)> Callback;
+ virtual ~Factory() {}
+ // Enumerates devices.
+ // Devices that have no USB-MIDI interfaces can be omitted.
+ // When the operation succeeds, |callback| will be called with |true| and
+ // devices.
+ // Otherwise |callback| will be called with |false| and empty devices.
+ // When this factory is destroyed during the operation, the operation
+ // will be canceled silently (i.e. |callback| will not be called).
+ virtual void EnumerateDevices(UsbMidiDeviceDelegate* delegate,
+ Callback callback) = 0;
+ };
+
virtual ~UsbMidiDevice() {}
// Returns the descriptor of this device.
diff --git a/media/midi/usb_midi_input_stream.cc b/media/midi/usb_midi_input_stream.cc
index f68436c..0096865 100644
--- a/media/midi/usb_midi_input_stream.cc
+++ b/media/midi/usb_midi_input_stream.cc
@@ -89,4 +89,17 @@ void UsbMidiInputStream::ProcessOnePacket(UsbMidiDevice* device,
delegate_->OnReceivedData(it->second, &packet[1], packet_size, timestamp);
}
+std::vector<UsbMidiInputStream::JackUniqueKey>
+UsbMidiInputStream::RegisteredJackKeysForTesting() const {
+ std::vector<JackUniqueKey> result(jack_dictionary_.size(),
+ JackUniqueKey(0, 0, 0));
+ for (std::map<JackUniqueKey, size_t>::const_iterator it =
+ jack_dictionary_.begin();
+ it != jack_dictionary_.end(); ++it) {
+ DCHECK_LT(it->second, result.size());
+ result[it->second] = it->first;
+ }
+ return result;
+}
+
} // namespace media
diff --git a/media/midi/usb_midi_input_stream.h b/media/midi/usb_midi_input_stream.h
index 70ed306..2207c5d 100644
--- a/media/midi/usb_midi_input_stream.h
+++ b/media/midi/usb_midi_input_stream.h
@@ -22,7 +22,7 @@ class UsbMidiDevice;
// Section 4 "USB-MIDI Event Packets" for details.
class MEDIA_EXPORT UsbMidiInputStream {
public:
- class Delegate {
+ class MEDIA_EXPORT Delegate {
public:
virtual ~Delegate() {}
// This function is called when some data arrives to a USB-MIDI jack.
@@ -33,6 +33,17 @@ class MEDIA_EXPORT UsbMidiInputStream {
double timestamp) = 0;
};
+ // This is public for testing.
+ 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;
+ };
+
UsbMidiInputStream(const std::vector<UsbMidiJack>& jacks,
Delegate* delegate);
~UsbMidiInputStream();
@@ -47,18 +58,10 @@ class MEDIA_EXPORT UsbMidiInputStream {
size_t size,
double timestamp);
+ std::vector<JackUniqueKey> RegisteredJackKeysForTesting() const;
+
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,
diff --git a/media/midi/usb_midi_jack.h b/media/midi/usb_midi_jack.h
index d3fe9a9..271cad8 100644
--- a/media/midi/usb_midi_jack.h
+++ b/media/midi/usb_midi_jack.h
@@ -16,10 +16,10 @@ class UsbMidiDevice;
struct MEDIA_EXPORT UsbMidiJack {
// The direction of the endpoint associated with an EMBEDDED MIDI jack.
// Note that an IN MIDI jack associated with an OUT endpoint has
- // ***OUT*** direction.
+ // ***DIRECTION_OUT*** direction.
enum Direction {
- IN,
- OUT,
+ DIRECTION_IN,
+ DIRECTION_OUT,
};
UsbMidiJack(UsbMidiDevice* device,
uint8 jack_id,
@@ -39,7 +39,7 @@ struct MEDIA_EXPORT UsbMidiJack {
uint8 endpoint_address;
Direction direction() const {
- return (endpoint_address & 0x80) ? IN : OUT;
+ return (endpoint_address & 0x80) ? DIRECTION_IN : DIRECTION_OUT;
}
uint8 endpoint_number() const {
return (endpoint_address & 0xf);
diff --git a/media/midi/usb_midi_output_stream.h b/media/midi/usb_midi_output_stream.h
index 008da4c..1d98d58 100644
--- a/media/midi/usb_midi_output_stream.h
+++ b/media/midi/usb_midi_output_stream.h
@@ -23,6 +23,8 @@ class MEDIA_EXPORT UsbMidiOutputStream {
// Converts |data| to USB-MIDI data and send it to the jack.
void Send(const std::vector<uint8>& data);
+ const UsbMidiJack& jack() const { return jack_; }
+
private:
size_t GetSize(const std::vector<uint8>& data) const;
uint8_t Get(const std::vector<uint8>& data, size_t index) const;