OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/midi/midi_manager_win.h" | 5 #include "media/midi/midi_manager_win.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <dbt.h> | |
9 #include <ks.h> | 8 #include <ks.h> |
10 #include <ksmedia.h> | 9 #include <ksmedia.h> |
11 #include <mmreg.h> | 10 #include <mmreg.h> |
12 // Prevent unnecessary functions from being included from <mmsystem.h> | 11 // Prevent unnecessary functions from being included from <mmsystem.h> |
13 #define MMNODRV | 12 #define MMNODRV |
14 #define MMNOSOUND | 13 #define MMNOSOUND |
15 #define MMNOWAVE | 14 #define MMNOWAVE |
16 #define MMNOAUX | 15 #define MMNOAUX |
17 #define MMNOMIXER | 16 #define MMNOMIXER |
18 #define MMNOTIMER | 17 #define MMNOTIMER |
(...skipping 10 matching lines...) Expand all Loading... |
29 #include "base/basictypes.h" | 28 #include "base/basictypes.h" |
30 #include "base/bind.h" | 29 #include "base/bind.h" |
31 #include "base/containers/hash_tables.h" | 30 #include "base/containers/hash_tables.h" |
32 #include "base/memory/scoped_ptr.h" | 31 #include "base/memory/scoped_ptr.h" |
33 #include "base/message_loop/message_loop.h" | 32 #include "base/message_loop/message_loop.h" |
34 #include "base/strings/string16.h" | 33 #include "base/strings/string16.h" |
35 #include "base/strings/string_number_conversions.h" | 34 #include "base/strings/string_number_conversions.h" |
36 #include "base/strings/string_piece.h" | 35 #include "base/strings/string_piece.h" |
37 #include "base/strings/stringprintf.h" | 36 #include "base/strings/stringprintf.h" |
38 #include "base/strings/utf_string_conversions.h" | 37 #include "base/strings/utf_string_conversions.h" |
| 38 #include "base/system_monitor/system_monitor.h" |
39 #include "base/threading/thread.h" | 39 #include "base/threading/thread.h" |
40 #include "base/threading/thread_checker.h" | 40 #include "base/threading/thread_checker.h" |
41 #include "base/timer/timer.h" | 41 #include "base/timer/timer.h" |
42 #include "base/win/message_window.h" | 42 #include "base/win/message_window.h" |
43 #include "device/usb/usb_ids.h" | 43 #include "device/usb/usb_ids.h" |
44 #include "media/midi/midi_manager.h" | 44 #include "media/midi/midi_manager.h" |
45 #include "media/midi/midi_message_queue.h" | 45 #include "media/midi/midi_message_queue.h" |
46 #include "media/midi/midi_message_util.h" | 46 #include "media/midi/midi_message_util.h" |
47 #include "media/midi/midi_port_info.h" | 47 #include "media/midi/midi_port_info.h" |
48 | 48 |
49 namespace media { | 49 namespace media { |
50 namespace { | 50 namespace { |
51 | 51 |
52 const wchar_t kWindowClassName[] = L"ChromeMidiDeviceMonitorWindow"; | |
53 const int kOnDeviceArrivalDelayMilliSec = 500; | |
54 static const size_t kBufferLength = 32 * 1024; | 52 static const size_t kBufferLength = 32 * 1024; |
55 | 53 |
56 // We assume that nullpter represents an invalid MIDI handle. | 54 // We assume that nullpter represents an invalid MIDI handle. |
57 const HMIDIIN kInvalidMidiInHandle = nullptr; | 55 const HMIDIIN kInvalidMidiInHandle = nullptr; |
58 const HMIDIOUT kInvalidMidiOutHandle = nullptr; | 56 const HMIDIOUT kInvalidMidiOutHandle = nullptr; |
59 | 57 |
60 // Note that |RegisterDeviceNotificationW| returns nullptr as an invalid handle. | |
61 const HDEVNOTIFY kInvalidDeviceNotifyHandle = nullptr; | |
62 | |
63 std::string GetInErrorMessage(MMRESULT result) { | 58 std::string GetInErrorMessage(MMRESULT result) { |
64 wchar_t text[MAXERRORLENGTH]; | 59 wchar_t text[MAXERRORLENGTH]; |
65 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text)); | 60 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text)); |
66 if (get_result != MMSYSERR_NOERROR) { | 61 if (get_result != MMSYSERR_NOERROR) { |
67 DLOG(ERROR) << "Failed to get error message." | 62 DLOG(ERROR) << "Failed to get error message." |
68 << " original error: " << result | 63 << " original error: " << result |
69 << " midiInGetErrorText error: " << get_result; | 64 << " midiInGetErrorText error: " << get_result; |
70 return std::string(); | 65 return std::string(); |
71 } | 66 } |
72 return base::WideToUTF8(text); | 67 return base::WideToUTF8(text); |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); | 279 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); |
285 } | 280 } |
286 static uint16 ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) { | 281 static uint16 ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) { |
287 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid)) | 282 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid)) |
288 return 0; | 283 return 0; |
289 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); | 284 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); |
290 } | 285 } |
291 }; | 286 }; |
292 | 287 |
293 std::string GetManufacturerName(const MidiDeviceInfo& info) { | 288 std::string GetManufacturerName(const MidiDeviceInfo& info) { |
294 if (info.is_usb_device) | 289 if (info.is_usb_device) { |
295 return device::UsbIds::GetVendorName(info.usb_vendor_id); | 290 const char* name = device::UsbIds::GetVendorName(info.usb_vendor_id); |
| 291 return std::string(name ? name : ""); |
| 292 } |
296 | 293 |
297 switch (info.manufacturer_id) { | 294 switch (info.manufacturer_id) { |
298 case MM_MICROSOFT: | 295 case MM_MICROSOFT: |
299 return "Microsoft Corporation"; | 296 return "Microsoft Corporation"; |
300 default: | 297 default: |
301 // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341. | 298 // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341. |
302 return ""; | 299 return ""; |
303 } | 300 } |
304 } | 301 } |
305 | 302 |
306 using PortNumberCache = base::hash_map< | 303 using PortNumberCache = base::hash_map< |
307 MidiDeviceInfo, | 304 MidiDeviceInfo, |
308 std::priority_queue<uint32, std::vector<uint32>, std::greater<uint32>>, | 305 std::priority_queue<uint32, std::vector<uint32>, std::greater<uint32>>, |
309 MidiDeviceInfo::Hasher>; | 306 MidiDeviceInfo::Hasher>; |
310 | 307 |
311 class DeviceMonitorWindow final { | |
312 public: | |
313 explicit DeviceMonitorWindow(base::Closure on_device_arrival) | |
314 : on_device_arrival_(on_device_arrival), | |
315 dev_notify_handle_(kInvalidDeviceNotifyHandle) { | |
316 window_.reset(new base::win::MessageWindow); | |
317 if (!window_->CreateNamed(base::Bind(&DeviceMonitorWindow::HandleMessage, | |
318 base::Unretained(this)), | |
319 base::string16(kWindowClassName))) { | |
320 LOG(ERROR) << "Failed to create message window: " << kWindowClassName; | |
321 window_.reset(); | |
322 } | |
323 DEV_BROADCAST_DEVICEINTERFACE kBloadcastRequest = { | |
324 sizeof(DEV_BROADCAST_DEVICEINTERFACE), DBT_DEVTYP_DEVICEINTERFACE, | |
325 }; | |
326 dev_notify_handle_ = RegisterDeviceNotificationW( | |
327 window_->hwnd(), &kBloadcastRequest, | |
328 DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); | |
329 } | |
330 | |
331 ~DeviceMonitorWindow() { | |
332 if (dev_notify_handle_ != kInvalidDeviceNotifyHandle) { | |
333 BOOL result = UnregisterDeviceNotification(dev_notify_handle_); | |
334 if (!result) { | |
335 const DWORD last_error = GetLastError(); | |
336 DLOG(ERROR) << "UnregisterDeviceNotification failed. error: " | |
337 << last_error; | |
338 } | |
339 dev_notify_handle_ = kInvalidDeviceNotifyHandle; | |
340 } | |
341 } | |
342 | |
343 private: | |
344 bool HandleMessage(UINT message, | |
345 WPARAM wparam, | |
346 LPARAM lparam, | |
347 LRESULT* result) { | |
348 if (message == WM_DEVICECHANGE) { | |
349 switch (wparam) { | |
350 case DBT_DEVICEARRIVAL: { | |
351 device_arrival_timer_.Stop(); | |
352 device_arrival_timer_.Start( | |
353 FROM_HERE, | |
354 base::TimeDelta::FromMilliseconds(kOnDeviceArrivalDelayMilliSec), | |
355 on_device_arrival_); | |
356 break; | |
357 } | |
358 } | |
359 } | |
360 return false; | |
361 } | |
362 | |
363 base::Closure on_device_arrival_; | |
364 base::OneShotTimer<DeviceMonitorWindow> device_arrival_timer_; | |
365 base::ThreadChecker thread_checker_; | |
366 scoped_ptr<base::win::MessageWindow> window_; | |
367 HDEVNOTIFY dev_notify_handle_; | |
368 | |
369 DISALLOW_COPY_AND_ASSIGN(DeviceMonitorWindow); | |
370 }; | |
371 | |
372 struct MidiInputDeviceState final : base::RefCounted<MidiInputDeviceState> { | 308 struct MidiInputDeviceState final : base::RefCounted<MidiInputDeviceState> { |
373 explicit MidiInputDeviceState(const MidiDeviceInfo& device_info) | 309 explicit MidiInputDeviceState(const MidiDeviceInfo& device_info) |
374 : device_info(device_info), | 310 : device_info(device_info), |
375 midi_handle(kInvalidMidiInHandle), | 311 midi_handle(kInvalidMidiInHandle), |
376 port_index(0), | 312 port_index(0), |
377 port_age(0), | 313 port_age(0), |
378 start_time_initialized(false) {} | 314 start_time_initialized(false) {} |
379 | 315 |
380 const MidiDeviceInfo device_info; | 316 const MidiDeviceInfo device_info; |
381 HMIDIIN midi_handle; | 317 HMIDIIN midi_handle; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 uint64 port_age; | 352 uint64 port_age; |
417 // True if the device is already closed and |midi_handle| is considered to be | 353 // True if the device is already closed and |midi_handle| is considered to be |
418 // invalid. | 354 // invalid. |
419 // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium | 355 // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium |
420 // project. | 356 // project. |
421 volatile bool closed; | 357 volatile bool closed; |
422 }; | 358 }; |
423 | 359 |
424 // The core logic of MIDI device handling for Windows. Basically this class is | 360 // The core logic of MIDI device handling for Windows. Basically this class is |
425 // shared among following 4 threads: | 361 // shared among following 4 threads: |
426 // 1. Device Monitor Thread | 362 // 1. Chrome IO Thread |
427 // 2. OS Multimedia Thread | 363 // 2. OS Multimedia Thread |
428 // 3. Misc. Task Thread | 364 // 3. Task Thread |
429 // 4. Sender Thread | 365 // 4. Sender Thread |
430 // | 366 // |
431 // Device Monitor Thread: | 367 // Chrome IO Thread: |
432 // This is a standalone UI thread that is dedicated for a message-only window | 368 // MidiManager runs on Chrome IO thread. Device change notification is |
433 // which will receive WM_DEVICECHANGE Win32 message whose | 369 // delivered to the thread through the SystemMonitor service. |
434 // wParam == DBT_DEVICEARRIVAL. This event will be used as the trigger to open | 370 // OnDevicesChanged() callback is invoked to update the MIDI device list. |
435 // all the new MIDI devices. Note that in the current implementation we will | 371 // Note that in the current implementation we will try to open all the |
436 // try to open all the existing devices in practice. This is OK because trying | 372 // existing devices in practice. This is OK because trying to reopen a MIDI |
437 // to reopen a MIDI device that is already opened would simply fail, and there | 373 // device that is already opened would simply fail, and there is no unwilling |
438 // is no unwilling side effect. Note also that this thread isn't responsible | 374 // side effect. |
439 // for updating the device database. It will be handled by MIM_OPEN/MOM_OPEN | |
440 // events dispatched to OS Multimedia Thread. | |
441 // | 375 // |
442 // OS Multimedia Thread: | 376 // OS Multimedia Thread: |
443 // This thread is maintained by the OS as a part of MIDI runtime, and | 377 // This thread is maintained by the OS as a part of MIDI runtime, and |
444 // responsible for receiving all the system initiated events such as device | 378 // responsible for receiving all the system initiated events such as device |
445 // open and close. For performance reasons, most of potentially blocking | 379 // close, and receiving data. For performance reasons, most of potentially |
446 // operations will be dispatched into Misc. Task Thread. | 380 // blocking operations will be dispatched into Task Thread. |
447 // | 381 // |
448 // Misc. Task Thread: | 382 // Task Thread: |
449 // This thread will be used to call back following methods of MidiManager. | 383 // This thread will be used to call back following methods of MidiManager. |
450 // - MidiManager::CompleteInitialization | 384 // - MidiManager::CompleteInitialization |
451 // - MidiManager::AddInputPort | 385 // - MidiManager::AddInputPort |
452 // - MidiManager::AddOutputPort | 386 // - MidiManager::AddOutputPort |
453 // - MidiManager::SetInputPortState | 387 // - MidiManager::SetInputPortState |
454 // - MidiManager::SetOutputPortState | 388 // - MidiManager::SetOutputPortState |
455 // - MidiManager::ReceiveMidiData | 389 // - MidiManager::ReceiveMidiData |
456 // | 390 // |
457 // Sender Thread: | 391 // Sender Thread: |
458 // This thread will be used to call Win32 APIs to send MIDI message at the | 392 // This thread will be used to call Win32 APIs to send MIDI message at the |
459 // specified time. We don't want to call MIDI send APIs on Misc. Task Thread | 393 // specified time. We don't want to call MIDI send APIs on Task Thread |
460 // because those APIs could be performed synchronously, hence they could block | 394 // because those APIs could be performed synchronously, hence they could block |
461 // the caller thread for a while. See the comment in | 395 // the caller thread for a while. See the comment in |
462 // SendLongMidiMessageInternal for details. Currently we expect that the | 396 // SendLongMidiMessageInternal for details. Currently we expect that the |
463 // blocking time would be less than 1 second. | 397 // blocking time would be less than 1 second. |
464 class MidiServiceWinImpl : public MidiServiceWin { | 398 class MidiServiceWinImpl : public MidiServiceWin, |
| 399 public base::SystemMonitor::DevicesChangedObserver { |
465 public: | 400 public: |
466 MidiServiceWinImpl() | 401 MidiServiceWinImpl() |
467 : delegate_(nullptr), | 402 : delegate_(nullptr), |
468 monitor_thread_("Windows MIDI device monitor thread"), | |
469 sender_thread_("Windows MIDI sender thread"), | 403 sender_thread_("Windows MIDI sender thread"), |
470 task_thread_("Windows MIDI task thread"), | 404 task_thread_("Windows MIDI task thread"), |
471 destructor_started(false) {} | 405 destructor_started(false) {} |
472 | 406 |
473 ~MidiServiceWinImpl() final { | 407 ~MidiServiceWinImpl() final { |
| 408 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and |
| 409 // RemoveDevicesChangeObserver() should be called on the same thread. |
| 410 CHECK(thread_checker_.CalledOnValidThread()); |
| 411 |
474 destructor_started = true; | 412 destructor_started = true; |
475 if (monitor_thread_.IsRunning()) { | 413 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this); |
476 monitor_thread_.message_loop()->PostTask( | |
477 FROM_HERE, | |
478 base::Bind(&MidiServiceWinImpl::StopDeviceMonitorOnMonitorThreadSync, | |
479 base::Unretained(this))); | |
480 } | |
481 { | 414 { |
482 std::vector<HMIDIIN> input_devices; | 415 std::vector<HMIDIIN> input_devices; |
483 { | 416 { |
484 base::AutoLock auto_lock(input_ports_lock_); | 417 base::AutoLock auto_lock(input_ports_lock_); |
485 for (auto it : input_device_map_) | 418 for (auto it : input_device_map_) |
486 input_devices.push_back(it.first); | 419 input_devices.push_back(it.first); |
487 } | 420 } |
488 { | 421 { |
489 for (const auto handle : input_devices) { | 422 for (const auto handle : input_devices) { |
490 MMRESULT result = midiInClose(handle); | 423 MMRESULT result = midiInClose(handle); |
(...skipping 22 matching lines...) Expand all Loading... |
513 result = midiOutReset(handle); | 446 result = midiOutReset(handle); |
514 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | 447 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
515 << "midiOutReset failed: " << GetOutErrorMessage(result); | 448 << "midiOutReset failed: " << GetOutErrorMessage(result); |
516 result = midiOutClose(handle); | 449 result = midiOutClose(handle); |
517 } | 450 } |
518 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | 451 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
519 << "midiOutClose failed: " << GetOutErrorMessage(result); | 452 << "midiOutClose failed: " << GetOutErrorMessage(result); |
520 } | 453 } |
521 } | 454 } |
522 } | 455 } |
523 monitor_thread_.Stop(); | |
524 sender_thread_.Stop(); | 456 sender_thread_.Stop(); |
525 task_thread_.Stop(); | 457 task_thread_.Stop(); |
526 } | 458 } |
527 | 459 |
528 // MidiServiceWin overrides: | 460 // MidiServiceWin overrides: |
529 void InitializeAsync(MidiServiceWinDelegate* delegate) final { | 461 void InitializeAsync(MidiServiceWinDelegate* delegate) final { |
| 462 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and |
| 463 // RemoveDevicesChangeObserver() should be called on the same thread. |
| 464 CHECK(thread_checker_.CalledOnValidThread()); |
| 465 |
530 delegate_ = delegate; | 466 delegate_ = delegate; |
531 sender_thread_.StartWithOptions( | 467 |
532 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | 468 sender_thread_.Start(); |
533 task_thread_.StartWithOptions( | 469 task_thread_.Start(); |
534 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | 470 |
535 monitor_thread_.StartWithOptions( | 471 // Start monitoring device changes. This should start before the |
536 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)); | 472 // following UpdateDeviceList() call not to miss the event happening |
537 monitor_thread_.message_loop()->PostTask( | 473 // between the call and the observer registration. |
538 FROM_HERE, base::Bind(&MidiServiceWinImpl::InitializeOnMonitorThread, | 474 base::SystemMonitor::Get()->AddDevicesChangedObserver(this); |
539 base::Unretained(this))); | 475 |
| 476 UpdateDeviceList(); |
| 477 |
| 478 task_thread_.message_loop()->PostTask( |
| 479 FROM_HERE, |
| 480 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread, |
| 481 base::Unretained(this), MIDI_OK)); |
540 } | 482 } |
541 | 483 |
542 void SendMidiDataAsync(uint32 port_number, | 484 void SendMidiDataAsync(uint32 port_number, |
543 const std::vector<uint8>& data, | 485 const std::vector<uint8>& data, |
544 base::TimeTicks time) final { | 486 base::TimeTicks time) final { |
545 if (destructor_started) { | 487 if (destructor_started) { |
546 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " | 488 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " |
547 "being destructed. port: " << port_number; | 489 "being destructed. port: " << port_number; |
548 return; | 490 return; |
549 } | 491 } |
(...skipping 17 matching lines...) Expand all Loading... |
567 state->port_age, data, time), | 509 state->port_age, data, time), |
568 time - now); | 510 time - now); |
569 } else { | 511 } else { |
570 sender_thread_.message_loop()->PostTask( | 512 sender_thread_.message_loop()->PostTask( |
571 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, | 513 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, |
572 base::Unretained(this), port_number, | 514 base::Unretained(this), port_number, |
573 state->port_age, data, time)); | 515 state->port_age, data, time)); |
574 } | 516 } |
575 } | 517 } |
576 | 518 |
| 519 // base::SystemMonitor::DevicesChangedObserver overrides: |
| 520 void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) final { |
| 521 CHECK(thread_checker_.CalledOnValidThread()); |
| 522 if (destructor_started) |
| 523 return; |
| 524 |
| 525 switch (device_type) { |
| 526 case base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE: |
| 527 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE: |
| 528 // Add case of other unrelated device types here. |
| 529 return; |
| 530 case base::SystemMonitor::DEVTYPE_UNKNOWN: |
| 531 // Interested in MIDI devices. Try updating the device list. |
| 532 UpdateDeviceList(); |
| 533 break; |
| 534 // No default here to capture new DeviceType by compile time. |
| 535 } |
| 536 } |
| 537 |
577 private: | 538 private: |
578 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( | 539 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( |
579 HMIDIIN midi_handle) { | 540 HMIDIIN midi_handle) { |
580 base::AutoLock auto_lock(input_ports_lock_); | 541 base::AutoLock auto_lock(input_ports_lock_); |
581 const auto it = input_device_map_.find(midi_handle); | 542 const auto it = input_device_map_.find(midi_handle); |
582 return (it != input_device_map_.end() ? it->second : nullptr); | 543 return (it != input_device_map_.end() ? it->second : nullptr); |
583 } | 544 } |
584 | 545 |
585 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( | 546 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( |
586 HMIDIOUT midi_handle) { | 547 HMIDIOUT midi_handle) { |
587 base::AutoLock auto_lock(output_ports_lock_); | 548 base::AutoLock auto_lock(output_ports_lock_); |
588 const auto it = output_device_map_.find(midi_handle); | 549 const auto it = output_device_map_.find(midi_handle); |
589 return (it != output_device_map_.end() ? it->second : nullptr); | 550 return (it != output_device_map_.end() ? it->second : nullptr); |
590 } | 551 } |
591 | 552 |
592 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( | 553 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( |
593 uint32 port_number) { | 554 uint32 port_number) { |
594 base::AutoLock auto_lock(output_ports_lock_); | 555 base::AutoLock auto_lock(output_ports_lock_); |
595 if (output_ports_.size() <= port_number) | 556 if (output_ports_.size() <= port_number) |
596 return nullptr; | 557 return nullptr; |
597 return output_ports_[port_number]; | 558 return output_ports_[port_number]; |
598 } | 559 } |
599 | 560 |
| 561 void UpdateDeviceList() { |
| 562 task_thread_.message_loop()->PostTask( |
| 563 FROM_HERE, base::Bind(&MidiServiceWinImpl::UpdateDeviceListOnTaskThread, |
| 564 base::Unretained(this))); |
| 565 } |
| 566 |
600 ///////////////////////////////////////////////////////////////////////////// | 567 ///////////////////////////////////////////////////////////////////////////// |
601 // Callbacks on the OS multimedia thread. | 568 // Callbacks on the OS multimedia thread. |
602 ///////////////////////////////////////////////////////////////////////////// | 569 ///////////////////////////////////////////////////////////////////////////// |
603 | 570 |
604 static void CALLBACK OnMidiInEventOnMultimediaThread(HMIDIIN midi_in_handle, | 571 static void CALLBACK |
605 UINT message, | 572 OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle, |
606 DWORD_PTR instance, | 573 UINT message, |
607 DWORD_PTR param1, | 574 DWORD_PTR instance, |
608 DWORD_PTR param2) { | 575 DWORD_PTR param1, |
| 576 DWORD_PTR param2) { |
609 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); | 577 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); |
610 if (!self) | 578 if (!self) |
611 return; | 579 return; |
612 switch (message) { | 580 switch (message) { |
613 case MIM_OPEN: | 581 case MIM_OPEN: |
614 self->OnMidiInOpenOnMultimediaThread(midi_in_handle); | 582 self->OnMidiInOpen(midi_in_handle); |
615 break; | 583 break; |
616 case MIM_DATA: | 584 case MIM_DATA: |
617 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); | 585 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); |
618 break; | 586 break; |
619 case MIM_LONGDATA: | 587 case MIM_LONGDATA: |
620 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, | 588 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, |
621 param2); | 589 param2); |
622 break; | 590 break; |
623 case MIM_CLOSE: | 591 case MIM_CLOSE: |
624 self->OnMidiInCloseOnMultimediaThread(midi_in_handle); | 592 self->OnMidiInCloseOnMultimediaThread(midi_in_handle); |
625 break; | 593 break; |
626 } | 594 } |
627 } | 595 } |
628 | 596 |
629 void OnMidiInOpenOnMultimediaThread(HMIDIIN midi_in_handle) { | 597 void OnMidiInOpen(HMIDIIN midi_in_handle) { |
630 UINT device_id = 0; | 598 UINT device_id = 0; |
631 MMRESULT result = midiInGetID(midi_in_handle, &device_id); | 599 MMRESULT result = midiInGetID(midi_in_handle, &device_id); |
632 if (result != MMSYSERR_NOERROR) { | 600 if (result != MMSYSERR_NOERROR) { |
633 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); | 601 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); |
634 return; | 602 return; |
635 } | 603 } |
636 MIDIINCAPS2W caps = {}; | 604 MIDIINCAPS2W caps = {}; |
637 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), | 605 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), |
638 sizeof(caps)); | 606 sizeof(caps)); |
639 if (result != MMSYSERR_NOERROR) { | 607 if (result != MMSYSERR_NOERROR) { |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
778 unused_input_ports_[device_info].push(port_number); | 746 unused_input_ports_[device_info].push(port_number); |
779 } | 747 } |
780 task_thread_.message_loop()->PostTask( | 748 task_thread_.message_loop()->PostTask( |
781 FROM_HERE, | 749 FROM_HERE, |
782 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, | 750 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, |
783 base::Unretained(this), port_number, | 751 base::Unretained(this), port_number, |
784 MIDI_PORT_DISCONNECTED)); | 752 MIDI_PORT_DISCONNECTED)); |
785 } | 753 } |
786 | 754 |
787 static void CALLBACK | 755 static void CALLBACK |
788 OnMidiOutEventOnMultimediaThread(HMIDIOUT midi_out_handle, | 756 OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle, |
789 UINT message, | 757 UINT message, |
790 DWORD_PTR instance, | 758 DWORD_PTR instance, |
791 DWORD_PTR param1, | 759 DWORD_PTR param1, |
792 DWORD_PTR param2) { | 760 DWORD_PTR param2) { |
793 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); | 761 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); |
794 if (!self) | 762 if (!self) |
795 return; | 763 return; |
796 switch (message) { | 764 switch (message) { |
797 case MOM_OPEN: | 765 case MOM_OPEN: |
798 self->OnMidiOutOpenOnMultimediaThread(midi_out_handle, param1, param2); | 766 self->OnMidiOutOpen(midi_out_handle, param1, param2); |
799 break; | 767 break; |
800 case MOM_DONE: | 768 case MOM_DONE: |
801 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); | 769 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); |
802 break; | 770 break; |
803 case MOM_CLOSE: | 771 case MOM_CLOSE: |
804 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); | 772 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); |
805 break; | 773 break; |
806 } | 774 } |
807 } | 775 } |
808 | 776 |
809 void OnMidiOutOpenOnMultimediaThread(HMIDIOUT midi_out_handle, | 777 void OnMidiOutOpen(HMIDIOUT midi_out_handle, |
810 DWORD_PTR param1, | 778 DWORD_PTR param1, |
811 DWORD_PTR param2) { | 779 DWORD_PTR param2) { |
812 UINT device_id = 0; | 780 UINT device_id = 0; |
813 MMRESULT result = midiOutGetID(midi_out_handle, &device_id); | 781 MMRESULT result = midiOutGetID(midi_out_handle, &device_id); |
814 if (result != MMSYSERR_NOERROR) { | 782 if (result != MMSYSERR_NOERROR) { |
815 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); | 783 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); |
816 return; | 784 return; |
817 } | 785 } |
818 MIDIOUTCAPS2W caps = {}; | 786 MIDIOUTCAPS2W caps = {}; |
819 result = midiOutGetDevCaps( | 787 result = midiOutGetDevCaps( |
820 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); | 788 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); |
821 if (result != MMSYSERR_NOERROR) { | 789 if (result != MMSYSERR_NOERROR) { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
899 state->closed = true; | 867 state->closed = true; |
900 } | 868 } |
901 task_thread_.message_loop()->PostTask( | 869 task_thread_.message_loop()->PostTask( |
902 FROM_HERE, | 870 FROM_HERE, |
903 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, | 871 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, |
904 base::Unretained(this), port_number, | 872 base::Unretained(this), port_number, |
905 MIDI_PORT_DISCONNECTED)); | 873 MIDI_PORT_DISCONNECTED)); |
906 } | 874 } |
907 | 875 |
908 ///////////////////////////////////////////////////////////////////////////// | 876 ///////////////////////////////////////////////////////////////////////////// |
909 // Callbacks on the monitor thread. | |
910 ///////////////////////////////////////////////////////////////////////////// | |
911 | |
912 void AssertOnMonitorThread() { | |
913 DCHECK_EQ(monitor_thread_.thread_id(), base::PlatformThread::CurrentId()); | |
914 } | |
915 | |
916 void InitializeOnMonitorThread() { | |
917 AssertOnMonitorThread(); | |
918 // Synchronously open all the existing MIDI devices. | |
919 TryOpenAllDevicesOnMonitorThreadSync(); | |
920 // Since |TryOpenAllDevicesOnMonitorThreadSync()| is a blocking call, it is | |
921 // guaranteed that all the initial MIDI ports are already opened here, and | |
922 // corresponding tasks to call |AddInputPortOnTaskThread()| and | |
923 // |AddOutputPortOnTaskThread()| are already queued in the Misc. Task | |
924 // Thread. This means that the following task to call | |
925 // |CompleteInitializationOnTaskThread()| are always called after all the | |
926 // pending tasks to call |AddInputPortOnTaskThread()| and | |
927 // |AddOutputPortOnTaskThread()| are completed. | |
928 task_thread_.message_loop()->PostTask( | |
929 FROM_HERE, | |
930 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread, | |
931 base::Unretained(this), MIDI_OK)); | |
932 // Start monitoring based on notifications generated by | |
933 // RegisterDeviceNotificationW API. | |
934 device_monitor_window_.reset(new DeviceMonitorWindow( | |
935 base::Bind(&MidiServiceWinImpl::TryOpenAllDevicesOnMonitorThreadSync, | |
936 base::Unretained(this)))); | |
937 } | |
938 | |
939 void StopDeviceMonitorOnMonitorThreadSync() { | |
940 device_monitor_window_.reset(); | |
941 } | |
942 | |
943 // Note that this is a blocking method. | |
944 void TryOpenAllDevicesOnMonitorThreadSync() { | |
945 AssertOnMonitorThread(); | |
946 const UINT num_in_devices = midiInGetNumDevs(); | |
947 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) { | |
948 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA, | |
949 // MIM_OPEN, and MIM_CLOSE events. | |
950 // - MIM_DATA: This is the only way to get a short MIDI message with | |
951 // timestamp information. | |
952 // - MIM_LONGDATA: This is the only way to get a long MIDI message with | |
953 // timestamp information. | |
954 // - MIM_OPEN: This event is sent the input device is opened. | |
955 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2) | |
956 // the MIDI device becomes unavailable for some reasons, e.g., the | |
957 // cable is disconnected. As for the former case, HMIDIOUT will be | |
958 // invalidated soon after the callback is finished. As for the later | |
959 // case, however, HMIDIOUT continues to be valid until midiInClose() | |
960 // is called. | |
961 HMIDIIN midi_handle = kInvalidMidiInHandle; | |
962 const MMRESULT result = midiInOpen( | |
963 &midi_handle, device_id, | |
964 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMultimediaThread), | |
965 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); | |
966 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) | |
967 << "Failed to open output device. " | |
968 << " id: " << device_id << " message: " << GetInErrorMessage(result); | |
969 } | |
970 | |
971 const UINT num_out_devices = midiOutGetNumDevs(); | |
972 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) { | |
973 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and | |
974 // MOM_CLOSE events. | |
975 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean | |
976 // up the backing store where a long MIDI message is stored. | |
977 // - MOM_OPEN: This event is sent the output device is opened. | |
978 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2) | |
979 // the MIDI device becomes unavailable for some reasons, e.g., the | |
980 // cable is disconnected. As for the former case, HMIDIOUT will be | |
981 // invalidated soon after the callback is finished. As for the later | |
982 // case, however, HMIDIOUT continues to be valid until midiOutClose() | |
983 // is called. | |
984 HMIDIOUT midi_handle = kInvalidMidiOutHandle; | |
985 const MMRESULT result = midiOutOpen( | |
986 &midi_handle, device_id, | |
987 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMultimediaThread), | |
988 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); | |
989 if (result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) { | |
990 DLOG(ERROR) << "Failed to open output device. " | |
991 << " id: " << device_id | |
992 << " message: " << GetOutErrorMessage(result); | |
993 } | |
994 } | |
995 } | |
996 | |
997 ///////////////////////////////////////////////////////////////////////////// | |
998 // Callbacks on the sender thread. | 877 // Callbacks on the sender thread. |
999 ///////////////////////////////////////////////////////////////////////////// | 878 ///////////////////////////////////////////////////////////////////////////// |
1000 | 879 |
1001 void AssertOnSenderThread() { | 880 void AssertOnSenderThread() { |
1002 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId()); | 881 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId()); |
1003 } | 882 } |
1004 | 883 |
1005 void SendOnSenderThread(uint32 port_number, | 884 void SendOnSenderThread(uint32 port_number, |
1006 uint64 port_age, | 885 uint64 port_age, |
1007 const std::vector<uint8>& data, | 886 const std::vector<uint8>& data, |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1051 } | 930 } |
1052 | 931 |
1053 ///////////////////////////////////////////////////////////////////////////// | 932 ///////////////////////////////////////////////////////////////////////////// |
1054 // Callbacks on the task thread. | 933 // Callbacks on the task thread. |
1055 ///////////////////////////////////////////////////////////////////////////// | 934 ///////////////////////////////////////////////////////////////////////////// |
1056 | 935 |
1057 void AssertOnTaskThread() { | 936 void AssertOnTaskThread() { |
1058 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId()); | 937 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId()); |
1059 } | 938 } |
1060 | 939 |
| 940 void UpdateDeviceListOnTaskThread() { |
| 941 AssertOnTaskThread(); |
| 942 const UINT num_in_devices = midiInGetNumDevs(); |
| 943 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) { |
| 944 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA, |
| 945 // MIM_OPEN, and MIM_CLOSE events. |
| 946 // - MIM_DATA: This is the only way to get a short MIDI message with |
| 947 // timestamp information. |
| 948 // - MIM_LONGDATA: This is the only way to get a long MIDI message with |
| 949 // timestamp information. |
| 950 // - MIM_OPEN: This event is sent the input device is opened. Note that |
| 951 // this message is called on the caller thread. |
| 952 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2) |
| 953 // the MIDI device becomes unavailable for some reasons, e.g., the |
| 954 // cable is disconnected. As for the former case, HMIDIOUT will be |
| 955 // invalidated soon after the callback is finished. As for the later |
| 956 // case, however, HMIDIOUT continues to be valid until midiInClose() |
| 957 // is called. |
| 958 HMIDIIN midi_handle = kInvalidMidiInHandle; |
| 959 const MMRESULT result = midiInOpen( |
| 960 &midi_handle, device_id, |
| 961 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread), |
| 962 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); |
| 963 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) |
| 964 << "Failed to open output device. " |
| 965 << " id: " << device_id << " message: " << GetInErrorMessage(result); |
| 966 } |
| 967 |
| 968 const UINT num_out_devices = midiOutGetNumDevs(); |
| 969 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) { |
| 970 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and |
| 971 // MOM_CLOSE events. |
| 972 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean |
| 973 // up the backing store where a long MIDI message is stored. |
| 974 // - MOM_OPEN: This event is sent the output device is opened. Note that |
| 975 // this message is called on the caller thread. |
| 976 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2) |
| 977 // the MIDI device becomes unavailable for some reasons, e.g., the |
| 978 // cable is disconnected. As for the former case, HMIDIOUT will be |
| 979 // invalidated soon after the callback is finished. As for the later |
| 980 // case, however, HMIDIOUT continues to be valid until midiOutClose() |
| 981 // is called. |
| 982 HMIDIOUT midi_handle = kInvalidMidiOutHandle; |
| 983 const MMRESULT result = midiOutOpen( |
| 984 &midi_handle, device_id, |
| 985 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread), |
| 986 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); |
| 987 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) |
| 988 << "Failed to open output device. " |
| 989 << " id: " << device_id << " message: " << GetOutErrorMessage(result); |
| 990 } |
| 991 } |
| 992 |
1061 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { | 993 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { |
1062 AssertOnTaskThread(); | 994 AssertOnTaskThread(); |
1063 auto state = GetInputDeviceFromHandle(midi_in_handle); | 995 auto state = GetInputDeviceFromHandle(midi_in_handle); |
1064 if (!state) | 996 if (!state) |
1065 return; | 997 return; |
1066 MMRESULT result = | 998 MMRESULT result = |
1067 midiInPrepareHeader(state->midi_handle, state->midi_header.get(), | 999 midiInPrepareHeader(state->midi_handle, state->midi_header.get(), |
1068 sizeof(*state->midi_header)); | 1000 sizeof(*state->midi_header)); |
1069 if (result != MMSYSERR_NOERROR) { | 1001 if (result != MMSYSERR_NOERROR) { |
1070 DLOG(ERROR) << "Failed to initialize input buffer: " | 1002 DLOG(ERROR) << "Failed to initialize input buffer: " |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1120 delegate_->OnSetOutputPortState(port_index, state); | 1052 delegate_->OnSetOutputPortState(port_index, state); |
1121 } | 1053 } |
1122 | 1054 |
1123 ///////////////////////////////////////////////////////////////////////////// | 1055 ///////////////////////////////////////////////////////////////////////////// |
1124 // Fields: | 1056 // Fields: |
1125 ///////////////////////////////////////////////////////////////////////////// | 1057 ///////////////////////////////////////////////////////////////////////////// |
1126 | 1058 |
1127 // Does not take ownership. | 1059 // Does not take ownership. |
1128 MidiServiceWinDelegate* delegate_; | 1060 MidiServiceWinDelegate* delegate_; |
1129 | 1061 |
1130 base::Thread monitor_thread_; | 1062 base::ThreadChecker thread_checker_; |
| 1063 |
1131 base::Thread sender_thread_; | 1064 base::Thread sender_thread_; |
1132 base::Thread task_thread_; | 1065 base::Thread task_thread_; |
1133 | 1066 |
1134 scoped_ptr<DeviceMonitorWindow> device_monitor_window_; | |
1135 | |
1136 base::Lock input_ports_lock_; | 1067 base::Lock input_ports_lock_; |
1137 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> | 1068 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> |
1138 input_device_map_; // GUARDED_BY(input_ports_lock_) | 1069 input_device_map_; // GUARDED_BY(input_ports_lock_) |
1139 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) | 1070 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) |
1140 std::vector<scoped_refptr<MidiInputDeviceState>> | 1071 std::vector<scoped_refptr<MidiInputDeviceState>> |
1141 input_ports_; // GUARDED_BY(input_ports_lock_) | 1072 input_ports_; // GUARDED_BY(input_ports_lock_) |
1142 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_) | 1073 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_) |
1143 | 1074 |
1144 base::Lock output_ports_lock_; | 1075 base::Lock output_ports_lock_; |
1145 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> | 1076 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> |
1146 output_device_map_; // GUARDED_BY(output_ports_lock_) | 1077 output_device_map_; // GUARDED_BY(output_ports_lock_) |
1147 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) | 1078 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) |
1148 std::vector<scoped_refptr<MidiOutputDeviceState>> | 1079 std::vector<scoped_refptr<MidiOutputDeviceState>> |
1149 output_ports_; // GUARDED_BY(output_ports_lock_) | 1080 output_ports_; // GUARDED_BY(output_ports_lock_) |
1150 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_) | 1081 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_) |
1151 | 1082 |
1152 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note | 1083 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note |
1153 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until | 1084 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until |
1154 // |monitor_thread_|, |sender_thread_|, and |task_thread_| are stopped. | 1085 // |sender_thread_|, and |task_thread_| are stopped. |
1155 // This flag can be used as the signal that when background tasks must be | 1086 // This flag can be used as the signal that when background tasks must be |
1156 // interrupted. | 1087 // interrupted. |
1157 // TODO(toyoshim): Use std::atomic<bool> when it is allowed. | 1088 // TODO(toyoshim): Use std::atomic<bool> when it is allowed. |
1158 volatile bool destructor_started; | 1089 volatile bool destructor_started; |
1159 | 1090 |
1160 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); | 1091 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); |
1161 }; | 1092 }; |
1162 | 1093 |
1163 } // namespace | 1094 } // namespace |
1164 | 1095 |
(...skipping 21 matching lines...) Expand all Loading... |
1186 // TOOD(toyoshim): This calculation should be done when the date is actually | 1117 // TOOD(toyoshim): This calculation should be done when the date is actually |
1187 // sent. | 1118 // sent. |
1188 client->AccumulateMidiBytesSent(data.size()); | 1119 client->AccumulateMidiBytesSent(data.size()); |
1189 } | 1120 } |
1190 | 1121 |
1191 MidiManager* MidiManager::Create() { | 1122 MidiManager* MidiManager::Create() { |
1192 return new MidiManagerWin(); | 1123 return new MidiManagerWin(); |
1193 } | 1124 } |
1194 | 1125 |
1195 } // namespace media | 1126 } // namespace media |
OLD | NEW |