blob: 19d9ce857c65fb9a746823ea07b3d0d82bd0f09d [file] [log] [blame]
shaochuane58f9c72016-08-30 22:27:08 -07001// Copyright 2016 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_winrt.h"
Keishi Hattori5b8de612021-11-27 09:25:52 +00006#include "base/memory/raw_ptr.h"
shaochuane58f9c72016-08-30 22:27:08 -07007
qiankun.miao53f2d662016-09-02 17:44:08 -07008#pragma warning(disable : 4467)
shaochuan80f1fba2016-09-01 20:44:51 -07009
Donna Wud5a689e2019-05-13 02:56:18 +000010#define INITGUID
11
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000012#include <windows.h>
shaochuan4eff30e2016-09-09 01:24:14 -070013
14#include <cfgmgr32.h>
shaochuane58f9c72016-08-30 22:27:08 -070015#include <comdef.h>
shaochuan4eff30e2016-09-09 01:24:14 -070016#include <devpkey.h>
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000017#include <initguid.h>
robliao048b1572017-04-21 12:46:39 -070018#include <objbase.h>
shaochuane58f9c72016-08-30 22:27:08 -070019#include <robuffer.h>
20#include <windows.devices.enumeration.h>
21#include <windows.devices.midi.h>
Robert Liao4a680c32017-10-18 19:10:01 +000022#include <wrl/client.h>
shaochuane58f9c72016-08-30 22:27:08 -070023#include <wrl/event.h>
24
25#include <iomanip>
Peter Boström1684b972021-05-01 01:31:25 +000026#include <memory>
shaochuane58f9c72016-08-30 22:27:08 -070027#include <unordered_map>
28#include <unordered_set>
29
30#include "base/bind.h"
shaochuan9ff63b82016-09-01 01:58:44 -070031#include "base/scoped_generic.h"
shaochuan17bc4a02016-09-06 01:42:12 -070032#include "base/strings/string_util.h"
shaochuane58f9c72016-08-30 22:27:08 -070033#include "base/strings/utf_string_conversions.h"
shaochuane58f9c72016-08-30 22:27:08 -070034#include "base/timer/timer.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000035#include "base/win/core_winrt_util.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000036#include "base/win/scoped_hstring.h"
junweifuf51c5a02017-11-03 06:37:09 +000037#include "base/win/winrt_storage_util.h"
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000038#include "media/midi/midi_service.h"
39#include "media/midi/task_service.h"
shaochuane58f9c72016-08-30 22:27:08 -070040
shaochuane58f9c72016-08-30 22:27:08 -070041namespace midi {
42namespace {
43
44namespace WRL = Microsoft::WRL;
Peter Kasting6e8c8472019-09-20 17:55:55 +000045namespace Win = ABI::Windows;
shaochuane58f9c72016-08-30 22:27:08 -070046
junweifua8cea852017-10-17 06:21:16 +000047using base::win::GetActivationFactory;
Peter Kasting6e8c8472019-09-20 17:55:55 +000048using base::win::ScopedHString;
toyoshimec2570a2016-10-21 02:15:27 -070049using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070050using mojom::Result;
Peter Kasting6e8c8472019-09-20 17:55:55 +000051using Win::Devices::Enumeration::DeviceInformationUpdate;
52using Win::Devices::Enumeration::DeviceWatcher;
53using Win::Devices::Enumeration::IDeviceInformation;
54using Win::Devices::Enumeration::IDeviceInformationUpdate;
55using Win::Devices::Enumeration::IDeviceWatcher;
56using Win::Foundation::IAsyncOperation;
57using Win::Foundation::ITypedEventHandler;
shaochuane58f9c72016-08-30 22:27:08 -070058
Xiaohan Wangfd3e0aa2020-02-22 04:08:43 +000059// Alias for printing HRESULT.
60const auto PrintHr = logging::SystemErrorCodeToString;
61
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000062enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000063 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000064 kComTaskRunner
65};
66
shaochuane58f9c72016-08-30 22:27:08 -070067template <typename T>
68std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070069 HSTRING result;
70 HRESULT hr = obj->get_Id(&result);
71 if (FAILED(hr)) {
72 VLOG(1) << "get_Id failed: " << PrintHr(hr);
73 return std::string();
74 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000075 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070076}
77
78template <typename T>
79std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070080 HSTRING result;
81 HRESULT hr = obj->get_DeviceId(&result);
82 if (FAILED(hr)) {
83 VLOG(1) << "get_DeviceId failed: " << PrintHr(hr);
84 return std::string();
85 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000086 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070087}
88
89std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070090 HSTRING result;
91 HRESULT hr = info->get_Name(&result);
92 if (FAILED(hr)) {
93 VLOG(1) << "get_Name failed: " << PrintHr(hr);
94 return std::string();
95 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000096 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070097}
98
shaochuan110262b2016-08-31 02:15:16 -070099// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
100// instance.
101bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Peter Kasting6e8c8472019-09-20 17:55:55 +0000102 WRL::ComPtr<Win::Devices::Midi::IMidiSynthesizerStatics>
103 midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000104 HRESULT hr =
Peter Kasting6e8c8472019-09-20 17:55:55 +0000105 GetActivationFactory<Win::Devices::Midi::IMidiSynthesizerStatics,
junweifua8cea852017-10-17 06:21:16 +0000106 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
107 &midi_synthesizer_statics);
108 if (FAILED(hr)) {
109 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
110 return false;
111 }
shaochuan110262b2016-08-31 02:15:16 -0700112 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000113 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700114 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
115 return result != FALSE;
116}
117
shaochuan4eff30e2016-09-09 01:24:14 -0700118void GetDevPropString(DEVINST handle,
119 const DEVPROPKEY* devprop_key,
120 std::string* out) {
121 DEVPROPTYPE devprop_type;
122 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700123
shaochuan4eff30e2016-09-09 01:24:14 -0700124 // Retrieve |buffer_size| and allocate buffer later for receiving data.
125 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
126 nullptr, &buffer_size, 0);
127 if (cr != CR_BUFFER_SMALL) {
128 // Here we print error codes in hex instead of using PrintHr() with
129 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
130 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
131 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
132 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700133 }
shaochuan4eff30e2016-09-09 01:24:14 -0700134 if (devprop_type != DEVPROP_TYPE_STRING) {
135 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
136 << "expected DEVPROP_TYPE_STRING";
137 return;
138 }
shaochuan17bc4a02016-09-06 01:42:12 -0700139
shaochuan4eff30e2016-09-09 01:24:14 -0700140 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
141
142 // Receive property data.
143 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
144 &buffer_size, 0);
145 if (cr != CR_SUCCESS)
146 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
147 else
Peter Kasting426665c2021-02-02 13:01:34 +0000148 *out = base::WideToUTF8(reinterpret_cast<wchar_t*>(buffer.get()));
shaochuan4eff30e2016-09-09 01:24:14 -0700149}
shaochuan17bc4a02016-09-06 01:42:12 -0700150
151// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700152// device driver through PnP Configuration Manager, given device (interface) ID
153// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
154// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700155//
156// Device instance ID is extracted from device (interface) ID provided by WinRT
157// APIs, for example from the following interface ID:
158// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
159// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700160//
161// However the extracted device instance ID represent a "software device"
162// provided by Microsoft, which is an interface on top of the hardware for each
163// input/output port. Therefore we further locate its parent device, which is
164// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700165void GetDriverInfoFromDeviceId(const std::string& dev_id,
166 std::string* out_manufacturer,
167 std::string* out_driver_version) {
Peter Kasting426665c2021-02-02 13:01:34 +0000168 std::wstring dev_instance_id =
shaochuan17bc4a02016-09-06 01:42:12 -0700169 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
170 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
171
shaochuan4eff30e2016-09-09 01:24:14 -0700172 DEVINST dev_instance_handle;
173 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
174 CM_LOCATE_DEVNODE_NORMAL);
175 if (cr != CR_SUCCESS) {
176 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700177 return;
178 }
179
shaochuan4eff30e2016-09-09 01:24:14 -0700180 DEVINST parent_handle;
181 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
182 if (cr != CR_SUCCESS) {
183 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700184 return;
185 }
186
shaochuan4eff30e2016-09-09 01:24:14 -0700187 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
188 out_manufacturer);
189 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
190 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700191}
192
shaochuane58f9c72016-08-30 22:27:08 -0700193// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
194const int64_t kInvalidTokenValue = 0;
195
196template <typename InterfaceType>
197struct MidiPort {
198 MidiPort() = default;
199
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000200 MidiPort(const MidiPort&) = delete;
201 MidiPort& operator=(const MidiPort&) = delete;
202
shaochuane58f9c72016-08-30 22:27:08 -0700203 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000204 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700205 EventRegistrationToken token_MessageReceived;
shaochuane58f9c72016-08-30 22:27:08 -0700206};
207
208} // namespace
209
210template <typename InterfaceType,
211 typename RuntimeType,
212 typename StaticsInterfaceType,
Peter Kasting426665c2021-02-02 13:01:34 +0000213 wchar_t const* runtime_class_id>
shaochuane58f9c72016-08-30 22:27:08 -0700214class MidiManagerWinrt::MidiPortManager {
215 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000216 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700217 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000218 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700219
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000220 virtual ~MidiPortManager() {
221 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
222 }
shaochuane58f9c72016-08-30 22:27:08 -0700223
224 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000225 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700226
junweifua8cea852017-10-17 06:21:16 +0000227 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
228 &midi_port_statics_);
229 if (FAILED(hr)) {
230 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700231 return false;
junweifua8cea852017-10-17 06:21:16 +0000232 }
shaochuane58f9c72016-08-30 22:27:08 -0700233
234 HSTRING device_selector = nullptr;
235 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
236 if (FAILED(hr)) {
237 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
238 return false;
239 }
240
Peter Kasting6e8c8472019-09-20 17:55:55 +0000241 WRL::ComPtr<Win::Devices::Enumeration::IDeviceInformationStatics>
242 dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000243 hr = GetActivationFactory<
Peter Kasting6e8c8472019-09-20 17:55:55 +0000244 Win::Devices::Enumeration::IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000245 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
246 &dev_info_statics);
247 if (FAILED(hr)) {
248 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700249 return false;
junweifua8cea852017-10-17 06:21:16 +0000250 }
shaochuane58f9c72016-08-30 22:27:08 -0700251
Xiaohan Wangf2832912020-02-21 18:38:02 +0000252 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector, &watcher_);
shaochuane58f9c72016-08-30 22:27:08 -0700253 if (FAILED(hr)) {
254 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
255 return false;
256 }
257
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000258 // Register callbacks to WinRT that post state-modifying tasks back to
259 // kComTaskRunner. All posted tasks run only during the MidiPortManager
260 // instance is alive. This is ensured by MidiManagerWinrt by calling
261 // UnbindInstance() before destructing any MidiPortManager instance. Thus
262 // we can handle raw pointers safely in the following blocks.
263 MidiPortManager* port_manager = this;
264 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700265
266 hr = watcher_->add_Added(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000267 WRL::Callback<ITypedEventHandler<
268 DeviceWatcher*, Win::Devices::Enumeration::DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000269 [port_manager, task_service](IDeviceWatcher* watcher,
270 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700271 if (!info) {
272 VLOG(1) << "DeviceWatcher.Added callback provides null "
273 "pointer, ignoring";
274 return S_OK;
275 }
276
shaochuan110262b2016-08-31 02:15:16 -0700277 // Disable Microsoft GS Wavetable Synth due to security reasons.
278 // http://crbug.com/499279
279 if (IsMicrosoftSynthesizer(info))
280 return S_OK;
281
shaochuane58f9c72016-08-30 22:27:08 -0700282 std::string dev_id = GetIdString(info),
283 dev_name = GetNameString(info);
284
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000285 task_service->PostBoundTask(
286 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
287 base::Unretained(port_manager),
288 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700289
290 return S_OK;
291 })
292 .Get(),
293 &token_Added_);
294 if (FAILED(hr)) {
295 VLOG(1) << "add_Added failed: " << PrintHr(hr);
296 return false;
297 }
298
299 hr = watcher_->add_EnumerationCompleted(
300 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000301 [port_manager, task_service](IDeviceWatcher* watcher,
302 IInspectable* insp) {
303 task_service->PostBoundTask(
304 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000305 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000306 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700307
308 return S_OK;
309 })
310 .Get(),
311 &token_EnumerationCompleted_);
312 if (FAILED(hr)) {
313 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
314 return false;
315 }
316
317 hr = watcher_->add_Removed(
318 WRL::Callback<
319 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000320 [port_manager, task_service](IDeviceWatcher* watcher,
321 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700322 if (!update) {
323 VLOG(1) << "DeviceWatcher.Removed callback provides null "
324 "pointer, ignoring";
325 return S_OK;
326 }
327
shaochuane58f9c72016-08-30 22:27:08 -0700328 std::string dev_id = GetIdString(update);
329
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000330 task_service->PostBoundTask(
331 kComTaskRunner,
332 base::BindOnce(&MidiPortManager::OnRemoved,
333 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700334
335 return S_OK;
336 })
337 .Get(),
338 &token_Removed_);
339 if (FAILED(hr)) {
340 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
341 return false;
342 }
343
344 hr = watcher_->add_Stopped(
345 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
346 [](IDeviceWatcher* watcher, IInspectable* insp) {
347 // Placeholder, does nothing for now.
348 return S_OK;
349 })
350 .Get(),
351 &token_Stopped_);
352 if (FAILED(hr)) {
353 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
354 return false;
355 }
356
357 hr = watcher_->add_Updated(
358 WRL::Callback<
359 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
360 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
361 // TODO(shaochuan): Check for fields to be updated here.
362 return S_OK;
363 })
364 .Get(),
365 &token_Updated_);
366 if (FAILED(hr)) {
367 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
368 return false;
369 }
370
371 hr = watcher_->Start();
372 if (FAILED(hr)) {
373 VLOG(1) << "Start failed: " << PrintHr(hr);
374 return false;
375 }
376
377 is_initialized_ = true;
378 return true;
379 }
380
381 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000382 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700383
384 HRESULT hr;
385
386 for (const auto& entry : ports_)
387 RemovePortEventHandlers(entry.second.get());
388
389 if (token_Added_.value != kInvalidTokenValue) {
390 hr = watcher_->remove_Added(token_Added_);
391 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
392 token_Added_.value = kInvalidTokenValue;
393 }
394 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
395 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
396 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
397 << PrintHr(hr);
398 token_EnumerationCompleted_.value = kInvalidTokenValue;
399 }
400 if (token_Removed_.value != kInvalidTokenValue) {
401 hr = watcher_->remove_Removed(token_Removed_);
402 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
403 token_Removed_.value = kInvalidTokenValue;
404 }
405 if (token_Stopped_.value != kInvalidTokenValue) {
406 hr = watcher_->remove_Stopped(token_Stopped_);
407 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
408 token_Stopped_.value = kInvalidTokenValue;
409 }
410 if (token_Updated_.value != kInvalidTokenValue) {
411 hr = watcher_->remove_Updated(token_Updated_);
412 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
413 token_Updated_.value = kInvalidTokenValue;
414 }
415
416 if (is_initialized_) {
417 hr = watcher_->Stop();
418 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
419 is_initialized_ = false;
420 }
421 }
422
423 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000424 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700425 CHECK(is_initialized_);
426
427 auto it = ports_.find(dev_id);
428 if (it == ports_.end())
429 return nullptr;
430 return it->second.get();
431 }
432
433 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000434 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700435 CHECK(is_initialized_);
436
437 return GetPortByDeviceId(port_ids_[port_index]);
438 }
439
440 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000441 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700442 // MidiPortManager instance.
Keishi Hattori5b8de612021-11-27 09:25:52 +0000443 raw_ptr<MidiService> midi_service_;
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000444
445 // Points to the MidiManagerWinrt instance, which is safe to be accessed
446 // from tasks that are invoked by TaskService.
Keishi Hattori5b8de612021-11-27 09:25:52 +0000447 raw_ptr<MidiManagerWinrt> midi_manager_;
shaochuane58f9c72016-08-30 22:27:08 -0700448
shaochuane58f9c72016-08-30 22:27:08 -0700449 private:
450 // DeviceWatcher callbacks:
451 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000452 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700453 CHECK(is_initialized_);
454
shaochuane58f9c72016-08-30 22:27:08 -0700455 port_names_[dev_id] = dev_name;
456
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000457 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700458 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700459 return;
shaochuane58f9c72016-08-30 22:27:08 -0700460
461 IAsyncOperation<RuntimeType*>* async_op;
462
shaochuan9ff63b82016-09-01 01:58:44 -0700463 HRESULT hr =
464 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700465 if (FAILED(hr)) {
466 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
467 return;
468 }
469
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000470 MidiPortManager* port_manager = this;
471 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700472
473 hr = async_op->put_Completed(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000474 WRL::Callback<
475 Win::Foundation::IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000476 [port_manager, task_service](
477 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700478 // A reference to |async_op| is kept in |async_ops_|, safe to pass
479 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000480 task_service->PostBoundTask(
481 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000482 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000483 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000484 base::Unretained(port_manager),
485 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700486
487 return S_OK;
488 })
489 .Get());
490 if (FAILED(hr)) {
491 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
492 return;
493 }
494
495 // Keep a reference to incompleted |async_op| for releasing later.
496 async_ops_.insert(async_op);
497 }
498
499 void OnEnumerationCompleted() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000500 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700501 CHECK(is_initialized_);
502
503 if (async_ops_.empty())
504 midi_manager_->OnPortManagerReady();
505 else
506 enumeration_completed_not_ready_ = true;
507 }
508
509 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000510 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700511 CHECK(is_initialized_);
512
shaochuan110262b2016-08-31 02:15:16 -0700513 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
514 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700515 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
516 if (!port) {
517 VLOG(1) << "Removing non-existent port " << dev_id;
518 return;
519 }
520
toyoshimec2570a2016-10-21 02:15:27 -0700521 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700522
523 RemovePortEventHandlers(port);
524 port->handle = nullptr;
525 }
526
shaochuanc2894522016-09-20 01:10:50 -0700527 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000528 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700529 CHECK(is_initialized_);
530
shaochuanc2894522016-09-20 01:10:50 -0700531 InterfaceType* handle = nullptr;
532 HRESULT hr = async_op->GetResults(&handle);
533 if (FAILED(hr)) {
534 VLOG(1) << "GetResults failed: " << PrintHr(hr);
535 return;
536 }
537
538 // Manually release COM interface to completed |async_op|.
539 auto it = async_ops_.find(async_op);
540 CHECK(it != async_ops_.end());
541 (*it)->Release();
542 async_ops_.erase(it);
543
544 if (!handle) {
545 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
546 "ignoring";
547 return;
548 }
549
shaochuane58f9c72016-08-30 22:27:08 -0700550 EventRegistrationToken token = {kInvalidTokenValue};
551 if (!RegisterOnMessageReceived(handle, &token))
552 return;
553
554 std::string dev_id = GetDeviceIdString(handle);
555
556 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
557
558 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700559 std::string manufacturer = "Unknown", driver_version = "Unknown";
560 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
561
Adithya Srinivasan33252732018-10-17 15:59:40 +0000562 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
563 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700564
565 port = new MidiPort<InterfaceType>;
566 port->index = static_cast<uint32_t>(port_ids_.size());
567
568 ports_[dev_id].reset(port);
569 port_ids_.push_back(dev_id);
570 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700571 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700572 }
573
574 port->handle = handle;
575 port->token_MessageReceived = token;
576
shaochuane58f9c72016-08-30 22:27:08 -0700577 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
578 midi_manager_->OnPortManagerReady();
579 enumeration_completed_not_ready_ = false;
580 }
581 }
582
583 // Overrided by MidiInPortManager to listen to input ports.
584 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
585 EventRegistrationToken* p_token) {
586 return true;
587 }
588
589 // Overrided by MidiInPortManager to remove MessageReceived event handler.
590 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
591
592 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000593 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700594
595 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700596 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700597
shaochuane58f9c72016-08-30 22:27:08 -0700598 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000599 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700600
601 // DeviceWatcher instance and event registration tokens for unsubscribing
602 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000603 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700604 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
605 token_EnumerationCompleted_ = {kInvalidTokenValue},
606 token_Removed_ = {kInvalidTokenValue},
607 token_Stopped_ = {kInvalidTokenValue},
608 token_Updated_ = {kInvalidTokenValue};
609
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000610 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700611 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
612 ports_;
613 std::vector<std::string> port_ids_;
614 std::unordered_map<std::string, std::string> port_names_;
615
616 // Keeps AsyncOperation references before the operation completes. Note that
617 // raw pointers are used here and the COM interfaces should be released
618 // manually.
619 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
620
621 // Set when device enumeration is completed but OnPortManagerReady() is not
622 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
623 // In such cases, OnPortManagerReady() will be called in
624 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
625 bool enumeration_completed_not_ready_ = false;
626
627 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000628 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700629 bool is_initialized_ = false;
630};
631
632class MidiManagerWinrt::MidiInPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000633 : public MidiPortManager<Win::Devices::Midi::IMidiInPort,
634 Win::Devices::Midi::MidiInPort,
635 Win::Devices::Midi::IMidiInPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700636 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
637 public:
638 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000639 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700640
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000641 MidiInPortManager(const MidiInPortManager&) = delete;
642 MidiInPortManager& operator=(const MidiInPortManager&) = delete;
643
shaochuane58f9c72016-08-30 22:27:08 -0700644 private:
645 // MidiPortManager overrides:
Peter Kasting6e8c8472019-09-20 17:55:55 +0000646 bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle,
shaochuane58f9c72016-08-30 22:27:08 -0700647 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000648 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700649
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000650 MidiInPortManager* port_manager = this;
651 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700652
653 HRESULT hr = handle->add_MessageReceived(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000654 WRL::Callback<ITypedEventHandler<
655 Win::Devices::Midi::MidiInPort*,
656 Win::Devices::Midi::MidiMessageReceivedEventArgs*>>(
657 [port_manager, task_service](
658 Win::Devices::Midi::IMidiInPort* handle,
659 Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700660 const base::TimeTicks now = base::TimeTicks::Now();
661
662 std::string dev_id = GetDeviceIdString(handle);
663
Peter Kasting6e8c8472019-09-20 17:55:55 +0000664 WRL::ComPtr<Win::Devices::Midi::IMidiMessage> message;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000665 HRESULT hr = args->get_Message(&message);
shaochuane58f9c72016-08-30 22:27:08 -0700666 if (FAILED(hr)) {
667 VLOG(1) << "get_Message failed: " << PrintHr(hr);
668 return hr;
669 }
670
Peter Kasting6e8c8472019-09-20 17:55:55 +0000671 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000672 hr = message->get_RawData(&buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700673 if (FAILED(hr)) {
674 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
675 return hr;
676 }
677
678 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000679 uint32_t data_length = 0;
680 hr = base::win::GetPointerToBufferData(
681 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700682 if (FAILED(hr))
683 return hr;
684
shaochuane58f9c72016-08-30 22:27:08 -0700685 std::vector<uint8_t> data(p_buffer_data,
686 p_buffer_data + data_length);
687
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000688 task_service->PostBoundTask(
689 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000690 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000691 base::Unretained(port_manager), dev_id, data,
692 now));
shaochuane58f9c72016-08-30 22:27:08 -0700693
694 return S_OK;
695 })
696 .Get(),
697 p_token);
698 if (FAILED(hr)) {
699 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
700 return false;
701 }
702
703 return true;
704 }
705
Peter Kasting6e8c8472019-09-20 17:55:55 +0000706 void RemovePortEventHandlers(
707 MidiPort<Win::Devices::Midi::IMidiInPort>* port) override {
shaochuane58f9c72016-08-30 22:27:08 -0700708 if (!(port->handle &&
709 port->token_MessageReceived.value != kInvalidTokenValue))
710 return;
711
712 HRESULT hr =
713 port->handle->remove_MessageReceived(port->token_MessageReceived);
714 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
715 port->token_MessageReceived.value = kInvalidTokenValue;
716 }
717
Adithya Srinivasan33252732018-10-17 15:59:40 +0000718 void AddPort(mojom::PortInfo info) final {
719 midi_manager_->AddInputPort(info);
720 }
shaochuane58f9c72016-08-30 22:27:08 -0700721
toyoshimec2570a2016-10-21 02:15:27 -0700722 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700723 midi_manager_->SetInputPortState(port_index, state);
724 }
725
shaochuane58f9c72016-08-30 22:27:08 -0700726 // Callback on receiving MIDI input message.
727 void OnMessageReceived(std::string dev_id,
728 std::vector<uint8_t> data,
729 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000730 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700731
Peter Kasting6e8c8472019-09-20 17:55:55 +0000732 MidiPort<Win::Devices::Midi::IMidiInPort>* port = GetPortByDeviceId(dev_id);
shaochuane58f9c72016-08-30 22:27:08 -0700733 CHECK(port);
734
735 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
736 }
shaochuane58f9c72016-08-30 22:27:08 -0700737};
738
739class MidiManagerWinrt::MidiOutPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000740 : public MidiPortManager<Win::Devices::Midi::IMidiOutPort,
741 Win::Devices::Midi::IMidiOutPort,
742 Win::Devices::Midi::IMidiOutPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700743 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
744 public:
745 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000746 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700747
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000748 MidiOutPortManager(const MidiOutPortManager&) = delete;
749 MidiOutPortManager& operator=(const MidiOutPortManager&) = delete;
750
shaochuane58f9c72016-08-30 22:27:08 -0700751 private:
752 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000753 void AddPort(mojom::PortInfo info) final {
754 midi_manager_->AddOutputPort(info);
755 }
shaochuane58f9c72016-08-30 22:27:08 -0700756
toyoshimec2570a2016-10-21 02:15:27 -0700757 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700758 midi_manager_->SetOutputPortState(port_index, state);
759 }
shaochuane58f9c72016-08-30 22:27:08 -0700760};
761
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000762namespace {
763
764// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
765// instance destruction.
766void FinalizeOnComRunner(
767 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
768 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
769 if (port_manager_in)
770 port_manager_in->StopWatcher();
771
772 if (port_manager_out)
773 port_manager_out->StopWatcher();
774}
775
776} // namespace
777
toyoshimf4d61522017-02-10 02:03:32 -0800778MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000779 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700780
781MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000782 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
783 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000784 if (!service()->task_service()->UnbindInstance())
785 return;
shaochuane58f9c72016-08-30 22:27:08 -0700786
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000787 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000788 service()->task_service()->PostStaticTask(
789 kComTaskRunner,
790 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
791 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700792}
793
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000794void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000795 if (!service()->task_service()->BindInstance())
796 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000797
798 service()->task_service()->PostBoundTask(
799 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
800 base::Unretained(this)));
801}
802
shaochuane58f9c72016-08-30 22:27:08 -0700803void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
804 uint32_t port_index,
805 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000806 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000807 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
808 service()->task_service()->PostBoundDelayedTask(
809 kComTaskRunner,
810 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
811 port_index, data),
812 delay);
813 service()->task_service()->PostBoundDelayedTask(
814 kComTaskRunner,
815 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
816 base::Unretained(this), client, data.size()),
817 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700818}
819
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000820void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700821 base::AutoLock auto_lock(lazy_init_member_lock_);
822
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000823 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
824
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000825 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
826 ScopedHString::ResolveCoreWinRTStringDelayload();
827 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000828 service()->task_service()->PostBoundTask(
829 kDefaultTaskRunner,
830 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
831 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700832 return;
833 }
834
Peter Boström1684b972021-05-01 01:31:25 +0000835 port_manager_in_ = std::make_unique<MidiInPortManager>(this);
836 port_manager_out_ = std::make_unique<MidiOutPortManager>(this);
shaochuane58f9c72016-08-30 22:27:08 -0700837
shaochuane58f9c72016-08-30 22:27:08 -0700838 if (!(port_manager_in_->StartWatcher() &&
839 port_manager_out_->StartWatcher())) {
840 port_manager_in_->StopWatcher();
841 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000842 service()->task_service()->PostBoundTask(
843 kDefaultTaskRunner,
844 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
845 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700846 }
847}
848
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000849void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700850 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000851 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700852
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000853 base::AutoLock auto_lock(lazy_init_member_lock_);
Peter Kasting6e8c8472019-09-20 17:55:55 +0000854 MidiPort<Win::Devices::Midi::IMidiOutPort>* port =
855 port_manager_out_->GetPortByIndex(port_index);
shaochuane58f9c72016-08-30 22:27:08 -0700856 if (!(port && port->handle)) {
857 VLOG(1) << "Port not available: " << port_index;
858 return;
859 }
860
Peter Kasting6e8c8472019-09-20 17:55:55 +0000861 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000862 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000863 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700864 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000865 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700866 return;
867 }
868
robliao3566d1a2017-04-18 17:28:09 -0700869 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700870 if (FAILED(hr)) {
871 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
872 return;
873 }
874}
875
876void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000877 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700878 DCHECK(port_manager_ready_count_ < 2);
879
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000880 if (++port_manager_ready_count_ == 2) {
881 service()->task_service()->PostBoundTask(
882 kDefaultTaskRunner,
883 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
884 base::Unretained(this), Result::OK));
885 }
shaochuane58f9c72016-08-30 22:27:08 -0700886}
887
shaochuane58f9c72016-08-30 22:27:08 -0700888} // namespace midi