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 14 matching lines...) Expand all Loading... | |
25 #include "media/midi/midi_service.h" | 25 #include "media/midi/midi_service.h" |
26 | 26 |
27 namespace midi { | 27 namespace midi { |
28 | 28 |
29 namespace { | 29 namespace { |
30 | 30 |
31 // Assumes that nullptr represents an invalid MIDI handle. | 31 // Assumes that nullptr represents an invalid MIDI handle. |
32 constexpr HMIDIIN kInvalidInHandle = nullptr; | 32 constexpr HMIDIIN kInvalidInHandle = nullptr; |
33 constexpr HMIDIOUT kInvalidOutHandle = nullptr; | 33 constexpr HMIDIOUT kInvalidOutHandle = nullptr; |
34 | 34 |
35 // Defines input buffer size. | |
36 constexpr size_t kBufferLength = 32 * 1024; | |
37 | |
35 // Global variables to identify MidiManager instance. | 38 // Global variables to identify MidiManager instance. |
36 constexpr int kInvalidInstanceId = -1; | 39 constexpr int kInvalidInstanceId = -1; |
37 int g_active_instance_id = kInvalidInstanceId; | 40 int g_active_instance_id = kInvalidInstanceId; |
38 int g_next_instance_id = 0; | 41 int g_next_instance_id = 0; |
39 DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr; | 42 DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr; |
40 | 43 |
44 // Global map to resolve MIDI input port index from HMIDIIN. | |
45 std::map<HMIDIIN, size_t> g_hmidiin_to_index_map; | |
46 | |
41 // Obtains base::Lock instance pointer to lock instance_id. | 47 // Obtains base::Lock instance pointer to lock instance_id. |
42 base::Lock* GetInstanceIdLock() { | 48 base::Lock* GetInstanceIdLock() { |
43 static base::Lock* lock = new base::Lock; | 49 static base::Lock* lock = new base::Lock; |
44 return lock; | 50 return lock; |
45 } | 51 } |
46 | 52 |
47 // Use single TaskRunner for all tasks running outside the I/O thread. | 53 // Use single TaskRunner for all tasks running outside the I/O thread. |
48 constexpr int kTaskRunner = 0; | 54 constexpr int kTaskRunner = 0; |
49 | 55 |
50 // Obtains base::Lock instance pointer to ensure tasks run safely on TaskRunner. | 56 // Obtains base::Lock instance pointer to ensure tasks run safely on TaskRunner. |
(...skipping 12 matching lines...) Expand all Loading... | |
63 base::AutoLock lock(*GetInstanceIdLock()); | 69 base::AutoLock lock(*GetInstanceIdLock()); |
64 if (instance_id != g_active_instance_id) | 70 if (instance_id != g_active_instance_id) |
65 return; | 71 return; |
66 } | 72 } |
67 task.Run(); | 73 task.Run(); |
68 } | 74 } |
69 | 75 |
70 // TODO(toyoshim): Factor out TaskRunner related functionaliries above, and | 76 // TODO(toyoshim): Factor out TaskRunner related functionaliries above, and |
71 // deprecate MidiScheduler. It should be available via MidiManager::scheduler(). | 77 // deprecate MidiScheduler. It should be available via MidiManager::scheduler(). |
72 | 78 |
79 // Utility class to handle MIDIHDR struct safely. | |
80 class MIDIHDRDeleter { | |
81 public: | |
82 void operator()(LPMIDIHDR header) { | |
83 if (!header) | |
84 return; | |
85 delete[] static_cast<char*>(header->lpData); | |
86 delete header; | |
87 } | |
88 }; | |
89 | |
90 using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>; | |
91 | |
92 ScopedMIDIHDR CreateMIDIHDR(size_t size) { | |
93 ScopedMIDIHDR hdr(new MIDIHDR); | |
94 ZeroMemory(hdr.get(), sizeof(*hdr)); | |
95 hdr->lpData = new char[size]; | |
96 hdr->dwBufferLength = static_cast<DWORD>(size); | |
97 return hdr; | |
98 } | |
99 | |
100 ScopedMIDIHDR CreateMIDIHDR(const std::vector<uint8_t>& data) { | |
101 ScopedMIDIHDR hdr(CreateMIDIHDR(data.size())); | |
102 std::copy(data.begin(), data.end(), hdr->lpData); | |
103 return hdr; | |
104 } | |
105 | |
73 // Helper functions to close MIDI device handles on TaskRunner asynchronously. | 106 // Helper functions to close MIDI device handles on TaskRunner asynchronously. |
74 void FinalizeInPort(HMIDIIN handle) { | 107 void FinalizeInPort(HMIDIIN handle, LPMIDIHDR lphdr) { |
108 // Resets the device. This stops receiving messages, and allows to release | |
109 // registered buffer headers. Otherwise, midiInUnprepareHeader() and | |
110 // midiInClose() will fail with MIDIERR_STILLPLAYING. | |
111 midiInReset(handle); | |
112 | |
113 // Obtains MIDIHDR ownership to release allocated buffers. | |
114 ScopedMIDIHDR hdr(lphdr); | |
115 if (hdr) | |
116 midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr)); | |
75 midiInClose(handle); | 117 midiInClose(handle); |
76 } | 118 } |
77 | 119 |
78 void FinalizeOutPort(HMIDIOUT handle) { | 120 void FinalizeOutPort(HMIDIOUT handle) { |
79 midiOutClose(handle); | 121 midiOutClose(handle); |
80 } | 122 } |
81 | 123 |
82 // Handles MIDI input port callbacks that runs on a system provided thread. | 124 // Handles MIDI input port callbacks that runs on a system provided thread. |
83 void CALLBACK HandleMidiInCallback(HMIDIIN hmi, | 125 void CALLBACK HandleMidiInCallback(HMIDIIN hmi, |
84 UINT msg, | 126 UINT msg, |
85 DWORD_PTR instance, | 127 DWORD_PTR instance, |
86 DWORD_PTR param1, | 128 DWORD_PTR param1, |
87 DWORD_PTR param2) { | 129 DWORD_PTR param2) { |
88 // TODO(toyoshim): Following patches will implement actual functions. | 130 if (msg != MIM_DATA && msg != MIM_LONGDATA) |
131 return; | |
132 int instance_id = static_cast<int>(instance); | |
133 DynamicallyInitializedMidiManagerWin* manager = nullptr; | |
134 | |
135 // Use |g_task_lock| so to ensure the instance can keep alive while running, | |
136 // and to access member variables that are used on TaskRunner. | |
137 base::AutoLock task_lock(*GetTaskLock()); | |
138 { | |
139 base::AutoLock lock(*GetInstanceIdLock()); | |
140 if (instance_id != g_active_instance_id) | |
141 return; | |
142 manager = g_manager_instance; | |
143 } | |
144 | |
145 auto found = g_hmidiin_to_index_map.find(hmi); | |
146 if (found == g_hmidiin_to_index_map.end()) | |
147 return; | |
148 size_t index = found->second; | |
149 | |
150 if (msg == MIM_DATA) { | |
151 const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff); | |
152 const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff); | |
153 const uint8_t second_data_byte = | |
154 static_cast<uint8_t>((param1 >> 16) & 0xff); | |
155 const size_t len = GetMessageLength(status_byte); | |
156 const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte}; | |
157 std::vector<uint8_t> data; | |
158 data.assign(kData, kData + len); | |
159 manager->PostReplyTask( | |
160 base::Bind(&DynamicallyInitializedMidiManagerWin::ReceiveMidiData, | |
161 base::Unretained(manager), index, data, | |
162 manager->CalculateInEventTime(index, param2))); | |
163 } else { // msg == MIM_LONGDATA | |
164 LPMIDIHDR hdr = reinterpret_cast<LPMIDIHDR>(param1); | |
165 if (hdr->dwBytesRecorded > 0) { | |
166 const uint8_t* src = reinterpret_cast<const uint8_t*>(hdr->lpData); | |
167 std::vector<uint8_t> data; | |
168 data.assign(src, src + hdr->dwBytesRecorded); | |
169 manager->PostReplyTask( | |
170 base::Bind(&DynamicallyInitializedMidiManagerWin::ReceiveMidiData, | |
171 base::Unretained(manager), index, data, | |
172 manager->CalculateInEventTime(index, param2))); | |
173 } | |
174 // TODO(toyoshim): Call midiInAddBuffer outside the callback. | |
175 midiInAddBuffer(hmi, hdr, sizeof(*hdr)); | |
176 } | |
89 } | 177 } |
90 | 178 |
91 // Handles MIDI output port callbacks that runs on a system provided thread. | 179 // Handles MIDI output port callbacks that runs on a system provided thread. |
92 void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, | 180 void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, |
93 UINT msg, | 181 UINT msg, |
94 DWORD_PTR instance, | 182 DWORD_PTR instance, |
95 DWORD_PTR param1, | 183 DWORD_PTR param1, |
96 DWORD_PTR param2) { | 184 DWORD_PTR param2) { |
97 // TODO(toyoshim): Following patches will implement actual functions. | 185 // TODO(toyoshim): Following patches will implement actual functions. |
98 } | 186 } |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
183 caps.wMid, | 271 caps.wMid, |
184 caps.wPid, | 272 caps.wPid, |
185 caps.vDriverVersion, | 273 caps.vDriverVersion, |
186 base::WideToUTF8( | 274 base::WideToUTF8( |
187 base::string16(caps.szPname, wcslen(caps.szPname)))), | 275 base::string16(caps.szPname, wcslen(caps.szPname)))), |
188 in_handle_(kInvalidInHandle), | 276 in_handle_(kInvalidInHandle), |
189 instance_id_(instance_id) {} | 277 instance_id_(instance_id) {} |
190 | 278 |
191 void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) { | 279 void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) { |
192 if (in_handle_ != kInvalidInHandle) { | 280 if (in_handle_ != kInvalidInHandle) { |
193 runner->PostTask(FROM_HERE, base::Bind(&FinalizeInPort, in_handle_)); | 281 runner->PostTask(FROM_HERE, |
282 base::Bind(&FinalizeInPort, in_handle_, hdr_.release())); | |
194 } | 283 } |
195 } | 284 } |
196 | 285 |
286 base::TimeTicks CalculateInEventTime(uint32_t elapsed_ms) const { | |
287 return start_time_ + base::TimeDelta::FromMilliseconds(elapsed_ms); | |
288 } | |
289 | |
197 // Port overrides: | 290 // Port overrides: |
198 void Open() override { | 291 void Open() override { |
199 MMRESULT result = | 292 MMRESULT result = |
200 midiInOpen(&in_handle_, device_id_, | 293 midiInOpen(&in_handle_, device_id_, |
201 reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback), | 294 reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback), |
202 instance_id_, CALLBACK_FUNCTION); | 295 instance_id_, CALLBACK_FUNCTION); |
296 if (result == MMSYSERR_NOERROR) { | |
297 hdr_ = CreateMIDIHDR(kBufferLength); | |
298 result = midiInPrepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_)); | |
299 } | |
300 if (result != MMSYSERR_NOERROR) | |
301 in_handle_ = kInvalidInHandle; | |
203 if (result == MMSYSERR_NOERROR) | 302 if (result == MMSYSERR_NOERROR) |
303 result = midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_)); | |
304 if (result == MMSYSERR_NOERROR) | |
305 result = midiInStart(in_handle_); | |
306 if (result == MMSYSERR_NOERROR) { | |
307 start_time_ = base::TimeTicks::Now(); | |
308 g_hmidiin_to_index_map[in_handle_] = index_; | |
204 Port::Open(); | 309 Port::Open(); |
205 else | 310 } else { |
311 if (in_handle_ != kInvalidInHandle) { | |
312 midiInUnprepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_)); | |
313 midiInClose(in_handle_); | |
314 in_handle_ = kInvalidInHandle; | |
Takashi Toyoshima
2017/02/24 06:34:28
Actually this was a real reason why Open() wasn't
| |
315 } | |
206 Disconnect(); | 316 Disconnect(); |
317 } | |
207 } | 318 } |
208 | 319 |
209 private: | 320 private: |
210 HMIDIIN in_handle_; | 321 HMIDIIN in_handle_; |
322 ScopedMIDIHDR hdr_; | |
323 base::TimeTicks start_time_; | |
211 int instance_id_; | 324 int instance_id_; |
212 }; | 325 }; |
213 | 326 |
214 // TODO(toyoshim): Following patches will implement actual functions. | 327 // TODO(toyoshim): Following patches will implement actual functions. |
215 class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { | 328 class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
216 public: | 329 public: |
217 OutPort(UINT device_id, const MIDIOUTCAPS2W& caps) | 330 OutPort(UINT device_id, const MIDIOUTCAPS2W& caps) |
218 : Port("output", | 331 : Port("output", |
219 device_id, | 332 device_id, |
220 caps.wMid, | 333 caps.wMid, |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
269 // Obtains the task runner for the current thread that hosts this instnace. | 382 // Obtains the task runner for the current thread that hosts this instnace. |
270 thread_runner_ = base::ThreadTaskRunnerHandle::Get(); | 383 thread_runner_ = base::ThreadTaskRunnerHandle::Get(); |
271 } | 384 } |
272 | 385 |
273 DynamicallyInitializedMidiManagerWin::~DynamicallyInitializedMidiManagerWin() { | 386 DynamicallyInitializedMidiManagerWin::~DynamicallyInitializedMidiManagerWin() { |
274 base::AutoLock lock(*GetInstanceIdLock()); | 387 base::AutoLock lock(*GetInstanceIdLock()); |
275 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); | 388 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); |
276 CHECK(thread_runner_->BelongsToCurrentThread()); | 389 CHECK(thread_runner_->BelongsToCurrentThread()); |
277 } | 390 } |
278 | 391 |
392 base::TimeTicks DynamicallyInitializedMidiManagerWin::CalculateInEventTime( | |
393 size_t index, | |
394 uint32_t elapsed_ms) const { | |
395 GetTaskLock()->AssertAcquired(); | |
396 CHECK_GT(input_ports_.size(), index); | |
397 return input_ports_[index]->CalculateInEventTime(elapsed_ms); | |
398 } | |
399 | |
400 void DynamicallyInitializedMidiManagerWin::ReceiveMidiData( | |
401 uint32_t index, | |
402 const std::vector<uint8_t>& data, | |
403 base::TimeTicks time) { | |
404 MidiManager::ReceiveMidiData(index, data.data(), data.size(), time); | |
405 } | |
406 | |
279 void DynamicallyInitializedMidiManagerWin::PostReplyTask( | 407 void DynamicallyInitializedMidiManagerWin::PostReplyTask( |
280 const base::Closure& task) { | 408 const base::Closure& task) { |
281 thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); | 409 thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); |
282 } | 410 } |
283 | 411 |
284 void DynamicallyInitializedMidiManagerWin::StartInitialization() { | 412 void DynamicallyInitializedMidiManagerWin::StartInitialization() { |
285 { | 413 { |
286 base::AutoLock lock(*GetInstanceIdLock()); | 414 base::AutoLock lock(*GetInstanceIdLock()); |
287 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); | 415 CHECK_EQ(kInvalidInstanceId, g_active_instance_id); |
288 instance_id_ = g_next_instance_id++; | 416 instance_id_ = g_next_instance_id++; |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
502 std::make_move_iterator(new_output_ports.begin()), | 630 std::make_move_iterator(new_output_ports.begin()), |
503 std::make_move_iterator(new_output_ports.end())); | 631 std::make_move_iterator(new_output_ports.end())); |
504 | 632 |
505 // TODO(toyoshim): This method may run before internal MIDI device lists that | 633 // TODO(toyoshim): This method may run before internal MIDI device lists that |
506 // Windows manages were updated. This may be because MIDI driver may be loaded | 634 // Windows manages were updated. This may be because MIDI driver may be loaded |
507 // after the raw device list was updated. To avoid this problem, we may want | 635 // after the raw device list was updated. To avoid this problem, we may want |
508 // to retry device check later if no changes are detected here. | 636 // to retry device check later if no changes are detected here. |
509 } | 637 } |
510 | 638 |
511 } // namespace midi | 639 } // namespace midi |
OLD | NEW |