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)); |