blob: d89b59d92b556680d3248818608a8d4e5ec2ffb1 [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;
43
44using namespace ABI::Windows::Devices::Enumeration;
45using namespace ABI::Windows::Devices::Midi;
46using namespace ABI::Windows::Foundation;
47using namespace ABI::Windows::Storage::Streams;
48
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000049using base::win::ScopedHString;
junweifua8cea852017-10-17 06:21:16 +000050using base::win::GetActivationFactory;
toyoshimec2570a2016-10-21 02:15:27 -070051using mojom::PortState;
toyoshim2f3a48f2016-10-17 01:54:13 -070052using mojom::Result;
shaochuane58f9c72016-08-30 22:27:08 -070053
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000054enum {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +000055 kDefaultTaskRunner = TaskService::kDefaultRunnerId,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +000056 kComTaskRunner
57};
58
shaochuane58f9c72016-08-30 22:27:08 -070059// Helpers for printing HRESULTs.
60struct PrintHr {
61 PrintHr(HRESULT hr) : hr(hr) {}
62 HRESULT hr;
63};
64
65std::ostream& operator<<(std::ostream& os, const PrintHr& phr) {
66 std::ios_base::fmtflags ff = os.flags();
67 os << _com_error(phr.hr).ErrorMessage() << " (0x" << std::hex
68 << std::uppercase << std::setfill('0') << std::setw(8) << phr.hr << ")";
69 os.flags(ff);
70 return os;
71}
72
shaochuane58f9c72016-08-30 22:27:08 -070073template <typename T>
74std::string GetIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070075 HSTRING result;
76 HRESULT hr = obj->get_Id(&result);
77 if (FAILED(hr)) {
78 VLOG(1) << "get_Id failed: " << PrintHr(hr);
79 return std::string();
80 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000081 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070082}
83
84template <typename T>
85std::string GetDeviceIdString(T* obj) {
shaochuan80f1fba2016-09-01 20:44:51 -070086 HSTRING result;
87 HRESULT hr = obj->get_DeviceId(&result);
88 if (FAILED(hr)) {
89 VLOG(1) << "get_DeviceId failed: " << PrintHr(hr);
90 return std::string();
91 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +000092 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -070093}
94
95std::string GetNameString(IDeviceInformation* info) {
shaochuan80f1fba2016-09-01 20:44:51 -070096 HSTRING result;
97 HRESULT hr = info->get_Name(&result);
98 if (FAILED(hr)) {
99 VLOG(1) << "get_Name failed: " << PrintHr(hr);
100 return std::string();
101 }
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000102 return ScopedHString(result).GetAsUTF8();
shaochuane58f9c72016-08-30 22:27:08 -0700103}
104
shaochuan110262b2016-08-31 02:15:16 -0700105// Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth
106// instance.
107bool IsMicrosoftSynthesizer(IDeviceInformation* info) {
Robert Liao4a680c32017-10-18 19:10:01 +0000108 WRL::ComPtr<IMidiSynthesizerStatics> midi_synthesizer_statics;
junweifua8cea852017-10-17 06:21:16 +0000109 HRESULT hr =
110 GetActivationFactory<IMidiSynthesizerStatics,
111 RuntimeClass_Windows_Devices_Midi_MidiSynthesizer>(
112 &midi_synthesizer_statics);
113 if (FAILED(hr)) {
114 VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr);
115 return false;
116 }
shaochuan110262b2016-08-31 02:15:16 -0700117 boolean result = FALSE;
junweifua8cea852017-10-17 06:21:16 +0000118 hr = midi_synthesizer_statics->IsSynthesizer(info, &result);
shaochuan110262b2016-08-31 02:15:16 -0700119 VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr);
120 return result != FALSE;
121}
122
shaochuan4eff30e2016-09-09 01:24:14 -0700123void GetDevPropString(DEVINST handle,
124 const DEVPROPKEY* devprop_key,
125 std::string* out) {
126 DEVPROPTYPE devprop_type;
127 unsigned long buffer_size = 0;
shaochuan17bc4a02016-09-06 01:42:12 -0700128
shaochuan4eff30e2016-09-09 01:24:14 -0700129 // Retrieve |buffer_size| and allocate buffer later for receiving data.
130 CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type,
131 nullptr, &buffer_size, 0);
132 if (cr != CR_BUFFER_SMALL) {
133 // Here we print error codes in hex instead of using PrintHr() with
134 // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of
135 // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s.
136 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
137 return;
shaochuan17bc4a02016-09-06 01:42:12 -0700138 }
shaochuan4eff30e2016-09-09 01:24:14 -0700139 if (devprop_type != DEVPROP_TYPE_STRING) {
140 VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, "
141 << "expected DEVPROP_TYPE_STRING";
142 return;
143 }
shaochuan17bc4a02016-09-06 01:42:12 -0700144
shaochuan4eff30e2016-09-09 01:24:14 -0700145 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
146
147 // Receive property data.
148 cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(),
149 &buffer_size, 0);
150 if (cr != CR_SUCCESS)
151 VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr;
152 else
153 *out = base::WideToUTF8(reinterpret_cast<base::char16*>(buffer.get()));
154}
shaochuan17bc4a02016-09-06 01:42:12 -0700155
156// Retrieves manufacturer (provider) and version information of underlying
shaochuan4eff30e2016-09-09 01:24:14 -0700157// device driver through PnP Configuration Manager, given device (interface) ID
158// provided by WinRT. |out_manufacturer| and |out_driver_version| won't be
159// modified if retrieval fails.
shaochuan17bc4a02016-09-06 01:42:12 -0700160//
161// Device instance ID is extracted from device (interface) ID provided by WinRT
162// APIs, for example from the following interface ID:
163// \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b}
164// we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002
shaochuan4eff30e2016-09-09 01:24:14 -0700165//
166// However the extracted device instance ID represent a "software device"
167// provided by Microsoft, which is an interface on top of the hardware for each
168// input/output port. Therefore we further locate its parent device, which is
169// the actual hardware device, for driver information.
shaochuan17bc4a02016-09-06 01:42:12 -0700170void GetDriverInfoFromDeviceId(const std::string& dev_id,
171 std::string* out_manufacturer,
172 std::string* out_driver_version) {
173 base::string16 dev_instance_id =
174 base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43));
175 base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id);
176
shaochuan4eff30e2016-09-09 01:24:14 -0700177 DEVINST dev_instance_handle;
178 CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0],
179 CM_LOCATE_DEVNODE_NORMAL);
180 if (cr != CR_SUCCESS) {
181 VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700182 return;
183 }
184
shaochuan4eff30e2016-09-09 01:24:14 -0700185 DEVINST parent_handle;
186 cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0);
187 if (cr != CR_SUCCESS) {
188 VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr;
shaochuan17bc4a02016-09-06 01:42:12 -0700189 return;
190 }
191
shaochuan4eff30e2016-09-09 01:24:14 -0700192 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider,
193 out_manufacturer);
194 GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion,
195 out_driver_version);
shaochuan17bc4a02016-09-06 01:42:12 -0700196}
197
shaochuane58f9c72016-08-30 22:27:08 -0700198// Tokens with value = 0 are considered invalid (as in <wrl/event.h>).
199const int64_t kInvalidTokenValue = 0;
200
201template <typename InterfaceType>
202struct MidiPort {
203 MidiPort() = default;
204
205 uint32_t index;
Robert Liao4a680c32017-10-18 19:10:01 +0000206 WRL::ComPtr<InterfaceType> handle;
shaochuane58f9c72016-08-30 22:27:08 -0700207 EventRegistrationToken token_MessageReceived;
208
209 private:
210 DISALLOW_COPY_AND_ASSIGN(MidiPort);
211};
212
213} // namespace
214
215template <typename InterfaceType,
216 typename RuntimeType,
217 typename StaticsInterfaceType,
218 base::char16 const* runtime_class_id>
219class MidiManagerWinrt::MidiPortManager {
220 public:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000221 // MidiPortManager instances should be constructed on the kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700222 MidiPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000223 : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700224
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000225 virtual ~MidiPortManager() {
226 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
227 }
shaochuane58f9c72016-08-30 22:27:08 -0700228
229 bool StartWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000230 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700231
junweifua8cea852017-10-17 06:21:16 +0000232 HRESULT hr = GetActivationFactory<StaticsInterfaceType, runtime_class_id>(
233 &midi_port_statics_);
234 if (FAILED(hr)) {
235 VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700236 return false;
junweifua8cea852017-10-17 06:21:16 +0000237 }
shaochuane58f9c72016-08-30 22:27:08 -0700238
239 HSTRING device_selector = nullptr;
240 hr = midi_port_statics_->GetDeviceSelector(&device_selector);
241 if (FAILED(hr)) {
242 VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr);
243 return false;
244 }
245
Robert Liao4a680c32017-10-18 19:10:01 +0000246 WRL::ComPtr<IDeviceInformationStatics> dev_info_statics;
junweifua8cea852017-10-17 06:21:16 +0000247 hr = GetActivationFactory<
shaochuane58f9c72016-08-30 22:27:08 -0700248 IDeviceInformationStatics,
junweifua8cea852017-10-17 06:21:16 +0000249 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>(
250 &dev_info_statics);
251 if (FAILED(hr)) {
252 VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700253 return false;
junweifua8cea852017-10-17 06:21:16 +0000254 }
shaochuane58f9c72016-08-30 22:27:08 -0700255
256 hr = dev_info_statics->CreateWatcherAqsFilter(device_selector,
robliao8d08e692017-05-11 10:14:00 -0700257 watcher_.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700258 if (FAILED(hr)) {
259 VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr);
260 return false;
261 }
262
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000263 // Register callbacks to WinRT that post state-modifying tasks back to
264 // kComTaskRunner. All posted tasks run only during the MidiPortManager
265 // instance is alive. This is ensured by MidiManagerWinrt by calling
266 // UnbindInstance() before destructing any MidiPortManager instance. Thus
267 // we can handle raw pointers safely in the following blocks.
268 MidiPortManager* port_manager = this;
269 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700270
271 hr = watcher_->add_Added(
272 WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000273 [port_manager, task_service](IDeviceWatcher* watcher,
274 IDeviceInformation* info) {
shaochuanc2894522016-09-20 01:10:50 -0700275 if (!info) {
276 VLOG(1) << "DeviceWatcher.Added callback provides null "
277 "pointer, ignoring";
278 return S_OK;
279 }
280
shaochuan110262b2016-08-31 02:15:16 -0700281 // Disable Microsoft GS Wavetable Synth due to security reasons.
282 // http://crbug.com/499279
283 if (IsMicrosoftSynthesizer(info))
284 return S_OK;
285
shaochuane58f9c72016-08-30 22:27:08 -0700286 std::string dev_id = GetIdString(info),
287 dev_name = GetNameString(info);
288
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000289 task_service->PostBoundTask(
290 kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded,
291 base::Unretained(port_manager),
292 dev_id, dev_name));
shaochuane58f9c72016-08-30 22:27:08 -0700293
294 return S_OK;
295 })
296 .Get(),
297 &token_Added_);
298 if (FAILED(hr)) {
299 VLOG(1) << "add_Added failed: " << PrintHr(hr);
300 return false;
301 }
302
303 hr = watcher_->add_EnumerationCompleted(
304 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000305 [port_manager, task_service](IDeviceWatcher* watcher,
306 IInspectable* insp) {
307 task_service->PostBoundTask(
308 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000309 base::BindOnce(&MidiPortManager::OnEnumerationCompleted,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000310 base::Unretained(port_manager)));
shaochuane58f9c72016-08-30 22:27:08 -0700311
312 return S_OK;
313 })
314 .Get(),
315 &token_EnumerationCompleted_);
316 if (FAILED(hr)) {
317 VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr);
318 return false;
319 }
320
321 hr = watcher_->add_Removed(
322 WRL::Callback<
323 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000324 [port_manager, task_service](IDeviceWatcher* watcher,
325 IDeviceInformationUpdate* update) {
shaochuanc2894522016-09-20 01:10:50 -0700326 if (!update) {
327 VLOG(1) << "DeviceWatcher.Removed callback provides null "
328 "pointer, ignoring";
329 return S_OK;
330 }
331
shaochuane58f9c72016-08-30 22:27:08 -0700332 std::string dev_id = GetIdString(update);
333
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000334 task_service->PostBoundTask(
335 kComTaskRunner,
336 base::BindOnce(&MidiPortManager::OnRemoved,
337 base::Unretained(port_manager), dev_id));
shaochuane58f9c72016-08-30 22:27:08 -0700338
339 return S_OK;
340 })
341 .Get(),
342 &token_Removed_);
343 if (FAILED(hr)) {
344 VLOG(1) << "add_Removed failed: " << PrintHr(hr);
345 return false;
346 }
347
348 hr = watcher_->add_Stopped(
349 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
350 [](IDeviceWatcher* watcher, IInspectable* insp) {
351 // Placeholder, does nothing for now.
352 return S_OK;
353 })
354 .Get(),
355 &token_Stopped_);
356 if (FAILED(hr)) {
357 VLOG(1) << "add_Stopped failed: " << PrintHr(hr);
358 return false;
359 }
360
361 hr = watcher_->add_Updated(
362 WRL::Callback<
363 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
364 [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
365 // TODO(shaochuan): Check for fields to be updated here.
366 return S_OK;
367 })
368 .Get(),
369 &token_Updated_);
370 if (FAILED(hr)) {
371 VLOG(1) << "add_Updated failed: " << PrintHr(hr);
372 return false;
373 }
374
375 hr = watcher_->Start();
376 if (FAILED(hr)) {
377 VLOG(1) << "Start failed: " << PrintHr(hr);
378 return false;
379 }
380
381 is_initialized_ = true;
382 return true;
383 }
384
385 void StopWatcher() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000386 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700387
388 HRESULT hr;
389
390 for (const auto& entry : ports_)
391 RemovePortEventHandlers(entry.second.get());
392
393 if (token_Added_.value != kInvalidTokenValue) {
394 hr = watcher_->remove_Added(token_Added_);
395 VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr);
396 token_Added_.value = kInvalidTokenValue;
397 }
398 if (token_EnumerationCompleted_.value != kInvalidTokenValue) {
399 hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_);
400 VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: "
401 << PrintHr(hr);
402 token_EnumerationCompleted_.value = kInvalidTokenValue;
403 }
404 if (token_Removed_.value != kInvalidTokenValue) {
405 hr = watcher_->remove_Removed(token_Removed_);
406 VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr);
407 token_Removed_.value = kInvalidTokenValue;
408 }
409 if (token_Stopped_.value != kInvalidTokenValue) {
410 hr = watcher_->remove_Stopped(token_Stopped_);
411 VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr);
412 token_Stopped_.value = kInvalidTokenValue;
413 }
414 if (token_Updated_.value != kInvalidTokenValue) {
415 hr = watcher_->remove_Updated(token_Updated_);
416 VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr);
417 token_Updated_.value = kInvalidTokenValue;
418 }
419
420 if (is_initialized_) {
421 hr = watcher_->Stop();
422 VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr);
423 is_initialized_ = false;
424 }
425 }
426
427 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000428 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700429 CHECK(is_initialized_);
430
431 auto it = ports_.find(dev_id);
432 if (it == ports_.end())
433 return nullptr;
434 return it->second.get();
435 }
436
437 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000438 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700439 CHECK(is_initialized_);
440
441 return GetPortByDeviceId(port_ids_[port_index]);
442 }
443
444 protected:
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000445 // Points to the MidiService instance, which is expected to outlive the
shaochuane58f9c72016-08-30 22:27:08 -0700446 // MidiPortManager instance.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000447 MidiService* midi_service_;
448
449 // Points to the MidiManagerWinrt instance, which is safe to be accessed
450 // from tasks that are invoked by TaskService.
shaochuane58f9c72016-08-30 22:27:08 -0700451 MidiManagerWinrt* midi_manager_;
452
shaochuane58f9c72016-08-30 22:27:08 -0700453 private:
454 // DeviceWatcher callbacks:
455 void OnAdded(std::string dev_id, std::string dev_name) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000456 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700457 CHECK(is_initialized_);
458
shaochuane58f9c72016-08-30 22:27:08 -0700459 port_names_[dev_id] = dev_name;
460
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000461 ScopedHString dev_id_hstring = ScopedHString::Create(dev_id);
shaochuan9ff63b82016-09-01 01:58:44 -0700462 if (!dev_id_hstring.is_valid())
shaochuane58f9c72016-08-30 22:27:08 -0700463 return;
shaochuane58f9c72016-08-30 22:27:08 -0700464
465 IAsyncOperation<RuntimeType*>* async_op;
466
shaochuan9ff63b82016-09-01 01:58:44 -0700467 HRESULT hr =
468 midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op);
shaochuane58f9c72016-08-30 22:27:08 -0700469 if (FAILED(hr)) {
470 VLOG(1) << "FromIdAsync failed: " << PrintHr(hr);
471 return;
472 }
473
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000474 MidiPortManager* port_manager = this;
475 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700476
477 hr = async_op->put_Completed(
478 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000479 [port_manager, task_service](
480 IAsyncOperation<RuntimeType*>* async_op, AsyncStatus status) {
shaochuane58f9c72016-08-30 22:27:08 -0700481 // A reference to |async_op| is kept in |async_ops_|, safe to pass
482 // outside.
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000483 task_service->PostBoundTask(
484 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000485 base::BindOnce(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000486 &MidiPortManager::OnCompletedGetPortFromIdAsync,
tzik9f09f552018-02-21 12:56:03 +0000487 base::Unretained(port_manager),
488 base::Unretained(async_op)));
shaochuane58f9c72016-08-30 22:27:08 -0700489
490 return S_OK;
491 })
492 .Get());
493 if (FAILED(hr)) {
494 VLOG(1) << "put_Completed failed: " << PrintHr(hr);
495 return;
496 }
497
498 // Keep a reference to incompleted |async_op| for releasing later.
499 async_ops_.insert(async_op);
500 }
501
502 void OnEnumerationCompleted() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000503 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700504 CHECK(is_initialized_);
505
506 if (async_ops_.empty())
507 midi_manager_->OnPortManagerReady();
508 else
509 enumeration_completed_not_ready_ = true;
510 }
511
512 void OnRemoved(std::string dev_id) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000513 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700514 CHECK(is_initialized_);
515
shaochuan110262b2016-08-31 02:15:16 -0700516 // Note: in case Microsoft GS Wavetable Synth triggers this event for some
517 // reason, it will be ignored here with log emitted.
shaochuane58f9c72016-08-30 22:27:08 -0700518 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
519 if (!port) {
520 VLOG(1) << "Removing non-existent port " << dev_id;
521 return;
522 }
523
toyoshimec2570a2016-10-21 02:15:27 -0700524 SetPortState(port->index, PortState::DISCONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700525
526 RemovePortEventHandlers(port);
527 port->handle = nullptr;
528 }
529
shaochuanc2894522016-09-20 01:10:50 -0700530 void OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000531 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700532 CHECK(is_initialized_);
533
shaochuanc2894522016-09-20 01:10:50 -0700534 InterfaceType* handle = nullptr;
535 HRESULT hr = async_op->GetResults(&handle);
536 if (FAILED(hr)) {
537 VLOG(1) << "GetResults failed: " << PrintHr(hr);
538 return;
539 }
540
541 // Manually release COM interface to completed |async_op|.
542 auto it = async_ops_.find(async_op);
543 CHECK(it != async_ops_.end());
544 (*it)->Release();
545 async_ops_.erase(it);
546
547 if (!handle) {
548 VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, "
549 "ignoring";
550 return;
551 }
552
shaochuane58f9c72016-08-30 22:27:08 -0700553 EventRegistrationToken token = {kInvalidTokenValue};
554 if (!RegisterOnMessageReceived(handle, &token))
555 return;
556
557 std::string dev_id = GetDeviceIdString(handle);
558
559 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
560
561 if (port == nullptr) {
shaochuan17bc4a02016-09-06 01:42:12 -0700562 std::string manufacturer = "Unknown", driver_version = "Unknown";
563 GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version);
564
Adithya Srinivasan33252732018-10-17 15:59:40 +0000565 AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id],
566 driver_version, PortState::OPENED));
shaochuane58f9c72016-08-30 22:27:08 -0700567
568 port = new MidiPort<InterfaceType>;
569 port->index = static_cast<uint32_t>(port_ids_.size());
570
571 ports_[dev_id].reset(port);
572 port_ids_.push_back(dev_id);
573 } else {
toyoshimec2570a2016-10-21 02:15:27 -0700574 SetPortState(port->index, PortState::CONNECTED);
shaochuane58f9c72016-08-30 22:27:08 -0700575 }
576
577 port->handle = handle;
578 port->token_MessageReceived = token;
579
shaochuane58f9c72016-08-30 22:27:08 -0700580 if (enumeration_completed_not_ready_ && async_ops_.empty()) {
581 midi_manager_->OnPortManagerReady();
582 enumeration_completed_not_ready_ = false;
583 }
584 }
585
586 // Overrided by MidiInPortManager to listen to input ports.
587 virtual bool RegisterOnMessageReceived(InterfaceType* handle,
588 EventRegistrationToken* p_token) {
589 return true;
590 }
591
592 // Overrided by MidiInPortManager to remove MessageReceived event handler.
593 virtual void RemovePortEventHandlers(MidiPort<InterfaceType>* port) {}
594
595 // Calls midi_manager_->Add{Input,Output}Port.
Adithya Srinivasan33252732018-10-17 15:59:40 +0000596 virtual void AddPort(mojom::PortInfo info) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700597
598 // Calls midi_manager_->Set{Input,Output}PortState.
toyoshimec2570a2016-10-21 02:15:27 -0700599 virtual void SetPortState(uint32_t port_index, PortState state) = 0;
shaochuane58f9c72016-08-30 22:27:08 -0700600
shaochuane58f9c72016-08-30 22:27:08 -0700601 // Midi{In,Out}PortStatics instance.
Robert Liao4a680c32017-10-18 19:10:01 +0000602 WRL::ComPtr<StaticsInterfaceType> midi_port_statics_;
shaochuane58f9c72016-08-30 22:27:08 -0700603
604 // DeviceWatcher instance and event registration tokens for unsubscribing
605 // events in destructor.
Robert Liao4a680c32017-10-18 19:10:01 +0000606 WRL::ComPtr<IDeviceWatcher> watcher_;
shaochuane58f9c72016-08-30 22:27:08 -0700607 EventRegistrationToken token_Added_ = {kInvalidTokenValue},
608 token_EnumerationCompleted_ = {kInvalidTokenValue},
609 token_Removed_ = {kInvalidTokenValue},
610 token_Stopped_ = {kInvalidTokenValue},
611 token_Updated_ = {kInvalidTokenValue};
612
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000613 // All manipulations to these fields should be done on kComTaskRunner.
shaochuane58f9c72016-08-30 22:27:08 -0700614 std::unordered_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
615 ports_;
616 std::vector<std::string> port_ids_;
617 std::unordered_map<std::string, std::string> port_names_;
618
619 // Keeps AsyncOperation references before the operation completes. Note that
620 // raw pointers are used here and the COM interfaces should be released
621 // manually.
622 std::unordered_set<IAsyncOperation<RuntimeType*>*> async_ops_;
623
624 // Set when device enumeration is completed but OnPortManagerReady() is not
625 // called since some ports are not yet ready (i.e. |async_ops_| is not empty).
626 // In such cases, OnPortManagerReady() will be called in
627 // OnCompletedGetPortFromIdAsync() when the last pending port is ready.
628 bool enumeration_completed_not_ready_ = false;
629
630 // Set if the instance is initialized without error. Should be checked in all
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000631 // methods on kComTaskRunner except StartWatcher().
shaochuane58f9c72016-08-30 22:27:08 -0700632 bool is_initialized_ = false;
633};
634
635class MidiManagerWinrt::MidiInPortManager final
636 : public MidiPortManager<IMidiInPort,
637 MidiInPort,
638 IMidiInPortStatics,
639 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
640 public:
641 MidiInPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000642 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700643
644 private:
645 // MidiPortManager overrides:
646 bool RegisterOnMessageReceived(IMidiInPort* handle,
647 EventRegistrationToken* p_token) override {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000648 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700649
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000650 MidiInPortManager* port_manager = this;
651 TaskService* task_service = midi_service_->task_service();
shaochuane58f9c72016-08-30 22:27:08 -0700652
653 HRESULT hr = handle->add_MessageReceived(
654 WRL::Callback<
655 ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>(
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000656 [port_manager, task_service](IMidiInPort* handle,
657 IMidiMessageReceivedEventArgs* args) {
shaochuane58f9c72016-08-30 22:27:08 -0700658 const base::TimeTicks now = base::TimeTicks::Now();
659
660 std::string dev_id = GetDeviceIdString(handle);
661
Robert Liao4a680c32017-10-18 19:10:01 +0000662 WRL::ComPtr<IMidiMessage> message;
robliao8d08e692017-05-11 10:14:00 -0700663 HRESULT hr = args->get_Message(message.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700664 if (FAILED(hr)) {
665 VLOG(1) << "get_Message failed: " << PrintHr(hr);
666 return hr;
667 }
668
Robert Liao4a680c32017-10-18 19:10:01 +0000669 WRL::ComPtr<IBuffer> buffer;
robliao8d08e692017-05-11 10:14:00 -0700670 hr = message->get_RawData(buffer.GetAddressOf());
shaochuane58f9c72016-08-30 22:27:08 -0700671 if (FAILED(hr)) {
672 VLOG(1) << "get_RawData failed: " << PrintHr(hr);
673 return hr;
674 }
675
676 uint8_t* p_buffer_data = nullptr;
junweifuf51c5a02017-11-03 06:37:09 +0000677 uint32_t data_length = 0;
678 hr = base::win::GetPointerToBufferData(
679 buffer.Get(), &p_buffer_data, &data_length);
shaochuane58f9c72016-08-30 22:27:08 -0700680 if (FAILED(hr))
681 return hr;
682
shaochuane58f9c72016-08-30 22:27:08 -0700683 std::vector<uint8_t> data(p_buffer_data,
684 p_buffer_data + data_length);
685
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000686 task_service->PostBoundTask(
687 kComTaskRunner,
Takashi Toyoshimad2bdc592017-09-13 10:02:54 +0000688 base::BindOnce(&MidiInPortManager::OnMessageReceived,
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000689 base::Unretained(port_manager), dev_id, data,
690 now));
shaochuane58f9c72016-08-30 22:27:08 -0700691
692 return S_OK;
693 })
694 .Get(),
695 p_token);
696 if (FAILED(hr)) {
697 VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr);
698 return false;
699 }
700
701 return true;
702 }
703
704 void RemovePortEventHandlers(MidiPort<IMidiInPort>* port) override {
705 if (!(port->handle &&
706 port->token_MessageReceived.value != kInvalidTokenValue))
707 return;
708
709 HRESULT hr =
710 port->handle->remove_MessageReceived(port->token_MessageReceived);
711 VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr);
712 port->token_MessageReceived.value = kInvalidTokenValue;
713 }
714
Adithya Srinivasan33252732018-10-17 15:59:40 +0000715 void AddPort(mojom::PortInfo info) final {
716 midi_manager_->AddInputPort(info);
717 }
shaochuane58f9c72016-08-30 22:27:08 -0700718
toyoshimec2570a2016-10-21 02:15:27 -0700719 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700720 midi_manager_->SetInputPortState(port_index, state);
721 }
722
shaochuane58f9c72016-08-30 22:27:08 -0700723 // Callback on receiving MIDI input message.
724 void OnMessageReceived(std::string dev_id,
725 std::vector<uint8_t> data,
726 base::TimeTicks time) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000727 DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700728
729 MidiPort<IMidiInPort>* port = GetPortByDeviceId(dev_id);
730 CHECK(port);
731
732 midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time);
733 }
734
shaochuane58f9c72016-08-30 22:27:08 -0700735 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
736};
737
738class MidiManagerWinrt::MidiOutPortManager final
739 : public MidiPortManager<IMidiOutPort,
740 IMidiOutPort,
741 IMidiOutPortStatics,
742 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
743 public:
744 MidiOutPortManager(MidiManagerWinrt* midi_manager)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000745 : MidiPortManager(midi_manager) {}
shaochuane58f9c72016-08-30 22:27:08 -0700746
747 private:
748 // MidiPortManager overrides:
Adithya Srinivasan33252732018-10-17 15:59:40 +0000749 void AddPort(mojom::PortInfo info) final {
750 midi_manager_->AddOutputPort(info);
751 }
shaochuane58f9c72016-08-30 22:27:08 -0700752
toyoshimec2570a2016-10-21 02:15:27 -0700753 void SetPortState(uint32_t port_index, PortState state) final {
shaochuane58f9c72016-08-30 22:27:08 -0700754 midi_manager_->SetOutputPortState(port_index, state);
755 }
756
shaochuane58f9c72016-08-30 22:27:08 -0700757 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
758};
759
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000760namespace {
761
762// FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager
763// instance destruction.
764void FinalizeOnComRunner(
765 std::unique_ptr<MidiManagerWinrt::MidiInPortManager> port_manager_in,
766 std::unique_ptr<MidiManagerWinrt::MidiOutPortManager> port_manager_out) {
767 if (port_manager_in)
768 port_manager_in->StopWatcher();
769
770 if (port_manager_out)
771 port_manager_out->StopWatcher();
772}
773
774} // namespace
775
toyoshimf4d61522017-02-10 02:03:32 -0800776MidiManagerWinrt::MidiManagerWinrt(MidiService* service)
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000777 : MidiManager(service) {}
shaochuane58f9c72016-08-30 22:27:08 -0700778
779MidiManagerWinrt::~MidiManagerWinrt() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000780 // Unbind and take a lock to ensure that InitializeOnComRunner should not run
781 // after here.
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000782 if (!service()->task_service()->UnbindInstance())
783 return;
shaochuane58f9c72016-08-30 22:27:08 -0700784
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000785 base::AutoLock auto_lock(lazy_init_member_lock_);
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000786 service()->task_service()->PostStaticTask(
787 kComTaskRunner,
788 base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_),
789 std::move(port_manager_out_)));
shaochuane58f9c72016-08-30 22:27:08 -0700790}
791
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000792void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima88b4ac02019-02-12 11:25:56 +0000793 if (!service()->task_service()->BindInstance())
794 return CompleteInitialization(Result::INITIALIZATION_ERROR);
Takashi Toyoshimae8240ab2018-10-03 09:30:11 +0000795
796 service()->task_service()->PostBoundTask(
797 kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner,
798 base::Unretained(this)));
799}
800
shaochuane58f9c72016-08-30 22:27:08 -0700801void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
802 uint32_t port_index,
803 const std::vector<uint8_t>& data,
tzik925e2c62018-02-02 07:39:45 +0000804 base::TimeTicks timestamp) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000805 base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp);
806 service()->task_service()->PostBoundDelayedTask(
807 kComTaskRunner,
808 base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this),
809 port_index, data),
810 delay);
811 service()->task_service()->PostBoundDelayedTask(
812 kComTaskRunner,
813 base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent,
814 base::Unretained(this), client, data.size()),
815 delay);
shaochuane58f9c72016-08-30 22:27:08 -0700816}
817
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000818void MidiManagerWinrt::InitializeOnComRunner() {
shaochuane58f9c72016-08-30 22:27:08 -0700819 base::AutoLock auto_lock(lazy_init_member_lock_);
820
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000821 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
822
Finnur Thorarinssonee8428f2017-09-30 23:36:49 +0000823 bool preload_success = base::win::ResolveCoreWinRTDelayload() &&
824 ScopedHString::ResolveCoreWinRTStringDelayload();
825 if (!preload_success) {
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000826 service()->task_service()->PostBoundTask(
827 kDefaultTaskRunner,
828 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
829 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuan9ff63b82016-09-01 01:58:44 -0700830 return;
831 }
832
shaochuane58f9c72016-08-30 22:27:08 -0700833 port_manager_in_.reset(new MidiInPortManager(this));
834 port_manager_out_.reset(new MidiOutPortManager(this));
835
shaochuane58f9c72016-08-30 22:27:08 -0700836 if (!(port_manager_in_->StartWatcher() &&
837 port_manager_out_->StartWatcher())) {
838 port_manager_in_->StopWatcher();
839 port_manager_out_->StopWatcher();
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000840 service()->task_service()->PostBoundTask(
841 kDefaultTaskRunner,
842 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
843 base::Unretained(this), Result::INITIALIZATION_ERROR));
shaochuane58f9c72016-08-30 22:27:08 -0700844 }
845}
846
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000847void MidiManagerWinrt::SendOnComRunner(uint32_t port_index,
shaochuane58f9c72016-08-30 22:27:08 -0700848 const std::vector<uint8_t>& data) {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000849 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700850
Takashi Toyoshimad4f37cd2018-10-01 07:43:39 +0000851 base::AutoLock auto_lock(lazy_init_member_lock_);
shaochuane58f9c72016-08-30 22:27:08 -0700852 MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index);
853 if (!(port && port->handle)) {
854 VLOG(1) << "Port not available: " << port_index;
855 return;
856 }
857
Robert Liao4a680c32017-10-18 19:10:01 +0000858 WRL::ComPtr<IBuffer> buffer;
junweifua8cea852017-10-17 06:21:16 +0000859 HRESULT hr = base::win::CreateIBufferFromData(
junweifuf51c5a02017-11-03 06:37:09 +0000860 data.data(), static_cast<UINT32>(data.size()), &buffer);
shaochuane58f9c72016-08-30 22:27:08 -0700861 if (FAILED(hr)) {
junweifua8cea852017-10-17 06:21:16 +0000862 VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr);
shaochuane58f9c72016-08-30 22:27:08 -0700863 return;
864 }
865
robliao3566d1a2017-04-18 17:28:09 -0700866 hr = port->handle->SendBuffer(buffer.Get());
shaochuane58f9c72016-08-30 22:27:08 -0700867 if (FAILED(hr)) {
868 VLOG(1) << "SendBuffer failed: " << PrintHr(hr);
869 return;
870 }
871}
872
873void MidiManagerWinrt::OnPortManagerReady() {
Takashi Toyoshima3f0ea8f2018-01-17 09:19:59 +0000874 DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner));
shaochuane58f9c72016-08-30 22:27:08 -0700875 DCHECK(port_manager_ready_count_ < 2);
876
Takashi Toyoshima5a6e6a32018-09-27 11:20:52 +0000877 if (++port_manager_ready_count_ == 2) {
878 service()->task_service()->PostBoundTask(
879 kDefaultTaskRunner,
880 base::BindOnce(&MidiManagerWinrt::CompleteInitialization,
881 base::Unretained(this), Result::OK));
882 }
shaochuane58f9c72016-08-30 22:27:08 -0700883}
884
shaochuane58f9c72016-08-30 22:27:08 -0700885} // namespace midi