| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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/dynamically_initialized_midi_manager_win.h" | 5 #include "media/midi/dynamically_initialized_midi_manager_win.h" |
| 6 | 6 |
| 7 #include <windows.h> | 7 #include <windows.h> |
| 8 | 8 |
| 9 #include <mmreg.h> | 9 #include <mmreg.h> |
| 10 #include <mmsystem.h> | 10 #include <mmsystem.h> |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 // Calculates event time from elapsed time that system provides. | 33 // Calculates event time from elapsed time that system provides. |
| 34 base::TimeTicks CalculateInEventTime(size_t index, uint32_t elapsed_ms) const; | 34 base::TimeTicks CalculateInEventTime(size_t index, uint32_t elapsed_ms) const; |
| 35 | 35 |
| 36 // Registers HMIDIIN handle to resolve port index. | 36 // Registers HMIDIIN handle to resolve port index. |
| 37 void RegisterInHandle(HMIDIIN handle, size_t index); | 37 void RegisterInHandle(HMIDIIN handle, size_t index); |
| 38 | 38 |
| 39 // Unregisters HMIDIIN handle. | 39 // Unregisters HMIDIIN handle. |
| 40 void UnregisterInHandle(HMIDIIN handle); | 40 void UnregisterInHandle(HMIDIIN handle); |
| 41 | 41 |
| 42 // Finds HMIDIIN handle and fullfil |out_index| with the port index. | 42 // Finds HMIDIIN handle and fullfil |out_index| with the port index. |
| 43 bool FindHandle(HMIDIIN hmi, size_t* out_index); | 43 bool FindInHandle(HMIDIIN hmi, size_t* out_index); |
| 44 | 44 |
| 45 // Restores used input buffer for the next data receive. | 45 // Restores used input buffer for the next data receive. |
| 46 void RestoreInBuffer(size_t index); | 46 void RestoreInBuffer(size_t index); |
| 47 | 47 |
| 48 // Ports accessors. | 48 // Ports accessors. |
| 49 std::vector<std::unique_ptr<InPort>>* inputs() { return &input_ports_; } | 49 std::vector<std::unique_ptr<InPort>>* inputs() { return &input_ports_; } |
| 50 std::vector<std::unique_ptr<OutPort>>* outputs() { return &output_ports_; } | 50 std::vector<std::unique_ptr<OutPort>>* outputs() { return &output_ports_; } |
| 51 | 51 |
| 52 // Handles MIDI input port callbacks that runs on a system provided thread. | 52 // Handles MIDI input port callbacks that runs on a system provided thread. |
| 53 static void CALLBACK HandleMidiInCallback(HMIDIIN hmi, | 53 static void CALLBACK HandleMidiInCallback(HMIDIIN hmi, |
| 54 UINT msg, | 54 UINT msg, |
| 55 DWORD_PTR instance, | 55 DWORD_PTR instance, |
| 56 DWORD_PTR param1, | 56 DWORD_PTR param1, |
| 57 DWORD_PTR param2); | 57 DWORD_PTR param2); |
| 58 | 58 |
| 59 // Handles MIDI output port callbacks that runs on a system provided thread. |
| 60 static void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, |
| 61 UINT msg, |
| 62 DWORD_PTR instance, |
| 63 DWORD_PTR param1, |
| 64 DWORD_PTR param2); |
| 65 |
| 59 private: | 66 private: |
| 60 // Holds all MIDI input or output ports connected once. | 67 // Holds all MIDI input or output ports connected once. |
| 61 std::vector<std::unique_ptr<InPort>> input_ports_; | 68 std::vector<std::unique_ptr<InPort>> input_ports_; |
| 62 std::vector<std::unique_ptr<OutPort>> output_ports_; | 69 std::vector<std::unique_ptr<OutPort>> output_ports_; |
| 63 | 70 |
| 64 // Map to resolve MIDI input port index from HMIDIIN. | 71 // Map to resolve MIDI input port index from HMIDIIN. |
| 65 std::map<HMIDIIN, size_t> hmidiin_to_index_map_; | 72 std::map<HMIDIIN, size_t> hmidiin_to_index_map_; |
| 66 }; | 73 }; |
| 67 | 74 |
| 68 namespace { | 75 namespace { |
| 69 | 76 |
| 70 // Assumes that nullptr represents an invalid MIDI handle. | 77 // Assumes that nullptr represents an invalid MIDI handle. |
| 71 constexpr HMIDIIN kInvalidInHandle = nullptr; | 78 constexpr HMIDIIN kInvalidInHandle = nullptr; |
| 72 constexpr HMIDIOUT kInvalidOutHandle = nullptr; | 79 constexpr HMIDIOUT kInvalidOutHandle = nullptr; |
| 73 | 80 |
| 81 // Defines SysEx message size limit. |
| 82 // TODO(crbug.com/383578): This restriction should be removed once Web MIDI |
| 83 // defines a standardized way to handle large sysex messages. |
| 84 // Note for built-in USB-MIDI driver: |
| 85 // From an observation on Windows 7/8.1 with a USB-MIDI keyboard, |
| 86 // midiOutLongMsg() will be always blocked. Sending 64 bytes or less data takes |
| 87 // roughly 300 usecs. Sending 2048 bytes or more data takes roughly |
| 88 // |message.size() / (75 * 1024)| secs in practice. Here we put 256 KB size |
| 89 // limit on SysEx message, with hoping that midiOutLongMsg will be blocked at |
| 90 // most 4 sec or so with a typical USB-MIDI device. |
| 91 // TODO(toyoshim): Consider to use linked small buffers so that midiOutReset() |
| 92 // can abort sending unhandled following buffers. |
| 93 constexpr size_t kSysExSizeLimit = 256 * 1024; |
| 94 |
| 74 // Defines input buffer size. | 95 // Defines input buffer size. |
| 75 constexpr size_t kBufferLength = 32 * 1024; | 96 constexpr size_t kBufferLength = 32 * 1024; |
| 76 | 97 |
| 77 // Global variables to identify MidiManager instance. | 98 // Global variables to identify MidiManager instance. |
| 78 constexpr int kInvalidInstanceId = -1; | 99 constexpr int kInvalidInstanceId = -1; |
| 79 int g_active_instance_id = kInvalidInstanceId; | 100 int g_active_instance_id = kInvalidInstanceId; |
| 80 DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr; | 101 DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr; |
| 81 | 102 |
| 82 // Obtains base::Lock instance pointer to lock instance_id. | 103 // Obtains base::Lock instance pointer to lock instance_id. |
| 83 base::Lock* GetInstanceIdLock() { | 104 base::Lock* GetInstanceIdLock() { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 134 using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>; | 155 using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>; |
| 135 | 156 |
| 136 ScopedMIDIHDR CreateMIDIHDR(size_t size) { | 157 ScopedMIDIHDR CreateMIDIHDR(size_t size) { |
| 137 ScopedMIDIHDR hdr(new MIDIHDR); | 158 ScopedMIDIHDR hdr(new MIDIHDR); |
| 138 ZeroMemory(hdr.get(), sizeof(*hdr)); | 159 ZeroMemory(hdr.get(), sizeof(*hdr)); |
| 139 hdr->lpData = new char[size]; | 160 hdr->lpData = new char[size]; |
| 140 hdr->dwBufferLength = static_cast<DWORD>(size); | 161 hdr->dwBufferLength = static_cast<DWORD>(size); |
| 141 return hdr; | 162 return hdr; |
| 142 } | 163 } |
| 143 | 164 |
| 165 ScopedMIDIHDR CreateMIDIHDR(const std::vector<uint8_t>& data) { |
| 166 ScopedMIDIHDR hdr(CreateMIDIHDR(data.size())); |
| 167 std::copy(data.begin(), data.end(), hdr->lpData); |
| 168 return hdr; |
| 169 } |
| 170 |
| 144 // Helper functions to close MIDI device handles on TaskRunner asynchronously. | 171 // Helper functions to close MIDI device handles on TaskRunner asynchronously. |
| 145 void FinalizeInPort(HMIDIIN handle, ScopedMIDIHDR hdr) { | 172 void FinalizeInPort(HMIDIIN handle, ScopedMIDIHDR hdr) { |
| 146 // Resets the device. This stops receiving messages, and allows to release | 173 // Resets the device. This stops receiving messages, and allows to release |
| 147 // registered buffer headers. Otherwise, midiInUnprepareHeader() and | 174 // registered buffer headers. Otherwise, midiInUnprepareHeader() and |
| 148 // midiInClose() will fail with MIDIERR_STILLPLAYING. | 175 // midiInClose() will fail with MIDIERR_STILLPLAYING. |
| 149 midiInReset(handle); | 176 midiInReset(handle); |
| 150 | 177 |
| 151 if (hdr) | 178 if (hdr) |
| 152 midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr)); | 179 midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr)); |
| 153 midiInClose(handle); | 180 midiInClose(handle); |
| 154 } | 181 } |
| 155 | 182 |
| 156 void FinalizeOutPort(HMIDIOUT handle) { | 183 void FinalizeOutPort(HMIDIOUT handle) { |
| 184 // Resets inflight buffers. This will cancel sending data that system |
| 185 // holds and were not sent yet. |
| 186 midiOutReset(handle); |
| 157 midiOutClose(handle); | 187 midiOutClose(handle); |
| 158 } | 188 } |
| 159 | 189 |
| 160 // Handles MIDI output port callbacks that runs on a system provided thread. | |
| 161 void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, | |
| 162 UINT msg, | |
| 163 DWORD_PTR instance, | |
| 164 DWORD_PTR param1, | |
| 165 DWORD_PTR param2) { | |
| 166 // TODO(toyoshim): Following patches will implement actual functions. | |
| 167 } | |
| 168 | |
| 169 // All instances of Port subclasses are always accessed behind a lock of | 190 // All instances of Port subclasses are always accessed behind a lock of |
| 170 // *GetTaskLock(). Port and subclasses implementation do not need to | 191 // *GetTaskLock(). Port and subclasses implementation do not need to |
| 171 // consider thread safety. | 192 // consider thread safety. |
| 172 class Port { | 193 class Port { |
| 173 public: | 194 public: |
| 174 Port(const std::string& type, | 195 Port(const std::string& type, |
| 175 uint32_t device_id, | 196 uint32_t device_id, |
| 176 uint16_t manufacturer_id, | 197 uint16_t manufacturer_id, |
| 177 uint16_t product_id, | 198 uint16_t product_id, |
| 178 uint32_t driver_version, | 199 uint32_t driver_version, |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 uint32_t device_id_; | 266 uint32_t device_id_; |
| 246 const uint16_t manufacturer_id_; | 267 const uint16_t manufacturer_id_; |
| 247 const uint16_t product_id_; | 268 const uint16_t product_id_; |
| 248 const uint32_t driver_version_; | 269 const uint32_t driver_version_; |
| 249 const std::string product_name_; | 270 const std::string product_name_; |
| 250 MidiPortInfo info_; | 271 MidiPortInfo info_; |
| 251 }; // class Port | 272 }; // class Port |
| 252 | 273 |
| 253 } // namespace | 274 } // namespace |
| 254 | 275 |
| 255 // TODO(toyoshim): Following patches will implement actual functions. | |
| 256 class DynamicallyInitializedMidiManagerWin::InPort final : public Port { | 276 class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| 257 public: | 277 public: |
| 258 InPort(DynamicallyInitializedMidiManagerWin* manager, | 278 InPort(DynamicallyInitializedMidiManagerWin* manager, |
| 259 int instance_id, | 279 int instance_id, |
| 260 UINT device_id, | 280 UINT device_id, |
| 261 const MIDIINCAPS2W& caps) | 281 const MIDIINCAPS2W& caps) |
| 262 : Port("input", | 282 : Port("input", |
| 263 device_id, | 283 device_id, |
| 264 caps.wMid, | 284 caps.wMid, |
| 265 caps.wPid, | 285 caps.wPid, |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 364 } | 384 } |
| 365 | 385 |
| 366 private: | 386 private: |
| 367 DynamicallyInitializedMidiManagerWin* manager_; | 387 DynamicallyInitializedMidiManagerWin* manager_; |
| 368 HMIDIIN in_handle_; | 388 HMIDIIN in_handle_; |
| 369 ScopedMIDIHDR hdr_; | 389 ScopedMIDIHDR hdr_; |
| 370 base::TimeTicks start_time_; | 390 base::TimeTicks start_time_; |
| 371 const int instance_id_; | 391 const int instance_id_; |
| 372 }; | 392 }; |
| 373 | 393 |
| 374 // TODO(toyoshim): Following patches will implement actual functions. | |
| 375 class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { | 394 class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| 376 public: | 395 public: |
| 377 OutPort(UINT device_id, const MIDIOUTCAPS2W& caps) | 396 OutPort(UINT device_id, const MIDIOUTCAPS2W& caps) |
| 378 : Port("output", | 397 : Port("output", |
| 379 device_id, | 398 device_id, |
| 380 caps.wMid, | 399 caps.wMid, |
| 381 caps.wPid, | 400 caps.wPid, |
| 382 caps.vDriverVersion, | 401 caps.vDriverVersion, |
| 383 base::WideToUTF8( | 402 base::WideToUTF8( |
| 384 base::string16(caps.szPname, wcslen(caps.szPname)))), | 403 base::string16(caps.szPname, wcslen(caps.szPname)))), |
| (...skipping 28 matching lines...) Expand all Loading... |
| 413 base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState, | 432 base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState, |
| 414 base::Unretained(manager), index_, info_.state)); | 433 base::Unretained(manager), index_, info_.state)); |
| 415 } | 434 } |
| 416 | 435 |
| 417 void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) { | 436 void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) { |
| 418 manager->PostReplyTask( | 437 manager->PostReplyTask( |
| 419 base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort, | 438 base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort, |
| 420 base::Unretained(manager), info_)); | 439 base::Unretained(manager), info_)); |
| 421 } | 440 } |
| 422 | 441 |
| 442 void Send(const std::vector<uint8_t>& data) { |
| 443 if (out_handle_ == kInvalidOutHandle) |
| 444 return; |
| 445 |
| 446 if (data.size() <= 3) { |
| 447 uint32_t message = 0; |
| 448 for (size_t i = 0; i < data.size(); ++i) |
| 449 message |= (static_cast<uint32_t>(data[i]) << (i * 8)); |
| 450 midiOutShortMsg(out_handle_, message); |
| 451 } else { |
| 452 if (data.size() > kSysExSizeLimit) { |
| 453 LOG(ERROR) << "Ignoring SysEx message due to the size limit" |
| 454 << ", size = " << data.size(); |
| 455 // TODO(toyoshim): Consider to report metrics here. |
| 456 return; |
| 457 } |
| 458 ScopedMIDIHDR hdr(CreateMIDIHDR(data)); |
| 459 MMRESULT result = |
| 460 midiOutPrepareHeader(out_handle_, hdr.get(), sizeof(*hdr)); |
| 461 if (result != MMSYSERR_NOERROR) |
| 462 return; |
| 463 result = midiOutLongMsg(out_handle_, hdr.get(), sizeof(*hdr)); |
| 464 if (result != MMSYSERR_NOERROR) { |
| 465 midiOutUnprepareHeader(out_handle_, hdr.get(), sizeof(*hdr)); |
| 466 } else { |
| 467 // MIDIHDR will be released on MOM_DONE. |
| 468 ignore_result(hdr.release()); |
| 469 } |
| 470 } |
| 471 } |
| 472 |
| 423 // Port overrides: | 473 // Port overrides: |
| 424 bool Connect() override { | 474 bool Connect() override { |
| 425 // Until |software| option is supported, disable Microsoft GS Wavetable | 475 // Until |software| option is supported, disable Microsoft GS Wavetable |
| 426 // Synth that has a known security issue. | 476 // Synth that has a known security issue. |
| 427 if (software_ && manufacturer_id_ == MM_MICROSOFT && | 477 if (software_ && manufacturer_id_ == MM_MICROSOFT && |
| 428 (product_id_ == MM_MSFT_WDMAUDIO_MIDIOUT || | 478 (product_id_ == MM_MSFT_WDMAUDIO_MIDIOUT || |
| 429 product_id_ == MM_MSFT_GENERIC_MIDISYNTH)) { | 479 product_id_ == MM_MSFT_GENERIC_MIDISYNTH)) { |
| 430 return false; | 480 return false; |
| 431 } | 481 } |
| 432 return Port::Connect(); | 482 return Port::Connect(); |
| 433 } | 483 } |
| 434 | 484 |
| 435 bool Disconnect() override { | 485 bool Disconnect() override { |
| 436 if (out_handle_ != kInvalidOutHandle) { | 486 if (out_handle_ != kInvalidOutHandle) { |
| 437 // Following API call may fail because device was already disconnected. | 487 // Following API call may fail because device was already disconnected. |
| 438 // But just in case. | 488 // But just in case. |
| 439 midiOutClose(out_handle_); | 489 midiOutClose(out_handle_); |
| 440 out_handle_ = kInvalidOutHandle; | 490 out_handle_ = kInvalidOutHandle; |
| 441 } | 491 } |
| 442 return Port::Disconnect(); | 492 return Port::Disconnect(); |
| 443 } | 493 } |
| 444 | 494 |
| 445 void Open() override { | 495 void Open() override { |
| 446 MMRESULT result = | 496 MMRESULT result = midiOutOpen( |
| 447 midiOutOpen(&out_handle_, device_id_, | 497 &out_handle_, device_id_, |
| 448 reinterpret_cast<DWORD_PTR>(&HandleMidiOutCallback), 0, | 498 reinterpret_cast<DWORD_PTR>(&PortManager::HandleMidiOutCallback), 0, |
| 449 CALLBACK_FUNCTION); | 499 CALLBACK_FUNCTION); |
| 450 if (result == MMSYSERR_NOERROR) { | 500 if (result == MMSYSERR_NOERROR) { |
| 451 Port::Open(); | 501 Port::Open(); |
| 452 } else { | 502 } else { |
| 453 out_handle_ = kInvalidOutHandle; | 503 out_handle_ = kInvalidOutHandle; |
| 454 Disconnect(); | 504 Disconnect(); |
| 455 } | 505 } |
| 456 } | 506 } |
| 457 | 507 |
| 458 const bool software_; | 508 const bool software_; |
| 459 HMIDIOUT out_handle_; | 509 HMIDIOUT out_handle_; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 474 GetTaskLock()->AssertAcquired(); | 524 GetTaskLock()->AssertAcquired(); |
| 475 hmidiin_to_index_map_[handle] = index; | 525 hmidiin_to_index_map_[handle] = index; |
| 476 } | 526 } |
| 477 | 527 |
| 478 void DynamicallyInitializedMidiManagerWin::PortManager::UnregisterInHandle( | 528 void DynamicallyInitializedMidiManagerWin::PortManager::UnregisterInHandle( |
| 479 HMIDIIN handle) { | 529 HMIDIIN handle) { |
| 480 GetTaskLock()->AssertAcquired(); | 530 GetTaskLock()->AssertAcquired(); |
| 481 hmidiin_to_index_map_.erase(handle); | 531 hmidiin_to_index_map_.erase(handle); |
| 482 } | 532 } |
| 483 | 533 |
| 484 bool DynamicallyInitializedMidiManagerWin::PortManager::FindHandle( | 534 bool DynamicallyInitializedMidiManagerWin::PortManager::FindInHandle( |
| 485 HMIDIIN hmi, | 535 HMIDIIN hmi, |
| 486 size_t* out_index) { | 536 size_t* out_index) { |
| 487 GetTaskLock()->AssertAcquired(); | 537 GetTaskLock()->AssertAcquired(); |
| 488 auto found = hmidiin_to_index_map_.find(hmi); | 538 auto found = hmidiin_to_index_map_.find(hmi); |
| 489 if (found == hmidiin_to_index_map_.end()) | 539 if (found == hmidiin_to_index_map_.end()) |
| 490 return false; | 540 return false; |
| 491 *out_index = found->second; | 541 *out_index = found->second; |
| 492 return true; | 542 return true; |
| 493 } | 543 } |
| 494 | 544 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 515 // and to access member variables that are used on TaskRunner. | 565 // and to access member variables that are used on TaskRunner. |
| 516 base::AutoLock task_lock(*GetTaskLock()); | 566 base::AutoLock task_lock(*GetTaskLock()); |
| 517 { | 567 { |
| 518 base::AutoLock lock(*GetInstanceIdLock()); | 568 base::AutoLock lock(*GetInstanceIdLock()); |
| 519 if (instance_id != g_active_instance_id) | 569 if (instance_id != g_active_instance_id) |
| 520 return; | 570 return; |
| 521 manager = g_manager_instance; | 571 manager = g_manager_instance; |
| 522 } | 572 } |
| 523 | 573 |
| 524 size_t index; | 574 size_t index; |
| 525 if (!manager->port_manager()->FindHandle(hmi, &index)) | 575 if (!manager->port_manager()->FindInHandle(hmi, &index)) |
| 526 return; | 576 return; |
| 527 | 577 |
| 528 DCHECK(msg == MIM_DATA || msg == MIM_LONGDATA); | 578 DCHECK(msg == MIM_DATA || msg == MIM_LONGDATA); |
| 529 if (msg == MIM_DATA) { | 579 if (msg == MIM_DATA) { |
| 530 const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff); | 580 const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff); |
| 531 const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff); | 581 const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff); |
| 532 const uint8_t second_data_byte = | 582 const uint8_t second_data_byte = |
| 533 static_cast<uint8_t>((param1 >> 16) & 0xff); | 583 static_cast<uint8_t>((param1 >> 16) & 0xff); |
| 534 const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte}; | 584 const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte}; |
| 535 const size_t len = GetMessageLength(status_byte); | 585 const size_t len = GetMessageLength(status_byte); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 551 &DynamicallyInitializedMidiManagerWin::ReceiveMidiData, | 601 &DynamicallyInitializedMidiManagerWin::ReceiveMidiData, |
| 552 base::Unretained(manager), index, data, | 602 base::Unretained(manager), index, data, |
| 553 manager->port_manager()->CalculateInEventTime(index, param2))); | 603 manager->port_manager()->CalculateInEventTime(index, param2))); |
| 554 } | 604 } |
| 555 manager->PostTask(base::Bind( | 605 manager->PostTask(base::Bind( |
| 556 &DynamicallyInitializedMidiManagerWin::PortManager::RestoreInBuffer, | 606 &DynamicallyInitializedMidiManagerWin::PortManager::RestoreInBuffer, |
| 557 base::Unretained(manager->port_manager()), index)); | 607 base::Unretained(manager->port_manager()), index)); |
| 558 } | 608 } |
| 559 } | 609 } |
| 560 | 610 |
| 611 void CALLBACK |
| 612 DynamicallyInitializedMidiManagerWin::PortManager::HandleMidiOutCallback( |
| 613 HMIDIOUT hmo, |
| 614 UINT msg, |
| 615 DWORD_PTR instance, |
| 616 DWORD_PTR param1, |
| 617 DWORD_PTR param2) { |
| 618 if (msg == MOM_DONE) { |
| 619 ScopedMIDIHDR hdr(reinterpret_cast<LPMIDIHDR>(param1)); |
| 620 if (!hdr) |
| 621 return; |
| 622 // TODO(toyoshim): Call midiOutUnprepareHeader outside the callback. |
| 623 // Since this callback may be invoked after the manager is destructed, |
| 624 // and can not send a task to the TaskRunner in such case, we need to |
| 625 // consider to track MIDIHDR per port, and clean it in port finalization |
| 626 // steps, too. |
| 627 midiOutUnprepareHeader(hmo, hdr.get(), sizeof(*hdr)); |
| 628 } |
| 629 } |
| 630 |
| 561 DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin( | 631 DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin( |
| 562 MidiService* service) | 632 MidiService* service) |
| 563 : MidiManager(service), | 633 : MidiManager(service), |
| 564 instance_id_(IssueNextInstanceId()), | 634 instance_id_(IssueNextInstanceId()), |
| 565 port_manager_(base::MakeUnique<PortManager>()) { | 635 port_manager_(base::MakeUnique<PortManager>()) { |
| 566 base::AutoLock lock(*GetInstanceIdLock()); | 636 base::AutoLock lock(*GetInstanceIdLock()); |
| 567 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); | 637 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); |
| 568 | 638 |
| 569 // Obtains the task runner for the current thread that hosts this instnace. | 639 // Obtains the task runner for the current thread that hosts this instnace. |
| 570 thread_runner_ = base::ThreadTaskRunnerHandle::Get(); | 640 thread_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 621 port->Finalize(service()->GetTaskRunner(kTaskRunner)); | 691 port->Finalize(service()->GetTaskRunner(kTaskRunner)); |
| 622 for (const auto& port : *port_manager_->outputs()) | 692 for (const auto& port : *port_manager_->outputs()) |
| 623 port->Finalize(service()->GetTaskRunner(kTaskRunner)); | 693 port->Finalize(service()->GetTaskRunner(kTaskRunner)); |
| 624 } | 694 } |
| 625 | 695 |
| 626 void DynamicallyInitializedMidiManagerWin::DispatchSendMidiData( | 696 void DynamicallyInitializedMidiManagerWin::DispatchSendMidiData( |
| 627 MidiManagerClient* client, | 697 MidiManagerClient* client, |
| 628 uint32_t port_index, | 698 uint32_t port_index, |
| 629 const std::vector<uint8_t>& data, | 699 const std::vector<uint8_t>& data, |
| 630 double timestamp) { | 700 double timestamp) { |
| 631 // TODO(toyoshim): Following patches will implement. | 701 if (timestamp != 0.0) { |
| 702 base::TimeTicks time = base::TimeTicks() + |
| 703 base::TimeDelta::FromMicroseconds( |
| 704 timestamp * base::Time::kMicrosecondsPerSecond); |
| 705 base::TimeTicks now = base::TimeTicks::Now(); |
| 706 if (now < time) { |
| 707 PostDelayedTask( |
| 708 base::Bind(&DynamicallyInitializedMidiManagerWin::SendOnTaskRunner, |
| 709 base::Unretained(this), client, port_index, data), |
| 710 time - now); |
| 711 return; |
| 712 } |
| 713 } |
| 714 PostTask(base::Bind(&DynamicallyInitializedMidiManagerWin::SendOnTaskRunner, |
| 715 base::Unretained(this), client, port_index, data)); |
| 632 } | 716 } |
| 633 | 717 |
| 634 void DynamicallyInitializedMidiManagerWin::OnDevicesChanged( | 718 void DynamicallyInitializedMidiManagerWin::OnDevicesChanged( |
| 635 base::SystemMonitor::DeviceType device_type) { | 719 base::SystemMonitor::DeviceType device_type) { |
| 636 // Notified on the I/O thread. | 720 // Notified on the I/O thread. |
| 637 CHECK(thread_runner_->BelongsToCurrentThread()); | 721 CHECK(thread_runner_->BelongsToCurrentThread()); |
| 638 | 722 |
| 639 switch (device_type) { | 723 switch (device_type) { |
| 640 case base::SystemMonitor::DEVTYPE_AUDIO: | 724 case base::SystemMonitor::DEVTYPE_AUDIO: |
| 641 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE: | 725 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE: |
| (...skipping 14 matching lines...) Expand all Loading... |
| 656 base::TimeTicks time) { | 740 base::TimeTicks time) { |
| 657 MidiManager::ReceiveMidiData(index, data.data(), data.size(), time); | 741 MidiManager::ReceiveMidiData(index, data.data(), data.size(), time); |
| 658 } | 742 } |
| 659 | 743 |
| 660 void DynamicallyInitializedMidiManagerWin::PostTask(const base::Closure& task) { | 744 void DynamicallyInitializedMidiManagerWin::PostTask(const base::Closure& task) { |
| 661 service() | 745 service() |
| 662 ->GetTaskRunner(kTaskRunner) | 746 ->GetTaskRunner(kTaskRunner) |
| 663 ->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); | 747 ->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); |
| 664 } | 748 } |
| 665 | 749 |
| 750 void DynamicallyInitializedMidiManagerWin::PostDelayedTask( |
| 751 const base::Closure& task, |
| 752 base::TimeDelta delay) { |
| 753 service() |
| 754 ->GetTaskRunner(kTaskRunner) |
| 755 ->PostDelayedTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task), |
| 756 delay); |
| 757 } |
| 758 |
| 666 void DynamicallyInitializedMidiManagerWin::PostReplyTask( | 759 void DynamicallyInitializedMidiManagerWin::PostReplyTask( |
| 667 const base::Closure& task) { | 760 const base::Closure& task) { |
| 668 thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); | 761 thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); |
| 669 } | 762 } |
| 670 | 763 |
| 671 void DynamicallyInitializedMidiManagerWin::InitializeOnTaskRunner() { | 764 void DynamicallyInitializedMidiManagerWin::InitializeOnTaskRunner() { |
| 672 UpdateDeviceListOnTaskRunner(); | 765 UpdateDeviceListOnTaskRunner(); |
| 673 PostReplyTask( | 766 PostReplyTask( |
| 674 base::Bind(&DynamicallyInitializedMidiManagerWin::CompleteInitialization, | 767 base::Bind(&DynamicallyInitializedMidiManagerWin::CompleteInitialization, |
| 675 base::Unretained(this), mojom::Result::OK)); | 768 base::Unretained(this), mojom::Result::OK)); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 718 }) == known_ports->end()) { | 811 }) == known_ports->end()) { |
| 719 size_t index = known_ports->size(); | 812 size_t index = known_ports->size(); |
| 720 port->set_index(index); | 813 port->set_index(index); |
| 721 known_ports->push_back(std::move(port)); | 814 known_ports->push_back(std::move(port)); |
| 722 (*known_ports)[index]->Connect(); | 815 (*known_ports)[index]->Connect(); |
| 723 (*known_ports)[index]->NotifyPortAdded(this); | 816 (*known_ports)[index]->NotifyPortAdded(this); |
| 724 } | 817 } |
| 725 } | 818 } |
| 726 } | 819 } |
| 727 | 820 |
| 821 void DynamicallyInitializedMidiManagerWin::SendOnTaskRunner( |
| 822 MidiManagerClient* client, |
| 823 uint32_t port_index, |
| 824 const std::vector<uint8_t>& data) { |
| 825 CHECK_GT(port_manager_->outputs()->size(), port_index); |
| 826 (*port_manager_->outputs())[port_index]->Send(data); |
| 827 // |client| will be checked inside MidiManager::AccumulateMidiBytesSent. |
| 828 PostReplyTask( |
| 829 base::Bind(&DynamicallyInitializedMidiManagerWin::AccumulateMidiBytesSent, |
| 830 base::Unretained(this), client, data.size())); |
| 831 } |
| 832 |
| 728 } // namespace midi | 833 } // namespace midi |
| OLD | NEW |