OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/midi/midi_manager_win.h" | |
6 | |
7 #include <Windows.h> | |
scherkus (not reviewing)
2013/11/27 22:07:30
Windows -> windows
yukawa
2013/12/04 17:51:22
Done.
| |
8 | |
9 // Prevent unnecessary functions from being included from <mmsystem.h> | |
10 #define MMNODRV | |
11 #define MMNOSOUND | |
12 #define MMNOWAVE | |
13 #define MMNOAUX | |
14 #define MMNOMIXER | |
15 #define MMNOTIMER | |
16 #define MMNOJOY | |
17 #define MMNOMCI | |
18 #define MMNOMMIO | |
19 #include <mmsystem.h> | |
20 | |
21 #include "base/bind.h" | |
22 #include "base/message_loop/message_loop.h" | |
23 #include "base/strings/string_number_conversions.h" | |
24 #include "base/strings/utf_string_conversions.h" | |
25 #include "base/threading/thread.h" | |
26 #include "media/midi/midi_message_queue.h" | |
27 #include "media/midi/midi_message_util.h" | |
28 #include "media/midi/midi_port_info.h" | |
29 | |
30 namespace media { | |
31 namespace { | |
32 | |
33 std::string GetInErrorMessage(MMRESULT result) { | |
34 wchar_t text[MAXERRORLENGTH]; | |
35 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text)); | |
36 if (get_result != MMSYSERR_NOERROR) { | |
37 DLOG(ERROR) << "Failed to get error message." | |
38 << " original error: " << result | |
39 << " midiInGetErrorText error: " << get_result; | |
40 return std::string(); | |
41 } | |
42 return WideToUTF8(text); | |
43 } | |
44 | |
45 std::string GetOutErrorMessage(MMRESULT result) { | |
46 wchar_t text[MAXERRORLENGTH]; | |
47 MMRESULT get_result = midiOutGetErrorText(result, text, arraysize(text)); | |
48 if (get_result != MMSYSERR_NOERROR) { | |
49 DLOG(ERROR) << "Failed to get error message." | |
50 << " original error: " << result | |
51 << " midiOutGetErrorText error: " << get_result; | |
52 return std::string(); | |
53 } | |
54 return WideToUTF8(text); | |
55 } | |
56 | |
57 class MIDIHDRDeleter { | |
58 public: | |
59 void operator()(MIDIHDR* header) { | |
60 if (!header) | |
61 return; | |
62 delete[] static_cast<char*>(header->lpData); | |
63 header->lpData = NULL; | |
64 header->dwBufferLength = 0; | |
65 delete header; | |
66 } | |
67 }; | |
68 | |
69 typedef scoped_ptr<MIDIHDR, MIDIHDRDeleter> ScopedMIDIHDR; | |
70 | |
71 ScopedMIDIHDR CreateMIDIHDR(size_t size) { | |
72 ScopedMIDIHDR header(new MIDIHDR); | |
73 ZeroMemory(header.get(), sizeof(*header)); | |
74 header->lpData = new char[size]; | |
75 header->dwBufferLength = size; | |
76 return header.Pass(); | |
77 } | |
78 | |
79 void SendShortMIDIMessageInternal(HMIDIOUT midi_out_handle, | |
80 const std::vector<uint8>& message) { | |
81 if (message.size() >= 4) | |
82 return; | |
83 | |
84 DWORD packed_message = 0; | |
85 for (size_t i = 0; i < message.size(); ++i) | |
86 packed_message |= (static_cast<uint32>(message[i]) << (i * 8)); | |
87 MMRESULT result = midiOutShortMsg(midi_out_handle, packed_message); | |
88 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
89 << "Failed to output short message: " << GetOutErrorMessage(result); | |
90 } | |
91 | |
92 void SendLongMIDIMessageInternal(HMIDIOUT midi_out_handle, | |
93 const std::vector<uint8>& message) { | |
94 // Implementation note: | |
95 // Sending long MIDI message can be performed synchronously or asynchronously | |
96 // depending on the driver. There are 2 options to support both cases: | |
97 // 1) Call midiOutLongMsg API and wait for its completion within this | |
scherkus (not reviewing)
2013/11/27 22:07:30
nit: can you annotate function calls in comments w
yukawa
2013/12/04 17:51:22
Done.
| |
98 // function. In this approach, we can avoid memory copy by directly pointing | |
99 // |message| as the data buffer to be sent. | |
100 // 2) Allocate a buffer and copy |message| to it, then call midiOutLongMsg | |
scherkus (not reviewing)
2013/11/27 22:07:30
ditto here + on line 102
yukawa
2013/12/04 17:51:22
Done.
| |
101 // API. The buffer will be freed in the MOM_DONE event hander, which tells | |
102 // us that the task of midiOutLongMsg API is completed. | |
103 // Here we use option 2) in favor of asynchronous design. | |
104 // | |
105 // Open question: | |
106 // In the era of USB-MIDI, OS built-in driver seems to always perform | |
107 // synchronously. Thus option 1) still might be a good option. | |
scherkus (not reviewing)
2013/11/27 22:07:30
how long are these synchronous operations?
as you
yukawa
2013/12/04 17:51:22
On my Windows environment with a certain USB-MIDI
| |
108 | |
109 ScopedMIDIHDR midi_header(CreateMIDIHDR(message.size())); | |
110 for (size_t i = 0; i < message.size(); ++i) | |
111 midi_header->lpData[i] = static_cast<char>(message[i]); | |
112 | |
113 MMRESULT result = midiOutPrepareHeader( | |
114 midi_out_handle, midi_header.get(), sizeof(*midi_header)); | |
115 if (result != MMSYSERR_NOERROR) { | |
116 DLOG(ERROR) << "Failed to prepare output buffer: " | |
117 << GetOutErrorMessage(result); | |
118 return; | |
119 } | |
120 | |
121 result = midiOutLongMsg( | |
122 midi_out_handle, midi_header.get(), sizeof(*midi_header)); | |
123 if (result != MMSYSERR_NOERROR) { | |
124 DLOG(ERROR) << "Failed to output long message: " | |
125 << GetOutErrorMessage(result); | |
126 result = midiOutUnprepareHeader( | |
127 midi_out_handle, midi_header.get(), sizeof(*midi_header)); | |
128 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
129 << "Failed to uninitialize output buffer: " | |
130 << GetOutErrorMessage(result); | |
131 return; | |
132 } | |
133 | |
134 // The ownership of |midi_header| is moved to MOM_DONE event handler. | |
135 midi_header.release(); | |
136 } | |
137 | |
138 void SendMIDIDataInternal(HMIDIOUT midi_out_handle, | |
139 const std::vector<uint8>& data) { | |
140 // MIDI Running status must not be enabled for |data|. | |
141 MIDIMessageQueue message_queue(false); | |
142 message_queue.Add(data); | |
143 std::vector<uint8> message; | |
144 while (true) { | |
145 message_queue.Get(&message); | |
146 if (message.empty()) | |
147 break; | |
148 // SendShortMIDIMessageInternal can send a MIDI message up to 3 bytes. | |
149 if (message.size() <= 3) | |
150 SendShortMIDIMessageInternal(midi_out_handle, message); | |
151 else | |
152 SendLongMIDIMessageInternal(midi_out_handle, message); | |
153 } | |
154 } | |
155 | |
156 } // namespace | |
157 | |
158 class MIDIManagerWin::InPortInfo { | |
159 public: | |
160 ~InPortInfo() { | |
161 Uninitialize(); | |
162 } | |
163 void set_port_index(int index) { | |
164 port_index_ = index; | |
165 } | |
166 int port_index() const { | |
167 return port_index_; | |
168 } | |
169 bool device_to_be_closed() const { | |
170 return device_to_be_closed_; | |
171 } | |
172 HMIDIIN midi_handle() const { | |
173 return midi_handle_; | |
174 } | |
175 const base::TimeDelta& start_time_offset() const { | |
176 return start_time_offset_; | |
177 } | |
178 | |
179 static scoped_ptr<InPortInfo> Create(MIDIManagerWin* manager, | |
180 UINT device_id) { | |
181 scoped_ptr<InPortInfo> obj(new InPortInfo(manager)); | |
182 if (!obj->Initialize(device_id)) | |
183 obj.reset(NULL); | |
scherkus (not reviewing)
2013/11/27 22:07:30
nit: no need for null
yukawa
2013/12/04 17:51:22
Done.
| |
184 return obj.Pass(); | |
185 } | |
186 | |
187 private: | |
188 static const int kInvalidPortIndex = -1; | |
189 static const size_t kBufferLength = 32 * 1024; | |
190 | |
191 explicit InPortInfo(MIDIManagerWin* manager) | |
192 : manager_(manager), | |
193 port_index_(kInvalidPortIndex), | |
194 midi_handle_(NULL), | |
195 started_(false), | |
196 device_to_be_closed_(false) { | |
197 } | |
198 | |
199 bool Initialize(DWORD device_id) { | |
200 Uninitialize(); | |
201 midi_header_ = CreateMIDIHDR(kBufferLength); | |
202 | |
203 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA, and | |
204 // MIM_CLOSE events. | |
205 // - MIM_DATA: This is the only way to get a short MIDI message with | |
206 // timestamp information. | |
207 // - MIM_LONGDATA: This is the only way to get a long MIDI message with | |
208 // timestamp information. | |
209 // - MIN_CLOSE: This event is notified when the device is removed logically | |
210 // or physically. | |
211 MMRESULT result = midiInOpen(&midi_handle_, | |
212 device_id, | |
213 reinterpret_cast<DWORD_PTR>(&HandleMessage), | |
214 reinterpret_cast<DWORD_PTR>(this), | |
215 CALLBACK_FUNCTION); | |
216 if (result != MMSYSERR_NOERROR) { | |
217 DLOG(ERROR) << "Failed to open output device. " | |
218 << " id: " << device_id | |
219 << " message: " << GetInErrorMessage(result); | |
220 return false; | |
221 } | |
222 result = midiInPrepareHeader( | |
223 midi_handle_, midi_header_.get(), sizeof(*midi_header_)); | |
224 if (result != MMSYSERR_NOERROR) { | |
225 DLOG(ERROR) << "Failed to initialize input buffer: " | |
226 << GetInErrorMessage(result); | |
227 return false; | |
228 } | |
229 result = midiInAddBuffer( | |
230 midi_handle_, midi_header_.get(), sizeof(*midi_header_)); | |
231 if (result != MMSYSERR_NOERROR) { | |
232 DLOG(ERROR) << "Failed to attach input buffer: " | |
233 << GetInErrorMessage(result); | |
234 return false; | |
235 } | |
236 result = midiInStart(midi_handle_); | |
237 if (result != MMSYSERR_NOERROR) { | |
238 DLOG(ERROR) << "Failed to start input port: " | |
239 << GetInErrorMessage(result); | |
240 return false; | |
241 } | |
242 started_ = true; | |
243 start_time_offset_ = base::TimeTicks::Now() - base::TimeTicks(); | |
244 return true; | |
245 } | |
246 | |
247 void Uninitialize() { | |
248 MMRESULT result = MMSYSERR_NOERROR; | |
249 if (midi_handle_ && started_) { | |
250 result = midiInStop(midi_handle_); | |
251 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
252 << "Failed to stop input port: " << GetInErrorMessage(result); | |
253 started_ = false; | |
254 start_time_offset_ = base::TimeDelta(); | |
255 } | |
256 if (midi_handle_) { | |
257 // midiInReset flushes pending messages. We ignore these messages. | |
258 device_to_be_closed_ = true; | |
259 result = midiInReset(midi_handle_); | |
260 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
261 << "Failed to reset input port: " << GetInErrorMessage(result); | |
262 result = midiInClose(midi_handle_); | |
263 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
264 << "Failed to close input port: " << GetInErrorMessage(result); | |
265 } | |
266 device_to_be_closed_ = false; | |
267 } | |
268 | |
269 static void CALLBACK HandleMessage(HMIDIIN midi_in_handle, | |
270 UINT message, | |
271 DWORD_PTR instance, | |
272 DWORD_PTR param1, | |
273 DWORD_PTR param2) { | |
274 // This method is called back by Windows multimedia subsystem and not always | |
275 // in the UI thread. | |
276 InPortInfo* self = reinterpret_cast<InPortInfo*>(instance); | |
277 if (!self) | |
278 return; | |
279 if (self->midi_handle() != midi_in_handle) | |
280 return; | |
281 self->HandleMessageInternal(message, param1, param2); | |
282 } | |
283 | |
284 void HandleMessageInternal(UINT message, DWORD_PTR param1, DWORD_PTR param2) { | |
285 // This method is called from HandleMessage method and not always in the UI | |
286 // thread. | |
287 switch (message) { | |
288 case MIM_DATA: | |
289 HandleShortMessageInternal(static_cast<uint8>(param1 & 0xff), | |
290 static_cast<uint8>((param1 >> 8) & 0xff), | |
291 static_cast<uint8>((param1 >> 16) & 0xff), | |
292 TickToTimeDelta(param2)); | |
293 return; | |
294 case MIM_LONGDATA: | |
295 HandleLongMessageInternal(reinterpret_cast<MIDIHDR*>(param1), | |
296 TickToTimeDelta(param2)); | |
297 return; | |
298 case MIM_CLOSE: | |
299 HandleCloseMessageInternal(); | |
300 } | |
301 } | |
302 | |
303 void HandleShortMessageInternal(uint8 status_byte, | |
304 uint8 first_data_byte, | |
305 uint8 second_data_byte, | |
306 base::TimeDelta timestamp) { | |
307 if (device_to_be_closed()) | |
308 return; | |
309 const size_t len = GetMIDIMessageLength(status_byte); | |
310 if (len == 0 || port_index() == kInvalidPortIndex) | |
311 return; | |
312 const uint8 kData[] = { status_byte, first_data_byte, second_data_byte }; | |
313 DCHECK_LE(len, arraysize(kData)); | |
314 manager_->ReceiveMIDIData(port_index(), kData, len, timestamp.InSecondsF()); | |
315 } | |
316 | |
317 void HandleLongMessageInternal(MIDIHDR* header, base::TimeDelta timestamp) { | |
318 if (header != midi_header_.get()) | |
319 return; | |
320 MMRESULT result = MMSYSERR_NOERROR; | |
321 if (device_to_be_closed()) { | |
322 if (midi_header_ && | |
323 (midi_header_->dwFlags & MHDR_PREPARED) == MHDR_PREPARED) { | |
324 result = midiInUnprepareHeader( | |
325 midi_handle_, midi_header_.get(), sizeof(*midi_header_)); | |
326 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
327 << "Failed to uninitialize input buffer: " | |
328 << GetInErrorMessage(result); | |
329 } | |
330 return; | |
331 } | |
332 if (header->dwBytesRecorded > 0 && port_index() != kInvalidPortIndex) { | |
333 manager_->ReceiveMIDIData(port_index_, | |
334 reinterpret_cast<const uint8*>(header->lpData), | |
335 header->dwBytesRecorded, | |
336 timestamp.InSecondsF()); | |
337 } | |
338 result = midiInAddBuffer(midi_handle(), header, sizeof(*header)); | |
339 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
340 << "Failed to attach input port: " << GetInErrorMessage(result); | |
341 } | |
342 | |
343 void HandleCloseMessageInternal() { | |
344 if (midi_handle_ && midi_header_ && | |
345 (midi_header_->dwFlags & MHDR_PREPARED) == MHDR_PREPARED) { | |
346 MMRESULT result = midiInUnprepareHeader( | |
347 midi_handle_, midi_header_.get(), sizeof(*midi_header_)); | |
348 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
349 << "Failed to uninitialize input buffer: " | |
350 << GetInErrorMessage(result); | |
351 } | |
352 midi_header_.reset(); | |
353 midi_handle_ = NULL; | |
354 port_index_ = kInvalidPortIndex; | |
355 return; | |
356 } | |
357 | |
358 base::TimeDelta TickToTimeDelta(DWORD tick) const { | |
359 const base::TimeDelta delta = | |
360 base::TimeDelta::FromMicroseconds(static_cast<uint32>(tick)); | |
361 return start_time_offset_ + delta; | |
362 } | |
363 | |
364 MIDIManagerWin* manager_; | |
365 int port_index_; | |
366 HMIDIIN midi_handle_; | |
367 ScopedMIDIHDR midi_header_; | |
368 base::TimeDelta start_time_offset_; | |
369 bool started_; | |
370 bool device_to_be_closed_; | |
371 DISALLOW_COPY_AND_ASSIGN(MIDIManagerWin::InPortInfo); | |
372 }; | |
373 | |
374 class MIDIManagerWin::OutPortInfo { | |
375 public: | |
376 ~OutPortInfo() { | |
377 Uninitialize(); | |
378 } | |
379 | |
380 HMIDIOUT midi_handle() const { | |
381 return midi_handle_; | |
382 } | |
383 | |
384 static scoped_ptr<OutPortInfo> Create(UINT device_id) { | |
385 scoped_ptr<OutPortInfo> obj(new OutPortInfo); | |
386 if (!obj->Initialize(device_id)) | |
387 obj.reset(NULL); | |
scherkus (not reviewing)
2013/11/27 22:07:30
nit: no need for NULL
yukawa
2013/12/04 17:51:22
Done.
| |
388 return obj.Pass(); | |
389 } | |
390 | |
391 private: | |
392 OutPortInfo() | |
393 : midi_handle_(NULL) {} | |
394 | |
395 bool Initialize(DWORD device_id) { | |
396 Uninitialize(); | |
397 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE and MOM_CLOSE | |
398 // events. | |
399 // - MOM_DONE: SendLongMIDIMessageInternal relies on this event to clean up | |
scherkus (not reviewing)
2013/11/27 22:07:30
ditto for function names in comments
yukawa
2013/12/04 17:51:22
Done.
| |
400 // the backing store where a long MIDI message is stored. | |
401 // - MOM_CLOSE: This event is notified when the device is removed logically | |
402 // or physically. | |
403 MMRESULT result = midiOutOpen(&midi_handle_, | |
404 device_id, | |
405 reinterpret_cast<DWORD_PTR>(&HandleMessage), | |
406 reinterpret_cast<DWORD_PTR>(this), | |
407 CALLBACK_FUNCTION); | |
408 if (result != MMSYSERR_NOERROR) { | |
409 DLOG(ERROR) << "Failed to open output device. " | |
410 << " id: " << device_id | |
411 << " message: "<< GetOutErrorMessage(result); | |
412 midi_handle_ = NULL; | |
413 return false; | |
414 } | |
415 return true; | |
416 } | |
417 | |
418 void Uninitialize() { | |
419 if (midi_handle_ == NULL) | |
scherkus (not reviewing)
2013/11/27 22:07:30
nit: use !midi_handle_
yukawa
2013/12/04 17:51:22
Done.
| |
420 return; | |
421 MMRESULT result = midiOutReset(midi_handle_); | |
422 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
423 << "Failed to reset output port: " << GetOutErrorMessage(result); | |
424 result = midiOutClose(midi_handle_); | |
425 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
426 << "Failed to close output port: " << GetOutErrorMessage(result); | |
427 midi_handle_ = NULL; | |
428 } | |
429 | |
430 static void CALLBACK HandleMessage(HMIDIIN midi_in_handle, | |
431 UINT message, | |
432 DWORD_PTR instance, | |
433 DWORD_PTR param1, | |
434 DWORD_PTR param2) { | |
435 // This method is called back by Windows multimedia subsystem and not always | |
436 // in the UI thread. | |
scherkus (not reviewing)
2013/11/27 22:07:30
s/in/on/
also what do you mean by "not always"?
yukawa
2013/12/04 17:51:22
Done and added comment.
| |
437 | |
438 OutPortInfo* self = reinterpret_cast<OutPortInfo*>(instance); | |
439 if (!self) | |
440 return; | |
441 switch (message) { | |
442 case MOM_DONE: { | |
443 // Take the ownership of the MIDIHDR object. | |
444 ScopedMIDIHDR header(reinterpret_cast<MIDIHDR*>(param1)); | |
445 if (!header) | |
446 return; | |
447 MMRESULT result = midiOutUnprepareHeader( | |
448 self->midi_handle(), header.get(), sizeof(*header)); | |
449 DLOG_IF(ERROR, result != MMSYSERR_NOERROR) | |
450 << "Failed to uninitialize output buffer: " | |
451 << GetOutErrorMessage(result); | |
452 return; | |
453 } | |
454 case MOM_CLOSE: | |
455 self->midi_handle_ = NULL; | |
456 return; | |
457 } | |
458 } | |
459 | |
460 HMIDIOUT midi_handle_; | |
461 DISALLOW_COPY_AND_ASSIGN(MIDIManagerWin::OutPortInfo); | |
462 }; | |
463 | |
464 MIDIManagerWin::MIDIManagerWin() { | |
465 } | |
466 | |
467 bool MIDIManagerWin::Initialize() { | |
468 { | |
scherkus (not reviewing)
2013/11/27 22:07:30
do we need the extra scope blocks?
I'm not seeing
yukawa
2013/12/04 17:51:22
OK. Removed these blocks.
| |
469 const UINT num_in_devices = midiInGetNumDevs(); | |
470 in_ports_.reserve(num_in_devices); | |
471 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) { | |
472 MIDIINCAPS caps = {}; | |
473 MMRESULT result = midiInGetDevCaps(device_id, &caps, sizeof(caps)); | |
474 if (result != MMSYSERR_NOERROR) { | |
475 DLOG(ERROR) << "Failed to obtain input device info: " | |
476 << GetInErrorMessage(result); | |
477 continue; | |
478 } | |
479 scoped_ptr<InPortInfo> in_port(InPortInfo::Create(this, device_id)); | |
480 if (!in_port) | |
481 continue; | |
482 MIDIPortInfo info( | |
483 base::IntToString(static_cast<int>(device_id)), | |
484 "", | |
485 base::WideToUTF8(caps.szPname), | |
486 base::IntToString(static_cast<int>(caps.vDriverVersion))); | |
487 AddInputPort(info); | |
488 in_port->set_port_index(input_ports_.size() - 1); | |
489 in_ports_.push_back(in_port.Pass()); | |
490 } | |
491 } | |
492 { | |
493 const UINT num_out_devices = midiOutGetNumDevs(); | |
494 out_ports_.reserve(num_out_devices); | |
495 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) { | |
496 MIDIOUTCAPS caps = {}; | |
497 MMRESULT result = midiOutGetDevCaps(device_id, &caps, sizeof(caps)); | |
498 if (result != MMSYSERR_NOERROR) { | |
499 DLOG(ERROR) << "Failed to obtain output device info: " | |
500 << GetOutErrorMessage(result); | |
501 continue; | |
502 } | |
503 scoped_ptr<OutPortInfo> out_port(OutPortInfo::Create(device_id)); | |
504 if (!out_port) | |
505 continue; | |
506 MIDIPortInfo info( | |
507 base::IntToString(static_cast<int>(device_id)), | |
508 "", | |
509 base::WideToUTF8(caps.szPname), | |
510 base::IntToString(static_cast<int>(caps.vDriverVersion))); | |
511 AddOutputPort(info); | |
512 out_ports_.push_back(out_port.Pass()); | |
513 } | |
514 } | |
515 return true; | |
516 } | |
517 | |
518 MIDIManagerWin::~MIDIManagerWin() { | |
519 // |send_thread_| must be terminated here to ensure that | |
520 // MIDIManagerWin::SendMIDIData will not be called any longer. A drawback | |
scherkus (not reviewing)
2013/11/27 22:07:30
ditto for function names in comments
yukawa
2013/12/04 17:51:22
Done.
| |
521 // of this approach is if |send_thread_| is somehow blocked forever, | |
scherkus (not reviewing)
2013/11/27 22:07:30
do you know how long does it take to run SendMIDID
yukawa
2013/12/04 17:51:22
It is supposed to be fast operation as long as the
| |
522 // the UI thread is also blocked forever. | |
523 send_thread_.reset(NULL); | |
scherkus (not reviewing)
2013/11/27 22:07:30
nit: NULL not needed
yukawa
2013/12/04 17:51:22
Done.
| |
524 } | |
525 | |
526 void MIDIManagerWin::SendMIDIData(MIDIManagerClient* client, | |
527 uint32 port_index, | |
528 const std::vector<uint8>& data, | |
529 double timestamp) { | |
530 DCHECK(CurrentlyOnMIDISendThread()); | |
531 // Caveat: Whether |this| is still valid or not is non-trivial question | |
532 // because this method is called back in a background thread that is owned by | |
533 // MIDIManager rather than MIDIManagerWin. Currently |this| is considered to | |
534 // be always valid because ~MIDIManagerWin() internally waits for the | |
535 // termination of this background thread. | |
scherkus (not reviewing)
2013/11/27 22:07:30
technically this comment would apply to MIDIManage
yukawa
2013/12/04 17:51:22
Mas issue has been resolved. PostDelayedTask() is
| |
536 | |
537 if (out_ports_.size() <= port_index) | |
538 return; | |
539 | |
540 // Check if the target device is still available. Note that |handle| can be | |
541 // NULL once the device is logically or physically removed. | |
542 const HMIDIOUT handle = out_ports_[port_index]->midi_handle(); | |
543 if (!handle) | |
544 return; | |
545 | |
546 base::TimeDelta delay; | |
547 if (timestamp != 0.0) { | |
548 base::TimeTicks time_to_send = | |
549 base::TimeTicks() + base::TimeDelta::FromMicroseconds( | |
550 timestamp * base::Time::kMicrosecondsPerSecond); | |
551 delay = time_to_send - base::TimeTicks::Now(); | |
552 } | |
553 | |
554 if (delay.InMilliseconds() <= 0) { | |
555 SendMIDIDataInternal(handle, data); | |
556 } else { | |
557 base::MessageLoop::current()->PostDelayedTask( | |
558 FROM_HERE, | |
559 base::Bind(&SendMIDIDataInternal, handle, data), | |
scherkus (not reviewing)
2013/11/27 22:07:30
since handle and data get copied, is it possible f
yukawa
2013/12/04 17:51:22
Yeah, your concern was real, although Windows stri
| |
560 delay); | |
561 } | |
562 | |
563 client->AccumulateMIDIBytesSent(data.size()); | |
564 } | |
565 | |
566 MIDIManager* MIDIManager::Create() { | |
567 return new MIDIManagerWin(); | |
568 } | |
569 | |
570 } // namespace media | |
OLD | NEW |