blob: 891e1d22a60ef0fc68481e0ea302477ea40c771b [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
Xiaohan Wangfd3e0aa2020-02-22 04:08:43 +000057// Alias for printing HRESULT.
58const auto PrintHr = logging::SystemErrorCodeToString;
59
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000060enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000061 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000062 kComTaskRunner
63};
64
shaochuane58f9c72016-08-30 22:27:08 -070065template <typename T>
66std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070067 HSTRING result;
68 HRESULT hr = obj->get_Id(&result);
69 if (FAILED(hr)) {
70 VLOG(1) << "get_Id failed: " << PrintHr(hr);
71 return std::string();
72 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000073 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070074}
75
76template <typename T>
77std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070078 HSTRING result;
79 HRESULT hr = obj->get_DeviceId(&result);
80 if (FAILED(hr)) {
81 VLOG(1) << "get_DeviceId 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
87std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070088 HSTRING result;
89 HRESULT hr = info->get_Name(&result);
90 if (FAILED(hr)) {
91 VLOG(1) << "get_Name failed: " << PrintHr(hr);
92 return std::string();
93 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000094 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070095}
96
shaochuan110262b2016-08-31 02:15:16 -070097// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
98// instance.
99bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Peter Kasting6e8c8472019-09-20 17:55:55 +0000100 WRL::ComPtr<Win::Devices::Midi::IMidiSynthesizerStatics>
101 midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000102 HRESULT hr =
Peter Kasting6e8c8472019-09-20 17:55:55 +0000103 GetActivationFactory<Win::Devices::Midi::IMidiSynthesizerStatics,
junweifua8cea852017-10-17 06:21:16 +0000104 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
105 &midi_synthesizer_statics);
106 if (FAILED(hr)) {
107 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
108 return false;
109 }
shaochuan110262b2016-08-31 02:15:16 -0700110 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000111 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700112 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
113 return result != FALSE;
114}
115
shaochuan4eff30e2016-09-09 01:24:14 -0700116void GetDevPropString(DEVINST handle,
117 const DEVPROPKEY* devprop_key,
118 std::string* out) {
119 DEVPROPTYPE devprop_type;
120 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700121
shaochuan4eff30e2016-09-09 01:24:14 -0700122 // Retrieve |buffer_size| and allocate buffer later for receiving data.
123 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
124 nullptr, &buffer_size, 0);
125 if (cr != CR_BUFFER_SMALL) {
126 // Here we print error codes in hex instead of using PrintHr() with
127 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
128 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
129 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
130 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700131 }
shaochuan4eff30e2016-09-09 01:24:14 -0700132 if (devprop_type != DEVPROP_TYPE_STRING) {
133 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
134 << "expected DEVPROP_TYPE_STRING";
135 return;
136 }
shaochuan17bc4a02016-09-06 01:42:12 -0700137
shaochuan4eff30e2016-09-09 01:24:14 -0700138 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
139
140 // Receive property data.
141 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
142 &buffer_size, 0);
143 if (cr != CR_SUCCESS)
144 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
145 else
146 *out = base::WideToUTF8(reinterpret_cast<base::char16*>(buffer.get()));
147}
shaochuan17bc4a02016-09-06 01:42:12 -0700148
149// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700150// device driver through PnP Configuration Manager, given device (interface) ID
151// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
152// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700153//
154// Device instance ID is extracted from device (interface) ID provided by WinRT
155// APIs, for example from the following interface ID:
156// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
157// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700158//
159// However the extracted device instance ID represent a "software device"
160// provided by Microsoft, which is an interface on top of the hardware for each
161// input/output port. Therefore we further locate its parent device, which is
162// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700163void GetDriverInfoFromDeviceId(const std::string& dev_id,
164 std::string* out_manufacturer,
165 std::string* out_driver_version) {
166 base::string16 dev_instance_id =
167 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
168 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
169
shaochuan4eff30e2016-09-09 01:24:14 -0700170 DEVINST dev_instance_handle;
171 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
172 CM_LOCATE_DEVNODE_NORMAL);
173 if (cr != CR_SUCCESS) {
174 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700175 return;
176 }
177
shaochuan4eff30e2016-09-09 01:24:14 -0700178 DEVINST parent_handle;
179 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
180 if (cr != CR_SUCCESS) {
181 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700182 return;
183 }
184
shaochuan4eff30e2016-09-09 01:24:14 -0700185 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
186 out_manufacturer);
187 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
188 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700189}
190
shaochuane58f9c72016-08-30 22:27:08 -0700191// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
192const int64_t kInvalidTokenValue = 0;
193
194template <typename InterfaceType>
195struct MidiPort {
196 MidiPort() = default;
197
198 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000199 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700200 EventRegistrationToken token_MessageReceived;
201
202 private:
203 DISALLOW_COPY_AND_ASSIGN(MidiPort);
204};
205
206} // namespace
207
208template <typename InterfaceType,
209 typename RuntimeType,
210 typename StaticsInterfaceType,
211 base::char16 const* runtime_class_id>
212class MidiManagerWinrt::MidiPortManager {
213 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000214 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700215 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000216 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700217
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000218 virtual ~MidiPortManager() {
219 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
220 }
shaochuane58f9c72016-08-30 22:27:08 -0700221
222 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000223 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700224
junweifua8cea852017-10-17 06:21:16 +0000225 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
226 &midi_port_statics_);
227 if (FAILED(hr)) {
228 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700229 return false;
junweifua8cea852017-10-17 06:21:16 +0000230 }
shaochuane58f9c72016-08-30 22:27:08 -0700231
232 HSTRING device_selector = nullptr;
233 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
234 if (FAILED(hr)) {
235 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
236 return false;
237 }
238
Peter Kasting6e8c8472019-09-20 17:55:55 +0000239 WRL::ComPtr<Win::Devices::Enumeration::IDeviceInformationStatics>
240 dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000241 hr = GetActivationFactory<
Peter Kasting6e8c8472019-09-20 17:55:55 +0000242 Win::Devices::Enumeration::IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000243 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
244 &dev_info_statics);
245 if (FAILED(hr)) {
246 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700247 return false;
junweifua8cea852017-10-17 06:21:16 +0000248 }
shaochuane58f9c72016-08-30 22:27:08 -0700249
Xiaohan Wangf2832912020-02-21 18:38:02 +0000250 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector, &watcher_);
shaochuane58f9c72016-08-30 22:27:08 -0700251 if (FAILED(hr)) {
252 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
253 return false;
254 }
255
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000256 // Register callbacks to WinRT that post state-modifying tasks back to
257 // kComTaskRunner. All posted tasks run only during the MidiPortManager
258 // instance is alive. This is ensured by MidiManagerWinrt by calling
259 // UnbindInstance() before destructing any MidiPortManager instance. Thus
260 // we can handle raw pointers safely in the following blocks.
261 MidiPortManager* port_manager = this;
262 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700263
264 hr = watcher_->add_Added(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000265 WRL::Callback<ITypedEventHandler<
266 DeviceWatcher*, Win::Devices::Enumeration::DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000267 [port_manager, task_service](IDeviceWatcher* watcher,
268 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700269 if (!info) {
270 VLOG(1) << "DeviceWatcher.Added callback provides null "
271 "pointer, ignoring";
272 return S_OK;
273 }
274
shaochuan110262b2016-08-31 02:15:16 -0700275 // Disable Microsoft GS Wavetable Synth due to security reasons.
276 // http://crbug.com/499279
277 if (IsMicrosoftSynthesizer(info))
278 return S_OK;
279
shaochuane58f9c72016-08-30 22:27:08 -0700280 std::string dev_id = GetIdString(info),
281 dev_name = GetNameString(info);
282
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000283 task_service->PostBoundTask(
284 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
285 base::Unretained(port_manager),
286 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700287
288 return S_OK;
289 })
290 .Get(),
291 &token_Added_);
292 if (FAILED(hr)) {
293 VLOG(1) << "add_Added failed: " << PrintHr(hr);
294 return false;
295 }
296
297 hr = watcher_->add_EnumerationCompleted(
298 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000299 [port_manager, task_service](IDeviceWatcher* watcher,
300 IInspectable* insp) {
301 task_service->PostBoundTask(
302 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000303 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000304 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700305
306 return S_OK;
307 })
308 .Get(),
309 &token_EnumerationCompleted_);
310 if (FAILED(hr)) {
311 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
312 return false;
313 }
314
315 hr = watcher_->add_Removed(
316 WRL::Callback<
317 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000318 [port_manager, task_service](IDeviceWatcher* watcher,
319 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700320 if (!update) {
321 VLOG(1) << "DeviceWatcher.Removed callback provides null "
322 "pointer, ignoring";
323 return S_OK;
324 }
325
shaochuane58f9c72016-08-30 22:27:08 -0700326 std::string dev_id = GetIdString(update);
327
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000328 task_service->PostBoundTask(
329 kComTaskRunner,
330 base::BindOnce(&MidiPortManager::OnRemoved,
331 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700332
333 return S_OK;
334 })
335 .Get(),
336 &token_Removed_);
337 if (FAILED(hr)) {
338 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
339 return false;
340 }
341
342 hr = watcher_->add_Stopped(
343 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
344 [](IDeviceWatcher* watcher, IInspectable* insp) {
345 // Placeholder, does nothing for now.
346 return S_OK;
347 })
348 .Get(),
349 &token_Stopped_);
350 if (FAILED(hr)) {
351 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
352 return false;
353 }
354
355 hr = watcher_->add_Updated(
356 WRL::Callback<
357 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
358 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
359 // TODO(shaochuan): Check for fields to be updated here.
360 return S_OK;
361 })
362 .Get(),
363 &token_Updated_);
364 if (FAILED(hr)) {
365 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
366 return false;
367 }
368
369 hr = watcher_->Start();
370 if (FAILED(hr)) {
371 VLOG(1) << "Start failed: " << PrintHr(hr);
372 return false;
373 }
374
375 is_initialized_ = true;
376 return true;
377 }
378
379 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000380 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700381
382 HRESULT hr;
383
384 for (const auto& entry : ports_)
385 RemovePortEventHandlers(entry.second.get());
386
387 if (token_Added_.value != kInvalidTokenValue) {
388 hr = watcher_->remove_Added(token_Added_);
389 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
390 token_Added_.value = kInvalidTokenValue;
391 }
392 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
393 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
394 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
395 << PrintHr(hr);
396 token_EnumerationCompleted_.value = kInvalidTokenValue;
397 }
398 if (token_Removed_.value != kInvalidTokenValue) {
399 hr = watcher_->remove_Removed(token_Removed_);
400 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
401 token_Removed_.value = kInvalidTokenValue;
402 }
403 if (token_Stopped_.value != kInvalidTokenValue) {
404 hr = watcher_->remove_Stopped(token_Stopped_);
405 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
406 token_Stopped_.value = kInvalidTokenValue;
407 }
408 if (token_Updated_.value != kInvalidTokenValue) {
409 hr = watcher_->remove_Updated(token_Updated_);
410 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
411 token_Updated_.value = kInvalidTokenValue;
412 }
413
414 if (is_initialized_) {
415 hr = watcher_->Stop();
416 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
417 is_initialized_ = false;
418 }
419 }
420
421 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000422 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700423 CHECK(is_initialized_);
424
425 auto it = ports_.find(dev_id);
426 if (it == ports_.end())
427 return nullptr;
428 return it->second.get();
429 }
430
431 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000432 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700433 CHECK(is_initialized_);
434
435 return GetPortByDeviceId(port_ids_[port_index]);
436 }
437
438 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000439 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700440 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000441 MidiService* midi_service_;
442
443 // Points to the MidiManagerWinrt instance, which is safe to be accessed
444 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700445 MidiManagerWinrt* midi_manager_;
446
shaochuane58f9c72016-08-30 22:27:08 -0700447 private:
448 // DeviceWatcher callbacks:
449 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000450 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700451 CHECK(is_initialized_);
452
shaochuane58f9c72016-08-30 22:27:08 -0700453 port_names_[dev_id] = dev_name;
454
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000455 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700456 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700457 return;
shaochuane58f9c72016-08-30 22:27:08 -0700458
459 IAsyncOperation<RuntimeType*>* async_op;
460
shaochuan9ff63b82016-09-01 01:58:44 -0700461 HRESULT hr =
462 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700463 if (FAILED(hr)) {
464 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
465 return;
466 }
467
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000468 MidiPortManager* port_manager = this;
469 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700470
471 hr = async_op->put_Completed(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000472 WRL::Callback<
473 Win::Foundation::IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000474 [port_manager, task_service](
475 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700476 // A reference to |async_op| is kept in |async_ops_|, safe to pass
477 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000478 task_service->PostBoundTask(
479 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000480 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000481 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000482 base::Unretained(port_manager),
483 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700484
485 return S_OK;
486 })
487 .Get());
488 if (FAILED(hr)) {
489 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
490 return;
491 }
492
493 // Keep a reference to incompleted |async_op| for releasing later.
494 async_ops_.insert(async_op);
495 }
496
497 void OnEnumerationCompleted() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000498 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700499 CHECK(is_initialized_);
500
501 if (async_ops_.empty())
502 midi_manager_->OnPortManagerReady();
503 else
504 enumeration_completed_not_ready_ = true;
505 }
506
507 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000508 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700509 CHECK(is_initialized_);
510
shaochuan110262b2016-08-31 02:15:16 -0700511 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
512 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700513 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
514 if (!port) {
515 VLOG(1) << "Removing non-existent port " << dev_id;
516 return;
517 }
518
toyoshimec2570a2016-10-21 02:15:27 -0700519 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700520
521 RemovePortEventHandlers(port);
522 port->handle = nullptr;
523 }
524
shaochuanc2894522016-09-20 01:10:50 -0700525 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000526 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700527 CHECK(is_initialized_);
528
shaochuanc2894522016-09-20 01:10:50 -0700529 InterfaceType* handle = nullptr;
530 HRESULT hr = async_op->GetResults(&handle);
531 if (FAILED(hr)) {
532 VLOG(1) << "GetResults failed: " << PrintHr(hr);
533 return;
534 }
535
536 // Manually release COM interface to completed |async_op|.
537 auto it = async_ops_.find(async_op);
538 CHECK(it != async_ops_.end());
539 (*it)->Release();
540 async_ops_.erase(it);
541
542 if (!handle) {
543 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
544 "ignoring";
545 return;
546 }
547
shaochuane58f9c72016-08-30 22:27:08 -0700548 EventRegistrationToken token = {kInvalidTokenValue};
549 if (!RegisterOnMessageReceived(handle, &token))
550 return;
551
552 std::string dev_id = GetDeviceIdString(handle);
553
554 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
555
556 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700557 std::string manufacturer = "Unknown", driver_version = "Unknown";
558 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
559
Adithya Srinivasan33252732018-10-17 15:59:40 +0000560 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
561 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700562
563 port = new MidiPort<InterfaceType>;
564 port->index = static_cast<uint32_t>(port_ids_.size());
565
566 ports_[dev_id].reset(port);
567 port_ids_.push_back(dev_id);
568 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700569 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700570 }
571
572 port->handle = handle;
573 port->token_MessageReceived = token;
574
shaochuane58f9c72016-08-30 22:27:08 -0700575 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
576 midi_manager_->OnPortManagerReady();
577 enumeration_completed_not_ready_ = false;
578 }
579 }
580
581 // Overrided by MidiInPortManager to listen to input ports.
582 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
583 EventRegistrationToken* p_token) {
584 return true;
585 }
586
587 // Overrided by MidiInPortManager to remove MessageReceived event handler.
588 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
589
590 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000591 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700592
593 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700594 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700595
shaochuane58f9c72016-08-30 22:27:08 -0700596 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000597 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700598
599 // DeviceWatcher instance and event registration tokens for unsubscribing
600 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000601 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700602 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
603 token_EnumerationCompleted_ = {kInvalidTokenValue},
604 token_Removed_ = {kInvalidTokenValue},
605 token_Stopped_ = {kInvalidTokenValue},
606 token_Updated_ = {kInvalidTokenValue};
607
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000608 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700609 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
610 ports_;
611 std::vector<std::string> port_ids_;
612 std::unordered_map<std::string, std::string> port_names_;
613
614 // Keeps AsyncOperation references before the operation completes. Note that
615 // raw pointers are used here and the COM interfaces should be released
616 // manually.
617 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
618
619 // Set when device enumeration is completed but OnPortManagerReady() is not
620 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
621 // In such cases, OnPortManagerReady() will be called in
622 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
623 bool enumeration_completed_not_ready_ = false;
624
625 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000626 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700627 bool is_initialized_ = false;
628};
629
630class MidiManagerWinrt::MidiInPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000631 : public MidiPortManager<Win::Devices::Midi::IMidiInPort,
632 Win::Devices::Midi::MidiInPort,
633 Win::Devices::Midi::IMidiInPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700634 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
635 public:
636 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000637 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700638
639 private:
640 // MidiPortManager overrides:
Peter Kasting6e8c8472019-09-20 17:55:55 +0000641 bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle,
shaochuane58f9c72016-08-30 22:27:08 -0700642 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000643 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700644
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000645 MidiInPortManager* port_manager = this;
646 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700647
648 HRESULT hr = handle->add_MessageReceived(
Peter Kasting6e8c8472019-09-20 17:55:55 +0000649 WRL::Callback<ITypedEventHandler<
650 Win::Devices::Midi::MidiInPort*,
651 Win::Devices::Midi::MidiMessageReceivedEventArgs*>>(
652 [port_manager, task_service](
653 Win::Devices::Midi::IMidiInPort* handle,
654 Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700655 const base::TimeTicks now = base::TimeTicks::Now();
656
657 std::string dev_id = GetDeviceIdString(handle);
658
Peter Kasting6e8c8472019-09-20 17:55:55 +0000659 WRL::ComPtr<Win::Devices::Midi::IMidiMessage> message;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000660 HRESULT hr = args->get_Message(&message);
shaochuane58f9c72016-08-30 22:27:08 -0700661 if (FAILED(hr)) {
662 VLOG(1) << "get_Message failed: " << PrintHr(hr);
663 return hr;
664 }
665
Peter Kasting6e8c8472019-09-20 17:55:55 +0000666 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
Xiaohan Wangf2832912020-02-21 18:38:02 +0000667 hr = message->get_RawData(&buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700668 if (FAILED(hr)) {
669 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
670 return hr;
671 }
672
673 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000674 uint32_t data_length = 0;
675 hr = base::win::GetPointerToBufferData(
676 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700677 if (FAILED(hr))
678 return hr;
679
shaochuane58f9c72016-08-30 22:27:08 -0700680 std::vector<uint8_t> data(p_buffer_data,
681 p_buffer_data + data_length);
682
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000683 task_service->PostBoundTask(
684 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000685 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000686 base::Unretained(port_manager), dev_id, data,
687 now));
shaochuane58f9c72016-08-30 22:27:08 -0700688
689 return S_OK;
690 })
691 .Get(),
692 p_token);
693 if (FAILED(hr)) {
694 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
695 return false;
696 }
697
698 return true;
699 }
700
Peter Kasting6e8c8472019-09-20 17:55:55 +0000701 void RemovePortEventHandlers(
702 MidiPort<Win::Devices::Midi::IMidiInPort>* port) override {
shaochuane58f9c72016-08-30 22:27:08 -0700703 if (!(port->handle &&
704 port->token_MessageReceived.value != kInvalidTokenValue))
705 return;
706
707 HRESULT hr =
708 port->handle->remove_MessageReceived(port->token_MessageReceived);
709 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
710 port->token_MessageReceived.value = kInvalidTokenValue;
711 }
712
Adithya Srinivasan33252732018-10-17 15:59:40 +0000713 void AddPort(mojom::PortInfo info) final {
714 midi_manager_->AddInputPort(info);
715 }
shaochuane58f9c72016-08-30 22:27:08 -0700716
toyoshimec2570a2016-10-21 02:15:27 -0700717 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700718 midi_manager_->SetInputPortState(port_index, state);
719 }
720
shaochuane58f9c72016-08-30 22:27:08 -0700721 // Callback on receiving MIDI input message.
722 void OnMessageReceived(std::string dev_id,
723 std::vector<uint8_t> data,
724 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000725 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700726
Peter Kasting6e8c8472019-09-20 17:55:55 +0000727 MidiPort<Win::Devices::Midi::IMidiInPort>* port = GetPortByDeviceId(dev_id);
shaochuane58f9c72016-08-30 22:27:08 -0700728 CHECK(port);
729
730 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
731 }
732
shaochuane58f9c72016-08-30 22:27:08 -0700733 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
734};
735
736class MidiManagerWinrt::MidiOutPortManager final
Peter Kasting6e8c8472019-09-20 17:55:55 +0000737 : public MidiPortManager<Win::Devices::Midi::IMidiOutPort,
738 Win::Devices::Midi::IMidiOutPort,
739 Win::Devices::Midi::IMidiOutPortStatics,
shaochuane58f9c72016-08-30 22:27:08 -0700740 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
741 public:
742 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000743 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700744
745 private:
746 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000747 void AddPort(mojom::PortInfo info) final {
748 midi_manager_->AddOutputPort(info);
749 }
shaochuane58f9c72016-08-30 22:27:08 -0700750
toyoshimec2570a2016-10-21 02:15:27 -0700751 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700752 midi_manager_->SetOutputPortState(port_index, state);
753 }
754
shaochuane58f9c72016-08-30 22:27:08 -0700755 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
756};
757
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000758namespace {
759
760// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
761// instance destruction.
762void FinalizeOnComRunner(
763 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
764 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
765 if (port_manager_in)
766 port_manager_in->StopWatcher();
767
768 if (port_manager_out)
769 port_manager_out->StopWatcher();
770}
771
772} // namespace
773
toyoshimf4d61522017-02-10 02:03:32 -0800774MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000775 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700776
777MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000778 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
779 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000780 if (!service()->task_service()->UnbindInstance())
781 return;
shaochuane58f9c72016-08-30 22:27:08 -0700782
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000783 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000784 service()->task_service()->PostStaticTask(
785 kComTaskRunner,
786 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
787 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700788}
789
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000790void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000791 if (!service()->task_service()->BindInstance())
792 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000793
794 service()->task_service()->PostBoundTask(
795 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
796 base::Unretained(this)));
797}
798
shaochuane58f9c72016-08-30 22:27:08 -0700799void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
800 uint32_t port_index,
801 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000802 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000803 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
804 service()->task_service()->PostBoundDelayedTask(
805 kComTaskRunner,
806 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
807 port_index, data),
808 delay);
809 service()->task_service()->PostBoundDelayedTask(
810 kComTaskRunner,
811 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
812 base::Unretained(this), client, data.size()),
813 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700814}
815
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000816void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700817 base::AutoLock auto_lock(lazy_init_member_lock_);
818
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000819 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
820
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000821 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
822 ScopedHString::ResolveCoreWinRTStringDelayload();
823 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000824 service()->task_service()->PostBoundTask(
825 kDefaultTaskRunner,
826 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
827 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700828 return;
829 }
830
shaochuane58f9c72016-08-30 22:27:08 -0700831 port_manager_in_.reset(new MidiInPortManager(this));
832 port_manager_out_.reset(new MidiOutPortManager(this));
833
shaochuane58f9c72016-08-30 22:27:08 -0700834 if (!(port_manager_in_->StartWatcher() &&
835 port_manager_out_->StartWatcher())) {
836 port_manager_in_->StopWatcher();
837 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000838 service()->task_service()->PostBoundTask(
839 kDefaultTaskRunner,
840 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
841 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700842 }
843}
844
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000845void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700846 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000847 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700848
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000849 base::AutoLock auto_lock(lazy_init_member_lock_);
Peter Kasting6e8c8472019-09-20 17:55:55 +0000850 MidiPort<Win::Devices::Midi::IMidiOutPort>* port =
851 port_manager_out_->GetPortByIndex(port_index);
shaochuane58f9c72016-08-30 22:27:08 -0700852 if (!(port && port->handle)) {
853 VLOG(1) << "Port not available: " << port_index;
854 return;
855 }
856
Peter Kasting6e8c8472019-09-20 17:55:55 +0000857 WRL::ComPtr<Win::Storage::Streams::IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000858 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000859 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700860 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000861 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700862 return;
863 }
864
robliao3566d1a2017-04-18 17:28:09 -0700865 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700866 if (FAILED(hr)) {
867 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
868 return;
869 }
870}
871
872void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000873 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700874 DCHECK(port_manager_ready_count_ < 2);
875
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000876 if (++port_manager_ready_count_ == 2) {
877 service()->task_service()->PostBoundTask(
878 kDefaultTaskRunner,
879 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
880 base::Unretained(this), Result::OK));
881 }
shaochuane58f9c72016-08-30 22:27:08 -0700882}
883
shaochuane58f9c72016-08-30 22:27:08 -0700884} // namespace midi