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

Side by Side Diff: media/midi/dynamically_initialized_midi_manager_win.cc

Issue 2701783003: Web MIDI: implement receiving for dynamic manager instantiation on Windows (Closed)
Patch Set: Created 3 years, 10 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 | « media/midi/dynamically_initialized_midi_manager_win.h ('k') | 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 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
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
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
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
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
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
OLDNEW
« no previous file with comments | « media/midi/dynamically_initialized_midi_manager_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698