diff options
author | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 09:27:41 +0000 |
---|---|---|
committer | yhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-29 09:27:41 +0000 |
commit | a2a4a77c398d65391bd3f46ac90ce79718cf3823 (patch) | |
tree | d8bac8b65a9dd148b77ec1d35915c1c1af2d0cd4 | |
parent | 976aa18fbf2a925265dac5ff524f9e33cd572973 (diff) | |
download | chromium_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.gyp | 7 | ||||
-rw-r--r-- | media/midi/midi_manager_usb.cc | 119 | ||||
-rw-r--r-- | media/midi/midi_manager_usb.h | 83 | ||||
-rw-r--r-- | media/midi/midi_manager_usb_unittest.cc | 318 | ||||
-rw-r--r-- | media/midi/usb_midi_descriptor_parser_unittest.cc | 6 | ||||
-rw-r--r-- | media/midi/usb_midi_device.h | 38 | ||||
-rw-r--r-- | media/midi/usb_midi_input_stream.cc | 13 | ||||
-rw-r--r-- | media/midi/usb_midi_input_stream.h | 25 | ||||
-rw-r--r-- | media/midi/usb_midi_jack.h | 8 | ||||
-rw-r--r-- | media/midi/usb_midi_output_stream.h | 2 |
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; |