blob: c3be612afd373f5736a828fdf33a104d91bb34d6 [file] [log] [blame]
yhirano@chromium.org3cecb692014-01-29 09:27:41 +00001// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/midi/midi_manager_usb.h"
6
Gyuyoung Kim34e191a2018-01-10 09:48:42 +00007#include <memory>
dchengc2aeece2015-12-27 00:54:00 -08008#include <utility>
9
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000010#include "base/bind.h"
Hans Wennborg5f6a4612020-04-24 20:55:49 +000011#include "base/check_op.h"
yhirano059c3782014-08-28 19:37:23 -070012#include "base/strings/stringprintf.h"
Takashi Toyoshimabdfe97f2017-09-14 09:24:40 +000013#include "media/midi/midi_service.h"
14#include "media/midi/task_service.h"
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000015#include "media/midi/usb_midi_descriptor_parser.h"
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000016
toyoshime147c5e2015-05-07 21:58:31 -070017namespace midi {
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000018
toyoshimec2570a2016-10-21 02:15:27 -070019using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070020using mojom::Result;
21
toyoshimf4d61522017-02-10 02:03:32 -080022MidiManagerUsb::MidiManagerUsb(MidiService* service,
23 std::unique_ptr<UsbMidiDevice::Factory> factory)
24 : MidiManager(service), device_factory_(std::move(factory)) {}
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000025
26MidiManagerUsb::~MidiManagerUsb() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +000027 if (!service()->task_service()->UnbindInstance())
28 return;
29
30 // Finalization steps should be implemented after the UnbindInstance() call
31 // above, if we need.
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000032}
33
toyoshim@chromium.org51c7f532014-05-01 17:17:32 +000034void MidiManagerUsb::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +000035 if (!service()->task_service()->BindInstance())
36 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimabdfe97f2017-09-14 09:24:40 +000037
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000038 Initialize();
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000039}
40
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000041void MidiManagerUsb::Initialize() {
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000042 // This is safe because EnumerateDevices cancels the operation on destruction.
43 device_factory_->EnumerateDevices(
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +000044 this, base::BindOnce(&MidiManagerUsb::OnEnumerateDevicesDone,
45 base::Unretained(this)));
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000046}
47
toyoshim@chromium.orgc82e66e2014-02-04 07:05:47 +000048void MidiManagerUsb::DispatchSendMidiData(MidiManagerClient* client,
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000049 uint32_t port_index,
Avi Drissman3528fd02015-12-18 20:11:31 -050050 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +000051 base::TimeTicks timestamp) {
yhirano86da2d82015-02-09 07:13:28 -080052 if (port_index >= output_streams_.size()) {
53 // |port_index| is provided by a renderer so we can't believe that it is
54 // in the valid range.
yhirano86da2d82015-02-09 07:13:28 -080055 return;
56 }
Takashi Toyoshimabdfe97f2017-09-14 09:24:40 +000057 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
58
toyoshimec5518e2015-04-03 00:53:39 -070059 // output_streams_[port_index] is alive unless MidiManagerUsb is deleted.
Takashi Toyoshimabdfe97f2017-09-14 09:24:40 +000060 service()->task_service()->PostBoundDelayedTask(
61 TaskService::kDefaultRunnerId,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +000062 base::BindOnce(&UsbMidiOutputStream::Send,
Takashi Toyoshimabdfe97f2017-09-14 09:24:40 +000063 base::Unretained(output_streams_[port_index].get()), data),
64 delay);
65 service()->task_service()->PostBoundDelayedTask(
66 TaskService::kDefaultRunnerId,
67 base::BindOnce(&MidiManager::AccumulateMidiBytesSent,
68 base::Unretained(this), client, data.size()),
69 delay);
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000070}
71
72void MidiManagerUsb::ReceiveUsbMidiData(UsbMidiDevice* device,
73 int endpoint_number,
Avi Drissman3528fd02015-12-18 20:11:31 -050074 const uint8_t* data,
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000075 size_t size,
yhirano@chromium.orgcfa642c2014-05-01 08:54:41 +000076 base::TimeTicks time) {
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000077 if (!input_stream_)
78 return;
79 input_stream_->OnReceivedData(device,
80 endpoint_number,
81 data,
82 size,
yhirano@chromium.orgcfa642c2014-05-01 08:54:41 +000083 time);
yhirano@chromium.org3cecb692014-01-29 09:27:41 +000084}
85
danakj75afea02016-04-25 20:36:04 -070086void MidiManagerUsb::OnDeviceAttached(std::unique_ptr<UsbMidiDevice> device) {
toyoshim00fbf702015-04-30 07:52:02 -070087 int device_id = static_cast<int>(devices_.size());
dchengc2aeece2015-12-27 00:54:00 -080088 devices_.push_back(std::move(device));
xiaofeng.zhang06152d82017-05-21 04:39:34 -070089 AddPorts(devices_.back().get(), device_id);
yhirano33315c62015-02-26 17:01:15 -080090}
91
yhirano807f97f2015-02-26 18:44:10 -080092void MidiManagerUsb::OnDeviceDetached(size_t index) {
93 if (index >= devices_.size()) {
94 return;
95 }
xiaofeng.zhang06152d82017-05-21 04:39:34 -070096 UsbMidiDevice* device = devices_[index].get();
yhirano807f97f2015-02-26 18:44:10 -080097 for (size_t i = 0; i < output_streams_.size(); ++i) {
98 if (output_streams_[i]->jack().device == device) {
toyoshimec2570a2016-10-21 02:15:27 -070099 SetOutputPortState(static_cast<uint32_t>(i), PortState::DISCONNECTED);
yhirano807f97f2015-02-26 18:44:10 -0800100 }
101 }
102 const std::vector<UsbMidiJack>& input_jacks = input_stream_->jacks();
103 for (size_t i = 0; i < input_jacks.size(); ++i) {
104 if (input_jacks[i].device == device) {
toyoshimec2570a2016-10-21 02:15:27 -0700105 SetInputPortState(static_cast<uint32_t>(i), PortState::DISCONNECTED);
yhirano807f97f2015-02-26 18:44:10 -0800106 }
107 }
108}
109
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000110void MidiManagerUsb::OnReceivedData(size_t jack_index,
Avi Drissman3528fd02015-12-18 20:11:31 -0500111 const uint8_t* data,
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000112 size_t size,
yhirano@chromium.orgcfa642c2014-05-01 08:54:41 +0000113 base::TimeTicks time) {
Avi Drissman3528fd02015-12-18 20:11:31 -0500114 ReceiveMidiData(static_cast<uint32_t>(jack_index), data, size, time);
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000115}
116
117
118void MidiManagerUsb::OnEnumerateDevicesDone(bool result,
119 UsbMidiDevice::Devices* devices) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000120 if (result) {
Peter Boström78053ff2021-04-02 23:09:28 +0000121 input_stream_ = std::make_unique<UsbMidiInputStream>(this);
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000122 devices->swap(devices_);
123 for (size_t i = 0; i < devices_.size(); ++i) {
124 if (!AddPorts(devices_[i].get(), static_cast<int>(i))) {
125 result = false;
126 break;
127 }
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000128 }
yhirano33315c62015-02-26 17:01:15 -0800129 }
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000130 service()->task_service()->PostBoundTask(
131 TaskService::kDefaultRunnerId,
132 base::BindOnce(&MidiManager::CompleteInitialization,
133 base::Unretained(this),
134 result ? Result::OK : Result::INITIALIZATION_ERROR));
yhirano33315c62015-02-26 17:01:15 -0800135}
136
137bool MidiManagerUsb::AddPorts(UsbMidiDevice* device, int device_id) {
138 UsbMidiDescriptorParser parser;
Avi Drissman3528fd02015-12-18 20:11:31 -0500139 std::vector<uint8_t> descriptor = device->GetDescriptors();
140 const uint8_t* data = descriptor.size() > 0 ? &descriptor[0] : NULL;
yhirano33315c62015-02-26 17:01:15 -0800141 std::vector<UsbMidiJack> jacks;
142 bool parse_result = parser.Parse(device,
143 data,
144 descriptor.size(),
145 &jacks);
146 if (!parse_result)
147 return false;
148
yhirano8e5f3742015-04-23 23:06:15 -0700149 std::string manufacturer(device->GetManufacturer());
150 std::string product_name(device->GetProductName());
151 std::string version(device->GetDeviceVersion());
152
yhirano33315c62015-02-26 17:01:15 -0800153 for (size_t j = 0; j < jacks.size(); ++j) {
yhirano8e5f3742015-04-23 23:06:15 -0700154 // Port ID must be unique in a MIDI manager. This ID setting is
155 // sufficiently unique although there is no user-friendly meaning.
156 // TODO(yhirano): Use a hashed string as ID.
157 std::string id(
yhiranobc742d82015-09-17 07:41:44 -0700158 base::StringPrintf("usb:port-%d-%ld", device_id, static_cast<long>(j)));
yhirano33315c62015-02-26 17:01:15 -0800159 if (jacks[j].direction() == UsbMidiJack::DIRECTION_OUT) {
xiaofeng.zhang06152d82017-05-21 04:39:34 -0700160 output_streams_.push_back(
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000161 std::make_unique<UsbMidiOutputStream>(jacks[j]));
Adithya Srinivasan33252732018-10-17 15:59:40 +0000162 AddOutputPort(mojom::PortInfo(id, manufacturer, product_name, version,
163 PortState::OPENED));
yhirano33315c62015-02-26 17:01:15 -0800164 } else {
165 DCHECK_EQ(jacks[j].direction(), UsbMidiJack::DIRECTION_IN);
166 input_stream_->Add(jacks[j]);
Adithya Srinivasan33252732018-10-17 15:59:40 +0000167 AddInputPort(mojom::PortInfo(id, manufacturer, product_name, version,
168 PortState::OPENED));
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000169 }
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000170 }
yhirano33315c62015-02-26 17:01:15 -0800171 return true;
yhirano@chromium.org3cecb692014-01-29 09:27:41 +0000172}
173
toyoshime147c5e2015-05-07 21:58:31 -0700174} // namespace midi