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

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

Issue 1056333002: Web MIDI: Improve device change monitornig on Windows (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review Created 5 years, 8 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
412 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
yukawa 2015/04/06 20:18:48 optional: We could update |destructor_started| fir
Takashi Toyoshima 2015/04/07 06:19:29 Oh, good catch. That's a good thing to be fixed. I
474 destructor_started = true; 413 destructor_started = true;
475 if (monitor_thread_.IsRunning()) {
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
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
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 switch (device_type) {
523 case base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE:
524 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
525 // Add case of other unrelated device types here.
526 return;
527 case base::SystemMonitor::DEVTYPE_UNKNOWN:
528 // Interested in MIDI devices. Try updating the device list.
529 UpdateDeviceList();
530 break;
531 // No default here to capture new DeviceType by compile time.
532 }
533 }
534
577 private: 535 private:
578 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( 536 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle(
579 HMIDIIN midi_handle) { 537 HMIDIIN midi_handle) {
580 base::AutoLock auto_lock(input_ports_lock_); 538 base::AutoLock auto_lock(input_ports_lock_);
581 const auto it = input_device_map_.find(midi_handle); 539 const auto it = input_device_map_.find(midi_handle);
582 return (it != input_device_map_.end() ? it->second : nullptr); 540 return (it != input_device_map_.end() ? it->second : nullptr);
583 } 541 }
584 542
585 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( 543 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle(
586 HMIDIOUT midi_handle) { 544 HMIDIOUT midi_handle) {
587 base::AutoLock auto_lock(output_ports_lock_); 545 base::AutoLock auto_lock(output_ports_lock_);
588 const auto it = output_device_map_.find(midi_handle); 546 const auto it = output_device_map_.find(midi_handle);
589 return (it != output_device_map_.end() ? it->second : nullptr); 547 return (it != output_device_map_.end() ? it->second : nullptr);
590 } 548 }
591 549
592 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( 550 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort(
593 uint32 port_number) { 551 uint32 port_number) {
594 base::AutoLock auto_lock(output_ports_lock_); 552 base::AutoLock auto_lock(output_ports_lock_);
595 if (output_ports_.size() <= port_number) 553 if (output_ports_.size() <= port_number)
596 return nullptr; 554 return nullptr;
597 return output_ports_[port_number]; 555 return output_ports_[port_number];
598 } 556 }
599 557
558 void UpdateDeviceList() {
559 task_thread_.message_loop()->PostTask(
560 FROM_HERE, base::Bind(&MidiServiceWinImpl::UpdateDeviceListOnTaskThread,
561 base::Unretained(this)));
562 }
563
600 ///////////////////////////////////////////////////////////////////////////// 564 /////////////////////////////////////////////////////////////////////////////
601 // Callbacks on the OS multimedia thread. 565 // Callbacks on the OS multimedia thread.
602 ///////////////////////////////////////////////////////////////////////////// 566 /////////////////////////////////////////////////////////////////////////////
603 567
604 static void CALLBACK OnMidiInEventOnMultimediaThread(HMIDIIN midi_in_handle, 568 static void CALLBACK
605 UINT message, 569 OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle,
606 DWORD_PTR instance, 570 UINT message,
607 DWORD_PTR param1, 571 DWORD_PTR instance,
608 DWORD_PTR param2) { 572 DWORD_PTR param1,
573 DWORD_PTR param2) {
609 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 574 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
610 if (!self) 575 if (!self)
611 return; 576 return;
612 switch (message) { 577 switch (message) {
613 case MIM_OPEN: 578 case MIM_OPEN:
614 self->OnMidiInOpenOnMultimediaThread(midi_in_handle); 579 self->OnMidiInOpen(midi_in_handle);
615 break; 580 break;
616 case MIM_DATA: 581 case MIM_DATA:
617 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); 582 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2);
618 break; 583 break;
619 case MIM_LONGDATA: 584 case MIM_LONGDATA:
620 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, 585 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1,
621 param2); 586 param2);
622 break; 587 break;
623 case MIM_CLOSE: 588 case MIM_CLOSE:
589 LOG(ERROR) << "MIM_CLOSE";
Takashi Toyoshima 2015/04/06 20:03:09 oops, this is mistakenly merged from local debuggi
yukawa 2015/04/06 20:18:48 Is this a debug code?
624 self->OnMidiInCloseOnMultimediaThread(midi_in_handle); 590 self->OnMidiInCloseOnMultimediaThread(midi_in_handle);
625 break; 591 break;
626 } 592 }
627 } 593 }
628 594
629 void OnMidiInOpenOnMultimediaThread(HMIDIIN midi_in_handle) { 595 void OnMidiInOpen(HMIDIIN midi_in_handle) {
630 UINT device_id = 0; 596 UINT device_id = 0;
631 MMRESULT result = midiInGetID(midi_in_handle, &device_id); 597 MMRESULT result = midiInGetID(midi_in_handle, &device_id);
632 if (result != MMSYSERR_NOERROR) { 598 if (result != MMSYSERR_NOERROR) {
633 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); 599 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result);
634 return; 600 return;
635 } 601 }
636 MIDIINCAPS2W caps = {}; 602 MIDIINCAPS2W caps = {};
637 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), 603 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps),
638 sizeof(caps)); 604 sizeof(caps));
639 if (result != MMSYSERR_NOERROR) { 605 if (result != MMSYSERR_NOERROR) {
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 unused_input_ports_[device_info].push(port_number); 744 unused_input_ports_[device_info].push(port_number);
779 } 745 }
780 task_thread_.message_loop()->PostTask( 746 task_thread_.message_loop()->PostTask(
781 FROM_HERE, 747 FROM_HERE,
782 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, 748 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread,
783 base::Unretained(this), port_number, 749 base::Unretained(this), port_number,
784 MIDI_PORT_DISCONNECTED)); 750 MIDI_PORT_DISCONNECTED));
785 } 751 }
786 752
787 static void CALLBACK 753 static void CALLBACK
788 OnMidiOutEventOnMultimediaThread(HMIDIOUT midi_out_handle, 754 OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle,
789 UINT message, 755 UINT message,
790 DWORD_PTR instance, 756 DWORD_PTR instance,
791 DWORD_PTR param1, 757 DWORD_PTR param1,
792 DWORD_PTR param2) { 758 DWORD_PTR param2) {
793 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 759 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
794 if (!self) 760 if (!self)
795 return; 761 return;
796 switch (message) { 762 switch (message) {
797 case MOM_OPEN: 763 case MOM_OPEN:
798 self->OnMidiOutOpenOnMultimediaThread(midi_out_handle, param1, param2); 764 self->OnMidiOutOpen(midi_out_handle, param1, param2);
799 break; 765 break;
800 case MOM_DONE: 766 case MOM_DONE:
801 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); 767 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1);
802 break; 768 break;
803 case MOM_CLOSE: 769 case MOM_CLOSE:
804 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); 770 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle);
805 break; 771 break;
806 } 772 }
807 } 773 }
808 774
809 void OnMidiOutOpenOnMultimediaThread(HMIDIOUT midi_out_handle, 775 void OnMidiOutOpen(HMIDIOUT midi_out_handle,
810 DWORD_PTR param1, 776 DWORD_PTR param1,
811 DWORD_PTR param2) { 777 DWORD_PTR param2) {
812 UINT device_id = 0; 778 UINT device_id = 0;
813 MMRESULT result = midiOutGetID(midi_out_handle, &device_id); 779 MMRESULT result = midiOutGetID(midi_out_handle, &device_id);
814 if (result != MMSYSERR_NOERROR) { 780 if (result != MMSYSERR_NOERROR) {
815 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); 781 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result);
816 return; 782 return;
817 } 783 }
818 MIDIOUTCAPS2W caps = {}; 784 MIDIOUTCAPS2W caps = {};
819 result = midiOutGetDevCaps( 785 result = midiOutGetDevCaps(
820 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); 786 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps));
821 if (result != MMSYSERR_NOERROR) { 787 if (result != MMSYSERR_NOERROR) {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
899 state->closed = true; 865 state->closed = true;
900 } 866 }
901 task_thread_.message_loop()->PostTask( 867 task_thread_.message_loop()->PostTask(
902 FROM_HERE, 868 FROM_HERE,
903 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, 869 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread,
904 base::Unretained(this), port_number, 870 base::Unretained(this), port_number,
905 MIDI_PORT_DISCONNECTED)); 871 MIDI_PORT_DISCONNECTED));
906 } 872 }
907 873
908 ///////////////////////////////////////////////////////////////////////////// 874 /////////////////////////////////////////////////////////////////////////////
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. 875 // Callbacks on the sender thread.
999 ///////////////////////////////////////////////////////////////////////////// 876 /////////////////////////////////////////////////////////////////////////////
1000 877
1001 void AssertOnSenderThread() { 878 void AssertOnSenderThread() {
1002 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId()); 879 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId());
1003 } 880 }
1004 881
1005 void SendOnSenderThread(uint32 port_number, 882 void SendOnSenderThread(uint32 port_number,
1006 uint64 port_age, 883 uint64 port_age,
1007 const std::vector<uint8>& data, 884 const std::vector<uint8>& data,
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1051 } 928 }
1052 929
1053 ///////////////////////////////////////////////////////////////////////////// 930 /////////////////////////////////////////////////////////////////////////////
1054 // Callbacks on the task thread. 931 // Callbacks on the task thread.
1055 ///////////////////////////////////////////////////////////////////////////// 932 /////////////////////////////////////////////////////////////////////////////
1056 933
1057 void AssertOnTaskThread() { 934 void AssertOnTaskThread() {
1058 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId()); 935 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId());
1059 } 936 }
1060 937
938 void UpdateDeviceListOnTaskThread() {
939 AssertOnTaskThread();
940 const UINT num_in_devices = midiInGetNumDevs();
941 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
942 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA,
943 // MIM_OPEN, and MIM_CLOSE events.
944 // - MIM_DATA: This is the only way to get a short MIDI message with
945 // timestamp information.
946 // - MIM_LONGDATA: This is the only way to get a long MIDI message with
947 // timestamp information.
948 // - MIM_OPEN: This event is sent the input device is opened. Note that
949 // this message is called on the caller thread.
950 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
951 // the MIDI device becomes unavailable for some reasons, e.g., the
952 // cable is disconnected. As for the former case, HMIDIOUT will be
953 // invalidated soon after the callback is finished. As for the later
954 // case, however, HMIDIOUT continues to be valid until midiInClose()
955 // is called.
956 HMIDIIN midi_handle = kInvalidMidiInHandle;
957 const MMRESULT result = midiInOpen(
958 &midi_handle, device_id,
959 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread),
960 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
961 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
962 << "Failed to open output device. "
963 << " id: " << device_id << " message: " << GetInErrorMessage(result);
964 }
965
966 const UINT num_out_devices = midiOutGetNumDevs();
967 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
968 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and
969 // MOM_CLOSE events.
970 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
971 // up the backing store where a long MIDI message is stored.
972 // - MOM_OPEN: This event is sent the output device is opened. Note that
973 // this message is called on the caller thread.
974 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
975 // the MIDI device becomes unavailable for some reasons, e.g., the
976 // cable is disconnected. As for the former case, HMIDIOUT will be
977 // invalidated soon after the callback is finished. As for the later
978 // case, however, HMIDIOUT continues to be valid until midiOutClose()
979 // is called.
980 HMIDIOUT midi_handle = kInvalidMidiOutHandle;
981 const MMRESULT result = midiOutOpen(
982 &midi_handle, device_id,
983 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread),
984 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
985 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
986 << "Failed to open output device. "
987 << " id: " << device_id << " message: " << GetOutErrorMessage(result);
988 }
989 }
990
1061 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { 991 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) {
1062 AssertOnTaskThread(); 992 AssertOnTaskThread();
1063 auto state = GetInputDeviceFromHandle(midi_in_handle); 993 auto state = GetInputDeviceFromHandle(midi_in_handle);
1064 if (!state) 994 if (!state)
1065 return; 995 return;
1066 MMRESULT result = 996 MMRESULT result =
1067 midiInPrepareHeader(state->midi_handle, state->midi_header.get(), 997 midiInPrepareHeader(state->midi_handle, state->midi_header.get(),
1068 sizeof(*state->midi_header)); 998 sizeof(*state->midi_header));
1069 if (result != MMSYSERR_NOERROR) { 999 if (result != MMSYSERR_NOERROR) {
1070 DLOG(ERROR) << "Failed to initialize input buffer: " 1000 DLOG(ERROR) << "Failed to initialize input buffer: "
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
1120 delegate_->OnSetOutputPortState(port_index, state); 1050 delegate_->OnSetOutputPortState(port_index, state);
1121 } 1051 }
1122 1052
1123 ///////////////////////////////////////////////////////////////////////////// 1053 /////////////////////////////////////////////////////////////////////////////
1124 // Fields: 1054 // Fields:
1125 ///////////////////////////////////////////////////////////////////////////// 1055 /////////////////////////////////////////////////////////////////////////////
1126 1056
1127 // Does not take ownership. 1057 // Does not take ownership.
1128 MidiServiceWinDelegate* delegate_; 1058 MidiServiceWinDelegate* delegate_;
1129 1059
1130 base::Thread monitor_thread_; 1060 base::ThreadChecker thread_checker_;
1061
1131 base::Thread sender_thread_; 1062 base::Thread sender_thread_;
1132 base::Thread task_thread_; 1063 base::Thread task_thread_;
1133 1064
1134 scoped_ptr<DeviceMonitorWindow> device_monitor_window_;
1135
1136 base::Lock input_ports_lock_; 1065 base::Lock input_ports_lock_;
1137 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> 1066 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>>
1138 input_device_map_; // GUARDED_BY(input_ports_lock_) 1067 input_device_map_; // GUARDED_BY(input_ports_lock_)
1139 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) 1068 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_)
1140 std::vector<scoped_refptr<MidiInputDeviceState>> 1069 std::vector<scoped_refptr<MidiInputDeviceState>>
1141 input_ports_; // GUARDED_BY(input_ports_lock_) 1070 input_ports_; // GUARDED_BY(input_ports_lock_)
1142 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_) 1071 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_)
1143 1072
1144 base::Lock output_ports_lock_; 1073 base::Lock output_ports_lock_;
1145 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> 1074 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>>
1146 output_device_map_; // GUARDED_BY(output_ports_lock_) 1075 output_device_map_; // GUARDED_BY(output_ports_lock_)
1147 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) 1076 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_)
1148 std::vector<scoped_refptr<MidiOutputDeviceState>> 1077 std::vector<scoped_refptr<MidiOutputDeviceState>>
1149 output_ports_; // GUARDED_BY(output_ports_lock_) 1078 output_ports_; // GUARDED_BY(output_ports_lock_)
1150 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_) 1079 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_)
1151 1080
1152 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note 1081 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note
1153 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until 1082 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until
1154 // |monitor_thread_|, |sender_thread_|, and |task_thread_| are stopped. 1083 // |sender_thread_|, and |task_thread_| are stopped.
1155 // This flag can be used as the signal that when background tasks must be 1084 // This flag can be used as the signal that when background tasks must be
1156 // interrupted. 1085 // interrupted.
1157 // TODO(toyoshim): Use std::atomic<bool> when it is allowed. 1086 // TODO(toyoshim): Use std::atomic<bool> when it is allowed.
1158 volatile bool destructor_started; 1087 volatile bool destructor_started;
1159 1088
1160 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); 1089 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl);
1161 }; 1090 };
1162 1091
1163 } // namespace 1092 } // namespace
1164 1093
(...skipping 21 matching lines...) Expand all
1186 // TOOD(toyoshim): This calculation should be done when the date is actually 1115 // TOOD(toyoshim): This calculation should be done when the date is actually
1187 // sent. 1116 // sent.
1188 client->AccumulateMidiBytesSent(data.size()); 1117 client->AccumulateMidiBytesSent(data.size());
1189 } 1118 }
1190 1119
1191 MidiManager* MidiManager::Create() { 1120 MidiManager* MidiManager::Create() {
1192 return new MidiManagerWin(); 1121 return new MidiManagerWin();
1193 } 1122 }
1194 1123
1195 } // namespace media 1124 } // namespace media
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698