blob: b4c0b133dbb3d6bf00cbe9952568795deb0968e3 [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>
25#include <unordered_map>
26#include <unordered_set>
27
28#include "base/bind.h"
shaochuan9ff63b82016-09-01 01:58:44 -070029#include "base/scoped_generic.h"
shaochuan17bc4a02016-09-06 01:42:12 -070030#include "base/strings/string_util.h"
shaochuane58f9c72016-08-30 22:27:08 -070031#include "base/strings/utf_string_conversions.h"
shaochuane58f9c72016-08-30 22:27:08 -070032#include "base/timer/timer.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000033#include "base/win/core_winrt_util.h"
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000034#include "base/win/scoped_hstring.h"
junweifuf51c5a02017-11-03 06:37:09 +000035#include "base/win/winrt_storage_util.h"
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000036#include "media/midi/midi_service.h"
37#include "media/midi/task_service.h"
shaochuane58f9c72016-08-30 22:27:08 -070038
shaochuane58f9c72016-08-30 22:27:08 -070039namespace midi {
40namespace {
41
42namespace WRL = Microsoft::WRL;
Peter Kasting6e8c8472019-09-20 17:55:55 +000043namespace Win = ABI::Windows;
shaochuane58f9c72016-08-30 22:27:08 -070044
junweifua8cea852017-10-17 06:21:16 +000045using base::win::GetActivationFactory;
Peter Kasting6e8c8472019-09-20 17:55:55 +000046using base::win::ScopedHString;
toyoshimec2570a2016-10-21 02:15:27 -070047using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070048using mojom::Result;
Peter Kasting6e8c8472019-09-20 17:55:55 +000049using Win::Devices::Enumeration::DeviceInformationUpdate;
50using Win::Devices::Enumeration::DeviceWatcher;
51using Win::Devices::Enumeration::IDeviceInformation;
52using Win::Devices::Enumeration::IDeviceInformationUpdate;
53using Win::Devices::Enumeration::IDeviceWatcher;
54using Win::Foundation::IAsyncOperation;
55using Win::Foundation::ITypedEventHandler;
shaochuane58f9c72016-08-30 22:27:08 -070056
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000057enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000058 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000059 kComTaskRunner
60};
61
shaochuane58f9c72016-08-30 22:27:08 -070062// Helpers for printing HRESULTs.
63struct PrintHr {
64 PrintHr(HRESULT hr) : hr(hr) {}
65 HRESULT hr;
66};
67
68std::ostream& operator<<(std::ostream& os, const PrintHr& phr) {
69 std::ios_base::fmtflags ff = os.flags();
70 os << _com_error(phr.hr).ErrorMessage() << " (0x" << std::hex
71 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr << ")";
72 os.flags(ff);
73 return os;
74}
75
shaochuane58f9c72016-08-30 22:27:08 -070076template <typename T>
77std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070078 HSTRING result;
79 HRESULT hr = obj->get_Id(&result);
80 if (FAILED(hr)) {
81 VLOG(1) << "get_Id failed: " << PrintHr(hr);
82 return std::string();
83 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000084 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070085}
86
87template <typename T>
88std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070089 HSTRING result;
90 HRESULT hr = obj->get_DeviceId(&result);
91 if (FAILED(hr)) {
92 VLOG(1) << "get_DeviceId 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
98std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070099 HSTRING result;
100 HRESULT hr = info->get_Name(&result);
101 if (FAILED(hr)) {
102 VLOG(1) << "get_Name failed: " << PrintHr(hr);
103 return std::string();
104 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000105 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -0700106}
107
shaochuan110262b2016-08-31 02:15:16 -0700108// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
109// instance.
110bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Peter Kasting6e8c8472019-09-20 17:55:55 +0000111 WRL::ComPtr<Win::Devices::Midi::IMidiSynthesizerStatics>
112 midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000113 HRESULT hr =
Peter Kasting6e8c8472019-09-20 17:55:55 +0000114 GetActivationFactory<Win::Devices::Midi::IMidiSynthesizerStatics,
junweifua8cea852017-10-17 06:21:16 +0000115 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
116 &midi_synthesizer_statics);
117 if (FAILED(hr)) {
118 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
119 return false;
120 }
shaochuan110262b2016-08-31 02:15:16 -0700121 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000122 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700123 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
124 return result != FALSE;
125}
126
shaochuan4eff30e2016-09-09 01:24:14 -0700127void GetDevPropString(DEVINST handle,
128 const DEVPROPKEY* devprop_key,
129 std::string* out) {
130 DEVPROPTYPE devprop_type;
131 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700132
shaochuan4eff30e2016-09-09 01:24:14 -0700133 // Retrieve |buffer_size| and allocate buffer later for receiving data.
134 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
135 nullptr, &buffer_size, 0);
136 if (cr != CR_BUFFER_SMALL) {
137 // Here we print error codes in hex instead of using PrintHr() with
138 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
139 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
140 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
141 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700142 }
shaochuan4eff30e2016-09-09 01:24:14 -0700143 if (devprop_type != DEVPROP_TYPE_STRING) {
144 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
145 << "expected DEVPROP_TYPE_STRING";
146 return;
147 }
shaochuan17bc4a02016-09-06 01:42:12 -0700148
shaochuan4eff30e2016-09-09 01:24:14 -0700149 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
150
151 // Receive property data.
152 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
153 &buffer_size, 0);
154 if (cr != CR_SUCCESS)
155 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
156 else
157 *out = base::WideToUTF8(reinterpret_cast<base::char16*>(buffer.get()));
158}
shaochuan17bc4a02016-09-06 01:42:12 -0700159
160// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700161// device driver through PnP Configuration Manager, given device (interface) ID
162// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
163// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700164//
165// Device instance ID is extracted from device (interface) ID provided by WinRT
166// APIs, for example from the following interface ID:
167// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
168// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700169//
170// However the extracted device instance ID represent a "software device"
171// provided by Microsoft, which is an interface on top of the hardware for each
172// input/output port. Therefore we further locate its parent device, which is
173// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700174void GetDriverInfoFromDeviceId(const std::string& dev_id,
175 std::string* out_manufacturer,
176 std::string* out_driver_version) {
177 base::string16 dev_instance_id =
178 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
179 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
180
shaochuan4eff30e2016-09-09 01:24:14 -0700181 DEVINST dev_instance_handle;
182 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
183 CM_LOCATE_DEVNODE_NORMAL);
184 if (cr != CR_SUCCESS) {
185 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700186 return;
187 }
188
shaochuan4eff30e2016-09-09 01:24:14 -0700189 DEVINST parent_handle;
190 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
191 if (cr != CR_SUCCESS) {
192 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700193 return;
194 }
195
shaochuan4eff30e2016-09-09 01:24:14 -0700196 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
197 out_manufacturer);
198 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
199 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700200}
201
shaochuane58f9c72016-08-30 22:27:08 -0700202// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
203const int64_t kInvalidTokenValue = 0;
204
205template <typename InterfaceType>
206struct MidiPort {
207 MidiPort() = default;
208
209 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000210 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700211 EventRegistrationToken token_MessageReceived;
212
213 private:
214 DISALLOW_COPY_AND_ASSIGN(MidiPort);
215};
216
217} // namespace
218
219template <typename InterfaceType,
220 typename RuntimeType,
221 typename StaticsInterfaceType,
222 base::char16 const* runtime_class_id>
223class MidiManagerWinrt::MidiPortManager {
224 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000225 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700226 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000227 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700228
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000229 virtual ~MidiPortManager() {
230 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
231 }
shaochuane58f9c72016-08-30 22:27:08 -0700232
233 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000234 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700235
junweifua8cea852017-10-17 06:21:16 +0000236 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
237 &midi_port_statics_);
238 if (FAILED(hr)) {
239 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700240 return false;
junweifua8cea852017-10-17 06:21:16 +0000241 }
shaochuane58f9c72016-08-30 22:27:08 -0700242
243 HSTRING device_selector = nullptr;
244 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
245 if (FAILED(hr)) {
246 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
247 return false;
248 }
249
Peter Kasting6e8c8472019-09-20 17:55:55 +0000250 WRL::ComPtr<Win::Devices::Enumeration::IDeviceInformationStatics>
251 dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000252 hr = GetActivationFactory<
Peter Kasting6e8c8472019-09-20 17:55:55 +0000253 Win::Devices::Enumeration::IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000254 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
255 &dev_info_statics);
256 if (FAILED(hr)) {
257 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700258 return false;
junweifua8cea852017-10-17 06:21:16 +0000259 }
shaochuane58f9c72016-08-30 22:27:08 -0700260
261 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector,
robliao8d08e692017-05-11 10:14:00 -0700262 watcher_.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700263 if (FAILED(hr)) {
264 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
265 return false;
266 }
267
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000268 // Register callbacks to WinRT that post state-modifying tasks back to
269 // kComTaskRunner. All posted tasks run only during the MidiPortManager
270 // instance is alive. This is ensured by MidiManagerWinrt by calling
271 // UnbindInstance() before destructing any MidiPortManager instance. Thus
272 // we can handle raw pointers safely in the following blocks.
273 MidiPortManager* port_manager = this;
274 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700275
276 hr = watcher_->add_Added(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000277 WRL::Callback<ITypedEventHandler<
278 DeviceWatcher*, Win::Devices::Enumeration::DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000279 [port_manager, task_service](IDeviceWatcher* watcher,
280 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700281 if (!info) {
282 VLOG(1) << "DeviceWatcher.Added callback provides null "
283 "pointer, ignoring";
284 return S_OK;
285 }
286
shaochuan110262b2016-08-31 02:15:16 -0700287 // Disable Microsoft GS Wavetable Synth due to security reasons.
288 // http://crbug.com/499279
289 if (IsMicrosoftSynthesizer(info))
290 return S_OK;
291
shaochuane58f9c72016-08-30 22:27:08 -0700292 std::string dev_id = GetIdString(info),
293 dev_name = GetNameString(info);
294
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000295 task_service->PostBoundTask(
296 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
297 base::Unretained(port_manager),
298 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700299
300 return S_OK;
301 })
302 .Get(),
303 &token_Added_);
304 if (FAILED(hr)) {
305 VLOG(1) << "add_Added failed: " << PrintHr(hr);
306 return false;
307 }
308
309 hr = watcher_->add_EnumerationCompleted(
310 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000311 [port_manager, task_service](IDeviceWatcher* watcher,
312 IInspectable* insp) {
313 task_service->PostBoundTask(
314 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000315 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000316 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700317
318 return S_OK;
319 })
320 .Get(),
321 &token_EnumerationCompleted_);
322 if (FAILED(hr)) {
323 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
324 return false;
325 }
326
327 hr = watcher_->add_Removed(
328 WRL::Callback<
329 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000330 [port_manager, task_service](IDeviceWatcher* watcher,
331 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700332 if (!update) {
333 VLOG(1) << "DeviceWatcher.Removed callback provides null "
334 "pointer, ignoring";
335 return S_OK;
336 }
337
shaochuane58f9c72016-08-30 22:27:08 -0700338 std::string dev_id = GetIdString(update);
339
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000340 task_service->PostBoundTask(
341 kComTaskRunner,
342 base::BindOnce(&MidiPortManager::OnRemoved,
343 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700344
345 return S_OK;
346 })
347 .Get(),
348 &token_Removed_);
349 if (FAILED(hr)) {
350 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
351 return false;
352 }
353
354 hr = watcher_->add_Stopped(
355 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
356 [](IDeviceWatcher* watcher, IInspectable* insp) {
357 // Placeholder, does nothing for now.
358 return S_OK;
359 })
360 .Get(),
361 &token_Stopped_);
362 if (FAILED(hr)) {
363 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
364 return false;
365 }
366
367 hr = watcher_->add_Updated(
368 WRL::Callback<
369 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
370 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
371 // TODO(shaochuan): Check for fields to be updated here.
372 return S_OK;
373 })
374 .Get(),
375 &token_Updated_);
376 if (FAILED(hr)) {
377 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
378 return false;
379 }
380
381 hr = watcher_->Start();
382 if (FAILED(hr)) {
383 VLOG(1) << "Start failed: " << PrintHr(hr);
384 return false;
385 }
386
387 is_initialized_ = true;
388 return true;
389 }
390
391 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000392 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700393
394 HRESULT hr;
395
396 for (const auto& entry : ports_)
397 RemovePortEventHandlers(entry.second.get());
398
399 if (token_Added_.value != kInvalidTokenValue) {
400 hr = watcher_->remove_Added(token_Added_);
401 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
402 token_Added_.value = kInvalidTokenValue;
403 }
404 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
405 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
406 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
407 << PrintHr(hr);
408 token_EnumerationCompleted_.value = kInvalidTokenValue;
409 }
410 if (token_Removed_.value != kInvalidTokenValue) {
411 hr = watcher_->remove_Removed(token_Removed_);
412 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
413 token_Removed_.value = kInvalidTokenValue;
414 }
415 if (token_Stopped_.value != kInvalidTokenValue) {
416 hr = watcher_->remove_Stopped(token_Stopped_);
417 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
418 token_Stopped_.value = kInvalidTokenValue;
419 }
420 if (token_Updated_.value != kInvalidTokenValue) {
421 hr = watcher_->remove_Updated(token_Updated_);
422 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
423 token_Updated_.value = kInvalidTokenValue;
424 }
425
426 if (is_initialized_) {
427 hr = watcher_->Stop();
428 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
429 is_initialized_ = false;
430 }
431 }
432
433 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
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 auto it = ports_.find(dev_id);
438 if (it == ports_.end())
439 return nullptr;
440 return it->second.get();
441 }
442
443 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000444 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700445 CHECK(is_initialized_);
446
447 return GetPortByDeviceId(port_ids_[port_index]);
448 }
449
450 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000451 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700452 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000453 MidiService* midi_service_;
454
455 // Points to the MidiManagerWinrt instance, which is safe to be accessed
456 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700457 MidiManagerWinrt* midi_manager_;
458
shaochuane58f9c72016-08-30 22:27:08 -0700459 private:
460 // DeviceWatcher callbacks:
461 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000462 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700463 CHECK(is_initialized_);
464
shaochuane58f9c72016-08-30 22:27:08 -0700465 port_names_[dev_id] = dev_name;
466
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000467 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700468 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700469 return;
shaochuane58f9c72016-08-30 22:27:08 -0700470
471 IAsyncOperation<RuntimeType*>* async_op;
472
shaochuan9ff63b82016-09-01 01:58:44 -0700473 HRESULT hr =
474 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700475 if (FAILED(hr)) {
476 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
477 return;
478 }
479
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000480 MidiPortManager* port_manager = this;
481 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700482
483 hr = async_op->put_Completed(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000484 WRL::Callback<
485 Win::Foundation::IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000486 [port_manager, task_service](
487 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700488 // A reference to |async_op| is kept in |async_ops_|, safe to pass
489 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000490 task_service->PostBoundTask(
491 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000492 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000493 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000494 base::Unretained(port_manager),
495 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700496
497 return S_OK;
498 })
499 .Get());
500 if (FAILED(hr)) {
501 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
502 return;
503 }
504
505 // Keep a reference to incompleted |async_op| for releasing later.
506 async_ops_.insert(async_op);
507 }
508
509 void OnEnumerationCompleted() {
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
513 if (async_ops_.empty())
514 midi_manager_->OnPortManagerReady();
515 else
516 enumeration_completed_not_ready_ = true;
517 }
518
519 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000520 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700521 CHECK(is_initialized_);
522
shaochuan110262b2016-08-31 02:15:16 -0700523 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
524 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700525 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
526 if (!port) {
527 VLOG(1) << "Removing non-existent port " << dev_id;
528 return;
529 }
530
toyoshimec2570a2016-10-21 02:15:27 -0700531 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700532
533 RemovePortEventHandlers(port);
534 port->handle = nullptr;
535 }
536
shaochuanc2894522016-09-20 01:10:50 -0700537 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000538 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700539 CHECK(is_initialized_);
540
shaochuanc2894522016-09-20 01:10:50 -0700541 InterfaceType* handle = nullptr;
542 HRESULT hr = async_op->GetResults(&handle);
543 if (FAILED(hr)) {
544 VLOG(1) << "GetResults failed: " << PrintHr(hr);
545 return;
546 }
547
548 // Manually release COM interface to completed |async_op|.
549 auto it = async_ops_.find(async_op);
550 CHECK(it != async_ops_.end());
551 (*it)->Release();
552 async_ops_.erase(it);
553
554 if (!handle) {
555 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
556 "ignoring";
557 return;
558 }
559
shaochuane58f9c72016-08-30 22:27:08 -0700560 EventRegistrationToken token = {kInvalidTokenValue};
561 if (!RegisterOnMessageReceived(handle, &token))
562 return;
563
564 std::string dev_id = GetDeviceIdString(handle);
565
566 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
567
568 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700569 std::string manufacturer = "Unknown", driver_version = "Unknown";
570 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
571
Adithya Srinivasan33252732018-10-17 15:59:40 +0000572 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
573 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700574
575 port = new MidiPort<InterfaceType>;
576 port->index = static_cast<uint32_t>(port_ids_.size());
577
578 ports_[dev_id].reset(port);
579 port_ids_.push_back(dev_id);
580 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700581 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700582 }
583
584 port->handle = handle;
585 port->token_MessageReceived = token;
586
shaochuane58f9c72016-08-30 22:27:08 -0700587 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
588 midi_manager_->OnPortManagerReady();
589 enumeration_completed_not_ready_ = false;
590 }
591 }
592
593 // Overrided by MidiInPortManager to listen to input ports.
594 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
595 EventRegistrationToken* p_token) {
596 return true;
597 }
598
599 // Overrided by MidiInPortManager to remove MessageReceived event handler.
600 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
601
602 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000603 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700604
605 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700606 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700607
shaochuane58f9c72016-08-30 22:27:08 -0700608 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000609 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700610
611 // DeviceWatcher instance and event registration tokens for unsubscribing
612 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000613 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700614 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
615 token_EnumerationCompleted_ = {kInvalidTokenValue},
616 token_Removed_ = {kInvalidTokenValue},
617 token_Stopped_ = {kInvalidTokenValue},
618 token_Updated_ = {kInvalidTokenValue};
619
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000620 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700621 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
622 ports_;
623 std::vector<std::string> port_ids_;
624 std::unordered_map<std::string, std::string> port_names_;
625
626 // Keeps AsyncOperation references before the operation completes. Note that
627 // raw pointers are used here and the COM interfaces should be released
628 // manually.
629 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
630
631 // Set when device enumeration is completed but OnPortManagerReady() is not
632 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
633 // In such cases, OnPortManagerReady() will be called in
634 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
635 bool enumeration_completed_not_ready_ = false;
636
637 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000638 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700639 bool is_initialized_ = false;
640};
641
642class MidiManagerWinrt::MidiInPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000643 : public MidiPortManager<Win::Devices::Midi::IMidiInPort,
644 Win::Devices::Midi::MidiInPort,
645 Win::Devices::Midi::IMidiInPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700646 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
647 public:
648 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000649 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700650
651 private:
652 // MidiPortManager overrides:
Peter Kasting6e8c8472019-09-20 17:55:55 +0000653 bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle,
shaochuane58f9c72016-08-30 22:27:08 -0700654 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000655 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700656
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000657 MidiInPortManager* port_manager = this;
658 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700659
660 HRESULT hr = handle->add_MessageReceived(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000661 WRL::Callback<ITypedEventHandler<
662 Win::Devices::Midi::MidiInPort*,
663 Win::Devices::Midi::MidiMessageReceivedEventArgs*>>(
664 [port_manager, task_service](
665 Win::Devices::Midi::IMidiInPort* handle,
666 Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700667 const base::TimeTicks now = base::TimeTicks::Now();
668
669 std::string dev_id = GetDeviceIdString(handle);
670
Peter Kasting6e8c8472019-09-20 17:55:55 +0000671 WRL::ComPtr<Win::Devices::Midi::IMidiMessage> message;
robliao8d08e692017-05-11 10:14:00 -0700672 HRESULT hr = args->get_Message(message.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700673 if (FAILED(hr)) {
674 VLOG(1) << "get_Message failed: " << PrintHr(hr);
675 return hr;
676 }
677
Peter Kasting6e8c8472019-09-20 17:55:55 +0000678 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
robliao8d08e692017-05-11 10:14:00 -0700679 hr = message->get_RawData(buffer.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700680 if (FAILED(hr)) {
681 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
682 return hr;
683 }
684
685 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000686 uint32_t data_length = 0;
687 hr = base::win::GetPointerToBufferData(
688 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700689 if (FAILED(hr))
690 return hr;
691
shaochuane58f9c72016-08-30 22:27:08 -0700692 std::vector<uint8_t> data(p_buffer_data,
693 p_buffer_data + data_length);
694
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000695 task_service->PostBoundTask(
696 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000697 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000698 base::Unretained(port_manager), dev_id, data,
699 now));
shaochuane58f9c72016-08-30 22:27:08 -0700700
701 return S_OK;
702 })
703 .Get(),
704 p_token);
705 if (FAILED(hr)) {
706 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
707 return false;
708 }
709
710 return true;
711 }
712
Peter Kasting6e8c8472019-09-20 17:55:55 +0000713 void RemovePortEventHandlers(
714 MidiPort<Win::Devices::Midi::IMidiInPort>* port) override {
shaochuane58f9c72016-08-30 22:27:08 -0700715 if (!(port->handle &&
716 port->token_MessageReceived.value != kInvalidTokenValue))
717 return;
718
719 HRESULT hr =
720 port->handle->remove_MessageReceived(port->token_MessageReceived);
721 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
722 port->token_MessageReceived.value = kInvalidTokenValue;
723 }
724
Adithya Srinivasan33252732018-10-17 15:59:40 +0000725 void AddPort(mojom::PortInfo info) final {
726 midi_manager_->AddInputPort(info);
727 }
shaochuane58f9c72016-08-30 22:27:08 -0700728
toyoshimec2570a2016-10-21 02:15:27 -0700729 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700730 midi_manager_->SetInputPortState(port_index, state);
731 }
732
shaochuane58f9c72016-08-30 22:27:08 -0700733 // Callback on receiving MIDI input message.
734 void OnMessageReceived(std::string dev_id,
735 std::vector<uint8_t> data,
736 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000737 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700738
Peter Kasting6e8c8472019-09-20 17:55:55 +0000739 MidiPort<Win::Devices::Midi::IMidiInPort>* port = GetPortByDeviceId(dev_id);
shaochuane58f9c72016-08-30 22:27:08 -0700740 CHECK(port);
741
742 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
743 }
744
shaochuane58f9c72016-08-30 22:27:08 -0700745 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
746};
747
748class MidiManagerWinrt::MidiOutPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000749 : public MidiPortManager<Win::Devices::Midi::IMidiOutPort,
750 Win::Devices::Midi::IMidiOutPort,
751 Win::Devices::Midi::IMidiOutPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700752 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
753 public:
754 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000755 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700756
757 private:
758 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000759 void AddPort(mojom::PortInfo info) final {
760 midi_manager_->AddOutputPort(info);
761 }
shaochuane58f9c72016-08-30 22:27:08 -0700762
toyoshimec2570a2016-10-21 02:15:27 -0700763 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700764 midi_manager_->SetOutputPortState(port_index, state);
765 }
766
shaochuane58f9c72016-08-30 22:27:08 -0700767 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
768};
769
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000770namespace {
771
772// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
773// instance destruction.
774void FinalizeOnComRunner(
775 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
776 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
777 if (port_manager_in)
778 port_manager_in->StopWatcher();
779
780 if (port_manager_out)
781 port_manager_out->StopWatcher();
782}
783
784} // namespace
785
toyoshimf4d61522017-02-10 02:03:32 -0800786MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000787 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700788
789MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000790 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
791 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000792 if (!service()->task_service()->UnbindInstance())
793 return;
shaochuane58f9c72016-08-30 22:27:08 -0700794
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000795 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000796 service()->task_service()->PostStaticTask(
797 kComTaskRunner,
798 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
799 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700800}
801
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000802void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000803 if (!service()->task_service()->BindInstance())
804 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000805
806 service()->task_service()->PostBoundTask(
807 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
808 base::Unretained(this)));
809}
810
shaochuane58f9c72016-08-30 22:27:08 -0700811void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
812 uint32_t port_index,
813 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000814 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000815 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
816 service()->task_service()->PostBoundDelayedTask(
817 kComTaskRunner,
818 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
819 port_index, data),
820 delay);
821 service()->task_service()->PostBoundDelayedTask(
822 kComTaskRunner,
823 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
824 base::Unretained(this), client, data.size()),
825 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700826}
827
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000828void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700829 base::AutoLock auto_lock(lazy_init_member_lock_);
830
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000831 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
832
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000833 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
834 ScopedHString::ResolveCoreWinRTStringDelayload();
835 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000836 service()->task_service()->PostBoundTask(
837 kDefaultTaskRunner,
838 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
839 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700840 return;
841 }
842
shaochuane58f9c72016-08-30 22:27:08 -0700843 port_manager_in_.reset(new MidiInPortManager(this));
844 port_manager_out_.reset(new MidiOutPortManager(this));
845
shaochuane58f9c72016-08-30 22:27:08 -0700846 if (!(port_manager_in_->StartWatcher() &&
847 port_manager_out_->StartWatcher())) {
848 port_manager_in_->StopWatcher();
849 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000850 service()->task_service()->PostBoundTask(
851 kDefaultTaskRunner,
852 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
853 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700854 }
855}
856
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000857void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700858 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000859 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700860
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000861 base::AutoLock auto_lock(lazy_init_member_lock_);
Peter Kasting6e8c8472019-09-20 17:55:55 +0000862 MidiPort<Win::Devices::Midi::IMidiOutPort>* port =
863 port_manager_out_->GetPortByIndex(port_index);
shaochuane58f9c72016-08-30 22:27:08 -0700864 if (!(port && port->handle)) {
865 VLOG(1) << "Port not available: " << port_index;
866 return;
867 }
868
Peter Kasting6e8c8472019-09-20 17:55:55 +0000869 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000870 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000871 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700872 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000873 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700874 return;
875 }
876
robliao3566d1a2017-04-18 17:28:09 -0700877 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700878 if (FAILED(hr)) {
879 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
880 return;
881 }
882}
883
884void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000885 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700886 DCHECK(port_manager_ready_count_ < 2);
887
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000888 if (++port_manager_ready_count_ == 2) {
889 service()->task_service()->PostBoundTask(
890 kDefaultTaskRunner,
891 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
892 base::Unretained(this), Result::OK));
893 }
shaochuane58f9c72016-08-30 22:27:08 -0700894}
895
shaochuane58f9c72016-08-30 22:27:08 -0700896} // namespace midi