Chromium Code Reviews| Index: media/midi/dynamically_initialized_midi_manager_win.cc |
| diff --git a/media/midi/dynamically_initialized_midi_manager_win.cc b/media/midi/dynamically_initialized_midi_manager_win.cc |
| index aae9d1274a788e5c850ca22caab0f58f414f2522..e29fee8c248447d3a266e5aa798e6fbb879abf57 100644 |
| --- a/media/midi/dynamically_initialized_midi_manager_win.cc |
| +++ b/media/midi/dynamically_initialized_midi_manager_win.cc |
| @@ -32,12 +32,18 @@ namespace { |
| constexpr HMIDIIN kInvalidInHandle = nullptr; |
| constexpr HMIDIOUT kInvalidOutHandle = nullptr; |
| +// Defines input buffer size. |
| +constexpr size_t kBufferLength = 32 * 1024; |
| + |
| // Global variables to identify MidiManager instance. |
| constexpr int kInvalidInstanceId = -1; |
| int g_active_instance_id = kInvalidInstanceId; |
| int g_next_instance_id = 0; |
| DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr; |
| +// Global map to resolve MIDI input port index from HMIDIIN. |
| +std::map<HMIDIIN, size_t> g_hmidiin_to_index_map; |
| + |
| // Obtains base::Lock instance pointer to lock instance_id. |
| base::Lock* GetInstanceIdLock() { |
| static base::Lock* lock = new base::Lock; |
| @@ -70,8 +76,44 @@ void RunTask(int instance_id, const base::Closure& task) { |
| // TODO(toyoshim): Factor out TaskRunner related functionaliries above, and |
| // deprecate MidiScheduler. It should be available via MidiManager::scheduler(). |
| +// Utility class to handle MIDIHDR struct safely. |
| +class MIDIHDRDeleter { |
| + public: |
| + void operator()(LPMIDIHDR header) { |
| + if (!header) |
| + return; |
| + delete[] static_cast<char*>(header->lpData); |
| + delete header; |
| + } |
| +}; |
| + |
| +using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>; |
| + |
| +ScopedMIDIHDR CreateMIDIHDR(size_t size) { |
| + ScopedMIDIHDR hdr(new MIDIHDR); |
| + ZeroMemory(hdr.get(), sizeof(*hdr)); |
| + hdr->lpData = new char[size]; |
| + hdr->dwBufferLength = static_cast<DWORD>(size); |
| + return hdr; |
| +} |
| + |
| +ScopedMIDIHDR CreateMIDIHDR(const std::vector<uint8_t>& data) { |
| + ScopedMIDIHDR hdr(CreateMIDIHDR(data.size())); |
| + std::copy(data.begin(), data.end(), hdr->lpData); |
| + return hdr; |
| +} |
| + |
| // Helper functions to close MIDI device handles on TaskRunner asynchronously. |
| -void FinalizeInPort(HMIDIIN handle) { |
| +void FinalizeInPort(HMIDIIN handle, LPMIDIHDR lphdr) { |
| + // Resets the device. This stops receiving messages, and allows to release |
| + // registered buffer headers. Otherwise, midiInUnprepareHeader() and |
| + // midiInClose() will fail with MIDIERR_STILLPLAYING. |
| + midiInReset(handle); |
| + |
| + // Obtains MIDIHDR ownership to release allocated buffers. |
| + ScopedMIDIHDR hdr(lphdr); |
| + if (hdr) |
| + midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr)); |
| midiInClose(handle); |
| } |
| @@ -85,7 +127,53 @@ void CALLBACK HandleMidiInCallback(HMIDIIN hmi, |
| DWORD_PTR instance, |
| DWORD_PTR param1, |
| DWORD_PTR param2) { |
| - // TODO(toyoshim): Following patches will implement actual functions. |
| + if (msg != MIM_DATA && msg != MIM_LONGDATA) |
| + return; |
| + int instance_id = static_cast<int>(instance); |
| + DynamicallyInitializedMidiManagerWin* manager = nullptr; |
| + |
| + // Use |g_task_lock| so to ensure the instance can keep alive while running, |
| + // and to access member variables that are used on TaskRunner. |
| + base::AutoLock task_lock(*GetTaskLock()); |
| + { |
| + base::AutoLock lock(*GetInstanceIdLock()); |
| + if (instance_id != g_active_instance_id) |
| + return; |
| + manager = g_manager_instance; |
| + } |
| + |
| + auto found = g_hmidiin_to_index_map.find(hmi); |
| + if (found == g_hmidiin_to_index_map.end()) |
| + return; |
| + size_t index = found->second; |
| + |
| + if (msg == MIM_DATA) { |
| + const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff); |
| + const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff); |
| + const uint8_t second_data_byte = |
| + static_cast<uint8_t>((param1 >> 16) & 0xff); |
| + const size_t len = GetMessageLength(status_byte); |
| + const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte}; |
| + std::vector<uint8_t> data; |
| + data.assign(kData, kData + len); |
| + manager->PostReplyTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::ReceiveMidiData, |
| + base::Unretained(manager), index, data, |
| + manager->CalculateInEventTime(index, param2))); |
| + } else { // msg == MIM_LONGDATA |
| + LPMIDIHDR hdr = reinterpret_cast<LPMIDIHDR>(param1); |
| + if (hdr->dwBytesRecorded > 0) { |
| + const uint8_t* src = reinterpret_cast<const uint8_t*>(hdr->lpData); |
| + std::vector<uint8_t> data; |
| + data.assign(src, src + hdr->dwBytesRecorded); |
| + manager->PostReplyTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::ReceiveMidiData, |
| + base::Unretained(manager), index, data, |
| + manager->CalculateInEventTime(index, param2))); |
| + } |
| + // TODO(toyoshim): Call midiInAddBuffer outside the callback. |
| + midiInAddBuffer(hmi, hdr, sizeof(*hdr)); |
| + } |
| } |
| // Handles MIDI output port callbacks that runs on a system provided thread. |
| @@ -190,24 +278,49 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) { |
| if (in_handle_ != kInvalidInHandle) { |
| - runner->PostTask(FROM_HERE, base::Bind(&FinalizeInPort, in_handle_)); |
| + runner->PostTask(FROM_HERE, |
| + base::Bind(&FinalizeInPort, in_handle_, hdr_.release())); |
| } |
| } |
| + base::TimeTicks CalculateInEventTime(uint32_t elapsed_ms) const { |
| + return start_time_ + base::TimeDelta::FromMilliseconds(elapsed_ms); |
| + } |
| + |
| // Port overrides: |
| void Open() override { |
| MMRESULT result = |
| midiInOpen(&in_handle_, device_id_, |
| reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback), |
| instance_id_, CALLBACK_FUNCTION); |
| + if (result == MMSYSERR_NOERROR) { |
| + hdr_ = CreateMIDIHDR(kBufferLength); |
| + result = midiInPrepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_)); |
| + } |
| + if (result != MMSYSERR_NOERROR) |
| + in_handle_ = kInvalidInHandle; |
| + if (result == MMSYSERR_NOERROR) |
| + result = midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_)); |
| if (result == MMSYSERR_NOERROR) |
| + result = midiInStart(in_handle_); |
| + if (result == MMSYSERR_NOERROR) { |
| + start_time_ = base::TimeTicks::Now(); |
| + g_hmidiin_to_index_map[in_handle_] = index_; |
| Port::Open(); |
| - else |
| + } else { |
| + if (in_handle_ != kInvalidInHandle) { |
| + midiInUnprepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_)); |
| + midiInClose(in_handle_); |
| + in_handle_ = kInvalidInHandle; |
|
Takashi Toyoshima
2017/02/24 06:34:28
Actually this was a real reason why Open() wasn't
|
| + } |
| Disconnect(); |
| + } |
| } |
| private: |
| HMIDIIN in_handle_; |
| + ScopedMIDIHDR hdr_; |
| + base::TimeTicks start_time_; |
| int instance_id_; |
| }; |
| @@ -276,6 +389,21 @@ DynamicallyInitializedMidiManagerWin::~DynamicallyInitializedMidiManagerWin() { |
| CHECK(thread_runner_->BelongsToCurrentThread()); |
| } |
| +base::TimeTicks DynamicallyInitializedMidiManagerWin::CalculateInEventTime( |
| + size_t index, |
| + uint32_t elapsed_ms) const { |
| + GetTaskLock()->AssertAcquired(); |
| + CHECK_GT(input_ports_.size(), index); |
| + return input_ports_[index]->CalculateInEventTime(elapsed_ms); |
| +} |
| + |
| +void DynamicallyInitializedMidiManagerWin::ReceiveMidiData( |
| + uint32_t index, |
| + const std::vector<uint8_t>& data, |
| + base::TimeTicks time) { |
| + MidiManager::ReceiveMidiData(index, data.data(), data.size(), time); |
| +} |
| + |
| void DynamicallyInitializedMidiManagerWin::PostReplyTask( |
| const base::Closure& task) { |
| thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); |