Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: media/midi/midi_manager_winrt.cc

Issue 2243183002: Web MIDI backend for Windows 10 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/midi/midi_manager_winrt.h ('k') | media/midi/midi_options.gni » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // 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
7 #include <robuffer.h>
8 #include <windows.devices.enumeration.h>
9 #include <windows.devices.midi.h>
10 #include <wrl/event.h>
11
12 #include "base/bind.h"
13 #include "base/containers/hash_tables.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/timer/timer.h"
17 #include "base/win/windows_version.h"
18
19 namespace media {
20 namespace midi {
21 namespace {
22
23 namespace WRL = Microsoft::WRL;
24
25 using namespace ABI::Windows::Devices::Enumeration;
26 using namespace ABI::Windows::Devices::Midi;
27 using namespace ABI::Windows::Foundation;
28 using namespace ABI::Windows::Storage::Streams;
29
30 // Factory functions that activate and create WinRT components. The caller takes
31 // ownership of the returning ComPtr.
32 template <typename InterfaceType, base::char16 const* runtime_class_id>
33 WRL::ComPtr<InterfaceType> WrlStaticsFactory() {
Shao-Chuan Lee 2016/08/18 08:57:50 Missing TODO: try replacing WRL::ComPtr with base:
34 WRL::ComPtr<InterfaceType> com_ptr;
35
36 HRESULT hr = GetActivationFactory(
37 WRL::Wrappers::HStringReference(runtime_class_id).Get(), &com_ptr);
38 DCHECK(SUCCEEDED(hr));
39
40 return std::move(com_ptr);
Shao-Chuan Lee 2016/08/22 06:53:17 std::move not required, NRVO applies here.
Shao-Chuan Lee 2016/08/23 04:29:04 Done.
41 }
42
43 WRL::ComPtr<IBufferFactory> GetBufferFactory() {
44 return std::move(
Shao-Chuan Lee 2016/08/22 06:53:17 ditto
Shao-Chuan Lee 2016/08/23 04:29:04 Done.
45 WrlStaticsFactory<IBufferFactory,
46 RuntimeClass_Windows_Storage_Streams_Buffer>());
47 }
48
49 WRL::ComPtr<IDeviceInformationStatics> GetDeviceInformationStatics() {
50 return std::move(
Shao-Chuan Lee 2016/08/22 06:53:17 ditto
Shao-Chuan Lee 2016/08/23 04:29:04 Done.
51 WrlStaticsFactory<
52 IDeviceInformationStatics,
53 RuntimeClass_Windows_Devices_Enumeration_DeviceInformation>());
54 }
55
56 template <typename T, HRESULT (T::*method)(HSTRING*)>
57 std::string GetStringFromObjectMethod(T* obj) {
58 HSTRING result;
59 HRESULT hr = (obj->*method)(&result);
60 DCHECK(SUCCEEDED(hr));
61
62 const base::char16* buffer = WindowsGetStringRawBuffer(result, nullptr);
63 if (buffer) {
Takashi Toyoshima 2016/08/18 11:09:22 You don't need "{}" for one line.
Shao-Chuan Lee 2016/08/22 06:53:17 Done.
64 return base::WideToUTF8(buffer);
65 }
66 return "";
Takashi Toyoshima 2016/08/18 11:09:22 returning std::string() is recommended to return a
Shao-Chuan Lee 2016/08/22 06:53:17 Done.
67 }
68
69 template <typename T>
70 std::string GetIdString(T* obj) {
71 return GetStringFromObjectMethod<T, &T::get_Id>(obj);
72 }
73
74 template <typename T>
75 std::string GetDeviceIdString(T* obj) {
76 return GetStringFromObjectMethod<T, &T::get_DeviceId>(obj);
77 }
78
79 std::string GetNameString(IDeviceInformation* info) {
80 return GetStringFromObjectMethod<IDeviceInformation,
81 &IDeviceInformation::get_Name>(info);
82 }
83
84 uint8_t* GetPointerToBufferData(IBuffer* buffer) {
85 WRL::ComPtr<IInspectable> inspectable(
86 reinterpret_cast<IInspectable*>(buffer));
87 WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> buffer_byte_access;
88
89 HRESULT hr = inspectable.As(&buffer_byte_access);
90 DCHECK(SUCCEEDED(hr));
91
92 uint8_t* ptr = nullptr;
93 hr = buffer_byte_access->Buffer(&ptr);
94 DCHECK(SUCCEEDED(hr));
95
96 // Lifetime of the pointing buffer is controlled by the buffer object.
97 return ptr;
98 }
99
100 template <typename InterfaceType>
101 struct MidiPort {
102 MidiPort() = default;
103
104 uint32_t index;
105 WRL::ComPtr<InterfaceType> handle;
106 base::TimeTicks start_time;
107
108 private:
109 DISALLOW_COPY_AND_ASSIGN(MidiPort);
110 };
111
112 template <typename InterfaceType,
113 typename RuntimeType,
114 typename StaticsInterfaceType,
115 base::char16 const* runtime_class_id>
116 class MidiPortManager {
117 public:
118 MidiPortManager() = default;
119
120 void StartWatcher() {
121 HRESULT hr;
122
123 HSTRING device_selector = nullptr;
124 hr = WrlStaticsFactory<StaticsInterfaceType, runtime_class_id>()
Shao-Chuan Lee 2016/08/18 08:57:50 Retrieve object from WrlStaticsFactory at construc
Shao-Chuan Lee 2016/08/23 04:29:04 Done.
125 ->GetDeviceSelector(&device_selector);
126 DCHECK(SUCCEEDED(hr));
127
128 hr = GetDeviceInformationStatics()->CreateWatcherAqsFilter(device_selector,
129 &watcher_);
130 DCHECK(SUCCEEDED(hr));
131
132 hr = watcher_->add_Added(
133 WRL::Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>>(
134 this, &MidiPortManager::OnAdded)
135 .Get(),
136 &token_Added_);
137 DCHECK(SUCCEEDED(hr));
138
139 hr = watcher_->add_EnumerationCompleted(
140 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
141 this, &MidiPortManager::OnEnumerationCompleted)
142 .Get(),
143 &token_EnumerationCompleted_);
144 DCHECK(SUCCEEDED(hr));
145
146 hr = watcher_->add_Removed(
147 WRL::Callback<
148 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
149 this, &MidiPortManager::OnRemoved)
150 .Get(),
151 &token_Removed_);
152 DCHECK(SUCCEEDED(hr));
153
154 hr = watcher_->add_Stopped(
155 WRL::Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>>(
156 this, &MidiPortManager::OnStopped)
157 .Get(),
158 &token_Stopped_);
159 DCHECK(SUCCEEDED(hr));
160
161 hr = watcher_->add_Updated(
162 WRL::Callback<
163 ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>>(
164 this, &MidiPortManager::OnUpdated)
165 .Get(),
166 &token_Updated_);
167 DCHECK(SUCCEEDED(hr));
168
169 hr = watcher_->Start();
170 DCHECK(SUCCEEDED(hr));
171 }
172
173 MidiPort<InterfaceType>* GetPortByDeviceId(std::string dev_id) {
174 base::AutoLock auto_lock(ports_lock_);
175 auto it = ports_.find(dev_id);
176 if (it == ports_.end())
177 return nullptr;
178 return it->second.get();
179 }
180
181 MidiPort<InterfaceType>* GetPortByIndex(uint32_t port_index) {
182 base::AutoLock auto_lock(ports_lock_);
183 auto it = ports_.find(port_ids_[port_index]);
184 if (it == ports_.end())
185 return nullptr;
186 return it->second.get();
187 }
188
189 // DeviceWatcher callbacks:
190 HRESULT OnAdded(IDeviceWatcher* watcher, IDeviceInformation* info) {
191 // TODO(shaochuan): Disable Microsoft GS Wavetable Synth due to security
192 // reasons. http://crbug.com/499279
193
194 {
195 base::AutoLock auto_lock(ports_lock_);
196 port_names_[GetIdString(info)] = GetNameString(info);
197 }
198
199 HSTRING dev_id_hstring;
200 HRESULT hr = info->get_Id(&dev_id_hstring);
201 DCHECK(SUCCEEDED(hr));
202
203 WRL::ComPtr<IAsyncOperation<RuntimeType*>> async_op;
204
205 hr = WrlStaticsFactory<StaticsInterfaceType, runtime_class_id>()
206 ->FromIdAsync(dev_id_hstring, &async_op);
207 DCHECK(SUCCEEDED(hr));
208
209 hr = async_op->put_Completed(
210 WRL::Callback<IAsyncOperationCompletedHandler<RuntimeType*>>(
211 this, &MidiPortManager::OnCompletedGetPortFromIdAsync)
212 .Get());
213 DCHECK(SUCCEEDED(hr));
214
215 {
216 base::AutoLock auto_lock(async_ops_lock_);
217 async_ops_.insert(std::move(async_op));
218 }
219
220 return S_OK;
221 }
222
223 HRESULT OnEnumerationCompleted(IDeviceWatcher* watcher, IInspectable* insp) {
224 // TODO(shaochuan)
225 return S_OK;
226 }
227
228 HRESULT OnRemoved(IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
229 MidiPort<InterfaceType>* port = GetPortByDeviceId(GetIdString(update));
230 DCHECK(port != nullptr);
231
232 SetPortState(port->index, MIDI_PORT_DISCONNECTED);
233
234 port->handle = nullptr;
235
236 return S_OK;
237 }
238
239 HRESULT OnStopped(IDeviceWatcher* watcher, IInspectable* insp) {
240 return S_OK;
241 }
242
243 HRESULT OnUpdated(IDeviceWatcher* watcher, IDeviceInformationUpdate* update) {
244 // TODO(shaochuan): Check for fields to be updated here.
245 return S_OK;
246 }
247
248 private:
249 HRESULT OnCompletedGetPortFromIdAsync(IAsyncOperation<RuntimeType*>* async_op,
250 AsyncStatus status) {
251 // TODO(shaochuan): Check if port open time is accurate.
252 const auto now = base::TimeTicks::Now();
253
254 InterfaceType* handle;
255 HRESULT hr = async_op->GetResults(&handle);
256 DCHECK(SUCCEEDED(hr));
257
258 RegisterOnMessageReceived(handle);
259
260 std::string dev_id = GetDeviceIdString(handle);
261
262 MidiPort<InterfaceType>* port = GetPortByDeviceId(dev_id);
263
264 if (port == nullptr) {
265 base::AutoLock auto_lock(ports_lock_);
266
267 // TODO(shaochuan): Fill in manufacturer and driver version.
268 AddPort(MidiPortInfo(dev_id, std::string("Manufacturer"),
269 port_names_[dev_id], std::string("DriverVersion"),
270 MIDI_PORT_OPENED));
271
272 port = new MidiPort<InterfaceType>;
273 port->index = static_cast<uint32_t>(port_ids_.size());
274
275 ports_[dev_id].reset(port);
276 port_ids_.push_back(dev_id);
277 } else {
278 SetPortState(port->index, MIDI_PORT_CONNECTED);
279 }
280
281 port->handle = handle;
282 port->start_time = now;
283
284 {
285 base::AutoLock auto_lock(async_ops_lock_);
286 auto it = async_ops_.find(async_op);
287 DCHECK(it != async_ops_.end());
288 async_ops_.erase(it);
289 }
290
291 return S_OK;
292 }
293
294 virtual void RegisterOnMessageReceived(InterfaceType* handle) {}
295
296 // Calls midi_manager_->Add{Input,Output}Port.
297 virtual void AddPort(MidiPortInfo info) = 0;
298
299 // Calls midi_manager_->Set{Input,Output}PortState.
300 virtual void SetPortState(uint32_t port_index, MidiPortState state) = 0;
301
302 WRL::ComPtr<IDeviceWatcher> watcher_;
303 EventRegistrationToken token_Added_, token_EnumerationCompleted_,
304 token_Removed_, token_Stopped_, token_Updated_;
305
306 // Locks required since fields are being manipulated by callbacks from WinRT
307 // on the OS callback thread.
308 base::Lock ports_lock_;
309 base::hash_map<std::string, std::unique_ptr<MidiPort<InterfaceType>>>
310 ports_; // GUARDED_BY(ports_lock_)
311 std::vector<std::string> port_ids_; // GUARDED_BY(ports_lock_)
312 base::hash_map<std::string, std::string>
313 port_names_; // GUARDED_BY(ports_lock_)
314
315 // Keeps AsyncOperation objects before the operation completes.
316 base::Lock async_ops_lock_;
317 std::set<WRL::ComPtr<IAsyncOperation<RuntimeType*>>>
318 async_ops_; // GUARDED_BY(async_ops_lock_)
319 };
320
321 } // namespace
322
323 class MidiManagerWinrt::MidiInPortManager final
324 : public MidiPortManager<IMidiInPort,
325 MidiInPort,
326 IMidiInPortStatics,
327 RuntimeClass_Windows_Devices_Midi_MidiInPort> {
328 public:
329 MidiInPortManager(MidiManagerWinrt* midi_manager)
330 : midi_manager_(midi_manager) {}
331
332 private:
333 // MidiPortManager overrides:
334 void RegisterOnMessageReceived(IMidiInPort* handle) override {
335 base::AutoLock auto_lock(tokens_lock_);
336 EventRegistrationToken& token = tokens_[GetDeviceIdString(handle)];
337
338 handle->add_MessageReceived(
339 WRL::Callback<
340 ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>>(
341 this, &MidiInPortManager::OnMessageReceived)
342 .Get(),
343 &token);
344 }
345
346 void AddPort(MidiPortInfo info) { midi_manager_->AddInputPort(info); }
347
348 void SetPortState(uint32_t port_index, MidiPortState state) {
349 midi_manager_->SetInputPortState(port_index, state);
350 }
351
352 // Callback on receiving MIDI input message.
353 HRESULT OnMessageReceived(IMidiInPort* handle,
354 IMidiMessageReceivedEventArgs* args) {
355 MidiPort<IMidiInPort>* port = GetPortByDeviceId(GetDeviceIdString(handle));
356 DCHECK(port != nullptr);
357
358 WRL::ComPtr<IMidiMessage> message;
359 HRESULT hr = args->get_Message(&message);
360 DCHECK(SUCCEEDED(hr));
361
362 WRL::ComPtr<IBuffer> buffer;
363 hr = message->get_RawData(&buffer);
364 DCHECK(SUCCEEDED(hr));
365
366 uint8_t* p_buffer_data = GetPointerToBufferData(buffer.Get());
367
368 uint32_t data_length;
369 hr = buffer->get_Length(&data_length);
370 DCHECK(SUCCEEDED(hr));
371 DCHECK(data_length != 0);
372
373 std::vector<uint8_t> data(p_buffer_data, p_buffer_data + data_length);
374
375 // Time since port opened in 100-nanosecond units.
376 TimeSpan time_span;
377 hr = message->get_Timestamp(&time_span);
378 DCHECK(SUCCEEDED(hr));
379
380 midi_manager_->ReceiveMidiData(
381 port->index, &data[0], data.size(),
382 port->start_time +
383 base::TimeDelta::FromMicroseconds(time_span.Duration / 10));
384
385 return S_OK;
386 }
387
388 MidiManagerWinrt* midi_manager_;
389
390 base::Lock tokens_lock_;
391 base::hash_map<std::string, EventRegistrationToken>
392 tokens_; // GUARDED_BY(tokens_lock_)
393
394 DISALLOW_COPY_AND_ASSIGN(MidiInPortManager);
395 };
396
397 class MidiManagerWinrt::MidiOutPortManager final
398 : public MidiPortManager<IMidiOutPort,
399 IMidiOutPort,
400 IMidiOutPortStatics,
401 RuntimeClass_Windows_Devices_Midi_MidiOutPort> {
402 public:
403 MidiOutPortManager(MidiManagerWinrt* midi_manager)
404 : midi_manager_(midi_manager) {}
405
406 private:
407 // MidiPortManager overrides:
408 void AddPort(MidiPortInfo info) { midi_manager_->AddOutputPort(info); }
409
410 void SetPortState(uint32_t port_index, MidiPortState state) {
411 midi_manager_->SetOutputPortState(port_index, state);
412 }
413
414 MidiManagerWinrt* midi_manager_;
415
416 DISALLOW_COPY_AND_ASSIGN(MidiOutPortManager);
417 };
418
419 MidiManagerWinrt::MidiManagerWinrt() : com_thread_("Windows MIDI COM Thread") {}
420
421 MidiManagerWinrt::~MidiManagerWinrt() {}
422
423 void MidiManagerWinrt::StartInitialization() {
Takashi Toyoshima 2016/08/18 11:09:22 You need a lock object to access class members to
424 DCHECK(base::win::GetVersion() >= base::win::VERSION_WIN10);
425
426 scheduler_.reset(new MidiScheduler(this));
Takashi Toyoshima 2016/08/18 11:09:21 Now scheduler_ is constructed on the I/O thread, a
Shao-Chuan Lee 2016/08/22 06:53:17 Changes in separate CL https://codereview.chromium
427
428 com_thread_.init_com_with_mta(true);
429 com_thread_.Start();
430
431 com_thread_.message_loop()->PostTask(
Takashi Toyoshima 2016/08/18 11:09:22 Instead of message_loop(), task_runner() is recomm
Shao-Chuan Lee 2016/08/22 06:53:17 Done.
432 FROM_HERE, base::Bind(&MidiManagerWinrt::InitializeOnComThread,
433 base::Unretained(this)));
434
435 CompleteInitialization(Result::OK);
Takashi Toyoshima 2016/08/18 11:09:22 As I commented for the previous patch set, it's fi
Shao-Chuan Lee 2016/08/23 04:29:04 Now called when both MidiPortManagers are ready (O
436 }
437
438 void MidiManagerWinrt::Finalize() {
439 // TODO(shaochuan): Check if everything is destructed gracefully.
Takashi Toyoshima 2016/08/18 11:09:21 As I commented for StartInitialization(), we need
440 port_manager_in_.reset();
Takashi Toyoshima 2016/08/18 11:09:22 Since these are initialized on the com thread, the
Shao-Chuan Lee 2016/08/23 04:29:04 Done.
441 port_manager_out_.reset();
442
443 scheduler_.reset();
444
445 com_thread_.Stop();
446 }
447
448 void MidiManagerWinrt::DispatchSendMidiData(MidiManagerClient* client,
449 uint32_t port_index,
450 const std::vector<uint8_t>& data,
451 double timestamp) {
452 com_thread_.message_loop()->PostTask(
Takashi Toyoshima 2016/08/18 11:09:21 task_runner
Shao-Chuan Lee 2016/08/22 06:53:17 Done.
453 FROM_HERE,
454 base::Bind(&MidiManagerWinrt::PostSendDataTaskOnComThread,
455 base::Unretained(this), client, port_index, data, timestamp));
456 }
457
458 void MidiManagerWinrt::AssertOnComThread() {
459 DCHECK_EQ(com_thread_.GetThreadId(), base::PlatformThread::CurrentId());
Takashi Toyoshima 2016/08/18 11:09:22 Use base/threading/therad_checker instead of havin
Shao-Chuan Lee 2016/08/22 06:53:17 Done.
460 }
461
462 void MidiManagerWinrt::InitializeOnComThread() {
463 AssertOnComThread();
464
465 port_manager_in_.reset(new MidiInPortManager(this));
466 port_manager_out_.reset(new MidiOutPortManager(this));
467
468 port_manager_in_->StartWatcher();
469 port_manager_out_->StartWatcher();
470 }
471
472 void MidiManagerWinrt::PostSendDataTaskOnComThread(
473 MidiManagerClient* client,
474 uint32_t port_index,
475 const std::vector<uint8_t>& data,
476 double timestamp) {
477 AssertOnComThread();
478
479 scheduler_->PostSendDataTask(
480 client, data.size(), timestamp,
481 base::Bind(&MidiManagerWinrt::SendOnComThread, base::Unretained(this),
482 port_index, data));
483 }
484
485 void MidiManagerWinrt::SendOnComThread(uint32_t port_index,
486 const std::vector<uint8_t>& data) {
487 AssertOnComThread();
488
489 WRL::ComPtr<IBuffer> buffer;
490 HRESULT hr =
491 GetBufferFactory()->Create(static_cast<UINT32>(data.size()), &buffer);
492 DCHECK(SUCCEEDED(hr));
493
Takashi Toyoshima 2016/08/18 11:09:22 Is it fine to continue following steps when HRESUL
Shao-Chuan Lee 2016/08/23 04:29:04 I guess buffer allocation may fail on cases such a
494 hr = buffer->put_Length(static_cast<UINT32>(data.size()));
495 DCHECK(SUCCEEDED(hr));
496
497 uint8_t* p_buffer_data = GetPointerToBufferData(buffer.Get());
498
499 std::copy(data.begin(), data.end(), p_buffer_data);
500
501 MidiPort<IMidiOutPort>* port = port_manager_out_->GetPortByIndex(port_index);
502 DCHECK(port != nullptr);
503
504 hr = port->handle->SendBuffer(buffer.Get());
505 DCHECK(SUCCEEDED(hr));
506 }
507
508 MidiManager* MidiManager::Create() {
509 return new MidiManagerWinrt();
510 }
511
512 } // namespace midi
513 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_winrt.h ('k') | media/midi/midi_options.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698