blob: cf269915a8a9311277af6be534ed1cb2f2d02b51 [file] [log] [blame]
toyoshimd28b59c2017-02-20 11:07:37 -08001// Copyright 2017 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
toyoshim63e32a52017-04-25 07:20:10 -07005#include "media/midi/midi_manager_win.h"
toyoshimd28b59c2017-02-20 11:07:37 -08006
7#include <windows.h>
8
toyoshim6ebf1182017-03-01 00:40:31 -08009#include <ks.h>
10#include <ksmedia.h>
toyoshimd28b59c2017-02-20 11:07:37 -080011#include <mmreg.h>
12#include <mmsystem.h>
13
14#include <algorithm>
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +000015#include <limits>
Donna Wud5a689e2019-05-13 02:56:18 +000016#include <map>
Peter Boström1684b972021-05-01 01:31:25 +000017#include <memory>
toyoshimd28b59c2017-02-20 11:07:37 -080018#include <string>
Avi Drissmanb9070c02022-01-08 23:21:38 +000019#include <tuple>
Donna Wud5a689e2019-05-13 02:56:18 +000020#include <utility>
toyoshimd28b59c2017-02-20 11:07:37 -080021
Sebastien Marchand2912d9f2019-01-25 16:49:37 +000022#include "base/bind.h"
toyoshimd28b59c2017-02-20 11:07:37 -080023#include "base/callback.h"
danakj70310852020-11-11 16:01:35 +000024#include "base/callback_helpers.h"
Lei Zhang21a44532021-05-26 19:26:48 +000025#include "base/cxx17_backports.h"
toyoshimd28b59c2017-02-20 11:07:37 -080026#include "base/logging.h"
Keishi Hattori5b8de612021-11-27 09:25:52 +000027#include "base/memory/raw_ptr.h"
toyoshimd28b59c2017-02-20 11:07:37 -080028#include "base/strings/stringprintf.h"
29#include "base/strings/utf_string_conversions.h"
30#include "base/synchronization/lock.h"
Patrick Monette3f9156f2021-10-15 19:13:42 +000031#include "base/task/single_thread_task_runner.h"
toyoshim15385b32017-04-24 23:52:01 -070032#include "base/win/windows_version.h"
toyoshim8a5ad422017-02-28 21:16:18 -080033#include "media/midi/message_util.h"
toyoshim15385b32017-04-24 23:52:01 -070034#include "media/midi/midi_manager_winrt.h"
toyoshimd28b59c2017-02-20 11:07:37 -080035#include "media/midi/midi_service.h"
Adithya Srinivasan33252732018-10-17 15:59:40 +000036#include "media/midi/midi_service.mojom.h"
toyoshim15385b32017-04-24 23:52:01 -070037#include "media/midi/midi_switches.h"
Donna Wucd8e1d02019-05-20 03:56:21 +000038#include "services/device/public/cpp/usb/usb_ids.h"
Anton Bikineev476333e2021-05-15 18:05:03 +000039#include "third_party/abseil-cpp/absl/types/optional.h"
toyoshimd28b59c2017-02-20 11:07:37 -080040
41namespace midi {
42
toyoshim8a5ad422017-02-28 21:16:18 -080043// Forward declaration of PortManager for anonymous functions and internal
44// classes to use it.
toyoshim63e32a52017-04-25 07:20:10 -070045class MidiManagerWin::PortManager {
toyoshim8a5ad422017-02-28 21:16:18 -080046 public:
47 // Calculates event time from elapsed time that system provides.
48 base::TimeTicks CalculateInEventTime(size_t index, uint32_t elapsed_ms) const;
49
50 // Registers HMIDIIN handle to resolve port index.
51 void RegisterInHandle(HMIDIIN handle, size_t index);
52
53 // Unregisters HMIDIIN handle.
54 void UnregisterInHandle(HMIDIIN handle);
55
56 // Finds HMIDIIN handle and fullfil |out_index| with the port index.
toyoshim6d87aaa2017-02-28 22:36:44 -080057 bool FindInHandle(HMIDIIN hmi, size_t* out_index);
toyoshim8a5ad422017-02-28 21:16:18 -080058
59 // Restores used input buffer for the next data receive.
60 void RestoreInBuffer(size_t index);
61
62 // Ports accessors.
63 std::vector<std::unique_ptr<InPort>>* inputs() { return &input_ports_; }
64 std::vector<std::unique_ptr<OutPort>>* outputs() { return &output_ports_; }
65
66 // Handles MIDI input port callbacks that runs on a system provided thread.
67 static void CALLBACK HandleMidiInCallback(HMIDIIN hmi,
68 UINT msg,
69 DWORD_PTR instance,
70 DWORD_PTR param1,
71 DWORD_PTR param2);
72
toyoshim6d87aaa2017-02-28 22:36:44 -080073 // Handles MIDI output port callbacks that runs on a system provided thread.
74 static void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo,
75 UINT msg,
76 DWORD_PTR instance,
77 DWORD_PTR param1,
78 DWORD_PTR param2);
79
toyoshim8a5ad422017-02-28 21:16:18 -080080 private:
81 // Holds all MIDI input or output ports connected once.
82 std::vector<std::unique_ptr<InPort>> input_ports_;
83 std::vector<std::unique_ptr<OutPort>> output_ports_;
84
85 // Map to resolve MIDI input port index from HMIDIIN.
86 std::map<HMIDIIN, size_t> hmidiin_to_index_map_;
87};
88
toyoshimd28b59c2017-02-20 11:07:37 -080089namespace {
90
toyoshimc32dd892017-02-24 02:13:14 -080091// Assumes that nullptr represents an invalid MIDI handle.
92constexpr HMIDIIN kInvalidInHandle = nullptr;
93constexpr HMIDIOUT kInvalidOutHandle = nullptr;
94
toyoshim6d87aaa2017-02-28 22:36:44 -080095// Defines SysEx message size limit.
96// TODO(crbug.com/383578): This restriction should be removed once Web MIDI
97// defines a standardized way to handle large sysex messages.
98// Note for built-in USB-MIDI driver:
99// From an observation on Windows 7/8.1 with a USB-MIDI keyboard,
100// midiOutLongMsg() will be always blocked. Sending 64 bytes or less data takes
101// roughly 300 usecs. Sending 2048 bytes or more data takes roughly
102// |message.size() / (75 * 1024)| secs in practice. Here we put 256 KB size
103// limit on SysEx message, with hoping that midiOutLongMsg will be blocked at
104// most 4 sec or so with a typical USB-MIDI device.
105// TODO(toyoshim): Consider to use linked small buffers so that midiOutReset()
106// can abort sending unhandled following buffers.
107constexpr size_t kSysExSizeLimit = 256 * 1024;
108
toyoshim8a5ad422017-02-28 21:16:18 -0800109// Defines input buffer size.
110constexpr size_t kBufferLength = 32 * 1024;
111
toyoshimd28b59c2017-02-20 11:07:37 -0800112// Global variables to identify MidiManager instance.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000113constexpr int64_t kInvalidInstanceId = -1;
114int64_t g_active_instance_id = kInvalidInstanceId;
toyoshim63e32a52017-04-25 07:20:10 -0700115MidiManagerWin* g_manager_instance = nullptr;
toyoshimd28b59c2017-02-20 11:07:37 -0800116
117// Obtains base::Lock instance pointer to lock instance_id.
118base::Lock* GetInstanceIdLock() {
119 static base::Lock* lock = new base::Lock;
120 return lock;
121}
122
123// Issues unique MidiManager instance ID.
Anton Bikineev476333e2021-05-15 18:05:03 +0000124int64_t IssueNextInstanceId(absl::optional<int64_t> override_id) {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000125 static int64_t id = kInvalidInstanceId;
126 if (override_id) {
127 int64_t result = ++id;
128 id = *override_id;
129 return result;
130 }
131 if (id == std::numeric_limits<int64_t>::max())
132 return kInvalidInstanceId;
toyoshimd28b59c2017-02-20 11:07:37 -0800133 return ++id;
134}
135
136// Use single TaskRunner for all tasks running outside the I/O thread.
137constexpr int kTaskRunner = 0;
138
139// Obtains base::Lock instance pointer to ensure tasks run safely on TaskRunner.
toyoshimc32dd892017-02-24 02:13:14 -0800140// Since all tasks on TaskRunner run behind a lock of *GetTaskLock(), we can
141// access all members even on the I/O thread if a lock of *GetTaskLock() is
142// obtained.
toyoshimd28b59c2017-02-20 11:07:37 -0800143base::Lock* GetTaskLock() {
144 static base::Lock* lock = new base::Lock;
145 return lock;
146}
147
148// Helper function to run a posted task on TaskRunner safely.
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000149void RunTask(int instance_id, base::OnceClosure task) {
toyoshimd28b59c2017-02-20 11:07:37 -0800150 // Obtains task lock to ensure that the instance should not complete
151 // Finalize() while running the |task|.
152 base::AutoLock task_lock(*GetTaskLock());
153 {
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000154 // If destructor finished before the lock avobe, do nothing.
toyoshimd28b59c2017-02-20 11:07:37 -0800155 base::AutoLock lock(*GetInstanceIdLock());
156 if (instance_id != g_active_instance_id)
157 return;
158 }
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000159 std::move(task).Run();
toyoshimd28b59c2017-02-20 11:07:37 -0800160}
161
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000162// TODO(toyoshim): Use midi::TaskService and deprecate its prototype
163// implementation above that is still used in this MidiManagerWin class.
toyoshimd28b59c2017-02-20 11:07:37 -0800164
Takashi Toyoshimaa09cbc72017-06-01 18:49:34 +0900165// Obtains base::Lock instance pointer to protect
166// |g_midi_in_get_num_devs_thread_id|.
167base::Lock* GetMidiInGetNumDevsThreadIdLock() {
168 static base::Lock* lock = new base::Lock;
169 return lock;
170}
171
172// Holds a thread id that calls midiInGetNumDevs() now. We use a platform
173// primitive to identify the thread because the following functions can be
174// called on a thread that Windows allocates internally, and Chrome or //base
175// library does not know.
176base::PlatformThreadId g_midi_in_get_num_devs_thread_id;
177
178// Prepares to call midiInGetNumDevs().
179void EnterMidiInGetNumDevs() {
180 base::AutoLock lock(*GetMidiInGetNumDevsThreadIdLock());
181 g_midi_in_get_num_devs_thread_id = base::PlatformThread::CurrentId();
182}
183
184// Finalizes to call midiInGetNumDevs().
185void LeaveMidiInGetNumDevs() {
186 base::AutoLock lock(*GetMidiInGetNumDevsThreadIdLock());
187 g_midi_in_get_num_devs_thread_id = base::PlatformThreadId();
188}
189
190// Checks if the current thread is running midiInGetNumDevs(), that means
191// current code is invoked inside midiInGetNumDevs().
192bool IsRunningInsideMidiInGetNumDevs() {
193 base::AutoLock lock(*GetMidiInGetNumDevsThreadIdLock());
194 return base::PlatformThread::CurrentId() == g_midi_in_get_num_devs_thread_id;
195}
196
toyoshim8a5ad422017-02-28 21:16:18 -0800197// Utility class to handle MIDIHDR struct safely.
198class MIDIHDRDeleter {
199 public:
200 void operator()(LPMIDIHDR header) {
201 if (!header)
202 return;
203 delete[] static_cast<char*>(header->lpData);
204 delete header;
205 }
206};
207
208using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>;
209
210ScopedMIDIHDR CreateMIDIHDR(size_t size) {
211 ScopedMIDIHDR hdr(new MIDIHDR);
212 ZeroMemory(hdr.get(), sizeof(*hdr));
213 hdr->lpData = new char[size];
214 hdr->dwBufferLength = static_cast<DWORD>(size);
215 return hdr;
216}
217
toyoshim6d87aaa2017-02-28 22:36:44 -0800218ScopedMIDIHDR CreateMIDIHDR(const std::vector<uint8_t>& data) {
219 ScopedMIDIHDR hdr(CreateMIDIHDR(data.size()));
220 std::copy(data.begin(), data.end(), hdr->lpData);
221 return hdr;
222}
223
toyoshimc32dd892017-02-24 02:13:14 -0800224// Helper functions to close MIDI device handles on TaskRunner asynchronously.
toyoshim8a5ad422017-02-28 21:16:18 -0800225void FinalizeInPort(HMIDIIN handle, ScopedMIDIHDR hdr) {
226 // Resets the device. This stops receiving messages, and allows to release
227 // registered buffer headers. Otherwise, midiInUnprepareHeader() and
228 // midiInClose() will fail with MIDIERR_STILLPLAYING.
229 midiInReset(handle);
230
231 if (hdr)
232 midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr));
toyoshimc32dd892017-02-24 02:13:14 -0800233 midiInClose(handle);
234}
235
236void FinalizeOutPort(HMIDIOUT handle) {
toyoshim6d87aaa2017-02-28 22:36:44 -0800237 // Resets inflight buffers. This will cancel sending data that system
238 // holds and were not sent yet.
239 midiOutReset(handle);
toyoshimc32dd892017-02-24 02:13:14 -0800240 midiOutClose(handle);
241}
242
toyoshim6ebf1182017-03-01 00:40:31 -0800243// Gets manufacturer name in string from identifiers.
244std::string GetManufacturerName(uint16_t id, const GUID& guid) {
245 if (IS_COMPATIBLE_USBAUDIO_MID(&guid)) {
246 const char* name =
247 device::UsbIds::GetVendorName(EXTRACT_USBAUDIO_MID(&guid));
248 if (name)
249 return std::string(name);
250 }
251 if (id == MM_MICROSOFT)
252 return "Microsoft Corporation";
253
254 // TODO(crbug.com/472341): Support other manufacture IDs.
255 return "";
256}
257
toyoshimc32dd892017-02-24 02:13:14 -0800258// All instances of Port subclasses are always accessed behind a lock of
259// *GetTaskLock(). Port and subclasses implementation do not need to
260// consider thread safety.
toyoshimd28b59c2017-02-20 11:07:37 -0800261class Port {
262 public:
263 Port(const std::string& type,
264 uint32_t device_id,
265 uint16_t manufacturer_id,
266 uint16_t product_id,
267 uint32_t driver_version,
toyoshim6ebf1182017-03-01 00:40:31 -0800268 const std::string& product_name,
269 const GUID& manufacturer_guid)
toyoshimd28b59c2017-02-20 11:07:37 -0800270 : index_(0u),
271 type_(type),
272 device_id_(device_id),
273 manufacturer_id_(manufacturer_id),
274 product_id_(product_id),
275 driver_version_(driver_version),
276 product_name_(product_name) {
toyoshim6ebf1182017-03-01 00:40:31 -0800277 info_.manufacturer =
278 GetManufacturerName(manufacturer_id, manufacturer_guid);
toyoshimd28b59c2017-02-20 11:07:37 -0800279 info_.name = product_name_;
280 info_.version = base::StringPrintf("%d.%d", HIBYTE(driver_version_),
281 LOBYTE(driver_version_));
282 info_.state = mojom::PortState::DISCONNECTED;
283 }
284
285 virtual ~Port() {}
286
287 bool operator==(const Port& other) const {
288 // Should not use |device_id| for comparison because it can be changed on
289 // each enumeration.
290 // Since the GUID will be changed on each enumeration for Microsoft GS
291 // Wavetable synth and might be done for others, do not use it for device
292 // comparison.
293 return manufacturer_id_ == other.manufacturer_id_ &&
294 product_id_ == other.product_id_ &&
295 driver_version_ == other.driver_version_ &&
296 product_name_ == other.product_name_;
297 }
298
299 bool IsConnected() const {
300 return info_.state != mojom::PortState::DISCONNECTED;
301 }
302
303 void set_index(size_t index) {
304 index_ = index;
305 // TODO(toyoshim): Use hashed ID.
Bruce Dawson66ae7db2017-08-04 17:57:45 +0000306 info_.id = base::StringPrintf("%s-%zd", type_.c_str(), index_);
toyoshimd28b59c2017-02-20 11:07:37 -0800307 }
308 size_t index() { return index_; }
309 void set_device_id(uint32_t device_id) { device_id_ = device_id; }
310 uint32_t device_id() { return device_id_; }
Adithya Srinivasan33252732018-10-17 15:59:40 +0000311 const mojom::PortInfo& info() { return info_; }
toyoshimd28b59c2017-02-20 11:07:37 -0800312
313 virtual bool Connect() {
314 if (info_.state != mojom::PortState::DISCONNECTED)
315 return false;
316
317 info_.state = mojom::PortState::CONNECTED;
318 // TODO(toyoshim) Until open() / close() are supported, open each device on
319 // connected.
320 Open();
321 return true;
322 }
323
324 virtual bool Disconnect() {
325 if (info_.state == mojom::PortState::DISCONNECTED)
326 return false;
327 info_.state = mojom::PortState::DISCONNECTED;
328 return true;
329 }
330
331 virtual void Open() { info_.state = mojom::PortState::OPENED; }
332
333 protected:
334 size_t index_;
335 std::string type_;
336 uint32_t device_id_;
337 const uint16_t manufacturer_id_;
338 const uint16_t product_id_;
339 const uint32_t driver_version_;
340 const std::string product_name_;
Adithya Srinivasan33252732018-10-17 15:59:40 +0000341 mojom::PortInfo info_;
toyoshimd28b59c2017-02-20 11:07:37 -0800342}; // class Port
343
344} // namespace
345
toyoshim63e32a52017-04-25 07:20:10 -0700346class MidiManagerWin::InPort final : public Port {
toyoshimd28b59c2017-02-20 11:07:37 -0800347 public:
toyoshim63e32a52017-04-25 07:20:10 -0700348 InPort(MidiManagerWin* manager,
toyoshim8a5ad422017-02-28 21:16:18 -0800349 int instance_id,
350 UINT device_id,
351 const MIDIINCAPS2W& caps)
toyoshimd28b59c2017-02-20 11:07:37 -0800352 : Port("input",
353 device_id,
354 caps.wMid,
355 caps.wPid,
356 caps.vDriverVersion,
Peter Kasting426665c2021-02-02 13:01:34 +0000357 base::WideToUTF8(std::wstring(caps.szPname, wcslen(caps.szPname))),
toyoshim6ebf1182017-03-01 00:40:31 -0800358 caps.ManufacturerGuid),
toyoshim8a5ad422017-02-28 21:16:18 -0800359 manager_(manager),
360 in_handle_(kInvalidInHandle),
361 instance_id_(instance_id) {}
toyoshimd28b59c2017-02-20 11:07:37 -0800362
toyoshim8a5ad422017-02-28 21:16:18 -0800363 static std::vector<std::unique_ptr<InPort>> EnumerateActivePorts(
toyoshim63e32a52017-04-25 07:20:10 -0700364 MidiManagerWin* manager,
toyoshim8a5ad422017-02-28 21:16:18 -0800365 int instance_id) {
toyoshimd28b59c2017-02-20 11:07:37 -0800366 std::vector<std::unique_ptr<InPort>> ports;
Takashi Toyoshimaa09cbc72017-06-01 18:49:34 +0900367
368 // Allow callback invocations indie midiInGetNumDevs().
369 EnterMidiInGetNumDevs();
toyoshimd28b59c2017-02-20 11:07:37 -0800370 const UINT num_devices = midiInGetNumDevs();
Takashi Toyoshimaa09cbc72017-06-01 18:49:34 +0900371 LeaveMidiInGetNumDevs();
372
toyoshimd28b59c2017-02-20 11:07:37 -0800373 for (UINT device_id = 0; device_id < num_devices; ++device_id) {
374 MIDIINCAPS2W caps;
375 MMRESULT result = midiInGetDevCaps(
376 device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), sizeof(caps));
377 if (result != MMSYSERR_NOERROR) {
378 LOG(ERROR) << "midiInGetDevCaps fails on device " << device_id;
379 continue;
380 }
toyoshim8a5ad422017-02-28 21:16:18 -0800381 ports.push_back(
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000382 std::make_unique<InPort>(manager, instance_id, device_id, caps));
toyoshimd28b59c2017-02-20 11:07:37 -0800383 }
384 return ports;
385 }
386
toyoshimc32dd892017-02-24 02:13:14 -0800387 void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) {
388 if (in_handle_ != kInvalidInHandle) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000389 runner->PostTask(FROM_HERE, base::BindOnce(&FinalizeInPort, in_handle_,
Jan Wilken Dörrieff8d12d2020-03-31 12:12:11 +0000390 std::move(hdr_)));
toyoshim8a5ad422017-02-28 21:16:18 -0800391 manager_->port_manager()->UnregisterInHandle(in_handle_);
toyoshimc32dd892017-02-24 02:13:14 -0800392 in_handle_ = kInvalidInHandle;
393 }
394 }
395
toyoshim8a5ad422017-02-28 21:16:18 -0800396 base::TimeTicks CalculateInEventTime(uint32_t elapsed_ms) const {
Peter Kasting99947e52021-10-02 03:06:35 +0000397 return start_time_ + base::Milliseconds(elapsed_ms);
toyoshim8a5ad422017-02-28 21:16:18 -0800398 }
399
400 void RestoreBuffer() {
401 if (in_handle_ == kInvalidInHandle || !hdr_)
402 return;
403 midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_));
404 }
405
toyoshim63e32a52017-04-25 07:20:10 -0700406 void NotifyPortStateSet(MidiManagerWin* manager) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000407 manager->PostReplyTask(base::BindOnce(
Bruce Dawson7fd26de2017-08-01 19:33:12 +0000408 &MidiManagerWin::SetInputPortState, base::Unretained(manager),
409 static_cast<uint32_t>(index_), info_.state));
toyoshimd28b59c2017-02-20 11:07:37 -0800410 }
411
toyoshim63e32a52017-04-25 07:20:10 -0700412 void NotifyPortAdded(MidiManagerWin* manager) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000413 manager->PostReplyTask(base::BindOnce(&MidiManagerWin::AddInputPort,
414 base::Unretained(manager), info_));
toyoshimd28b59c2017-02-20 11:07:37 -0800415 }
toyoshimc32dd892017-02-24 02:13:14 -0800416
417 // Port overrides:
418 bool Disconnect() override {
419 if (in_handle_ != kInvalidInHandle) {
420 // Following API call may fail because device was already disconnected.
421 // But just in case.
422 midiInClose(in_handle_);
toyoshim8a5ad422017-02-28 21:16:18 -0800423 manager_->port_manager()->UnregisterInHandle(in_handle_);
toyoshimc32dd892017-02-24 02:13:14 -0800424 in_handle_ = kInvalidInHandle;
425 }
426 return Port::Disconnect();
427 }
428
429 void Open() override {
toyoshim8a5ad422017-02-28 21:16:18 -0800430 MMRESULT result = midiInOpen(
431 &in_handle_, device_id_,
432 reinterpret_cast<DWORD_PTR>(&PortManager::HandleMidiInCallback),
433 instance_id_, CALLBACK_FUNCTION);
toyoshimc32dd892017-02-24 02:13:14 -0800434 if (result == MMSYSERR_NOERROR) {
toyoshim8a5ad422017-02-28 21:16:18 -0800435 hdr_ = CreateMIDIHDR(kBufferLength);
436 result = midiInPrepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_));
437 }
438 if (result != MMSYSERR_NOERROR)
439 in_handle_ = kInvalidInHandle;
440 if (result == MMSYSERR_NOERROR)
441 result = midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_));
442 if (result == MMSYSERR_NOERROR)
443 result = midiInStart(in_handle_);
444 if (result == MMSYSERR_NOERROR) {
445 start_time_ = base::TimeTicks::Now();
446 manager_->port_manager()->RegisterInHandle(in_handle_, index_);
toyoshimc32dd892017-02-24 02:13:14 -0800447 Port::Open();
448 } else {
toyoshim8a5ad422017-02-28 21:16:18 -0800449 if (in_handle_ != kInvalidInHandle) {
450 midiInUnprepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_));
451 hdr_.reset();
452 midiInClose(in_handle_);
453 in_handle_ = kInvalidInHandle;
454 }
toyoshimc32dd892017-02-24 02:13:14 -0800455 Disconnect();
456 }
457 }
458
459 private:
Keishi Hattori5b8de612021-11-27 09:25:52 +0000460 raw_ptr<MidiManagerWin> manager_;
toyoshimc32dd892017-02-24 02:13:14 -0800461 HMIDIIN in_handle_;
toyoshim8a5ad422017-02-28 21:16:18 -0800462 ScopedMIDIHDR hdr_;
463 base::TimeTicks start_time_;
464 const int instance_id_;
toyoshimd28b59c2017-02-20 11:07:37 -0800465};
466
toyoshim63e32a52017-04-25 07:20:10 -0700467class MidiManagerWin::OutPort final : public Port {
toyoshimd28b59c2017-02-20 11:07:37 -0800468 public:
469 OutPort(UINT device_id, const MIDIOUTCAPS2W& caps)
470 : Port("output",
471 device_id,
472 caps.wMid,
473 caps.wPid,
474 caps.vDriverVersion,
Peter Kasting426665c2021-02-02 13:01:34 +0000475 base::WideToUTF8(std::wstring(caps.szPname, wcslen(caps.szPname))),
toyoshim6ebf1182017-03-01 00:40:31 -0800476 caps.ManufacturerGuid),
toyoshimc32dd892017-02-24 02:13:14 -0800477 software_(caps.wTechnology == MOD_SWSYNTH),
478 out_handle_(kInvalidOutHandle) {}
toyoshimd28b59c2017-02-20 11:07:37 -0800479
480 static std::vector<std::unique_ptr<OutPort>> EnumerateActivePorts() {
481 std::vector<std::unique_ptr<OutPort>> ports;
482 const UINT num_devices = midiOutGetNumDevs();
483 for (UINT device_id = 0; device_id < num_devices; ++device_id) {
484 MIDIOUTCAPS2W caps;
485 MMRESULT result = midiOutGetDevCaps(
486 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps));
487 if (result != MMSYSERR_NOERROR) {
488 LOG(ERROR) << "midiOutGetDevCaps fails on device " << device_id;
489 continue;
490 }
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000491 ports.push_back(std::make_unique<OutPort>(device_id, caps));
toyoshimd28b59c2017-02-20 11:07:37 -0800492 }
493 return ports;
494 }
495
toyoshimc32dd892017-02-24 02:13:14 -0800496 void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) {
497 if (out_handle_ != kInvalidOutHandle) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000498 runner->PostTask(FROM_HERE,
499 base::BindOnce(&FinalizeOutPort, out_handle_));
toyoshimc32dd892017-02-24 02:13:14 -0800500 out_handle_ = kInvalidOutHandle;
toyoshimd28b59c2017-02-20 11:07:37 -0800501 }
toyoshimd28b59c2017-02-20 11:07:37 -0800502 }
503
toyoshim63e32a52017-04-25 07:20:10 -0700504 void NotifyPortStateSet(MidiManagerWin* manager) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000505 manager->PostReplyTask(base::BindOnce(
Bruce Dawson7fd26de2017-08-01 19:33:12 +0000506 &MidiManagerWin::SetOutputPortState, base::Unretained(manager),
507 static_cast<uint32_t>(index_), info_.state));
toyoshimd28b59c2017-02-20 11:07:37 -0800508 }
509
toyoshim63e32a52017-04-25 07:20:10 -0700510 void NotifyPortAdded(MidiManagerWin* manager) {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000511 manager->PostReplyTask(base::BindOnce(&MidiManagerWin::AddOutputPort,
512 base::Unretained(manager), info_));
toyoshimd28b59c2017-02-20 11:07:37 -0800513 }
514
toyoshim6d87aaa2017-02-28 22:36:44 -0800515 void Send(const std::vector<uint8_t>& data) {
516 if (out_handle_ == kInvalidOutHandle)
517 return;
518
519 if (data.size() <= 3) {
520 uint32_t message = 0;
521 for (size_t i = 0; i < data.size(); ++i)
522 message |= (static_cast<uint32_t>(data[i]) << (i * 8));
523 midiOutShortMsg(out_handle_, message);
524 } else {
525 if (data.size() > kSysExSizeLimit) {
526 LOG(ERROR) << "Ignoring SysEx message due to the size limit"
527 << ", size = " << data.size();
528 // TODO(toyoshim): Consider to report metrics here.
529 return;
530 }
531 ScopedMIDIHDR hdr(CreateMIDIHDR(data));
532 MMRESULT result =
533 midiOutPrepareHeader(out_handle_, hdr.get(), sizeof(*hdr));
534 if (result != MMSYSERR_NOERROR)
535 return;
536 result = midiOutLongMsg(out_handle_, hdr.get(), sizeof(*hdr));
537 if (result != MMSYSERR_NOERROR) {
538 midiOutUnprepareHeader(out_handle_, hdr.get(), sizeof(*hdr));
539 } else {
540 // MIDIHDR will be released on MOM_DONE.
Avi Drissmanb9070c02022-01-08 23:21:38 +0000541 std::ignore = hdr.release();
toyoshim6d87aaa2017-02-28 22:36:44 -0800542 }
543 }
544 }
545
toyoshimc32dd892017-02-24 02:13:14 -0800546 // Port overrides:
547 bool Connect() override {
548 // Until |software| option is supported, disable Microsoft GS Wavetable
549 // Synth that has a known security issue.
550 if (software_ && manufacturer_id_ == MM_MICROSOFT &&
551 (product_id_ == MM_MSFT_WDMAUDIO_MIDIOUT ||
552 product_id_ == MM_MSFT_GENERIC_MIDISYNTH)) {
553 return false;
554 }
555 return Port::Connect();
556 }
557
558 bool Disconnect() override {
559 if (out_handle_ != kInvalidOutHandle) {
560 // Following API call may fail because device was already disconnected.
561 // But just in case.
562 midiOutClose(out_handle_);
563 out_handle_ = kInvalidOutHandle;
564 }
565 return Port::Disconnect();
566 }
567
568 void Open() override {
toyoshim6d87aaa2017-02-28 22:36:44 -0800569 MMRESULT result = midiOutOpen(
570 &out_handle_, device_id_,
571 reinterpret_cast<DWORD_PTR>(&PortManager::HandleMidiOutCallback), 0,
572 CALLBACK_FUNCTION);
toyoshimc32dd892017-02-24 02:13:14 -0800573 if (result == MMSYSERR_NOERROR) {
574 Port::Open();
575 } else {
576 out_handle_ = kInvalidOutHandle;
577 Disconnect();
578 }
579 }
580
toyoshimd28b59c2017-02-20 11:07:37 -0800581 const bool software_;
toyoshimc32dd892017-02-24 02:13:14 -0800582 HMIDIOUT out_handle_;
toyoshimd28b59c2017-02-20 11:07:37 -0800583};
584
toyoshim63e32a52017-04-25 07:20:10 -0700585base::TimeTicks MidiManagerWin::PortManager::CalculateInEventTime(
toyoshim8a5ad422017-02-28 21:16:18 -0800586 size_t index,
587 uint32_t elapsed_ms) const {
588 GetTaskLock()->AssertAcquired();
589 CHECK_GT(input_ports_.size(), index);
590 return input_ports_[index]->CalculateInEventTime(elapsed_ms);
591}
592
toyoshim63e32a52017-04-25 07:20:10 -0700593void MidiManagerWin::PortManager::RegisterInHandle(HMIDIIN handle,
594 size_t index) {
toyoshim8a5ad422017-02-28 21:16:18 -0800595 GetTaskLock()->AssertAcquired();
596 hmidiin_to_index_map_[handle] = index;
597}
598
toyoshim63e32a52017-04-25 07:20:10 -0700599void MidiManagerWin::PortManager::UnregisterInHandle(HMIDIIN handle) {
toyoshim8a5ad422017-02-28 21:16:18 -0800600 GetTaskLock()->AssertAcquired();
601 hmidiin_to_index_map_.erase(handle);
602}
603
toyoshim63e32a52017-04-25 07:20:10 -0700604bool MidiManagerWin::PortManager::FindInHandle(HMIDIIN hmi, size_t* out_index) {
toyoshim8a5ad422017-02-28 21:16:18 -0800605 GetTaskLock()->AssertAcquired();
606 auto found = hmidiin_to_index_map_.find(hmi);
607 if (found == hmidiin_to_index_map_.end())
608 return false;
609 *out_index = found->second;
610 return true;
611}
612
toyoshim63e32a52017-04-25 07:20:10 -0700613void MidiManagerWin::PortManager::RestoreInBuffer(size_t index) {
toyoshim8a5ad422017-02-28 21:16:18 -0800614 GetTaskLock()->AssertAcquired();
615 CHECK_GT(input_ports_.size(), index);
616 input_ports_[index]->RestoreBuffer();
617}
618
619void CALLBACK
toyoshim63e32a52017-04-25 07:20:10 -0700620MidiManagerWin::PortManager::HandleMidiInCallback(HMIDIIN hmi,
621 UINT msg,
622 DWORD_PTR instance,
623 DWORD_PTR param1,
624 DWORD_PTR param2) {
toyoshim8a5ad422017-02-28 21:16:18 -0800625 if (msg != MIM_DATA && msg != MIM_LONGDATA)
626 return;
627 int instance_id = static_cast<int>(instance);
toyoshim63e32a52017-04-25 07:20:10 -0700628 MidiManagerWin* manager = nullptr;
toyoshim8a5ad422017-02-28 21:16:18 -0800629
630 // Use |g_task_lock| so to ensure the instance can keep alive while running,
631 // and to access member variables that are used on TaskRunner.
Takashi Toyoshimaa09cbc72017-06-01 18:49:34 +0900632 // Exceptionally, we do not take the lock when this callback is invoked inside
633 // midiInGetNumDevs() on the caller thread because the lock is already
634 // obtained by the current caller thread.
635 std::unique_ptr<base::AutoLock> task_lock;
636 if (IsRunningInsideMidiInGetNumDevs())
637 GetTaskLock()->AssertAcquired();
638 else
Peter Boström1684b972021-05-01 01:31:25 +0000639 task_lock = std::make_unique<base::AutoLock>(*GetTaskLock());
toyoshim8a5ad422017-02-28 21:16:18 -0800640 {
641 base::AutoLock lock(*GetInstanceIdLock());
642 if (instance_id != g_active_instance_id)
643 return;
644 manager = g_manager_instance;
645 }
646
647 size_t index;
toyoshim6d87aaa2017-02-28 22:36:44 -0800648 if (!manager->port_manager()->FindInHandle(hmi, &index))
toyoshim8a5ad422017-02-28 21:16:18 -0800649 return;
650
651 DCHECK(msg == MIM_DATA || msg == MIM_LONGDATA);
652 if (msg == MIM_DATA) {
653 const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff);
654 const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff);
655 const uint8_t second_data_byte =
656 static_cast<uint8_t>((param1 >> 16) & 0xff);
657 const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte};
658 const size_t len = GetMessageLength(status_byte);
Avi Drissman2c637192018-12-25 20:26:39 +0000659 DCHECK_LE(len, base::size(kData));
toyoshim8a5ad422017-02-28 21:16:18 -0800660 std::vector<uint8_t> data;
661 data.assign(kData, kData + len);
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000662 manager->PostReplyTask(base::BindOnce(
Yutaka Hirano138dd962017-08-01 08:14:15 +0000663 &MidiManagerWin::ReceiveMidiData, base::Unretained(manager),
664 static_cast<uint32_t>(index), data,
665 manager->port_manager()->CalculateInEventTime(index, param2)));
toyoshim8a5ad422017-02-28 21:16:18 -0800666 } else {
667 DCHECK_EQ(static_cast<UINT>(MIM_LONGDATA), msg);
668 LPMIDIHDR hdr = reinterpret_cast<LPMIDIHDR>(param1);
669 if (hdr->dwBytesRecorded > 0) {
670 const uint8_t* src = reinterpret_cast<const uint8_t*>(hdr->lpData);
671 std::vector<uint8_t> data;
672 data.assign(src, src + hdr->dwBytesRecorded);
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000673 manager->PostReplyTask(base::BindOnce(
Yutaka Hirano138dd962017-08-01 08:14:15 +0000674 &MidiManagerWin::ReceiveMidiData, base::Unretained(manager),
675 static_cast<uint32_t>(index), data,
676 manager->port_manager()->CalculateInEventTime(index, param2)));
toyoshim8a5ad422017-02-28 21:16:18 -0800677 }
toyoshim824deed2017-06-07 16:45:43 -0700678 manager->port_manager()->RestoreInBuffer(index);
toyoshim8a5ad422017-02-28 21:16:18 -0800679 }
680}
681
toyoshim6d87aaa2017-02-28 22:36:44 -0800682void CALLBACK
toyoshim63e32a52017-04-25 07:20:10 -0700683MidiManagerWin::PortManager::HandleMidiOutCallback(HMIDIOUT hmo,
684 UINT msg,
685 DWORD_PTR instance,
686 DWORD_PTR param1,
687 DWORD_PTR param2) {
toyoshim6d87aaa2017-02-28 22:36:44 -0800688 if (msg == MOM_DONE) {
689 ScopedMIDIHDR hdr(reinterpret_cast<LPMIDIHDR>(param1));
690 if (!hdr)
691 return;
692 // TODO(toyoshim): Call midiOutUnprepareHeader outside the callback.
693 // Since this callback may be invoked after the manager is destructed,
694 // and can not send a task to the TaskRunner in such case, we need to
695 // consider to track MIDIHDR per port, and clean it in port finalization
696 // steps, too.
697 midiOutUnprepareHeader(hmo, hdr.get(), sizeof(*hdr));
698 }
699}
700
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000701// static
702void MidiManagerWin::OverflowInstanceIdForTesting() {
703 IssueNextInstanceId(std::numeric_limits<int64_t>::max());
704}
705
toyoshim63e32a52017-04-25 07:20:10 -0700706MidiManagerWin::MidiManagerWin(MidiService* service)
toyoshim8a5ad422017-02-28 21:16:18 -0800707 : MidiManager(service),
Anton Bikineev476333e2021-05-15 18:05:03 +0000708 instance_id_(IssueNextInstanceId(absl::nullopt)),
Gyuyoung Kim34e191a2018-01-10 09:48:42 +0000709 port_manager_(std::make_unique<PortManager>()) {
toyoshimd28b59c2017-02-20 11:07:37 -0800710 base::AutoLock lock(*GetInstanceIdLock());
711 CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
712
713 // Obtains the task runner for the current thread that hosts this instnace.
714 thread_runner_ = base::ThreadTaskRunnerHandle::Get();
715}
716
toyoshim63e32a52017-04-25 07:20:10 -0700717MidiManagerWin::~MidiManagerWin() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000718 // Initialization failed. Exit without running actual finalization that should
719 // not be needed.
720 if (instance_id_ == kInvalidInstanceId)
721 return;
722
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000723 // Unregisters on the I/O thread. OnDevicesChanged() won't be called any more.
toyoshimd28b59c2017-02-20 11:07:37 -0800724 CHECK(thread_runner_->BelongsToCurrentThread());
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000725 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
726
727 // Posts tasks that finalize each device port without MidiManager instance
728 // on TaskRunner. If another MidiManager instance is created, its
729 // initialization runs on the same task runner after all tasks posted here
730 // finish.
731 for (const auto& port : *port_manager_->inputs())
732 port->Finalize(service()->GetTaskRunner(kTaskRunner));
733 for (const auto& port : *port_manager_->outputs())
734 port->Finalize(service()->GetTaskRunner(kTaskRunner));
735
736 // Invalidate instance bound tasks.
737 {
738 base::AutoLock lock(*GetInstanceIdLock());
739 CHECK_EQ(instance_id_, g_active_instance_id);
740 g_active_instance_id = kInvalidInstanceId;
741 CHECK_EQ(this, g_manager_instance);
742 g_manager_instance = nullptr;
743 }
744
745 // Ensures that no bound task runs on TaskRunner so to destruct the instance
746 // safely.
747 // Tasks that did not started yet will do nothing after invalidate the
748 // instance ID above.
749 // Behind the lock below, we can safely access all members for finalization
750 // even on the I/O thread.
751 base::AutoLock lock(*GetTaskLock());
toyoshimd28b59c2017-02-20 11:07:37 -0800752}
753
toyoshim63e32a52017-04-25 07:20:10 -0700754void MidiManagerWin::StartInitialization() {
toyoshimd28b59c2017-02-20 11:07:37 -0800755 {
756 base::AutoLock lock(*GetInstanceIdLock());
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000757 if (instance_id_ == kInvalidInstanceId)
758 return CompleteInitialization(mojom::Result::INITIALIZATION_ERROR);
759
toyoshimd28b59c2017-02-20 11:07:37 -0800760 CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
761 g_active_instance_id = instance_id_;
762 CHECK_EQ(nullptr, g_manager_instance);
763 g_manager_instance = this;
764 }
765 // Registers on the I/O thread to be notified on the I/O thread.
766 CHECK(thread_runner_->BelongsToCurrentThread());
767 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
768
769 // Starts asynchronous initialization on TaskRunner.
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000770 PostTask(base::BindOnce(&MidiManagerWin::InitializeOnTaskRunner,
771 base::Unretained(this)));
toyoshimd28b59c2017-02-20 11:07:37 -0800772}
773
toyoshim63e32a52017-04-25 07:20:10 -0700774void MidiManagerWin::DispatchSendMidiData(MidiManagerClient* client,
775 uint32_t port_index,
776 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000777 base::TimeTicks timestamp) {
Takashi Toyoshimaafb27d52017-09-13 11:50:41 +0000778 PostDelayedTask(
779 base::BindOnce(&MidiManagerWin::SendOnTaskRunner, base::Unretained(this),
780 client, port_index, data),
781 MidiService::TimestampToTimeDeltaDelay(timestamp));
toyoshimd28b59c2017-02-20 11:07:37 -0800782}
783
toyoshim63e32a52017-04-25 07:20:10 -0700784void MidiManagerWin::OnDevicesChanged(
toyoshimd28b59c2017-02-20 11:07:37 -0800785 base::SystemMonitor::DeviceType device_type) {
786 // Notified on the I/O thread.
787 CHECK(thread_runner_->BelongsToCurrentThread());
788
789 switch (device_type) {
790 case base::SystemMonitor::DEVTYPE_AUDIO:
791 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
792 // Add case of other unrelated device types here.
793 return;
794 case base::SystemMonitor::DEVTYPE_UNKNOWN: {
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000795 PostTask(base::BindOnce(&MidiManagerWin::UpdateDeviceListOnTaskRunner,
796 base::Unretained(this)));
toyoshimd28b59c2017-02-20 11:07:37 -0800797 break;
798 }
799 }
800}
801
toyoshim63e32a52017-04-25 07:20:10 -0700802void MidiManagerWin::ReceiveMidiData(uint32_t index,
803 const std::vector<uint8_t>& data,
804 base::TimeTicks time) {
toyoshim8a5ad422017-02-28 21:16:18 -0800805 MidiManager::ReceiveMidiData(index, data.data(), data.size(), time);
806}
807
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000808void MidiManagerWin::PostTask(base::OnceClosure task) {
toyoshimd28b59c2017-02-20 11:07:37 -0800809 service()
810 ->GetTaskRunner(kTaskRunner)
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000811 ->PostTask(FROM_HERE,
812 base::BindOnce(&RunTask, instance_id_, std::move(task)));
toyoshimd28b59c2017-02-20 11:07:37 -0800813}
814
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000815void MidiManagerWin::PostDelayedTask(base::OnceClosure task,
toyoshim63e32a52017-04-25 07:20:10 -0700816 base::TimeDelta delay) {
toyoshim6d87aaa2017-02-28 22:36:44 -0800817 service()
818 ->GetTaskRunner(kTaskRunner)
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000819 ->PostDelayedTask(FROM_HERE,
820 base::BindOnce(&RunTask, instance_id_, std::move(task)),
toyoshim6d87aaa2017-02-28 22:36:44 -0800821 delay);
822}
823
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000824void MidiManagerWin::PostReplyTask(base::OnceClosure task) {
825 thread_runner_->PostTask(
826 FROM_HERE, base::BindOnce(&RunTask, instance_id_, std::move(task)));
toyoshim8a5ad422017-02-28 21:16:18 -0800827}
828
toyoshim63e32a52017-04-25 07:20:10 -0700829void MidiManagerWin::InitializeOnTaskRunner() {
toyoshimd28b59c2017-02-20 11:07:37 -0800830 UpdateDeviceListOnTaskRunner();
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000831 PostReplyTask(base::BindOnce(&MidiManagerWin::CompleteInitialization,
832 base::Unretained(this), mojom::Result::OK));
toyoshimd28b59c2017-02-20 11:07:37 -0800833}
834
toyoshim63e32a52017-04-25 07:20:10 -0700835void MidiManagerWin::UpdateDeviceListOnTaskRunner() {
toyoshimd28b59c2017-02-20 11:07:37 -0800836 std::vector<std::unique_ptr<InPort>> active_input_ports =
toyoshim8a5ad422017-02-28 21:16:18 -0800837 InPort::EnumerateActivePorts(this, instance_id_);
838 ReflectActiveDeviceList(this, port_manager_->inputs(), &active_input_ports);
toyoshimd28b59c2017-02-20 11:07:37 -0800839
840 std::vector<std::unique_ptr<OutPort>> active_output_ports =
841 OutPort::EnumerateActivePorts();
toyoshim8a5ad422017-02-28 21:16:18 -0800842 ReflectActiveDeviceList(this, port_manager_->outputs(), &active_output_ports);
toyoshimd28b59c2017-02-20 11:07:37 -0800843
844 // TODO(toyoshim): This method may run before internal MIDI device lists that
845 // Windows manages were updated. This may be because MIDI driver may be loaded
846 // after the raw device list was updated. To avoid this problem, we may want
847 // to retry device check later if no changes are detected here.
848}
849
850template <typename T>
toyoshim63e32a52017-04-25 07:20:10 -0700851void MidiManagerWin::ReflectActiveDeviceList(MidiManagerWin* manager,
852 std::vector<T>* known_ports,
853 std::vector<T>* active_ports) {
toyoshimd28b59c2017-02-20 11:07:37 -0800854 // Update existing port states.
855 for (const auto& port : *known_ports) {
856 const auto& it = std::find_if(
857 active_ports->begin(), active_ports->end(),
858 [&port](const auto& candidate) { return *candidate == *port; });
859 if (it == active_ports->end()) {
860 if (port->Disconnect())
861 port->NotifyPortStateSet(this);
862 } else {
863 port->set_device_id((*it)->device_id());
864 if (port->Connect())
865 port->NotifyPortStateSet(this);
866 }
867 }
868
869 // Find new ports from active ports and append them to known ports.
870 for (auto& port : *active_ports) {
871 if (std::find_if(known_ports->begin(), known_ports->end(),
872 [&port](const auto& candidate) {
873 return *candidate == *port;
874 }) == known_ports->end()) {
875 size_t index = known_ports->size();
876 port->set_index(index);
877 known_ports->push_back(std::move(port));
878 (*known_ports)[index]->Connect();
879 (*known_ports)[index]->NotifyPortAdded(this);
880 }
881 }
882}
883
toyoshim63e32a52017-04-25 07:20:10 -0700884void MidiManagerWin::SendOnTaskRunner(MidiManagerClient* client,
885 uint32_t port_index,
886 const std::vector<uint8_t>& data) {
toyoshim6d87aaa2017-02-28 22:36:44 -0800887 CHECK_GT(port_manager_->outputs()->size(), port_index);
888 (*port_manager_->outputs())[port_index]->Send(data);
889 // |client| will be checked inside MidiManager::AccumulateMidiBytesSent.
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000890 PostReplyTask(base::BindOnce(&MidiManagerWin::AccumulateMidiBytesSent,
891 base::Unretained(this), client, data.size()));
toyoshim6d87aaa2017-02-28 22:36:44 -0800892}
893
toyoshim15385b32017-04-24 23:52:01 -0700894MidiManager* MidiManager::Create(MidiService* service) {
895 if (base::FeatureList::IsEnabled(features::kMidiManagerWinrt) &&
Bruce Dawson21c1e442019-04-20 02:30:09 +0000896 base::win::GetVersion() >= base::win::Version::WIN10) {
toyoshim15385b32017-04-24 23:52:01 -0700897 return new MidiManagerWinrt(service);
toyoshim63e32a52017-04-25 07:20:10 -0700898 }
899 return new MidiManagerWin(service);
toyoshim15385b32017-04-24 23:52:01 -0700900}
901
toyoshimd28b59c2017-02-20 11:07:37 -0800902} // namespace midi