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

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: update 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 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 return 0; 278 return 0;
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) {
Takashi Toyoshima 2015/04/06 18:02:38 This fix is split to another CL.
yukawa 2015/04/06 18:41:41 Acknowledged.
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 thread-safe SystemMonitor service.
yukawa 2015/04/06 18:41:40 I'm afraid that "thread-safe" here is ambiguous.
Takashi Toyoshima 2015/04/06 19:51:47 Aha. "thread-safe" is always a vague word. I just
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 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
yukawa 2015/04/06 18:41:40 We might want to make sure that |RemoveDevicesChan
Takashi Toyoshima 2015/04/06 19:51:47 ok, ThreadChecker is our friend.
474 destructor_started = true; 409 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 { 410 {
482 std::vector<HMIDIIN> input_devices; 411 std::vector<HMIDIIN> input_devices;
483 { 412 {
484 base::AutoLock auto_lock(input_ports_lock_); 413 base::AutoLock auto_lock(input_ports_lock_);
485 for (auto it : input_device_map_) 414 for (auto it : input_device_map_)
486 input_devices.push_back(it.first); 415 input_devices.push_back(it.first);
487 } 416 }
488 { 417 {
489 for (const auto handle : input_devices) { 418 for (const auto handle : input_devices) {
490 MMRESULT result = midiInClose(handle); 419 MMRESULT result = midiInClose(handle);
(...skipping 22 matching lines...) Expand all
513 result = midiOutReset(handle); 442 result = midiOutReset(handle);
514 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) 443 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
515 << "midiOutReset failed: " << GetOutErrorMessage(result); 444 << "midiOutReset failed: " << GetOutErrorMessage(result);
516 result = midiOutClose(handle); 445 result = midiOutClose(handle);
517 } 446 }
518 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) 447 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
519 << "midiOutClose failed: " << GetOutErrorMessage(result); 448 << "midiOutClose failed: " << GetOutErrorMessage(result);
520 } 449 }
521 } 450 }
522 } 451 }
523 monitor_thread_.Stop();
524 sender_thread_.Stop(); 452 sender_thread_.Stop();
525 task_thread_.Stop(); 453 task_thread_.Stop();
526 } 454 }
527 455
528 // MidiServiceWin overrides: 456 // MidiServiceWin overrides:
529 void InitializeAsync(MidiServiceWinDelegate* delegate) final { 457 void InitializeAsync(MidiServiceWinDelegate* delegate) final {
530 delegate_ = delegate; 458 delegate_ = delegate;
531 sender_thread_.StartWithOptions( 459 sender_thread_.Start();
Takashi Toyoshima 2015/04/06 18:02:38 TYPE_IO has a misleading name, but it isn't for bl
yukawa 2015/04/06 18:41:40 Acknowledged.
532 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 460 task_thread_.Start();
533 task_thread_.StartWithOptions( 461
534 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); 462 // Start monitoring device changes. This should start before the
yukawa 2015/04/06 18:41:40 Can you elaborate a bit more on why we can avoid r
Takashi Toyoshima 2015/04/06 19:51:47 Done.
535 monitor_thread_.StartWithOptions( 463 // following open operation to avoid a race.
536 base::Thread::Options(base::MessageLoop::TYPE_UI, 0)); 464 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
537 monitor_thread_.message_loop()->PostTask( 465
538 FROM_HERE, base::Bind(&MidiServiceWinImpl::InitializeOnMonitorThread, 466 UpdateDeviceList();
539 base::Unretained(this))); 467
468 task_thread_.message_loop()->PostTask(
469 FROM_HERE,
470 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread,
471 base::Unretained(this), MIDI_OK));
540 } 472 }
541 473
542 void SendMidiDataAsync(uint32 port_number, 474 void SendMidiDataAsync(uint32 port_number,
543 const std::vector<uint8>& data, 475 const std::vector<uint8>& data,
544 base::TimeTicks time) final { 476 base::TimeTicks time) final {
545 if (destructor_started) { 477 if (destructor_started) {
546 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " 478 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is "
547 "being destructed. port: " << port_number; 479 "being destructed. port: " << port_number;
548 return; 480 return;
549 } 481 }
(...skipping 17 matching lines...) Expand all
567 state->port_age, data, time), 499 state->port_age, data, time),
568 time - now); 500 time - now);
569 } else { 501 } else {
570 sender_thread_.message_loop()->PostTask( 502 sender_thread_.message_loop()->PostTask(
571 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, 503 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread,
572 base::Unretained(this), port_number, 504 base::Unretained(this), port_number,
573 state->port_age, data, time)); 505 state->port_age, data, time));
574 } 506 }
575 } 507 }
576 508
509 // base::SystemMonitor::DevicesChangedObserver overrides:
510 void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) final {
yukawa 2015/04/06 18:41:40 Can we predict (and have an assertion) that which
Takashi Toyoshima 2015/04/06 19:51:47 Done.
511 switch (device_type) {
512 case base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE:
513 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
514 // Add case of other unrelated device types here.
515 return;
516 case base::SystemMonitor::DEVTYPE_UNKNOWN:
517 // Interested in MIDI devices. Try updating the device list.
518 UpdateDeviceList();
yukawa 2015/04/06 18:41:40 |UpdateDeviceList()| is supposed to be a blocking
Takashi Toyoshima 2015/04/06 19:51:47 OnDeviceChanged is invoked via PostTask on the reg
yukawa 2015/04/06 20:18:48 The question which thread should be used for openi
519 break;
520 // No default here to capture new DeviceType by compile time.
521 }
522 }
523
577 private: 524 private:
578 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( 525 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle(
579 HMIDIIN midi_handle) { 526 HMIDIIN midi_handle) {
580 base::AutoLock auto_lock(input_ports_lock_); 527 base::AutoLock auto_lock(input_ports_lock_);
581 const auto it = input_device_map_.find(midi_handle); 528 const auto it = input_device_map_.find(midi_handle);
582 return (it != input_device_map_.end() ? it->second : nullptr); 529 return (it != input_device_map_.end() ? it->second : nullptr);
583 } 530 }
584 531
585 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( 532 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle(
586 HMIDIOUT midi_handle) { 533 HMIDIOUT midi_handle) {
587 base::AutoLock auto_lock(output_ports_lock_); 534 base::AutoLock auto_lock(output_ports_lock_);
588 const auto it = output_device_map_.find(midi_handle); 535 const auto it = output_device_map_.find(midi_handle);
589 return (it != output_device_map_.end() ? it->second : nullptr); 536 return (it != output_device_map_.end() ? it->second : nullptr);
590 } 537 }
591 538
592 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( 539 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort(
593 uint32 port_number) { 540 uint32 port_number) {
594 base::AutoLock auto_lock(output_ports_lock_); 541 base::AutoLock auto_lock(output_ports_lock_);
595 if (output_ports_.size() <= port_number) 542 if (output_ports_.size() <= port_number)
596 return nullptr; 543 return nullptr;
597 return output_ports_[port_number]; 544 return output_ports_[port_number];
598 } 545 }
599 546
547 void UpdateDeviceList() {
Takashi Toyoshima 2015/04/06 18:02:38 Name is changed because we do not want to open the
yukawa 2015/04/06 18:41:40 Acknowledged.
548 const UINT num_in_devices = midiInGetNumDevs();
549 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
550 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA,
551 // MIM_OPEN, and MIM_CLOSE events.
552 // - MIM_DATA: This is the only way to get a short MIDI message with
553 // timestamp information.
554 // - MIM_LONGDATA: This is the only way to get a long MIDI message with
555 // timestamp information.
556 // - MIM_OPEN: This event is sent the input device is opened. Note that
Takashi Toyoshima 2015/04/06 18:02:38 Note is added.
yukawa 2015/04/06 18:41:40 Acknowledged.
557 // this message is called on the caller thread.
558 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
559 // the MIDI device becomes unavailable for some reasons, e.g., the
560 // cable is disconnected. As for the former case, HMIDIOUT will be
561 // invalidated soon after the callback is finished. As for the later
562 // case, however, HMIDIOUT continues to be valid until midiInClose()
563 // is called.
564 HMIDIIN midi_handle = kInvalidMidiInHandle;
565 const MMRESULT result = midiInOpen(
566 &midi_handle, device_id,
567 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread),
568 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
569 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
570 << "Failed to open output device. "
571 << " id: " << device_id << " message: " << GetInErrorMessage(result);
572 }
573
574 const UINT num_out_devices = midiOutGetNumDevs();
575 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
576 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and
577 // MOM_CLOSE events.
578 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
579 // up the backing store where a long MIDI message is stored.
580 // - MOM_OPEN: This event is sent the output device is opened. Note that
581 // this message is called on the caller thread.
582 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
583 // the MIDI device becomes unavailable for some reasons, e.g., the
584 // cable is disconnected. As for the former case, HMIDIOUT will be
585 // invalidated soon after the callback is finished. As for the later
586 // case, however, HMIDIOUT continues to be valid until midiOutClose()
587 // is called.
588 HMIDIOUT midi_handle = kInvalidMidiOutHandle;
589 const MMRESULT result = midiOutOpen(
590 &midi_handle, device_id,
591 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread),
592 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
593 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
Takashi Toyoshima 2015/04/06 18:02:38 Minor cleanup to use DLOG_IF as the first loop for
yukawa 2015/04/06 18:41:40 Acknowledged.
594 << "Failed to open output device. "
595 << " id: " << device_id << " message: " << GetOutErrorMessage(result);
596 }
597 }
598
600 ///////////////////////////////////////////////////////////////////////////// 599 /////////////////////////////////////////////////////////////////////////////
601 // Callbacks on the OS multimedia thread. 600 // Callbacks on the OS multimedia thread.
602 ///////////////////////////////////////////////////////////////////////////// 601 /////////////////////////////////////////////////////////////////////////////
603 602
604 static void CALLBACK OnMidiInEventOnMultimediaThread(HMIDIIN midi_in_handle, 603 static void CALLBACK
605 UINT message, 604 OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle,
Takashi Toyoshima 2015/04/06 18:02:38 Name is changed because MIM_OPEN is not called on
yukawa 2015/04/06 18:41:41 Acknowledged.
606 DWORD_PTR instance, 605 UINT message,
607 DWORD_PTR param1, 606 DWORD_PTR instance,
608 DWORD_PTR param2) { 607 DWORD_PTR param1,
608 DWORD_PTR param2) {
609 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 609 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
610 if (!self) 610 if (!self)
611 return; 611 return;
612 switch (message) { 612 switch (message) {
613 case MIM_OPEN: 613 case MIM_OPEN:
614 self->OnMidiInOpenOnMultimediaThread(midi_in_handle); 614 self->OnMidiInOpen(midi_in_handle);
615 break; 615 break;
616 case MIM_DATA: 616 case MIM_DATA:
617 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); 617 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2);
618 break; 618 break;
619 case MIM_LONGDATA: 619 case MIM_LONGDATA:
620 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, 620 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1,
621 param2); 621 param2);
622 break; 622 break;
623 case MIM_CLOSE: 623 case MIM_CLOSE:
624 self->OnMidiInCloseOnMultimediaThread(midi_in_handle); 624 self->OnMidiInCloseOnMultimediaThread(midi_in_handle);
625 break; 625 break;
626 } 626 }
627 } 627 }
628 628
629 void OnMidiInOpenOnMultimediaThread(HMIDIIN midi_in_handle) { 629 void OnMidiInOpen(HMIDIIN midi_in_handle) {
630 UINT device_id = 0; 630 UINT device_id = 0;
631 MMRESULT result = midiInGetID(midi_in_handle, &device_id); 631 MMRESULT result = midiInGetID(midi_in_handle, &device_id);
632 if (result != MMSYSERR_NOERROR) { 632 if (result != MMSYSERR_NOERROR) {
633 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); 633 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result);
634 return; 634 return;
635 } 635 }
636 MIDIINCAPS2W caps = {}; 636 MIDIINCAPS2W caps = {};
637 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), 637 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps),
638 sizeof(caps)); 638 sizeof(caps));
639 if (result != MMSYSERR_NOERROR) { 639 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); 778 unused_input_ports_[device_info].push(port_number);
779 } 779 }
780 task_thread_.message_loop()->PostTask( 780 task_thread_.message_loop()->PostTask(
781 FROM_HERE, 781 FROM_HERE,
782 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, 782 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread,
783 base::Unretained(this), port_number, 783 base::Unretained(this), port_number,
784 MIDI_PORT_DISCONNECTED)); 784 MIDI_PORT_DISCONNECTED));
785 } 785 }
786 786
787 static void CALLBACK 787 static void CALLBACK
788 OnMidiOutEventOnMultimediaThread(HMIDIOUT midi_out_handle, 788 OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle,
789 UINT message, 789 UINT message,
790 DWORD_PTR instance, 790 DWORD_PTR instance,
791 DWORD_PTR param1, 791 DWORD_PTR param1,
792 DWORD_PTR param2) { 792 DWORD_PTR param2) {
793 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); 793 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
794 if (!self) 794 if (!self)
795 return; 795 return;
796 switch (message) { 796 switch (message) {
797 case MOM_OPEN: 797 case MOM_OPEN:
798 self->OnMidiOutOpenOnMultimediaThread(midi_out_handle, param1, param2); 798 self->OnMidiOutOpen(midi_out_handle, param1, param2);
799 break; 799 break;
800 case MOM_DONE: 800 case MOM_DONE:
801 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); 801 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1);
802 break; 802 break;
803 case MOM_CLOSE: 803 case MOM_CLOSE:
804 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); 804 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle);
805 break; 805 break;
806 } 806 }
807 } 807 }
808 808
809 void OnMidiOutOpenOnMultimediaThread(HMIDIOUT midi_out_handle, 809 void OnMidiOutOpen(HMIDIOUT midi_out_handle,
810 DWORD_PTR param1, 810 DWORD_PTR param1,
811 DWORD_PTR param2) { 811 DWORD_PTR param2) {
812 UINT device_id = 0; 812 UINT device_id = 0;
813 MMRESULT result = midiOutGetID(midi_out_handle, &device_id); 813 MMRESULT result = midiOutGetID(midi_out_handle, &device_id);
814 if (result != MMSYSERR_NOERROR) { 814 if (result != MMSYSERR_NOERROR) {
815 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); 815 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result);
816 return; 816 return;
817 } 817 }
818 MIDIOUTCAPS2W caps = {}; 818 MIDIOUTCAPS2W caps = {};
819 result = midiOutGetDevCaps( 819 result = midiOutGetDevCaps(
820 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); 820 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps));
821 if (result != MMSYSERR_NOERROR) { 821 if (result != MMSYSERR_NOERROR) {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
899 state->closed = true; 899 state->closed = true;
900 } 900 }
901 task_thread_.message_loop()->PostTask( 901 task_thread_.message_loop()->PostTask(
902 FROM_HERE, 902 FROM_HERE,
903 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, 903 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread,
904 base::Unretained(this), port_number, 904 base::Unretained(this), port_number,
905 MIDI_PORT_DISCONNECTED)); 905 MIDI_PORT_DISCONNECTED));
906 } 906 }
907 907
908 ///////////////////////////////////////////////////////////////////////////// 908 /////////////////////////////////////////////////////////////////////////////
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. 909 // Callbacks on the sender thread.
999 ///////////////////////////////////////////////////////////////////////////// 910 /////////////////////////////////////////////////////////////////////////////
1000 911
1001 void AssertOnSenderThread() { 912 void AssertOnSenderThread() {
1002 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId()); 913 DCHECK_EQ(sender_thread_.thread_id(), base::PlatformThread::CurrentId());
1003 } 914 }
1004 915
1005 void SendOnSenderThread(uint32 port_number, 916 void SendOnSenderThread(uint32 port_number,
1006 uint64 port_age, 917 uint64 port_age,
1007 const std::vector<uint8>& data, 918 const std::vector<uint8>& data,
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1051 } 962 }
1052 963
1053 ///////////////////////////////////////////////////////////////////////////// 964 /////////////////////////////////////////////////////////////////////////////
1054 // Callbacks on the task thread. 965 // Callbacks on the task thread.
1055 ///////////////////////////////////////////////////////////////////////////// 966 /////////////////////////////////////////////////////////////////////////////
1056 967
1057 void AssertOnTaskThread() { 968 void AssertOnTaskThread() {
1058 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId()); 969 DCHECK_EQ(task_thread_.thread_id(), base::PlatformThread::CurrentId());
1059 } 970 }
1060 971
972
yukawa 2015/04/06 18:41:41 Is this blank line intentional?
Takashi Toyoshima 2015/04/06 19:51:47 oops, that's a mistake.
1061 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { 973 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) {
1062 AssertOnTaskThread(); 974 AssertOnTaskThread();
1063 auto state = GetInputDeviceFromHandle(midi_in_handle); 975 auto state = GetInputDeviceFromHandle(midi_in_handle);
1064 if (!state) 976 if (!state)
1065 return; 977 return;
1066 MMRESULT result = 978 MMRESULT result =
1067 midiInPrepareHeader(state->midi_handle, state->midi_header.get(), 979 midiInPrepareHeader(state->midi_handle, state->midi_header.get(),
1068 sizeof(*state->midi_header)); 980 sizeof(*state->midi_header));
1069 if (result != MMSYSERR_NOERROR) { 981 if (result != MMSYSERR_NOERROR) {
1070 DLOG(ERROR) << "Failed to initialize input buffer: " 982 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); 1032 delegate_->OnSetOutputPortState(port_index, state);
1121 } 1033 }
1122 1034
1123 ///////////////////////////////////////////////////////////////////////////// 1035 /////////////////////////////////////////////////////////////////////////////
1124 // Fields: 1036 // Fields:
1125 ///////////////////////////////////////////////////////////////////////////// 1037 /////////////////////////////////////////////////////////////////////////////
1126 1038
1127 // Does not take ownership. 1039 // Does not take ownership.
1128 MidiServiceWinDelegate* delegate_; 1040 MidiServiceWinDelegate* delegate_;
1129 1041
1130 base::Thread monitor_thread_;
1131 base::Thread sender_thread_; 1042 base::Thread sender_thread_;
1132 base::Thread task_thread_; 1043 base::Thread task_thread_;
1133 1044
1134 scoped_ptr<DeviceMonitorWindow> device_monitor_window_;
1135
1136 base::Lock input_ports_lock_; 1045 base::Lock input_ports_lock_;
1137 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> 1046 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>>
1138 input_device_map_; // GUARDED_BY(input_ports_lock_) 1047 input_device_map_; // GUARDED_BY(input_ports_lock_)
1139 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) 1048 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_)
1140 std::vector<scoped_refptr<MidiInputDeviceState>> 1049 std::vector<scoped_refptr<MidiInputDeviceState>>
1141 input_ports_; // GUARDED_BY(input_ports_lock_) 1050 input_ports_; // GUARDED_BY(input_ports_lock_)
1142 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_) 1051 std::vector<uint64> input_ports_ages_; // GUARDED_BY(input_ports_lock_)
1143 1052
1144 base::Lock output_ports_lock_; 1053 base::Lock output_ports_lock_;
1145 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> 1054 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>>
1146 output_device_map_; // GUARDED_BY(output_ports_lock_) 1055 output_device_map_; // GUARDED_BY(output_ports_lock_)
1147 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) 1056 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_)
1148 std::vector<scoped_refptr<MidiOutputDeviceState>> 1057 std::vector<scoped_refptr<MidiOutputDeviceState>>
1149 output_ports_; // GUARDED_BY(output_ports_lock_) 1058 output_ports_; // GUARDED_BY(output_ports_lock_)
1150 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_) 1059 std::vector<uint64> output_ports_ages_; // GUARDED_BY(output_ports_lock_)
1151 1060
1152 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note 1061 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note
1153 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until 1062 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until
1154 // |monitor_thread_|, |sender_thread_|, and |task_thread_| are stopped. 1063 // |sender_thread_|, and |task_thread_| are stopped.
1155 // This flag can be used as the signal that when background tasks must be 1064 // This flag can be used as the signal that when background tasks must be
1156 // interrupted. 1065 // interrupted.
1157 // TODO(toyoshim): Use std::atomic<bool> when it is allowed. 1066 // TODO(toyoshim): Use std::atomic<bool> when it is allowed.
1158 volatile bool destructor_started; 1067 volatile bool destructor_started;
1159 1068
1160 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); 1069 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl);
1161 }; 1070 };
1162 1071
1163 } // namespace 1072 } // namespace
1164 1073
(...skipping 21 matching lines...) Expand all
1186 // TOOD(toyoshim): This calculation should be done when the date is actually 1095 // TOOD(toyoshim): This calculation should be done when the date is actually
1187 // sent. 1096 // sent.
1188 client->AccumulateMidiBytesSent(data.size()); 1097 client->AccumulateMidiBytesSent(data.size());
1189 } 1098 }
1190 1099
1191 MidiManager* MidiManager::Create() { 1100 MidiManager* MidiManager::Create() {
1192 return new MidiManagerWin(); 1101 return new MidiManagerWin();
1193 } 1102 }
1194 1103
1195 } // namespace media 1104 } // 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