Chromium Code Reviews| Index: media/midi/midi_manager_winrt.cc |
| diff --git a/media/midi/midi_manager_winrt.cc b/media/midi/midi_manager_winrt.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1f4f7af483dcdb4a89d74e9e602268e6cabe0e63 |
| --- /dev/null |
| +++ b/media/midi/midi_manager_winrt.cc |
| @@ -0,0 +1,537 @@ |
| +// 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" |
| + |
| +#include <robuffer.h> |
| +#include <windows.devices.enumeration.h> |
| +#include <windows.devices.midi.h> |
| +#include <wrl/event.h> |
| + |
| +#include <functional> |
| + |
| +#include "base/bind.h" |
| +#include "base/containers/hash_tables.h" |
| +#include "base/strings/string16.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/timer/timer.h" |
| +#include "base/win/windows_version.h" |
| + |
| +namespace media { |
| +namespace midi { |
| +namespace { |
| + |
| +namespace WRL = Microsoft::WRL; |
| + |
| +using namespace ABI::Windows::Devices::Enumeration; |
| +using namespace ABI::Windows::Devices::Midi; |
| +using namespace ABI::Windows::Foundation; |
| +using namespace ABI::Windows::Storage::Streams; |
| + |
| +using std::placeholders::_1; |
| +using std::placeholders::_2; |
|
Shao-Chuan Lee
2016/08/18 08:48:57
Unused, removed
|
| + |
| +template <typename InterfaceType> |
| +InterfaceType* GetStatics(const base::string16& class_id) { |
| + InterfaceType* ptr; |
|
Takashi Toyoshima
2016/08/16 09:30:29
Probably com_ptr or something is better.
Usually w
Shao-Chuan Lee
2016/08/18 08:48:56
Done. Also changed to using ComPtr to have ref-cou
|
| + |
| + HRESULT hr = GetActivationFactory( |
| + WRL::Wrappers::HStringReference(class_id.c_str()).Get(), &ptr); |
| + DCHECK(!FAILED(hr)); |
| + |
| + return ptr; |
| +} |
| + |
| +auto GetBufferFactory() { |
|
Takashi Toyoshima
2016/08/16 09:30:29
Generally speaking, trailing return types are not
Shao-Chuan Lee
2016/08/18 08:48:56
Done. Now these factory functions return ComPtr wh
|
| + return GetStatics<IBufferFactory>( |
| + RuntimeClass_Windows_Storage_Streams_Buffer); |
| +} |
| + |
| +auto GetDeviceInformationStatics() { |
| + return GetStatics<IDeviceInformationStatics>( |
| + RuntimeClass_Windows_Devices_Enumeration_DeviceInformation); |
| +} |
| + |
| +auto GetMidiInPortStatics() { |
| + return GetStatics<IMidiInPortStatics>( |
| + RuntimeClass_Windows_Devices_Midi_MidiInPort); |
| +} |
| + |
| +auto GetMidiOutPortStatics() { |
| + return GetStatics<IMidiOutPortStatics>( |
| + RuntimeClass_Windows_Devices_Midi_MidiOutPort); |
| +} |
| + |
| +template <typename T> |
| +inline std::string GetIdString(T obj) { |
|
Takashi Toyoshima
2016/08/16 09:30:29
Shall we make this "inline" compiler's decision so
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + HSTRING hs; |
|
Takashi Toyoshima
2016/08/16 09:30:28
"HRESULT hr" is very traditional abbreviation, but
Shao-Chuan Lee
2016/08/18 08:48:56
Using `HSTRING result' now.
|
| + HRESULT hr = obj->get_Id(&hs); |
| + DCHECK(!FAILED(hr)); |
|
Takashi Toyoshima
2016/08/16 09:30:29
Just a question: can we assume this call should su
Shao-Chuan Lee
2016/08/18 08:48:56
HSTRING uses nullptr to represent empty strings, i
Takashi Toyoshima
2016/08/18 11:09:21
Acknowledged.
|
| + return base::WideToUTF8(WindowsGetStringRawBuffer(hs, nullptr)); |
|
Shao-Chuan Lee
2016/08/18 08:48:56
This is calling base::WideToUTF8(const std::wstrin
|
| +} |
| + |
| +template <typename T> |
|
Takashi Toyoshima
2016/08/16 09:30:29
same comments on the following methods.
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| +inline std::string GetDeviceIdString(T obj) { |
| + HSTRING hs; |
| + HRESULT hr = obj->get_DeviceId(&hs); |
| + DCHECK(!FAILED(hr)); |
| + return base::WideToUTF8(WindowsGetStringRawBuffer(hs, nullptr)); |
| +} |
| + |
| +inline std::string GetNameString(IDeviceInformation* info) { |
| + HSTRING hs; |
| + HRESULT hr = info->get_Name(&hs); |
| + DCHECK(!FAILED(hr)); |
| + return base::WideToUTF8(WindowsGetStringRawBuffer(hs, nullptr)); |
| +} |
| + |
| +template <typename InterfaceType> |
| +struct MidiPort { |
| + MidiPort() {} |
| + |
| + uint32_t index; |
| + WRL::ComPtr<InterfaceType> handle; |
| + base::TimeTicks start_time; |
|
Takashi Toyoshima
2016/08/16 09:30:29
This looks working differently from the spec.
|
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(MidiPort); |
| +}; |
| + |
| +template <typename InterfaceType, typename RuntimeType> |
| +class MidiPortManager { |
| + public: |
| + MidiPortManager() {} |
|
Takashi Toyoshima
2016/08/16 09:30:29
FYI, we are allowed to use "= default" if you want
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + |
| + void StartWatcher() { |
| + HRESULT hr; |
| + |
| + hr = GetDeviceInformationStatics()->CreateWatcherAqsFilter( |
| + GetDeviceSelector(), &watcher_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->add_Added( |
| + WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>( |
| + std::bind(&MidiPortManager::OnAdded, this, _1, _2)) |
| + .Get(), |
| + &token_Added_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->add_EnumerationCompleted( |
| + WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>( |
| + std::bind(&MidiPortManager::OnEnumerationCompleted, this, _1, _2)) |
| + .Get(), |
| + &token_EnumerationCompleted_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->add_Removed( |
| + WRL::Callback< |
| + ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>( |
| + std::bind(&MidiPortManager::OnRemoved, this, _1, _2)) |
| + .Get(), |
| + &token_Removed_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->add_Stopped( |
| + WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>( |
| + std::bind(&MidiPortManager::OnStopped, this, _1, _2)) |
| + .Get(), |
| + &token_Stopped_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->add_Updated( |
| + WRL::Callback< |
| + ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>( |
| + std::bind(&MidiPortManager::OnUpdated, this, _1, _2)) |
| + .Get(), |
| + &token_Updated_); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = watcher_->Start(); |
| + DCHECK(!FAILED(hr)); |
| + } |
| + |
| + MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) { |
| + base::AutoLock auto_lock(ports_lock_); |
| + auto it = ports_.find(dev_id); |
| + if (it == ports_.end()) |
| + return nullptr; |
| + return it->second.get(); |
| + } |
| + |
| + MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) { |
| + base::AutoLock auto_lock(ports_lock_); |
| + auto it = ports_.find(port_ids_[port_index]); |
| + if (it == ports_.end()) |
| + return nullptr; |
| + return it->second.get(); |
| + } |
| + |
| + // DeviceWatcher callbacks: |
| + HRESULT OnAdded(IDeviceWatcher* watcher, IDeviceInformation* info) { |
| + // TODO(shaochuan): Disable Microsoft GS Wavetable Synth due to security |
|
Takashi Toyoshima
2016/08/16 09:30:29
Do you know if we can identify software virtual po
Shao-Chuan Lee
2016/08/18 08:48:57
The MS synth can be identified using WinRT MidiSyn
Takashi Toyoshima
2016/08/18 11:09:21
Acknowledged.
|
| + // reasons. https://bugs.chromium.org/p/project-zero/issues/detail?id=454 |
| + |
| + HSTRING dev_id_hs; |
|
Takashi Toyoshima
2016/08/16 09:30:29
Can you use GetIdString you defined?
Shao-Chuan Lee
2016/08/18 08:48:57
This HSTRING is required for RegisterGetPortFromId
Takashi Toyoshima
2016/08/18 11:09:21
Ah, I see. I misunderstood these lines.
|
| + HRESULT hr = info->get_Id(&dev_id_hs); |
| + DCHECK(!FAILED(hr)); |
| + |
| + std::string dev_id = |
| + base::WideToUTF8(WindowsGetStringRawBuffer(dev_id_hs, nullptr)); |
|
Shao-Chuan Lee
2016/08/18 08:48:56
Now using GetIdString to reuse nullptr checking co
|
| + |
| + { |
| + base::AutoLock auto_lock(ports_lock_); |
| + port_names_[dev_id] = GetNameString(info); |
| + } |
| + |
| + WRL::ComPtr<IAsyncOperation<RuntimeType*>> async_op; |
| + |
| + RegisterGetPortFromIdAsync(&async_op, dev_id_hs); |
| + |
| + hr = async_op->put_Completed( |
| + WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>( |
| + std::bind(&MidiPortManager::OnCompletedGetPortFromIdAsync, this, _1, |
| + _2)) |
| + .Get()); |
| + DCHECK(!FAILED(hr)); |
| + |
| + { |
| + base::AutoLock auto_lock(async_ops_lock_); |
| + async_ops_.insert(std::move(async_op)); |
| + } |
| + |
| + return S_OK; |
| + } |
| + |
| + HRESULT OnEnumerationCompleted(IDeviceWatcher* watcher, IInspectable* insp) { |
| + // TODO(shaochuan) |
|
Takashi Toyoshima
2016/08/16 09:30:28
What's missing here?
At least, we should complete
Shao-Chuan Lee
2016/08/18 08:48:56
Should CompleteInitialization() be moved here? I w
Takashi Toyoshima
2016/08/18 11:09:21
This isn't a problem for two reasons.
- MidiManag
|
| + return S_OK; |
| + } |
| + |
| + HRESULT OnRemoved(IDeviceWatcher* watcher, IDeviceInformationUpdate* update) { |
| + MidiPort<InterfaceType>* port = GetPortByDeviceId(GetIdString(update)); |
| + DCHECK(port != nullptr); |
| + |
| + SetPortState(port->index, MIDI_PORT_DISCONNECTED); |
| + |
| + port->handle = nullptr; |
| + |
| + return S_OK; |
| + } |
| + |
| + HRESULT OnStopped(IDeviceWatcher* watcher, IInspectable* insp) { |
| + // TODO(shaochuan) |
|
Takashi Toyoshima
2016/08/16 09:30:29
ditto
Shao-Chuan Lee
2016/08/18 08:48:56
Does nothing for now, leaving as a placeholder for
|
| + return S_OK; |
| + } |
| + |
| + HRESULT OnUpdated(IDeviceWatcher* watcher, IDeviceInformationUpdate* update) { |
| + // TODO(shaochuan) |
|
Takashi Toyoshima
2016/08/16 09:30:29
ditto
Shao-Chuan Lee
2016/08/18 08:48:56
Should update device information here, will check
|
| + return S_OK; |
| + } |
| + |
| + private: |
| + HRESULT OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op, |
| + AsyncStatus status) { |
| + const auto now = base::TimeTicks::Now(); |
| + |
| + InterfaceType* handle; |
| + HRESULT hr = async_op->GetResults(&handle); |
| + DCHECK(!FAILED(hr)); |
| + |
| + RegisterOnMessageReceived(handle); |
| + |
| + std::string dev_id = GetDeviceIdString(handle); |
| + |
| + MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id); |
| + |
| + if (port == nullptr) { |
| + base::AutoLock auto_lock(ports_lock_); |
| + |
| + // TODO(shaochuan) |
|
Takashi Toyoshima
2016/08/16 09:30:28
Clear comments for this TODO?
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + AddPort(MidiPortInfo(dev_id, std::string("Manufacturer"), |
| + port_names_[dev_id], std::string("DriverVersion"), |
| + MIDI_PORT_OPENED)); |
| + |
| + port = new MidiPort<InterfaceType>; |
| + port->index = static_cast<uint32_t>(port_ids_.size()); |
| + |
| + ports_[dev_id].reset(port); |
| + port_ids_.push_back(dev_id); |
| + } else { |
| + SetPortState(port->index, MIDI_PORT_CONNECTED); |
| + } |
| + |
| + port->handle = handle; |
| + port->start_time = now; |
| + |
| + { |
| + base::AutoLock auto_lock(async_ops_lock_); |
| + auto it = async_ops_.find(async_op); |
| + DCHECK(it != async_ops_.end()); |
| + async_ops_.erase(it); |
| + } |
| + |
| + return S_OK; |
| + } |
| + |
| + // Implemented by child classes to call corresponding input/output methods. |
|
Takashi Toyoshima
2016/08/16 09:30:28
"to return a corresponding device selector for inp
Shao-Chuan Lee
2016/08/18 08:48:56
This comment was meant to describe all the followi
|
| + virtual HSTRING GetDeviceSelector() = 0; |
| + |
| + virtual void RegisterGetPortFromIdAsync( |
|
Takashi Toyoshima
2016/08/16 09:30:29
Also can you add some comments for other virtual m
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + IAsyncOperation<RuntimeType*>** p_async_op, |
| + HSTRING dev_id) = 0; |
| + |
| + virtual void RegisterOnMessageReceived(InterfaceType* handle) = 0; |
| + |
| + virtual void AddPort(MidiPortInfo info) = 0; |
| + |
| + virtual void SetPortState(uint32_t port_index, MidiPortState state) = 0; |
| + |
| + WRL::ComPtr<IDeviceWatcher> watcher_; |
| + EventRegistrationToken token_Added_, token_EnumerationCompleted_, |
| + token_Removed_, token_Stopped_, token_Updated_; |
| + |
| + base::Lock ports_lock_; |
|
Takashi Toyoshima
2016/08/16 09:30:29
Can you explain (== add comments) why locks are ne
Shao-Chuan Lee
2016/08/18 08:48:56
Locks are required since fields are manipulated fr
|
| + base::hash_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>> |
| + ports_; // GUARDED_BY(ports_lock_) |
| + std::vector<std::string> port_ids_; // GUARDED_BY(ports_lock_) |
| + base::hash_map<std::string, std::string> |
| + port_names_; // GUARDED_BY(ports_lock_) |
| + |
| + base::Lock async_ops_lock_; |
| + std::set<WRL::ComPtr<IAsyncOperation<RuntimeType*>>> |
| + async_ops_; // GUARDED_BY(async_ops_lock_) |
| +}; |
| + |
| +} // namespace |
| + |
| +class MidiManagerWinrt::MidiInPortManager final |
| + : public MidiPortManager<IMidiInPort, MidiInPort> { |
| + public: |
| + MidiInPortManager(MidiManagerWinrt* midi_manager) |
| + : midi_manager_(midi_manager) {} |
| + |
| + private: |
| + // MidiPortManager overrides: |
| + HSTRING GetDeviceSelector() override { |
| + HSTRING selector = nullptr; |
| + HRESULT hr = GetMidiInPortStatics()->GetDeviceSelector(&selector); |
| + DCHECK(!FAILED(hr)); |
| + return selector; |
| + } |
| + |
| + void RegisterGetPortFromIdAsync(IAsyncOperation<MidiInPort*>** p_async_op, |
| + HSTRING dev_id) override { |
| + HRESULT hr = GetMidiInPortStatics()->FromIdAsync(dev_id, p_async_op); |
| + DCHECK(!FAILED(hr)); |
| + } |
| + |
| + void RegisterOnMessageReceived(IMidiInPort* handle) override { |
| + base::AutoLock auto_lock(tokens_lock_); |
| + EventRegistrationToken& token = tokens_[GetDeviceIdString(handle)]; |
| + |
| + handle->add_MessageReceived( |
| + WRL::Callback< |
| + ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>( |
| + std::bind(&MidiInPortManager::OnMessageReceived, this, _1, _2)) |
| + .Get(), |
| + &token); |
| + } |
| + |
| + void AddPort(MidiPortInfo info) { midi_manager_->AddInputPort(info); } |
| + |
| + void SetPortState(uint32_t port_index, MidiPortState state) { |
| + midi_manager_->SetInputPortState(port_index, state); |
|
Takashi Toyoshima
2016/08/16 09:30:29
Probably, this call should be called on the I/O th
Shao-Chuan Lee
2016/08/18 08:48:56
This is currently called on the OS callback thread
Takashi Toyoshima
2016/08/18 11:09:21
Basically, if you can not find a clear comment, it
|
| + } |
| + |
| + // Callback on receiving MIDI input message. |
| + HRESULT OnMessageReceived(IMidiInPort* handle, |
| + IMidiMessageReceivedEventArgs* args) { |
| + MidiPort<IMidiInPort>* port = GetPortByDeviceId(GetDeviceIdString(handle)); |
| + DCHECK(port != nullptr); |
| + |
| + WRL::ComPtr<IMidiMessage> message; |
| + HRESULT hr = args->get_Message(&message); |
| + DCHECK(!FAILED(hr)); |
| + |
| + WRL::ComPtr<IBuffer> buffer; |
| + hr = message->get_RawData(&buffer); |
| + DCHECK(!FAILED(hr)); |
| + |
| + // Use BufferByteAccess object to access buffer memory directly. |
| + WRL::ComPtr<IInspectable> insp( |
|
Takashi Toyoshima
2016/08/16 09:30:29
Shall we have a clear name?
Shao-Chuan Lee
2016/08/18 08:48:56
Moved to a separate function, using `buffer_byte_a
|
| + reinterpret_cast<IInspectable*>(buffer.Get())); |
| + WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bba; |
|
Takashi Toyoshima
2016/08/16 09:30:29
ditto
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + hr = insp.As(&bba); |
| + DCHECK(!FAILED(hr)); |
| + |
| + uint8_t* data_arr = nullptr; |
|
Takashi Toyoshima
2016/08/16 09:30:29
ditto
Shao-Chuan Lee
2016/08/18 08:48:56
Using `p_buffer_data'.
|
| + hr = bba->Buffer(reinterpret_cast<byte**>(&data_arr)); |
|
Shao-Chuan Lee
2016/08/18 08:48:56
Removing reinterpret_cast here since byte** is com
|
| + DCHECK(!FAILED(hr)); |
| + |
| + uint32_t len; |
|
Takashi Toyoshima
2016/08/16 09:30:29
probably we may also want to avoid the name like l
Shao-Chuan Lee
2016/08/18 08:48:57
Using `data_length'.
|
| + hr = buffer->get_Length(&len); |
| + DCHECK(!FAILED(hr)); |
| + DCHECK(len != 0); |
| + |
| + std::vector<uint8_t> data(data_arr, data_arr + len); |
| + |
| + // Time since port opened in 100-nanosecond units. |
| + TimeSpan time_span; |
| + hr = message->get_Timestamp(&time_span); |
| + DCHECK(!FAILED(hr)); |
| + |
| + midi_manager_->ReceiveMidiData( |
| + port->index, &data[0], data.size(), |
| + port->start_time + |
|
Takashi Toyoshima
2016/08/16 09:30:29
Can we check if how much start_time+time_span can
Shao-Chuan Lee
2016/08/18 08:48:57
message->get_Timestamp() returns "the duration fro
|
| + base::TimeDelta::FromMicroseconds(time_span.Duration / 10)); |
| + |
| + return S_OK; |
| + } |
| + |
| + MidiManagerWinrt* midi_manager_; |
| + |
| + base::Lock tokens_lock_; |
| + base::hash_map<std::string, EventRegistrationToken> |
| + tokens_; // GUARDED_BY(tokens_lock_) |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MidiInPortManager); |
| +}; |
| + |
| +class MidiManagerWinrt::MidiOutPortManager final |
| + : public MidiPortManager<IMidiOutPort, IMidiOutPort> { |
| + public: |
| + MidiOutPortManager(MidiManagerWinrt* midi_manager) |
| + : midi_manager_(midi_manager) {} |
| + |
| + private: |
| + // MidiPortManager overrides: |
| + HSTRING GetDeviceSelector() override { |
| + HSTRING selector = nullptr; |
| + HRESULT hr = GetMidiOutPortStatics()->GetDeviceSelector(&selector); |
| + DCHECK(!FAILED(hr)); |
| + return selector; |
| + } |
| + |
| + void RegisterGetPortFromIdAsync(IAsyncOperation<IMidiOutPort*>** p_async_op, |
| + HSTRING dev_id) override { |
| + HRESULT hr = GetMidiOutPortStatics()->FromIdAsync(dev_id, p_async_op); |
| + DCHECK(!FAILED(hr)); |
| + } |
| + |
| + void RegisterOnMessageReceived(IMidiOutPort* handle) override {} |
| + |
| + void AddPort(MidiPortInfo info) { midi_manager_->AddOutputPort(info); } |
| + |
| + void SetPortState(uint32_t port_index, MidiPortState state) { |
| + midi_manager_->SetOutputPortState(port_index, state); |
| + } |
| + |
| + MidiManagerWinrt* midi_manager_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager); |
| +}; |
| + |
| +MidiManagerWinrt::MidiManagerWinrt() : com_thread_("Windows MIDI COM Thread") {} |
| + |
| +MidiManagerWinrt::~MidiManagerWinrt() {} |
| + |
| +void MidiManagerWinrt::StartInitialization() { |
| + DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN10); |
| + |
| + scheduler_.reset(new MidiScheduler(this)); |
| + |
| + com_thread_.init_com_with_mta(true); |
| + com_thread_.Start(); |
| + |
| + com_thread_.message_loop()->PostTask( |
| + FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread, |
| + base::Unretained(this))); |
| + |
| + CompleteInitialization(Result::OK); |
| +} |
| + |
| +void MidiManagerWinrt::Finalize() { |
| + // TODO(shaochuan): Check if everything is destructed gracefully. |
| + port_manager_in_.reset(); |
| + port_manager_out_.reset(); |
| + |
| + scheduler_.reset(); |
| + |
| + com_thread_.Stop(); |
| +} |
| + |
| +void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client, |
| + uint32_t port_index, |
| + const std::vector<uint8_t>& data, |
| + double timestamp) { |
| + com_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&MidiManagerWinrt::PostSendDataTaskOnComThread, |
| + base::Unretained(this), client, port_index, data, timestamp)); |
| +} |
| + |
| +void MidiManagerWinrt::AssertOnComThread() { |
| + DCHECK_EQ(com_thread_.GetThreadId(), base::PlatformThread::CurrentId()); |
|
Takashi Toyoshima
2016/08/16 09:30:29
Ah, you may want to replace this with the thread c
Takashi Toyoshima
2016/08/18 11:09:21
not fixed yet?
|
| +} |
| + |
| +void MidiManagerWinrt::InitializeOnComThread() { |
| + AssertOnComThread(); |
| + |
| + port_manager_in_.reset(new MidiInPortManager(this)); |
| + port_manager_out_.reset(new MidiOutPortManager(this)); |
| + |
| + port_manager_in_->StartWatcher(); |
| + port_manager_out_->StartWatcher(); |
| +} |
| + |
| +void MidiManagerWinrt::PostSendDataTaskOnComThread( |
| + MidiManagerClient* client, |
| + uint32_t port_index, |
| + const std::vector<uint8_t>& data, |
| + double timestamp) { |
| + AssertOnComThread(); |
| + |
| + scheduler_->PostSendDataTask( |
| + client, data.size(), timestamp, |
| + base::Bind(&MidiManagerWinrt::SendOnComThread, base::Unretained(this), |
| + port_index, data)); |
| +} |
| + |
| +void MidiManagerWinrt::SendOnComThread(uint32_t port_index, |
| + const std::vector<uint8_t>& data) { |
| + AssertOnComThread(); |
| + |
| + WRL::ComPtr<IBuffer> buffer; |
| + HRESULT hr = |
| + GetBufferFactory()->Create(static_cast<UINT32>(data.size()), &buffer); |
| + DCHECK(!FAILED(hr)); |
| + |
| + hr = buffer->put_Length(static_cast<UINT32>(data.size())); |
| + DCHECK(!FAILED(hr)); |
| + |
| + // Use BufferByteAccess object to access buffer memory directly. |
|
Takashi Toyoshima
2016/08/16 09:30:29
Clear names.
Shao-Chuan Lee
2016/08/18 08:48:56
Done.
|
| + WRL::ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(buffer.Get())); |
| + WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bba; |
| + hr = insp.As(&bba); |
| + DCHECK(!FAILED(hr)); |
| + |
| + uint8_t* data_arr = nullptr; |
| + hr = bba->Buffer(reinterpret_cast<byte**>(&data_arr)); |
|
Shao-Chuan Lee
2016/08/18 08:48:56
ditto
|
| + DCHECK(!FAILED(hr)); |
| + |
| + std::copy(data.begin(), data.end(), data_arr); |
| + |
| + MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index); |
| + DCHECK(port != nullptr); |
| + |
| + hr = port->handle->SendBuffer(buffer.Get()); |
| + DCHECK(!FAILED(hr)); |
| +} |
| + |
| +MidiManager* MidiManager::Create() { |
| + return new MidiManagerWinrt(); |
| +} |
| + |
| +} // namespace midi |
| +} // namespace media |