yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 1 | // 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 Kim | 34e191a | 2018-01-10 09:48:42 +0000 | [diff] [blame] | 7 | #include <memory> |
dcheng | c2aeece | 2015-12-27 00:54:00 -0800 | [diff] [blame] | 8 | #include <utility> |
| 9 | |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 10 | #include "base/bind.h" |
Hans Wennborg | 5f6a461 | 2020-04-24 20:55:49 +0000 | [diff] [blame] | 11 | #include "base/check_op.h" |
yhirano | 059c378 | 2014-08-28 19:37:23 -0700 | [diff] [blame] | 12 | #include "base/strings/stringprintf.h" |
Takashi Toyoshima | bdfe97f | 2017-09-14 09:24:40 +0000 | [diff] [blame] | 13 | #include "media/midi/midi_service.h" |
| 14 | #include "media/midi/task_service.h" |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 15 | #include "media/midi/usb_midi_descriptor_parser.h" |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 16 | |
toyoshim | e147c5e | 2015-05-07 21:58:31 -0700 | [diff] [blame] | 17 | namespace midi { |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 18 | |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 19 | using mojom::PortState; |
toyoshim | 2f3a48f | 2016-10-17 01:54:13 -0700 | [diff] [blame] | 20 | using mojom::Result; |
| 21 | |
toyoshim | f4d6152 | 2017-02-10 02:03:32 -0800 | [diff] [blame] | 22 | MidiManagerUsb::MidiManagerUsb(MidiService* service, |
| 23 | std::unique_ptr<UsbMidiDevice::Factory> factory) |
| 24 | : MidiManager(service), device_factory_(std::move(factory)) {} |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 25 | |
| 26 | MidiManagerUsb::~MidiManagerUsb() { |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 27 | 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.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 32 | } |
| 33 | |
toyoshim@chromium.org | 51c7f53 | 2014-05-01 17:17:32 +0000 | [diff] [blame] | 34 | void MidiManagerUsb::StartInitialization() { |
Takashi Toyoshima | 88b4ac0 | 2019-02-12 11:25:56 +0000 | [diff] [blame] | 35 | if (!service()->task_service()->BindInstance()) |
| 36 | return CompleteInitialization(Result::INITIALIZATION_ERROR); |
Takashi Toyoshima | bdfe97f | 2017-09-14 09:24:40 +0000 | [diff] [blame] | 37 | |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 38 | Initialize(); |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 39 | } |
| 40 | |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 41 | void MidiManagerUsb::Initialize() { |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 42 | // This is safe because EnumerateDevices cancels the operation on destruction. |
| 43 | device_factory_->EnumerateDevices( |
Takashi Toyoshima | d2bdc59 | 2017-09-13 10:02:54 +0000 | [diff] [blame] | 44 | this, base::BindOnce(&MidiManagerUsb::OnEnumerateDevicesDone, |
| 45 | base::Unretained(this))); |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 46 | } |
| 47 | |
toyoshim@chromium.org | c82e66e | 2014-02-04 07:05:47 +0000 | [diff] [blame] | 48 | void MidiManagerUsb::DispatchSendMidiData(MidiManagerClient* client, |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 49 | uint32_t port_index, |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 50 | const std::vector<uint8_t>& data, |
tzik | 925e2c6 | 2018-02-02 07:39:45 +0000 | [diff] [blame] | 51 | base::TimeTicks timestamp) { |
yhirano | 86da2d8 | 2015-02-09 07:13:28 -0800 | [diff] [blame] | 52 | 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. |
yhirano | 86da2d8 | 2015-02-09 07:13:28 -0800 | [diff] [blame] | 55 | return; |
| 56 | } |
Takashi Toyoshima | bdfe97f | 2017-09-14 09:24:40 +0000 | [diff] [blame] | 57 | base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp); |
| 58 | |
toyoshim | ec5518e | 2015-04-03 00:53:39 -0700 | [diff] [blame] | 59 | // output_streams_[port_index] is alive unless MidiManagerUsb is deleted. |
Takashi Toyoshima | bdfe97f | 2017-09-14 09:24:40 +0000 | [diff] [blame] | 60 | service()->task_service()->PostBoundDelayedTask( |
| 61 | TaskService::kDefaultRunnerId, |
Takashi Toyoshima | d2bdc59 | 2017-09-13 10:02:54 +0000 | [diff] [blame] | 62 | base::BindOnce(&UsbMidiOutputStream::Send, |
Takashi Toyoshima | bdfe97f | 2017-09-14 09:24:40 +0000 | [diff] [blame] | 63 | 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.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | void MidiManagerUsb::ReceiveUsbMidiData(UsbMidiDevice* device, |
| 73 | int endpoint_number, |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 74 | const uint8_t* data, |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 75 | size_t size, |
yhirano@chromium.org | cfa642c | 2014-05-01 08:54:41 +0000 | [diff] [blame] | 76 | base::TimeTicks time) { |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 77 | if (!input_stream_) |
| 78 | return; |
| 79 | input_stream_->OnReceivedData(device, |
| 80 | endpoint_number, |
| 81 | data, |
| 82 | size, |
yhirano@chromium.org | cfa642c | 2014-05-01 08:54:41 +0000 | [diff] [blame] | 83 | time); |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 84 | } |
| 85 | |
danakj | 75afea0 | 2016-04-25 20:36:04 -0700 | [diff] [blame] | 86 | void MidiManagerUsb::OnDeviceAttached(std::unique_ptr<UsbMidiDevice> device) { |
toyoshim | 00fbf70 | 2015-04-30 07:52:02 -0700 | [diff] [blame] | 87 | int device_id = static_cast<int>(devices_.size()); |
dcheng | c2aeece | 2015-12-27 00:54:00 -0800 | [diff] [blame] | 88 | devices_.push_back(std::move(device)); |
xiaofeng.zhang | 06152d8 | 2017-05-21 04:39:34 -0700 | [diff] [blame] | 89 | AddPorts(devices_.back().get(), device_id); |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 90 | } |
| 91 | |
yhirano | 807f97f | 2015-02-26 18:44:10 -0800 | [diff] [blame] | 92 | void MidiManagerUsb::OnDeviceDetached(size_t index) { |
| 93 | if (index >= devices_.size()) { |
| 94 | return; |
| 95 | } |
xiaofeng.zhang | 06152d8 | 2017-05-21 04:39:34 -0700 | [diff] [blame] | 96 | UsbMidiDevice* device = devices_[index].get(); |
yhirano | 807f97f | 2015-02-26 18:44:10 -0800 | [diff] [blame] | 97 | for (size_t i = 0; i < output_streams_.size(); ++i) { |
| 98 | if (output_streams_[i]->jack().device == device) { |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 99 | SetOutputPortState(static_cast<uint32_t>(i), PortState::DISCONNECTED); |
yhirano | 807f97f | 2015-02-26 18:44:10 -0800 | [diff] [blame] | 100 | } |
| 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) { |
toyoshim | ec2570a | 2016-10-21 02:15:27 -0700 | [diff] [blame] | 105 | SetInputPortState(static_cast<uint32_t>(i), PortState::DISCONNECTED); |
yhirano | 807f97f | 2015-02-26 18:44:10 -0800 | [diff] [blame] | 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 110 | void MidiManagerUsb::OnReceivedData(size_t jack_index, |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 111 | const uint8_t* data, |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 112 | size_t size, |
yhirano@chromium.org | cfa642c | 2014-05-01 08:54:41 +0000 | [diff] [blame] | 113 | base::TimeTicks time) { |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 114 | ReceiveMidiData(static_cast<uint32_t>(jack_index), data, size, time); |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | |
| 118 | void MidiManagerUsb::OnEnumerateDevicesDone(bool result, |
| 119 | UsbMidiDevice::Devices* devices) { |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 120 | if (result) { |
Peter Boström | 78053ff | 2021-04-02 23:09:28 +0000 | [diff] [blame] | 121 | input_stream_ = std::make_unique<UsbMidiInputStream>(this); |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 122 | 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.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 128 | } |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 129 | } |
Takashi Toyoshima | 5a6e6a3 | 2018-09-27 11:20:52 +0000 | [diff] [blame] | 130 | service()->task_service()->PostBoundTask( |
| 131 | TaskService::kDefaultRunnerId, |
| 132 | base::BindOnce(&MidiManager::CompleteInitialization, |
| 133 | base::Unretained(this), |
| 134 | result ? Result::OK : Result::INITIALIZATION_ERROR)); |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | bool MidiManagerUsb::AddPorts(UsbMidiDevice* device, int device_id) { |
| 138 | UsbMidiDescriptorParser parser; |
Avi Drissman | 3528fd0 | 2015-12-18 20:11:31 -0500 | [diff] [blame] | 139 | std::vector<uint8_t> descriptor = device->GetDescriptors(); |
| 140 | const uint8_t* data = descriptor.size() > 0 ? &descriptor[0] : NULL; |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 141 | 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 | |
yhirano | 8e5f374 | 2015-04-23 23:06:15 -0700 | [diff] [blame] | 149 | std::string manufacturer(device->GetManufacturer()); |
| 150 | std::string product_name(device->GetProductName()); |
| 151 | std::string version(device->GetDeviceVersion()); |
| 152 | |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 153 | for (size_t j = 0; j < jacks.size(); ++j) { |
yhirano | 8e5f374 | 2015-04-23 23:06:15 -0700 | [diff] [blame] | 154 | // 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( |
yhirano | bc742d8 | 2015-09-17 07:41:44 -0700 | [diff] [blame] | 158 | base::StringPrintf("usb:port-%d-%ld", device_id, static_cast<long>(j))); |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 159 | if (jacks[j].direction() == UsbMidiJack::DIRECTION_OUT) { |
xiaofeng.zhang | 06152d8 | 2017-05-21 04:39:34 -0700 | [diff] [blame] | 160 | output_streams_.push_back( |
Gyuyoung Kim | 34e191a | 2018-01-10 09:48:42 +0000 | [diff] [blame] | 161 | std::make_unique<UsbMidiOutputStream>(jacks[j])); |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 162 | AddOutputPort(mojom::PortInfo(id, manufacturer, product_name, version, |
| 163 | PortState::OPENED)); |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 164 | } else { |
| 165 | DCHECK_EQ(jacks[j].direction(), UsbMidiJack::DIRECTION_IN); |
| 166 | input_stream_->Add(jacks[j]); |
Adithya Srinivasan | 3325273 | 2018-10-17 15:59:40 +0000 | [diff] [blame] | 167 | AddInputPort(mojom::PortInfo(id, manufacturer, product_name, version, |
| 168 | PortState::OPENED)); |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 169 | } |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 170 | } |
yhirano | 33315c6 | 2015-02-26 17:01:15 -0800 | [diff] [blame] | 171 | return true; |
yhirano@chromium.org | 3cecb69 | 2014-01-29 09:27:41 +0000 | [diff] [blame] | 172 | } |
| 173 | |
toyoshim | e147c5e | 2015-05-07 21:58:31 -0700 | [diff] [blame] | 174 | } // namespace midi |