// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/midi/midi_manager_winrt.h" #pragma warning(disable : 4467) #define INITGUID #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "base/bind.h" #include "base/scoped_generic.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/timer/timer.h" #include "base/win/core_winrt_util.h" #include "base/win/scoped_hstring.h" #include "base/win/winrt_storage_util.h" #include "media/midi/midi_service.h" #include "media/midi/task_service.h" namespace midi { namespace { namespace WRL = Microsoft::WRL; namespace Win = ABI::Windows; using base::win::GetActivationFactory; using base::win::ScopedHString; using mojom::PortState; using mojom::Result; using Win::Devices::Enumeration::DeviceInformationUpdate; using Win::Devices::Enumeration::DeviceWatcher; using Win::Devices::Enumeration::IDeviceInformation; using Win::Devices::Enumeration::IDeviceInformationUpdate; using Win::Devices::Enumeration::IDeviceWatcher; using Win::Foundation::IAsyncOperation; using Win::Foundation::ITypedEventHandler; // Alias for printing HRESULT. const auto PrintHr = logging::SystemErrorCodeToString; enum { kDefaultTaskRunner = TaskService::kDefaultRunnerId, kComTaskRunner }; template std::string GetIdString(T* obj) { HSTRING result; HRESULT hr = obj->get_Id(&result); if (FAILED(hr)) { VLOG(1) << "get_Id failed: " << PrintHr(hr); return std::string(); } return ScopedHString(result).GetAsUTF8(); } template std::string GetDeviceIdString(T* obj) { HSTRING result; HRESULT hr = obj->get_DeviceId(&result); if (FAILED(hr)) { VLOG(1) << "get_DeviceId failed: " << PrintHr(hr); return std::string(); } return ScopedHString(result).GetAsUTF8(); } std::string GetNameString(IDeviceInformation* info) { HSTRING result; HRESULT hr = info->get_Name(&result); if (FAILED(hr)) { VLOG(1) << "get_Name failed: " << PrintHr(hr); return std::string(); } return ScopedHString(result).GetAsUTF8(); } // Checks if given DeviceInformation represent a Microsoft GS Wavetable Synth // instance. bool IsMicrosoftSynthesizer(IDeviceInformation* info) { WRL::ComPtr midi_synthesizer_statics; HRESULT hr = GetActivationFactory( &midi_synthesizer_statics); if (FAILED(hr)) { VLOG(1) << "IMidiSynthesizerStatics factory failed: " << PrintHr(hr); return false; } boolean result = FALSE; hr = midi_synthesizer_statics->IsSynthesizer(info, &result); VLOG_IF(1, FAILED(hr)) << "IsSynthesizer failed: " << PrintHr(hr); return result != FALSE; } void GetDevPropString(DEVINST handle, const DEVPROPKEY* devprop_key, std::string* out) { DEVPROPTYPE devprop_type; unsigned long buffer_size = 0; // Retrieve |buffer_size| and allocate buffer later for receiving data. CONFIGRET cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, nullptr, &buffer_size, 0); if (cr != CR_BUFFER_SMALL) { // Here we print error codes in hex instead of using PrintHr() with // HRESULT_FROM_WIN32() and CM_MapCrToWin32Err(), since only a minor set of // CONFIGRET values are mapped to Win32 errors. Same for following VLOG()s. VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr; return; } if (devprop_type != DEVPROP_TYPE_STRING) { VLOG(1) << "CM_Get_DevNode_Property returns wrong data type, " << "expected DEVPROP_TYPE_STRING"; return; } std::unique_ptr buffer(new uint8_t[buffer_size]); // Receive property data. cr = CM_Get_DevNode_Property(handle, devprop_key, &devprop_type, buffer.get(), &buffer_size, 0); if (cr != CR_SUCCESS) VLOG(1) << "CM_Get_DevNode_Property failed: CONFIGRET 0x" << std::hex << cr; else *out = base::WideToUTF8(reinterpret_cast(buffer.get())); } // Retrieves manufacturer (provider) and version information of underlying // device driver through PnP Configuration Manager, given device (interface) ID // provided by WinRT. |out_manufacturer| and |out_driver_version| won't be // modified if retrieval fails. // // Device instance ID is extracted from device (interface) ID provided by WinRT // APIs, for example from the following interface ID: // \\?\SWD#MMDEVAPI#MIDII_60F39FCA.P_0002#{504be32c-ccf6-4d2c-b73f-6f8b3747e22b} // we extract the device instance ID: SWD\MMDEVAPI\MIDII_60F39FCA.P_0002 // // However the extracted device instance ID represent a "software device" // provided by Microsoft, which is an interface on top of the hardware for each // input/output port. Therefore we further locate its parent device, which is // the actual hardware device, for driver information. void GetDriverInfoFromDeviceId(const std::string& dev_id, std::string* out_manufacturer, std::string* out_driver_version) { base::string16 dev_instance_id = base::UTF8ToWide(dev_id.substr(4, dev_id.size() - 43)); base::ReplaceChars(dev_instance_id, L"#", L"\\", &dev_instance_id); DEVINST dev_instance_handle; CONFIGRET cr = CM_Locate_DevNode(&dev_instance_handle, &dev_instance_id[0], CM_LOCATE_DEVNODE_NORMAL); if (cr != CR_SUCCESS) { VLOG(1) << "CM_Locate_DevNode failed: CONFIGRET 0x" << std::hex << cr; return; } DEVINST parent_handle; cr = CM_Get_Parent(&parent_handle, dev_instance_handle, 0); if (cr != CR_SUCCESS) { VLOG(1) << "CM_Get_Parent failed: CONFIGRET 0x" << std::hex << cr; return; } GetDevPropString(parent_handle, &DEVPKEY_Device_DriverProvider, out_manufacturer); GetDevPropString(parent_handle, &DEVPKEY_Device_DriverVersion, out_driver_version); } // Tokens with value = 0 are considered invalid (as in ). const int64_t kInvalidTokenValue = 0; template struct MidiPort { MidiPort() = default; uint32_t index; WRL::ComPtr handle; EventRegistrationToken token_MessageReceived; private: DISALLOW_COPY_AND_ASSIGN(MidiPort); }; } // namespace template class MidiManagerWinrt::MidiPortManager { public: // MidiPortManager instances should be constructed on the kComTaskRunner. MidiPortManager(MidiManagerWinrt* midi_manager) : midi_service_(midi_manager->service()), midi_manager_(midi_manager) {} virtual ~MidiPortManager() { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); } bool StartWatcher() { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); HRESULT hr = GetActivationFactory( &midi_port_statics_); if (FAILED(hr)) { VLOG(1) << "StaticsInterfaceType factory failed: " << PrintHr(hr); return false; } HSTRING device_selector = nullptr; hr = midi_port_statics_->GetDeviceSelector(&device_selector); if (FAILED(hr)) { VLOG(1) << "GetDeviceSelector failed: " << PrintHr(hr); return false; } WRL::ComPtr dev_info_statics; hr = GetActivationFactory< Win::Devices::Enumeration::IDeviceInformationStatics, RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>( &dev_info_statics); if (FAILED(hr)) { VLOG(1) << "IDeviceInformationStatics failed: " << PrintHr(hr); return false; } hr = dev_info_statics->CreateWatcherAqsFilter(device_selector, &watcher_); if (FAILED(hr)) { VLOG(1) << "CreateWatcherAqsFilter failed: " << PrintHr(hr); return false; } // Register callbacks to WinRT that post state-modifying tasks back to // kComTaskRunner. All posted tasks run only during the MidiPortManager // instance is alive. This is ensured by MidiManagerWinrt by calling // UnbindInstance() before destructing any MidiPortManager instance. Thus // we can handle raw pointers safely in the following blocks. MidiPortManager* port_manager = this; TaskService* task_service = midi_service_->task_service(); hr = watcher_->add_Added( WRL::Callback>( [port_manager, task_service](IDeviceWatcher* watcher, IDeviceInformation* info) { if (!info) { VLOG(1) << "DeviceWatcher.Added callback provides null " "pointer, ignoring"; return S_OK; } // Disable Microsoft GS Wavetable Synth due to security reasons. // http://crbug.com/499279 if (IsMicrosoftSynthesizer(info)) return S_OK; std::string dev_id = GetIdString(info), dev_name = GetNameString(info); task_service->PostBoundTask( kComTaskRunner, base::BindOnce(&MidiPortManager::OnAdded, base::Unretained(port_manager), dev_id, dev_name)); return S_OK; }) .Get(), &token_Added_); if (FAILED(hr)) { VLOG(1) << "add_Added failed: " << PrintHr(hr); return false; } hr = watcher_->add_EnumerationCompleted( WRL::Callback>( [port_manager, task_service](IDeviceWatcher* watcher, IInspectable* insp) { task_service->PostBoundTask( kComTaskRunner, base::BindOnce(&MidiPortManager::OnEnumerationCompleted, base::Unretained(port_manager))); return S_OK; }) .Get(), &token_EnumerationCompleted_); if (FAILED(hr)) { VLOG(1) << "add_EnumerationCompleted failed: " << PrintHr(hr); return false; } hr = watcher_->add_Removed( WRL::Callback< ITypedEventHandler>( [port_manager, task_service](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) { if (!update) { VLOG(1) << "DeviceWatcher.Removed callback provides null " "pointer, ignoring"; return S_OK; } std::string dev_id = GetIdString(update); task_service->PostBoundTask( kComTaskRunner, base::BindOnce(&MidiPortManager::OnRemoved, base::Unretained(port_manager), dev_id)); return S_OK; }) .Get(), &token_Removed_); if (FAILED(hr)) { VLOG(1) << "add_Removed failed: " << PrintHr(hr); return false; } hr = watcher_->add_Stopped( WRL::Callback>( [](IDeviceWatcher* watcher, IInspectable* insp) { // Placeholder, does nothing for now. return S_OK; }) .Get(), &token_Stopped_); if (FAILED(hr)) { VLOG(1) << "add_Stopped failed: " << PrintHr(hr); return false; } hr = watcher_->add_Updated( WRL::Callback< ITypedEventHandler>( [](IDeviceWatcher* watcher, IDeviceInformationUpdate* update) { // TODO(shaochuan): Check for fields to be updated here. return S_OK; }) .Get(), &token_Updated_); if (FAILED(hr)) { VLOG(1) << "add_Updated failed: " << PrintHr(hr); return false; } hr = watcher_->Start(); if (FAILED(hr)) { VLOG(1) << "Start failed: " << PrintHr(hr); return false; } is_initialized_ = true; return true; } void StopWatcher() { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); HRESULT hr; for (const auto& entry : ports_) RemovePortEventHandlers(entry.second.get()); if (token_Added_.value != kInvalidTokenValue) { hr = watcher_->remove_Added(token_Added_); VLOG_IF(1, FAILED(hr)) << "remove_Added failed: " << PrintHr(hr); token_Added_.value = kInvalidTokenValue; } if (token_EnumerationCompleted_.value != kInvalidTokenValue) { hr = watcher_->remove_EnumerationCompleted(token_EnumerationCompleted_); VLOG_IF(1, FAILED(hr)) << "remove_EnumerationCompleted failed: " << PrintHr(hr); token_EnumerationCompleted_.value = kInvalidTokenValue; } if (token_Removed_.value != kInvalidTokenValue) { hr = watcher_->remove_Removed(token_Removed_); VLOG_IF(1, FAILED(hr)) << "remove_Removed failed: " << PrintHr(hr); token_Removed_.value = kInvalidTokenValue; } if (token_Stopped_.value != kInvalidTokenValue) { hr = watcher_->remove_Stopped(token_Stopped_); VLOG_IF(1, FAILED(hr)) << "remove_Stopped failed: " << PrintHr(hr); token_Stopped_.value = kInvalidTokenValue; } if (token_Updated_.value != kInvalidTokenValue) { hr = watcher_->remove_Updated(token_Updated_); VLOG_IF(1, FAILED(hr)) << "remove_Updated failed: " << PrintHr(hr); token_Updated_.value = kInvalidTokenValue; } if (is_initialized_) { hr = watcher_->Stop(); VLOG_IF(1, FAILED(hr)) << "Stop failed: " << PrintHr(hr); is_initialized_ = false; } } MidiPort* GetPortByDeviceId(std::string dev_id) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); auto it = ports_.find(dev_id); if (it == ports_.end()) return nullptr; return it->second.get(); } MidiPort* GetPortByIndex(uint32_t port_index) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); return GetPortByDeviceId(port_ids_[port_index]); } protected: // Points to the MidiService instance, which is expected to outlive the // MidiPortManager instance. MidiService* midi_service_; // Points to the MidiManagerWinrt instance, which is safe to be accessed // from tasks that are invoked by TaskService. MidiManagerWinrt* midi_manager_; private: // DeviceWatcher callbacks: void OnAdded(std::string dev_id, std::string dev_name) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); port_names_[dev_id] = dev_name; ScopedHString dev_id_hstring = ScopedHString::Create(dev_id); if (!dev_id_hstring.is_valid()) return; IAsyncOperation* async_op; HRESULT hr = midi_port_statics_->FromIdAsync(dev_id_hstring.get(), &async_op); if (FAILED(hr)) { VLOG(1) << "FromIdAsync failed: " << PrintHr(hr); return; } MidiPortManager* port_manager = this; TaskService* task_service = midi_service_->task_service(); hr = async_op->put_Completed( WRL::Callback< Win::Foundation::IAsyncOperationCompletedHandler>( [port_manager, task_service]( IAsyncOperation* async_op, AsyncStatus status) { // A reference to |async_op| is kept in |async_ops_|, safe to pass // outside. task_service->PostBoundTask( kComTaskRunner, base::BindOnce( &MidiPortManager::OnCompletedGetPortFromIdAsync, base::Unretained(port_manager), base::Unretained(async_op))); return S_OK; }) .Get()); if (FAILED(hr)) { VLOG(1) << "put_Completed failed: " << PrintHr(hr); return; } // Keep a reference to incompleted |async_op| for releasing later. async_ops_.insert(async_op); } void OnEnumerationCompleted() { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); if (async_ops_.empty()) midi_manager_->OnPortManagerReady(); else enumeration_completed_not_ready_ = true; } void OnRemoved(std::string dev_id) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); // Note: in case Microsoft GS Wavetable Synth triggers this event for some // reason, it will be ignored here with log emitted. MidiPort* port = GetPortByDeviceId(dev_id); if (!port) { VLOG(1) << "Removing non-existent port " << dev_id; return; } SetPortState(port->index, PortState::DISCONNECTED); RemovePortEventHandlers(port); port->handle = nullptr; } void OnCompletedGetPortFromIdAsync(IAsyncOperation* async_op) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); CHECK(is_initialized_); InterfaceType* handle = nullptr; HRESULT hr = async_op->GetResults(&handle); if (FAILED(hr)) { VLOG(1) << "GetResults failed: " << PrintHr(hr); return; } // Manually release COM interface to completed |async_op|. auto it = async_ops_.find(async_op); CHECK(it != async_ops_.end()); (*it)->Release(); async_ops_.erase(it); if (!handle) { VLOG(1) << "Midi{In,Out}Port.FromIdAsync callback provides null pointer, " "ignoring"; return; } EventRegistrationToken token = {kInvalidTokenValue}; if (!RegisterOnMessageReceived(handle, &token)) return; std::string dev_id = GetDeviceIdString(handle); MidiPort* port = GetPortByDeviceId(dev_id); if (port == nullptr) { std::string manufacturer = "Unknown", driver_version = "Unknown"; GetDriverInfoFromDeviceId(dev_id, &manufacturer, &driver_version); AddPort(mojom::PortInfo(dev_id, manufacturer, port_names_[dev_id], driver_version, PortState::OPENED)); port = new MidiPort; port->index = static_cast(port_ids_.size()); ports_[dev_id].reset(port); port_ids_.push_back(dev_id); } else { SetPortState(port->index, PortState::CONNECTED); } port->handle = handle; port->token_MessageReceived = token; if (enumeration_completed_not_ready_ && async_ops_.empty()) { midi_manager_->OnPortManagerReady(); enumeration_completed_not_ready_ = false; } } // Overrided by MidiInPortManager to listen to input ports. virtual bool RegisterOnMessageReceived(InterfaceType* handle, EventRegistrationToken* p_token) { return true; } // Overrided by MidiInPortManager to remove MessageReceived event handler. virtual void RemovePortEventHandlers(MidiPort* port) {} // Calls midi_manager_->Add{Input,Output}Port. virtual void AddPort(mojom::PortInfo info) = 0; // Calls midi_manager_->Set{Input,Output}PortState. virtual void SetPortState(uint32_t port_index, PortState state) = 0; // Midi{In,Out}PortStatics instance. WRL::ComPtr midi_port_statics_; // DeviceWatcher instance and event registration tokens for unsubscribing // events in destructor. WRL::ComPtr watcher_; EventRegistrationToken token_Added_ = {kInvalidTokenValue}, token_EnumerationCompleted_ = {kInvalidTokenValue}, token_Removed_ = {kInvalidTokenValue}, token_Stopped_ = {kInvalidTokenValue}, token_Updated_ = {kInvalidTokenValue}; // All manipulations to these fields should be done on kComTaskRunner. std::unordered_map>> ports_; std::vector port_ids_; std::unordered_map port_names_; // Keeps AsyncOperation references before the operation completes. Note that // raw pointers are used here and the COM interfaces should be released // manually. std::unordered_set*> async_ops_; // Set when device enumeration is completed but OnPortManagerReady() is not // called since some ports are not yet ready (i.e. |async_ops_| is not empty). // In such cases, OnPortManagerReady() will be called in // OnCompletedGetPortFromIdAsync() when the last pending port is ready. bool enumeration_completed_not_ready_ = false; // Set if the instance is initialized without error. Should be checked in all // methods on kComTaskRunner except StartWatcher(). bool is_initialized_ = false; }; class MidiManagerWinrt::MidiInPortManager final : public MidiPortManager { public: MidiInPortManager(MidiManagerWinrt* midi_manager) : MidiPortManager(midi_manager) {} private: // MidiPortManager overrides: bool RegisterOnMessageReceived(Win::Devices::Midi::IMidiInPort* handle, EventRegistrationToken* p_token) override { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); MidiInPortManager* port_manager = this; TaskService* task_service = midi_service_->task_service(); HRESULT hr = handle->add_MessageReceived( WRL::Callback>( [port_manager, task_service]( Win::Devices::Midi::IMidiInPort* handle, Win::Devices::Midi::IMidiMessageReceivedEventArgs* args) { const base::TimeTicks now = base::TimeTicks::Now(); std::string dev_id = GetDeviceIdString(handle); WRL::ComPtr message; HRESULT hr = args->get_Message(&message); if (FAILED(hr)) { VLOG(1) << "get_Message failed: " << PrintHr(hr); return hr; } WRL::ComPtr buffer; hr = message->get_RawData(&buffer); if (FAILED(hr)) { VLOG(1) << "get_RawData failed: " << PrintHr(hr); return hr; } uint8_t* p_buffer_data = nullptr; uint32_t data_length = 0; hr = base::win::GetPointerToBufferData( buffer.Get(), &p_buffer_data, &data_length); if (FAILED(hr)) return hr; std::vector data(p_buffer_data, p_buffer_data + data_length); task_service->PostBoundTask( kComTaskRunner, base::BindOnce(&MidiInPortManager::OnMessageReceived, base::Unretained(port_manager), dev_id, data, now)); return S_OK; }) .Get(), p_token); if (FAILED(hr)) { VLOG(1) << "add_MessageReceived failed: " << PrintHr(hr); return false; } return true; } void RemovePortEventHandlers( MidiPort* port) override { if (!(port->handle && port->token_MessageReceived.value != kInvalidTokenValue)) return; HRESULT hr = port->handle->remove_MessageReceived(port->token_MessageReceived); VLOG_IF(1, FAILED(hr)) << "remove_MessageReceived failed: " << PrintHr(hr); port->token_MessageReceived.value = kInvalidTokenValue; } void AddPort(mojom::PortInfo info) final { midi_manager_->AddInputPort(info); } void SetPortState(uint32_t port_index, PortState state) final { midi_manager_->SetInputPortState(port_index, state); } // Callback on receiving MIDI input message. void OnMessageReceived(std::string dev_id, std::vector data, base::TimeTicks time) { DCHECK(midi_service_->task_service()->IsOnTaskRunner(kComTaskRunner)); MidiPort* port = GetPortByDeviceId(dev_id); CHECK(port); midi_manager_->ReceiveMidiData(port->index, &data[0], data.size(), time); } DISALLOW_COPY_AND_ASSIGN(MidiInPortManager); }; class MidiManagerWinrt::MidiOutPortManager final : public MidiPortManager { public: MidiOutPortManager(MidiManagerWinrt* midi_manager) : MidiPortManager(midi_manager) {} private: // MidiPortManager overrides: void AddPort(mojom::PortInfo info) final { midi_manager_->AddOutputPort(info); } void SetPortState(uint32_t port_index, PortState state) final { midi_manager_->SetOutputPortState(port_index, state); } DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager); }; namespace { // FinalizeOnComRunner() run on kComTaskRunner even after the MidiManager // instance destruction. void FinalizeOnComRunner( std::unique_ptr port_manager_in, std::unique_ptr port_manager_out) { if (port_manager_in) port_manager_in->StopWatcher(); if (port_manager_out) port_manager_out->StopWatcher(); } } // namespace MidiManagerWinrt::MidiManagerWinrt(MidiService* service) : MidiManager(service) {} MidiManagerWinrt::~MidiManagerWinrt() { // Unbind and take a lock to ensure that InitializeOnComRunner should not run // after here. if (!service()->task_service()->UnbindInstance()) return; base::AutoLock auto_lock(lazy_init_member_lock_); service()->task_service()->PostStaticTask( kComTaskRunner, base::BindOnce(&FinalizeOnComRunner, std::move(port_manager_in_), std::move(port_manager_out_))); } void MidiManagerWinrt::StartInitialization() { if (!service()->task_service()->BindInstance()) return CompleteInitialization(Result::INITIALIZATION_ERROR); service()->task_service()->PostBoundTask( kComTaskRunner, base::BindOnce(&MidiManagerWinrt::InitializeOnComRunner, base::Unretained(this))); } void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client, uint32_t port_index, const std::vector& data, base::TimeTicks timestamp) { base::TimeDelta delay = MidiService::TimestampToTimeDeltaDelay(timestamp); service()->task_service()->PostBoundDelayedTask( kComTaskRunner, base::BindOnce(&MidiManagerWinrt::SendOnComRunner, base::Unretained(this), port_index, data), delay); service()->task_service()->PostBoundDelayedTask( kComTaskRunner, base::BindOnce(&MidiManagerWinrt::AccumulateMidiBytesSent, base::Unretained(this), client, data.size()), delay); } void MidiManagerWinrt::InitializeOnComRunner() { base::AutoLock auto_lock(lazy_init_member_lock_); DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner)); bool preload_success = base::win::ResolveCoreWinRTDelayload() && ScopedHString::ResolveCoreWinRTStringDelayload(); if (!preload_success) { service()->task_service()->PostBoundTask( kDefaultTaskRunner, base::BindOnce(&MidiManagerWinrt::CompleteInitialization, base::Unretained(this), Result::INITIALIZATION_ERROR)); return; } port_manager_in_.reset(new MidiInPortManager(this)); port_manager_out_.reset(new MidiOutPortManager(this)); if (!(port_manager_in_->StartWatcher() && port_manager_out_->StartWatcher())) { port_manager_in_->StopWatcher(); port_manager_out_->StopWatcher(); service()->task_service()->PostBoundTask( kDefaultTaskRunner, base::BindOnce(&MidiManagerWinrt::CompleteInitialization, base::Unretained(this), Result::INITIALIZATION_ERROR)); } } void MidiManagerWinrt::SendOnComRunner(uint32_t port_index, const std::vector& data) { DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner)); base::AutoLock auto_lock(lazy_init_member_lock_); MidiPort* port = port_manager_out_->GetPortByIndex(port_index); if (!(port && port->handle)) { VLOG(1) << "Port not available: " << port_index; return; } WRL::ComPtr buffer; HRESULT hr = base::win::CreateIBufferFromData( data.data(), static_cast(data.size()), &buffer); if (FAILED(hr)) { VLOG(1) << "CreateIBufferFromData failed: " << PrintHr(hr); return; } hr = port->handle->SendBuffer(buffer.Get()); if (FAILED(hr)) { VLOG(1) << "SendBuffer failed: " << PrintHr(hr); return; } } void MidiManagerWinrt::OnPortManagerReady() { DCHECK(service()->task_service()->IsOnTaskRunner(kComTaskRunner)); DCHECK(port_manager_ready_count_ < 2); if (++port_manager_ready_count_ == 2) { service()->task_service()->PostBoundTask( kDefaultTaskRunner, base::BindOnce(&MidiManagerWinrt::CompleteInitialization, base::Unretained(this), Result::OK)); } } } // namespace midi