blob: 24494d9894c10576227bff7231621ce82e2e1bec [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"
6
qiankun.miao53f2d662016-09-02 17:44:08 -07007#pragma warning(disable : 4467)
shaochuan80f1fba2016-09-01 20:44:51 -07008
Donna Wud5a689e2019-05-13 02:56:18 +00009#define INITGUID
10
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000011#include <windows.h>
shaochuan4eff30e2016-09-09 01:24:14 -070012
13#include <cfgmgr32.h>
shaochuane58f9c72016-08-30 22:27:08 -070014#include <comdef.h>
shaochuan4eff30e2016-09-09 01:24:14 -070015#include <devpkey.h>
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000016#include <initguid.h>
robliao048b1572017-04-21 12:46:39 -070017#include <objbase.h>
shaochuane58f9c72016-08-30 22:27:08 -070018#include <robuffer.h>
19#include <windows.devices.enumeration.h>
20#include <windows.devices.midi.h>
Robert Liao4a680c32017-10-18 19:10:01 +000021#include <wrl/client.h>
shaochuane58f9c72016-08-30 22:27:08 -070022#include <wrl/event.h>
23
24#include <iomanip>
Peter Boström1684b972021-05-01 01:31:25 +000025#include <memory>
shaochuane58f9c72016-08-30 22:27:08 -070026#include <unordered_map>
27#include <unordered_set>
28
29#include "base/bind.h"
shaochuan9ff63b82016-09-01 01:58:44 -070030#include "base/scoped_generic.h"
shaochuan17bc4a02016-09-06 01:42:12 -070031#include "base/strings/string_util.h"
shaochuane58f9c72016-08-30 22:27:08 -070032#include "base/strings/utf_string_conversions.h"
shaochuane58f9c72016-08-30 22:27:08 -070033#include "base/timer/timer.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000034#include "base/win/core_winrt_util.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000035#include "base/win/scoped_hstring.h"
junweifuf51c5a02017-11-03 06:37:09 +000036#include "base/win/winrt_storage_util.h"
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000037#include "media/midi/midi_service.h"
38#include "media/midi/task_service.h"
shaochuane58f9c72016-08-30 22:27:08 -070039
shaochuane58f9c72016-08-30 22:27:08 -070040namespace midi {
41namespace {
42
43namespace WRL = Microsoft::WRL;
Peter Kasting6e8c8472019-09-20 17:55:55 +000044namespace Win = ABI::Windows;
shaochuane58f9c72016-08-30 22:27:08 -070045
junweifua8cea852017-10-17 06:21:16 +000046using base::win::GetActivationFactory;
Peter Kasting6e8c8472019-09-20 17:55:55 +000047using base::win::ScopedHString;
toyoshimec2570a2016-10-21 02:15:27 -070048using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070049using mojom::Result;
Peter Kasting6e8c8472019-09-20 17:55:55 +000050using Win::Devices::Enumeration::DeviceInformationUpdate;
51using Win::Devices::Enumeration::DeviceWatcher;
52using Win::Devices::Enumeration::IDeviceInformation;
53using Win::Devices::Enumeration::IDeviceInformationUpdate;
54using Win::Devices::Enumeration::IDeviceWatcher;
55using Win::Foundation::IAsyncOperation;
56using Win::Foundation::ITypedEventHandler;
shaochuane58f9c72016-08-30 22:27:08 -070057
Xiaohan Wangfd3e0aa2020-02-22 04:08:43 +000058// Alias for printing HRESULT.
59const auto PrintHr = logging::SystemErrorCodeToString;
60
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000061enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000062 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000063 kComTaskRunner
64};
65
shaochuane58f9c72016-08-30 22:27:08 -070066template <typename T>
67std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070068 HSTRING result;
69 HRESULT hr = obj->get_Id(&result);
70 if (FAILED(hr)) {
71 VLOG(1) << "get_Id failed: " << PrintHr(hr);
72 return std::string();
73 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000074 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070075}
76
77template <typename T>
78std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070079 HSTRING result;
80 HRESULT hr = obj->get_DeviceId(&result);
81 if (FAILED(hr)) {
82 VLOG(1) << "get_DeviceId failed: " << PrintHr(hr);
83 return std::string();
84 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000085 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070086}
87
88std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070089 HSTRING result;
90 HRESULT hr = info->get_Name(&result);
91 if (FAILED(hr)) {
92 VLOG(1) << "get_Name failed: " << PrintHr(hr);
93 return std::string();
94 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000095 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070096}
97
shaochuan110262b2016-08-31 02:15:16 -070098// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
99// instance.
100bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Peter Kasting6e8c8472019-09-20 17:55:55 +0000101 WRL::ComPtr<Win::Devices::Midi::IMidiSynthesizerStatics>
102 midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000103 HRESULT hr =
Peter Kasting6e8c8472019-09-20 17:55:55 +0000104 GetActivationFactory<Win::Devices::Midi::IMidiSynthesizerStatics,
junweifua8cea852017-10-17 06:21:16 +0000105 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
106 &midi_synthesizer_statics);
107 if (FAILED(hr)) {
108 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
109 return false;
110 }
shaochuan110262b2016-08-31 02:15:16 -0700111 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000112 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700113 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
114 return result != FALSE;
115}
116
shaochuan4eff30e2016-09-09 01:24:14 -0700117void GetDevPropString(DEVINST handle,
118 const DEVPROPKEY* devprop_key,
119 std::string* out) {
120 DEVPROPTYPE devprop_type;
121 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700122
shaochuan4eff30e2016-09-09 01:24:14 -0700123 // Retrieve |buffer_size| and allocate buffer later for receiving data.
124 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
125 nullptr, &buffer_size, 0);
126 if (cr != CR_BUFFER_SMALL) {
127 // Here we print error codes in hex instead of using PrintHr() with
128 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
129 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
130 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
131 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700132 }
shaochuan4eff30e2016-09-09 01:24:14 -0700133 if (devprop_type != DEVPROP_TYPE_STRING) {
134 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
135 << "expected DEVPROP_TYPE_STRING";
136 return;
137 }
shaochuan17bc4a02016-09-06 01:42:12 -0700138
shaochuan4eff30e2016-09-09 01:24:14 -0700139 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
140
141 // Receive property data.
142 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
143 &buffer_size, 0);
144 if (cr != CR_SUCCESS)
145 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
146 else
Peter Kasting426665c2021-02-02 13:01:34 +0000147 *out = base::WideToUTF8(reinterpret_cast<wchar_t*>(buffer.get()));
shaochuan4eff30e2016-09-09 01:24:14 -0700148}
shaochuan17bc4a02016-09-06 01:42:12 -0700149
150// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700151// device driver through PnP Configuration Manager, given device (interface) ID
152// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
153// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700154//
155// Device instance ID is extracted from device (interface) ID provided by WinRT
156// APIs, for example from the following interface ID:
157// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
158// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700159//
160// However the extracted device instance ID represent a "software device"
161// provided by Microsoft, which is an interface on top of the hardware for each
162// input/output port. Therefore we further locate its parent device, which is
163// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700164void GetDriverInfoFromDeviceId(const std::string& dev_id,
165 std::string* out_manufacturer,
166 std::string* out_driver_version) {
Peter Kasting426665c2021-02-02 13:01:34 +0000167 std::wstring dev_instance_id =
shaochuan17bc4a02016-09-06 01:42:12 -0700168 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
169 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
170
shaochuan4eff30e2016-09-09 01:24:14 -0700171 DEVINST dev_instance_handle;
172 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
173 CM_LOCATE_DEVNODE_NORMAL);
174 if (cr != CR_SUCCESS) {
175 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700176 return;
177 }
178
shaochuan4eff30e2016-09-09 01:24:14 -0700179 DEVINST parent_handle;
180 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
181 if (cr != CR_SUCCESS) {
182 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700183 return;
184 }
185
shaochuan4eff30e2016-09-09 01:24:14 -0700186 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
187 out_manufacturer);
188 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
189 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700190}
191
shaochuane58f9c72016-08-30 22:27:08 -0700192// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
193const int64_t kInvalidTokenValue = 0;
194
195template <typename InterfaceType>
196struct MidiPort {
197 MidiPort() = default;
198
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000199 MidiPort(const MidiPort&) = delete;
200 MidiPort& operator=(const MidiPort&) = delete;
201
shaochuane58f9c72016-08-30 22:27:08 -0700202 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000203 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700204 EventRegistrationToken token_MessageReceived;
shaochuane58f9c72016-08-30 22:27:08 -0700205};
206
207} // namespace
208
209template <typename InterfaceType,
210 typename RuntimeType,
211 typename StaticsInterfaceType,
Peter Kasting426665c2021-02-02 13:01:34 +0000212 wchar_t const* runtime_class_id>
shaochuane58f9c72016-08-30 22:27:08 -0700213class MidiManagerWinrt::MidiPortManager {
214 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000215 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700216 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000217 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700218
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000219 virtual ~MidiPortManager() {
220 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
221 }
shaochuane58f9c72016-08-30 22:27:08 -0700222
223 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000224 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700225
junweifua8cea852017-10-17 06:21:16 +0000226 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
227 &midi_port_statics_);
228 if (FAILED(hr)) {
229 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700230 return false;
junweifua8cea852017-10-17 06:21:16 +0000231 }
shaochuane58f9c72016-08-30 22:27:08 -0700232
233 HSTRING device_selector = nullptr;
234 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
235 if (FAILED(hr)) {
236 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
237 return false;
238 }
239
Peter Kasting6e8c8472019-09-20 17:55:55 +0000240 WRL::ComPtr<Win::Devices::Enumeration::IDeviceInformationStatics>
241 dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000242 hr = GetActivationFactory<
Peter Kasting6e8c8472019-09-20 17:55:55 +0000243 Win::Devices::Enumeration::IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000244 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
245 &dev_info_statics);
246 if (FAILED(hr)) {
247 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700248 return false;
junweifua8cea852017-10-17 06:21:16 +0000249 }
shaochuane58f9c72016-08-30 22:27:08 -0700250
Xiaohan Wangf2832912020-02-21 18:38:02 +0000251 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector, &watcher_);
shaochuane58f9c72016-08-30 22:27:08 -0700252 if (FAILED(hr)) {
253 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
254 return false;
255 }
256
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000257 // Register callbacks to WinRT that post state-modifying tasks back to
258 // kComTaskRunner. All posted tasks run only during the MidiPortManager
259 // instance is alive. This is ensured by MidiManagerWinrt by calling
260 // UnbindInstance() before destructing any MidiPortManager instance. Thus
261 // we can handle raw pointers safely in the following blocks.
262 MidiPortManager* port_manager = this;
263 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700264
265 hr = watcher_->add_Added(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000266 WRL::Callback<ITypedEventHandler<
267 DeviceWatcher*, Win::Devices::Enumeration::DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000268 [port_manager, task_service](IDeviceWatcher* watcher,
269 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700270 if (!info) {
271 VLOG(1) << "DeviceWatcher.Added callback provides null "
272 "pointer, ignoring";
273 return S_OK;
274 }
275
shaochuan110262b2016-08-31 02:15:16 -0700276 // Disable Microsoft GS Wavetable Synth due to security reasons.
277 // http://crbug.com/499279
278 if (IsMicrosoftSynthesizer(info))
279 return S_OK;
280
shaochuane58f9c72016-08-30 22:27:08 -0700281 std::string dev_id = GetIdString(info),
282 dev_name = GetNameString(info);
283
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000284 task_service->PostBoundTask(
285 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
286 base::Unretained(port_manager),
287 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700288
289 return S_OK;
290 })
291 .Get(),
292 &token_Added_);
293 if (FAILED(hr)) {
294 VLOG(1) << "add_Added failed: " << PrintHr(hr);
295 return false;
296 }
297
298 hr = watcher_->add_EnumerationCompleted(
299 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000300 [port_manager, task_service](IDeviceWatcher* watcher,
301 IInspectable* insp) {
302 task_service->PostBoundTask(
303 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000304 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000305 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700306
307 return S_OK;
308 })
309 .Get(),
310 &token_EnumerationCompleted_);
311 if (FAILED(hr)) {
312 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
313 return false;
314 }
315
316 hr = watcher_->add_Removed(
317 WRL::Callback<
318 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000319 [port_manager, task_service](IDeviceWatcher* watcher,
320 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700321 if (!update) {
322 VLOG(1) << "DeviceWatcher.Removed callback provides null "
323 "pointer, ignoring";
324 return S_OK;
325 }
326
shaochuane58f9c72016-08-30 22:27:08 -0700327 std::string dev_id = GetIdString(update);
328
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000329 task_service->PostBoundTask(
330 kComTaskRunner,
331 base::BindOnce(&MidiPortManager::OnRemoved,
332 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700333
334 return S_OK;
335 })
336 .Get(),
337 &token_Removed_);
338 if (FAILED(hr)) {
339 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
340 return false;
341 }
342
343 hr = watcher_->add_Stopped(
344 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
345 [](IDeviceWatcher* watcher, IInspectable* insp) {
346 // Placeholder, does nothing for now.
347 return S_OK;
348 })
349 .Get(),
350 &token_Stopped_);
351 if (FAILED(hr)) {
352 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
353 return false;
354 }
355
356 hr = watcher_->add_Updated(
357 WRL::Callback<
358 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
359 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
360 // TODO(shaochuan): Check for fields to be updated here.
361 return S_OK;
362 })
363 .Get(),
364 &token_Updated_);
365 if (FAILED(hr)) {
366 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
367 return false;
368 }
369
370 hr = watcher_->Start();
371 if (FAILED(hr)) {
372 VLOG(1) << "Start failed: " << PrintHr(hr);
373 return false;
374 }
375
376 is_initialized_ = true;
377 return true;
378 }
379
380 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000381 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700382
383 HRESULT hr;
384
385 for (const auto& entry : ports_)
386 RemovePortEventHandlers(entry.second.get());
387
388 if (token_Added_.value != kInvalidTokenValue) {
389 hr = watcher_->remove_Added(token_Added_);
390 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
391 token_Added_.value = kInvalidTokenValue;
392 }
393 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
394 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
395 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
396 << PrintHr(hr);
397 token_EnumerationCompleted_.value = kInvalidTokenValue;
398 }
399 if (token_Removed_.value != kInvalidTokenValue) {
400 hr = watcher_->remove_Removed(token_Removed_);
401 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
402 token_Removed_.value = kInvalidTokenValue;
403 }
404 if (token_Stopped_.value != kInvalidTokenValue) {
405 hr = watcher_->remove_Stopped(token_Stopped_);
406 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
407 token_Stopped_.value = kInvalidTokenValue;
408 }
409 if (token_Updated_.value != kInvalidTokenValue) {
410 hr = watcher_->remove_Updated(token_Updated_);
411 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
412 token_Updated_.value = kInvalidTokenValue;
413 }
414
415 if (is_initialized_) {
416 hr = watcher_->Stop();
417 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
418 is_initialized_ = false;
419 }
420 }
421
422 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000423 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700424 CHECK(is_initialized_);
425
426 auto it = ports_.find(dev_id);
427 if (it == ports_.end())
428 return nullptr;
429 return it->second.get();
430 }
431
432 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000433 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700434 CHECK(is_initialized_);
435
436 return GetPortByDeviceId(port_ids_[port_index]);
437 }
438
439 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000440 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700441 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000442 MidiService* midi_service_;
443
444 // Points to the MidiManagerWinrt instance, which is safe to be accessed
445 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700446 MidiManagerWinrt* midi_manager_;
447
shaochuane58f9c72016-08-30 22:27:08 -0700448 private:
449 // DeviceWatcher callbacks:
450 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000451 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700452 CHECK(is_initialized_);
453
shaochuane58f9c72016-08-30 22:27:08 -0700454 port_names_[dev_id] = dev_name;
455
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000456 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700457 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700458 return;
shaochuane58f9c72016-08-30 22:27:08 -0700459
460 IAsyncOperation<RuntimeType*>* async_op;
461
shaochuan9ff63b82016-09-01 01:58:44 -0700462 HRESULT hr =
463 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700464 if (FAILED(hr)) {
465 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
466 return;
467 }
468
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000469 MidiPortManager* port_manager = this;
470 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700471
472 hr = async_op->put_Completed(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000473 WRL::Callback<
474 Win::Foundation::IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000475 [port_manager, task_service](
476 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700477 // A reference to |async_op| is kept in |async_ops_|, safe to pass
478 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000479 task_service->PostBoundTask(
480 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000481 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000482 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000483 base::Unretained(port_manager),
484 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700485
486 return S_OK;
487 })
488 .Get());
489 if (FAILED(hr)) {
490 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
491 return;
492 }
493
494 // Keep a reference to incompleted |async_op| for releasing later.
495 async_ops_.insert(async_op);
496 }
497
498 void OnEnumerationCompleted() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000499 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700500 CHECK(is_initialized_);
501
502 if (async_ops_.empty())
503 midi_manager_->OnPortManagerReady();
504 else
505 enumeration_completed_not_ready_ = true;
506 }
507
508 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000509 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700510 CHECK(is_initialized_);
511
shaochuan110262b2016-08-31 02:15:16 -0700512 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
513 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700514 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
515 if (!port) {
516 VLOG(1) << "Removing non-existent port " << dev_id;
517 return;
518 }
519
toyoshimec2570a2016-10-21 02:15:27 -0700520 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700521
522 RemovePortEventHandlers(port);
523 port->handle = nullptr;
524 }
525
shaochuanc2894522016-09-20 01:10:50 -0700526 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000527 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700528 CHECK(is_initialized_);
529
shaochuanc2894522016-09-20 01:10:50 -0700530 InterfaceType* handle = nullptr;
531 HRESULT hr = async_op->GetResults(&handle);
532 if (FAILED(hr)) {
533 VLOG(1) << "GetResults failed: " << PrintHr(hr);
534 return;
535 }
536
537 // Manually release COM interface to completed |async_op|.
538 auto it = async_ops_.find(async_op);
539 CHECK(it != async_ops_.end());
540 (*it)->Release();
541 async_ops_.erase(it);
542
543 if (!handle) {
544 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
545 "ignoring";
546 return;
547 }
548
shaochuane58f9c72016-08-30 22:27:08 -0700549 EventRegistrationToken token = {kInvalidTokenValue};
550 if (!RegisterOnMessageReceived(handle, &token))
551 return;
552
553 std::string dev_id = GetDeviceIdString(handle);
554
555 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
556
557 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700558 std::string manufacturer = "Unknown", driver_version = "Unknown";
559 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
560
Adithya Srinivasan33252732018-10-17 15:59:40 +0000561 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
562 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700563
564 port = new MidiPort<InterfaceType>;
565 port->index = static_cast<uint32_t>(port_ids_.size());
566
567 ports_[dev_id].reset(port);
568 port_ids_.push_back(dev_id);
569 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700570 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700571 }
572
573 port->handle = handle;
574 port->token_MessageReceived = token;
575
shaochuane58f9c72016-08-30 22:27:08 -0700576 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
577 midi_manager_->OnPortManagerReady();
578 enumeration_completed_not_ready_ = false;
579 }
580 }
581
582 // Overrided by MidiInPortManager to listen to input ports.
583 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
584 EventRegistrationToken* p_token) {
585 return true;
586 }
587
588 // Overrided by MidiInPortManager to remove MessageReceived event handler.
589 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
590
591 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000592 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700593
594 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700595 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700596
shaochuane58f9c72016-08-30 22:27:08 -0700597 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000598 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700599
600 // DeviceWatcher instance and event registration tokens for unsubscribing
601 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000602 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700603 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
604 token_EnumerationCompleted_ = {kInvalidTokenValue},
605 token_Removed_ = {kInvalidTokenValue},
606 token_Stopped_ = {kInvalidTokenValue},
607 token_Updated_ = {kInvalidTokenValue};
608
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000609 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700610 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
611 ports_;
612 std::vector<std::string> port_ids_;
613 std::unordered_map<std::string, std::string> port_names_;
614
615 // Keeps AsyncOperation references before the operation completes. Note that
616 // raw pointers are used here and the COM interfaces should be released
617 // manually.
618 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
619
620 // Set when device enumeration is completed but OnPortManagerReady() is not
621 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
622 // In such cases, OnPortManagerReady() will be called in
623 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
624 bool enumeration_completed_not_ready_ = false;
625
626 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000627 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700628 bool is_initialized_ = false;
629};
630
631class MidiManagerWinrt::MidiInPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000632 : public MidiPortManager<Win::Devices::Midi::IMidiInPort,
633 Win::Devices::Midi::MidiInPort,
634 Win::Devices::Midi::IMidiInPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700635 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
636 public:
637 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000638 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700639
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000640 MidiInPortManager(const MidiInPortManager&) = delete;
641 MidiInPortManager& operator=(const MidiInPortManager&) = delete;
642
shaochuane58f9c72016-08-30 22:27:08 -0700643 private:
644 // MidiPortManager overrides:
Peter Kasting6e8c8472019-09-20 17:55:55 +0000645 bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle,
shaochuane58f9c72016-08-30 22:27:08 -0700646 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000647 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700648
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000649 MidiInPortManager* port_manager = this;
650 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700651
652 HRESULT hr = handle->add_MessageReceived(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000653 WRL::Callback<ITypedEventHandler<
654 Win::Devices::Midi::MidiInPort*,
655 Win::Devices::Midi::MidiMessageReceivedEventArgs*>>(
656 [port_manager, task_service](
657 Win::Devices::Midi::IMidiInPort* handle,
658 Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700659 const base::TimeTicks now = base::TimeTicks::Now();
660
661 std::string dev_id = GetDeviceIdString(handle);
662
Peter Kasting6e8c8472019-09-20 17:55:55 +0000663 WRL::ComPtr<Win::Devices::Midi::IMidiMessage> message;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000664 HRESULT hr = args->get_Message(&message);
shaochuane58f9c72016-08-30 22:27:08 -0700665 if (FAILED(hr)) {
666 VLOG(1) << "get_Message failed: " << PrintHr(hr);
667 return hr;
668 }
669
Peter Kasting6e8c8472019-09-20 17:55:55 +0000670 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000671 hr = message->get_RawData(&buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700672 if (FAILED(hr)) {
673 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
674 return hr;
675 }
676
677 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000678 uint32_t data_length = 0;
679 hr = base::win::GetPointerToBufferData(
680 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700681 if (FAILED(hr))
682 return hr;
683
shaochuane58f9c72016-08-30 22:27:08 -0700684 std::vector<uint8_t> data(p_buffer_data,
685 p_buffer_data + data_length);
686
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000687 task_service->PostBoundTask(
688 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000689 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000690 base::Unretained(port_manager), dev_id, data,
691 now));
shaochuane58f9c72016-08-30 22:27:08 -0700692
693 return S_OK;
694 })
695 .Get(),
696 p_token);
697 if (FAILED(hr)) {
698 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
699 return false;
700 }
701
702 return true;
703 }
704
Peter Kasting6e8c8472019-09-20 17:55:55 +0000705 void RemovePortEventHandlers(
706 MidiPort<Win::Devices::Midi::IMidiInPort>* port) override {
shaochuane58f9c72016-08-30 22:27:08 -0700707 if (!(port->handle &&
708 port->token_MessageReceived.value != kInvalidTokenValue))
709 return;
710
711 HRESULT hr =
712 port->handle->remove_MessageReceived(port->token_MessageReceived);
713 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
714 port->token_MessageReceived.value = kInvalidTokenValue;
715 }
716
Adithya Srinivasan33252732018-10-17 15:59:40 +0000717 void AddPort(mojom::PortInfo info) final {
718 midi_manager_->AddInputPort(info);
719 }
shaochuane58f9c72016-08-30 22:27:08 -0700720
toyoshimec2570a2016-10-21 02:15:27 -0700721 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700722 midi_manager_->SetInputPortState(port_index, state);
723 }
724
shaochuane58f9c72016-08-30 22:27:08 -0700725 // Callback on receiving MIDI input message.
726 void OnMessageReceived(std::string dev_id,
727 std::vector<uint8_t> data,
728 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000729 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700730
Peter Kasting6e8c8472019-09-20 17:55:55 +0000731 MidiPort<Win::Devices::Midi::IMidiInPort>* port = GetPortByDeviceId(dev_id);
shaochuane58f9c72016-08-30 22:27:08 -0700732 CHECK(port);
733
734 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
735 }
shaochuane58f9c72016-08-30 22:27:08 -0700736};
737
738class MidiManagerWinrt::MidiOutPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000739 : public MidiPortManager<Win::Devices::Midi::IMidiOutPort,
740 Win::Devices::Midi::IMidiOutPort,
741 Win::Devices::Midi::IMidiOutPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700742 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
743 public:
744 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000745 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700746
Peter Boström5e5c4fa2021-10-15 21:43:24 +0000747 MidiOutPortManager(const MidiOutPortManager&) = delete;
748 MidiOutPortManager& operator=(const MidiOutPortManager&) = delete;
749
shaochuane58f9c72016-08-30 22:27:08 -0700750 private:
751 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000752 void AddPort(mojom::PortInfo info) final {
753 midi_manager_->AddOutputPort(info);
754 }
shaochuane58f9c72016-08-30 22:27:08 -0700755
toyoshimec2570a2016-10-21 02:15:27 -0700756 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700757 midi_manager_->SetOutputPortState(port_index, state);
758 }
shaochuane58f9c72016-08-30 22:27:08 -0700759};
760
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000761namespace {
762
763// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
764// instance destruction.
765void FinalizeOnComRunner(
766 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
767 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
768 if (port_manager_in)
769 port_manager_in->StopWatcher();
770
771 if (port_manager_out)
772 port_manager_out->StopWatcher();
773}
774
775} // namespace
776
toyoshimf4d61522017-02-10 02:03:32 -0800777MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000778 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700779
780MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000781 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
782 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000783 if (!service()->task_service()->UnbindInstance())
784 return;
shaochuane58f9c72016-08-30 22:27:08 -0700785
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000786 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000787 service()->task_service()->PostStaticTask(
788 kComTaskRunner,
789 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
790 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700791}
792
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000793void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000794 if (!service()->task_service()->BindInstance())
795 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000796
797 service()->task_service()->PostBoundTask(
798 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
799 base::Unretained(this)));
800}
801
shaochuane58f9c72016-08-30 22:27:08 -0700802void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
803 uint32_t port_index,
804 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000805 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000806 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
807 service()->task_service()->PostBoundDelayedTask(
808 kComTaskRunner,
809 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
810 port_index, data),
811 delay);
812 service()->task_service()->PostBoundDelayedTask(
813 kComTaskRunner,
814 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
815 base::Unretained(this), client, data.size()),
816 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700817}
818
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000819void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700820 base::AutoLock auto_lock(lazy_init_member_lock_);
821
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000822 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
823
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000824 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
825 ScopedHString::ResolveCoreWinRTStringDelayload();
826 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000827 service()->task_service()->PostBoundTask(
828 kDefaultTaskRunner,
829 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
830 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700831 return;
832 }
833
Peter Boström1684b972021-05-01 01:31:25 +0000834 port_manager_in_ = std::make_unique<MidiInPortManager>(this);
835 port_manager_out_ = std::make_unique<MidiOutPortManager>(this);
shaochuane58f9c72016-08-30 22:27:08 -0700836
shaochuane58f9c72016-08-30 22:27:08 -0700837 if (!(port_manager_in_->StartWatcher() &&
838 port_manager_out_->StartWatcher())) {
839 port_manager_in_->StopWatcher();
840 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000841 service()->task_service()->PostBoundTask(
842 kDefaultTaskRunner,
843 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
844 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700845 }
846}
847
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000848void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700849 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000850 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700851
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000852 base::AutoLock auto_lock(lazy_init_member_lock_);
Peter Kasting6e8c8472019-09-20 17:55:55 +0000853 MidiPort<Win::Devices::Midi::IMidiOutPort>* port =
854 port_manager_out_->GetPortByIndex(port_index);
shaochuane58f9c72016-08-30 22:27:08 -0700855 if (!(port && port->handle)) {
856 VLOG(1) << "Port not available: " << port_index;
857 return;
858 }
859
Peter Kasting6e8c8472019-09-20 17:55:55 +0000860 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000861 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000862 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700863 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000864 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700865 return;
866 }
867
robliao3566d1a2017-04-18 17:28:09 -0700868 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700869 if (FAILED(hr)) {
870 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
871 return;
872 }
873}
874
875void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000876 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700877 DCHECK(port_manager_ready_count_ < 2);
878
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000879 if (++port_manager_ready_count_ == 2) {
880 service()->task_service()->PostBoundTask(
881 kDefaultTaskRunner,
882 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
883 base::Unretained(this), Result::OK));
884 }
shaochuane58f9c72016-08-30 22:27:08 -0700885}
886
shaochuane58f9c72016-08-30 22:27:08 -0700887} // namespace midi