blob: 196249a1bfdab423c3d71623df26e4033d85a1f6 [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
Xiaohan Wangf2832912020-02-21 18:38:02 +0000261 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector, &watcher_);
shaochuane58f9c72016-08-30 22:27:08 -0700262 if (FAILED(hr)) {
263 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
264 return false;
265 }
266
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000267 // Register callbacks to WinRT that post state-modifying tasks back to
268 // kComTaskRunner. All posted tasks run only during the MidiPortManager
269 // instance is alive. This is ensured by MidiManagerWinrt by calling
270 // UnbindInstance() before destructing any MidiPortManager instance. Thus
271 // we can handle raw pointers safely in the following blocks.
272 MidiPortManager* port_manager = this;
273 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700274
275 hr = watcher_->add_Added(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000276 WRL::Callback<ITypedEventHandler<
277 DeviceWatcher*, Win::Devices::Enumeration::DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000278 [port_manager, task_service](IDeviceWatcher* watcher,
279 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700280 if (!info) {
281 VLOG(1) << "DeviceWatcher.Added callback provides null "
282 "pointer, ignoring";
283 return S_OK;
284 }
285
shaochuan110262b2016-08-31 02:15:16 -0700286 // Disable Microsoft GS Wavetable Synth due to security reasons.
287 // http://crbug.com/499279
288 if (IsMicrosoftSynthesizer(info))
289 return S_OK;
290
shaochuane58f9c72016-08-30 22:27:08 -0700291 std::string dev_id = GetIdString(info),
292 dev_name = GetNameString(info);
293
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000294 task_service->PostBoundTask(
295 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
296 base::Unretained(port_manager),
297 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700298
299 return S_OK;
300 })
301 .Get(),
302 &token_Added_);
303 if (FAILED(hr)) {
304 VLOG(1) << "add_Added failed: " << PrintHr(hr);
305 return false;
306 }
307
308 hr = watcher_->add_EnumerationCompleted(
309 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000310 [port_manager, task_service](IDeviceWatcher* watcher,
311 IInspectable* insp) {
312 task_service->PostBoundTask(
313 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000314 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000315 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700316
317 return S_OK;
318 })
319 .Get(),
320 &token_EnumerationCompleted_);
321 if (FAILED(hr)) {
322 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
323 return false;
324 }
325
326 hr = watcher_->add_Removed(
327 WRL::Callback<
328 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000329 [port_manager, task_service](IDeviceWatcher* watcher,
330 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700331 if (!update) {
332 VLOG(1) << "DeviceWatcher.Removed callback provides null "
333 "pointer, ignoring";
334 return S_OK;
335 }
336
shaochuane58f9c72016-08-30 22:27:08 -0700337 std::string dev_id = GetIdString(update);
338
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000339 task_service->PostBoundTask(
340 kComTaskRunner,
341 base::BindOnce(&MidiPortManager::OnRemoved,
342 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700343
344 return S_OK;
345 })
346 .Get(),
347 &token_Removed_);
348 if (FAILED(hr)) {
349 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
350 return false;
351 }
352
353 hr = watcher_->add_Stopped(
354 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
355 [](IDeviceWatcher* watcher, IInspectable* insp) {
356 // Placeholder, does nothing for now.
357 return S_OK;
358 })
359 .Get(),
360 &token_Stopped_);
361 if (FAILED(hr)) {
362 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
363 return false;
364 }
365
366 hr = watcher_->add_Updated(
367 WRL::Callback<
368 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
369 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
370 // TODO(shaochuan): Check for fields to be updated here.
371 return S_OK;
372 })
373 .Get(),
374 &token_Updated_);
375 if (FAILED(hr)) {
376 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
377 return false;
378 }
379
380 hr = watcher_->Start();
381 if (FAILED(hr)) {
382 VLOG(1) << "Start failed: " << PrintHr(hr);
383 return false;
384 }
385
386 is_initialized_ = true;
387 return true;
388 }
389
390 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000391 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700392
393 HRESULT hr;
394
395 for (const auto& entry : ports_)
396 RemovePortEventHandlers(entry.second.get());
397
398 if (token_Added_.value != kInvalidTokenValue) {
399 hr = watcher_->remove_Added(token_Added_);
400 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
401 token_Added_.value = kInvalidTokenValue;
402 }
403 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
404 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
405 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
406 << PrintHr(hr);
407 token_EnumerationCompleted_.value = kInvalidTokenValue;
408 }
409 if (token_Removed_.value != kInvalidTokenValue) {
410 hr = watcher_->remove_Removed(token_Removed_);
411 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
412 token_Removed_.value = kInvalidTokenValue;
413 }
414 if (token_Stopped_.value != kInvalidTokenValue) {
415 hr = watcher_->remove_Stopped(token_Stopped_);
416 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
417 token_Stopped_.value = kInvalidTokenValue;
418 }
419 if (token_Updated_.value != kInvalidTokenValue) {
420 hr = watcher_->remove_Updated(token_Updated_);
421 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
422 token_Updated_.value = kInvalidTokenValue;
423 }
424
425 if (is_initialized_) {
426 hr = watcher_->Stop();
427 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
428 is_initialized_ = false;
429 }
430 }
431
432 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
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 auto it = ports_.find(dev_id);
437 if (it == ports_.end())
438 return nullptr;
439 return it->second.get();
440 }
441
442 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000443 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700444 CHECK(is_initialized_);
445
446 return GetPortByDeviceId(port_ids_[port_index]);
447 }
448
449 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000450 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700451 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000452 MidiService* midi_service_;
453
454 // Points to the MidiManagerWinrt instance, which is safe to be accessed
455 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700456 MidiManagerWinrt* midi_manager_;
457
shaochuane58f9c72016-08-30 22:27:08 -0700458 private:
459 // DeviceWatcher callbacks:
460 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000461 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700462 CHECK(is_initialized_);
463
shaochuane58f9c72016-08-30 22:27:08 -0700464 port_names_[dev_id] = dev_name;
465
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000466 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700467 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700468 return;
shaochuane58f9c72016-08-30 22:27:08 -0700469
470 IAsyncOperation<RuntimeType*>* async_op;
471
shaochuan9ff63b82016-09-01 01:58:44 -0700472 HRESULT hr =
473 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700474 if (FAILED(hr)) {
475 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
476 return;
477 }
478
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000479 MidiPortManager* port_manager = this;
480 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700481
482 hr = async_op->put_Completed(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000483 WRL::Callback<
484 Win::Foundation::IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000485 [port_manager, task_service](
486 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700487 // A reference to |async_op| is kept in |async_ops_|, safe to pass
488 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000489 task_service->PostBoundTask(
490 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000491 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000492 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000493 base::Unretained(port_manager),
494 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700495
496 return S_OK;
497 })
498 .Get());
499 if (FAILED(hr)) {
500 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
501 return;
502 }
503
504 // Keep a reference to incompleted |async_op| for releasing later.
505 async_ops_.insert(async_op);
506 }
507
508 void OnEnumerationCompleted() {
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
512 if (async_ops_.empty())
513 midi_manager_->OnPortManagerReady();
514 else
515 enumeration_completed_not_ready_ = true;
516 }
517
518 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000519 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700520 CHECK(is_initialized_);
521
shaochuan110262b2016-08-31 02:15:16 -0700522 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
523 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700524 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
525 if (!port) {
526 VLOG(1) << "Removing non-existent port " << dev_id;
527 return;
528 }
529
toyoshimec2570a2016-10-21 02:15:27 -0700530 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700531
532 RemovePortEventHandlers(port);
533 port->handle = nullptr;
534 }
535
shaochuanc2894522016-09-20 01:10:50 -0700536 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000537 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700538 CHECK(is_initialized_);
539
shaochuanc2894522016-09-20 01:10:50 -0700540 InterfaceType* handle = nullptr;
541 HRESULT hr = async_op->GetResults(&handle);
542 if (FAILED(hr)) {
543 VLOG(1) << "GetResults failed: " << PrintHr(hr);
544 return;
545 }
546
547 // Manually release COM interface to completed |async_op|.
548 auto it = async_ops_.find(async_op);
549 CHECK(it != async_ops_.end());
550 (*it)->Release();
551 async_ops_.erase(it);
552
553 if (!handle) {
554 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
555 "ignoring";
556 return;
557 }
558
shaochuane58f9c72016-08-30 22:27:08 -0700559 EventRegistrationToken token = {kInvalidTokenValue};
560 if (!RegisterOnMessageReceived(handle, &token))
561 return;
562
563 std::string dev_id = GetDeviceIdString(handle);
564
565 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
566
567 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700568 std::string manufacturer = "Unknown", driver_version = "Unknown";
569 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
570
Adithya Srinivasan33252732018-10-17 15:59:40 +0000571 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
572 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700573
574 port = new MidiPort<InterfaceType>;
575 port->index = static_cast<uint32_t>(port_ids_.size());
576
577 ports_[dev_id].reset(port);
578 port_ids_.push_back(dev_id);
579 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700580 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700581 }
582
583 port->handle = handle;
584 port->token_MessageReceived = token;
585
shaochuane58f9c72016-08-30 22:27:08 -0700586 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
587 midi_manager_->OnPortManagerReady();
588 enumeration_completed_not_ready_ = false;
589 }
590 }
591
592 // Overrided by MidiInPortManager to listen to input ports.
593 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
594 EventRegistrationToken* p_token) {
595 return true;
596 }
597
598 // Overrided by MidiInPortManager to remove MessageReceived event handler.
599 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
600
601 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000602 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700603
604 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700605 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700606
shaochuane58f9c72016-08-30 22:27:08 -0700607 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000608 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700609
610 // DeviceWatcher instance and event registration tokens for unsubscribing
611 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000612 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700613 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
614 token_EnumerationCompleted_ = {kInvalidTokenValue},
615 token_Removed_ = {kInvalidTokenValue},
616 token_Stopped_ = {kInvalidTokenValue},
617 token_Updated_ = {kInvalidTokenValue};
618
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000619 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700620 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
621 ports_;
622 std::vector<std::string> port_ids_;
623 std::unordered_map<std::string, std::string> port_names_;
624
625 // Keeps AsyncOperation references before the operation completes. Note that
626 // raw pointers are used here and the COM interfaces should be released
627 // manually.
628 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
629
630 // Set when device enumeration is completed but OnPortManagerReady() is not
631 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
632 // In such cases, OnPortManagerReady() will be called in
633 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
634 bool enumeration_completed_not_ready_ = false;
635
636 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000637 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700638 bool is_initialized_ = false;
639};
640
641class MidiManagerWinrt::MidiInPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000642 : public MidiPortManager<Win::Devices::Midi::IMidiInPort,
643 Win::Devices::Midi::MidiInPort,
644 Win::Devices::Midi::IMidiInPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700645 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
646 public:
647 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000648 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700649
650 private:
651 // MidiPortManager overrides:
Peter Kasting6e8c8472019-09-20 17:55:55 +0000652 bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle,
shaochuane58f9c72016-08-30 22:27:08 -0700653 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000654 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700655
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000656 MidiInPortManager* port_manager = this;
657 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700658
659 HRESULT hr = handle->add_MessageReceived(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000660 WRL::Callback<ITypedEventHandler<
661 Win::Devices::Midi::MidiInPort*,
662 Win::Devices::Midi::MidiMessageReceivedEventArgs*>>(
663 [port_manager, task_service](
664 Win::Devices::Midi::IMidiInPort* handle,
665 Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700666 const base::TimeTicks now = base::TimeTicks::Now();
667
668 std::string dev_id = GetDeviceIdString(handle);
669
Peter Kasting6e8c8472019-09-20 17:55:55 +0000670 WRL::ComPtr<Win::Devices::Midi::IMidiMessage> message;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000671 HRESULT hr = args->get_Message(&message);
shaochuane58f9c72016-08-30 22:27:08 -0700672 if (FAILED(hr)) {
673 VLOG(1) << "get_Message failed: " << PrintHr(hr);
674 return hr;
675 }
676
Peter Kasting6e8c8472019-09-20 17:55:55 +0000677 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000678 hr = message->get_RawData(&buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700679 if (FAILED(hr)) {
680 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
681 return hr;
682 }
683
684 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000685 uint32_t data_length = 0;
686 hr = base::win::GetPointerToBufferData(
687 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700688 if (FAILED(hr))
689 return hr;
690
shaochuane58f9c72016-08-30 22:27:08 -0700691 std::vector<uint8_t> data(p_buffer_data,
692 p_buffer_data + data_length);
693
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000694 task_service->PostBoundTask(
695 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000696 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000697 base::Unretained(port_manager), dev_id, data,
698 now));
shaochuane58f9c72016-08-30 22:27:08 -0700699
700 return S_OK;
701 })
702 .Get(),
703 p_token);
704 if (FAILED(hr)) {
705 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
706 return false;
707 }
708
709 return true;
710 }
711
Peter Kasting6e8c8472019-09-20 17:55:55 +0000712 void RemovePortEventHandlers(
713 MidiPort<Win::Devices::Midi::IMidiInPort>* port) override {
shaochuane58f9c72016-08-30 22:27:08 -0700714 if (!(port->handle &&
715 port->token_MessageReceived.value != kInvalidTokenValue))
716 return;
717
718 HRESULT hr =
719 port->handle->remove_MessageReceived(port->token_MessageReceived);
720 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
721 port->token_MessageReceived.value = kInvalidTokenValue;
722 }
723
Adithya Srinivasan33252732018-10-17 15:59:40 +0000724 void AddPort(mojom::PortInfo info) final {
725 midi_manager_->AddInputPort(info);
726 }
shaochuane58f9c72016-08-30 22:27:08 -0700727
toyoshimec2570a2016-10-21 02:15:27 -0700728 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700729 midi_manager_->SetInputPortState(port_index, state);
730 }
731
shaochuane58f9c72016-08-30 22:27:08 -0700732 // Callback on receiving MIDI input message.
733 void OnMessageReceived(std::string dev_id,
734 std::vector<uint8_t> data,
735 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000736 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700737
Peter Kasting6e8c8472019-09-20 17:55:55 +0000738 MidiPort<Win::Devices::Midi::IMidiInPort>* port = GetPortByDeviceId(dev_id);
shaochuane58f9c72016-08-30 22:27:08 -0700739 CHECK(port);
740
741 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
742 }
743
shaochuane58f9c72016-08-30 22:27:08 -0700744 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
745};
746
747class MidiManagerWinrt::MidiOutPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000748 : public MidiPortManager<Win::Devices::Midi::IMidiOutPort,
749 Win::Devices::Midi::IMidiOutPort,
750 Win::Devices::Midi::IMidiOutPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700751 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
752 public:
753 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000754 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700755
756 private:
757 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000758 void AddPort(mojom::PortInfo info) final {
759 midi_manager_->AddOutputPort(info);
760 }
shaochuane58f9c72016-08-30 22:27:08 -0700761
toyoshimec2570a2016-10-21 02:15:27 -0700762 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700763 midi_manager_->SetOutputPortState(port_index, state);
764 }
765
shaochuane58f9c72016-08-30 22:27:08 -0700766 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
767};
768
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000769namespace {
770
771// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
772// instance destruction.
773void FinalizeOnComRunner(
774 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
775 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
776 if (port_manager_in)
777 port_manager_in->StopWatcher();
778
779 if (port_manager_out)
780 port_manager_out->StopWatcher();
781}
782
783} // namespace
784
toyoshimf4d61522017-02-10 02:03:32 -0800785MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000786 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700787
788MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000789 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
790 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000791 if (!service()->task_service()->UnbindInstance())
792 return;
shaochuane58f9c72016-08-30 22:27:08 -0700793
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000794 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000795 service()->task_service()->PostStaticTask(
796 kComTaskRunner,
797 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
798 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700799}
800
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000801void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000802 if (!service()->task_service()->BindInstance())
803 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000804
805 service()->task_service()->PostBoundTask(
806 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
807 base::Unretained(this)));
808}
809
shaochuane58f9c72016-08-30 22:27:08 -0700810void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
811 uint32_t port_index,
812 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000813 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000814 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
815 service()->task_service()->PostBoundDelayedTask(
816 kComTaskRunner,
817 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
818 port_index, data),
819 delay);
820 service()->task_service()->PostBoundDelayedTask(
821 kComTaskRunner,
822 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
823 base::Unretained(this), client, data.size()),
824 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700825}
826
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000827void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700828 base::AutoLock auto_lock(lazy_init_member_lock_);
829
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000830 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
831
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000832 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
833 ScopedHString::ResolveCoreWinRTStringDelayload();
834 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000835 service()->task_service()->PostBoundTask(
836 kDefaultTaskRunner,
837 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
838 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700839 return;
840 }
841
shaochuane58f9c72016-08-30 22:27:08 -0700842 port_manager_in_.reset(new MidiInPortManager(this));
843 port_manager_out_.reset(new MidiOutPortManager(this));
844
shaochuane58f9c72016-08-30 22:27:08 -0700845 if (!(port_manager_in_->StartWatcher() &&
846 port_manager_out_->StartWatcher())) {
847 port_manager_in_->StopWatcher();
848 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000849 service()->task_service()->PostBoundTask(
850 kDefaultTaskRunner,
851 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
852 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700853 }
854}
855
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000856void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700857 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000858 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700859
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000860 base::AutoLock auto_lock(lazy_init_member_lock_);
Peter Kasting6e8c8472019-09-20 17:55:55 +0000861 MidiPort<Win::Devices::Midi::IMidiOutPort>* port =
862 port_manager_out_->GetPortByIndex(port_index);
shaochuane58f9c72016-08-30 22:27:08 -0700863 if (!(port && port->handle)) {
864 VLOG(1) << "Port not available: " << port_index;
865 return;
866 }
867
Peter Kasting6e8c8472019-09-20 17:55:55 +0000868 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000869 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000870 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700871 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000872 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700873 return;
874 }
875
robliao3566d1a2017-04-18 17:28:09 -0700876 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700877 if (FAILED(hr)) {
878 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
879 return;
880 }
881}
882
883void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000884 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700885 DCHECK(port_manager_ready_count_ < 2);
886
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000887 if (++port_manager_ready_count_ == 2) {
888 service()->task_service()->PostBoundTask(
889 kDefaultTaskRunner,
890 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
891 base::Unretained(this), Result::OK));
892 }
shaochuane58f9c72016-08-30 22:27:08 -0700893}
894
shaochuane58f9c72016-08-30 22:27:08 -0700895} // namespace midi