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

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

Issue 2841493003: Web MIDI: remove old Windows backend (Closed)
Patch Set: rebase Created 3 years, 8 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/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
(Empty)
1 // Copyright 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>
8 #include <ks.h>
9 #include <ksmedia.h>
10 #include <mmreg.h>
11 // Prevent unnecessary functions from being included from <mmsystem.h>
12 #define MMNODRV
13 #define MMNOSOUND
14 #define MMNOWAVE
15 #define MMNOAUX
16 #define MMNOMIXER
17 #define MMNOTIMER
18 #define MMNOJOY
19 #define MMNOMCI
20 #define MMNOMMIO
21 #include <mmsystem.h>
22 #include <stddef.h>
23
24 #include <algorithm>
25 #include <functional>
26 #include <queue>
27 #include <string>
28
29 #include "base/bind.h"
30 #include "base/containers/hash_tables.h"
31 #include "base/feature_list.h"
32 #include "base/macros.h"
33 #include "base/message_loop/message_loop.h"
34 #include "base/single_thread_task_runner.h"
35 #include "base/strings/string16.h"
36 #include "base/strings/string_number_conversions.h"
37 #include "base/strings/string_piece.h"
38 #include "base/strings/stringprintf.h"
39 #include "base/strings/utf_string_conversions.h"
40 #include "base/system_monitor/system_monitor.h"
41 #include "base/threading/thread_checker.h"
42 #include "base/timer/timer.h"
43 #include "base/win/message_window.h"
44 #include "base/win/windows_version.h"
45 #include "device/usb/usb_ids.h"
46 #include "media/midi/dynamically_initialized_midi_manager_win.h"
47 #include "media/midi/message_util.h"
48 #include "media/midi/midi_manager_winrt.h"
49 #include "media/midi/midi_message_queue.h"
50 #include "media/midi/midi_port_info.h"
51 #include "media/midi/midi_switches.h"
52
53 namespace midi {
54 namespace {
55
56 using mojom::PortState;
57 using mojom::Result;
58
59 static const size_t kBufferLength = 32 * 1024;
60
61 // We assume that nullpter represents an invalid MIDI handle.
62 const HMIDIIN kInvalidMidiInHandle = nullptr;
63 const HMIDIOUT kInvalidMidiOutHandle = nullptr;
64
65 std::string GetInErrorMessage(MMRESULT result) {
66 wchar_t text[MAXERRORLENGTH];
67 MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text));
68 if (get_result != MMSYSERR_NOERROR) {
69 DLOG(ERROR) << "Failed to get error message."
70 << " original error: " << result
71 << " midiInGetErrorText error: " << get_result;
72 return std::string();
73 }
74 return base::WideToUTF8(text);
75 }
76
77 std::string GetOutErrorMessage(MMRESULT result) {
78 wchar_t text[MAXERRORLENGTH];
79 MMRESULT get_result = midiOutGetErrorText(result, text, arraysize(text));
80 if (get_result != MMSYSERR_NOERROR) {
81 DLOG(ERROR) << "Failed to get error message."
82 << " original error: " << result
83 << " midiOutGetErrorText error: " << get_result;
84 return std::string();
85 }
86 return base::WideToUTF8(text);
87 }
88
89 std::string MmversionToString(MMVERSION version) {
90 return base::StringPrintf("%d.%d", HIBYTE(version), LOBYTE(version));
91 }
92
93 void CloseOutputPortOnTaskThread(HMIDIOUT midi_out_handle) {
94 midiOutClose(midi_out_handle);
95 }
96
97 class MIDIHDRDeleter {
98 public:
99 void operator()(MIDIHDR* header) {
100 if (!header)
101 return;
102 delete[] static_cast<char*>(header->lpData);
103 header->lpData = NULL;
104 header->dwBufferLength = 0;
105 delete header;
106 }
107 };
108
109 using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>;
110
111 ScopedMIDIHDR CreateMIDIHDR(size_t size) {
112 ScopedMIDIHDR header(new MIDIHDR);
113 ZeroMemory(header.get(), sizeof(*header));
114 header->lpData = new char[size];
115 header->dwBufferLength = static_cast<DWORD>(size);
116 return header;
117 }
118
119 void SendShortMidiMessageInternal(HMIDIOUT midi_out_handle,
120 const std::vector<uint8_t>& message) {
121 DCHECK_LE(message.size(), static_cast<size_t>(3))
122 << "A short MIDI message should be up to 3 bytes.";
123
124 DWORD packed_message = 0;
125 for (size_t i = 0; i < message.size(); ++i)
126 packed_message |= (static_cast<uint32_t>(message[i]) << (i * 8));
127 MMRESULT result = midiOutShortMsg(midi_out_handle, packed_message);
128 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
129 << "Failed to output short message: " << GetOutErrorMessage(result);
130 }
131
132 void SendLongMidiMessageInternal(HMIDIOUT midi_out_handle,
133 const std::vector<uint8_t>& message) {
134 // Implementation note:
135 // Sending a long MIDI message can be performed synchronously or
136 // asynchronously depending on the driver. There are 2 options to support both
137 // cases:
138 // 1) Call midiOutLongMsg() API and wait for its completion within this
139 // function. In this approach, we can avoid memory copy by directly pointing
140 // |message| as the data buffer to be sent.
141 // 2) Allocate a buffer and copy |message| to it, then call midiOutLongMsg()
142 // API. The buffer will be freed in the MOM_DONE event hander, which tells
143 // us that the task of midiOutLongMsg() API is completed.
144 // Here we choose option 2) in favor of asynchronous design.
145
146 // Note for built-in USB-MIDI driver:
147 // From an observation on Windows 7/8.1 with a USB-MIDI keyboard,
148 // midiOutLongMsg() will be always blocked. Sending 64 bytes or less data
149 // takes roughly 300 usecs. Sending 2048 bytes or more data takes roughly
150 // |message.size() / (75 * 1024)| secs in practice. Here we put 256 KB size
151 // limit on SysEx message, with hoping that midiOutLongMsg will be blocked at
152 // most 4 sec or so with a typical USB-MIDI device.
153 // TODO(crbug.com/383578): This restriction should be removed once Web MIDI
154 // defines a standardized way to handle large sysex messages.
155 const size_t kSysExSizeLimit = 256 * 1024;
156 if (message.size() >= kSysExSizeLimit) {
157 DVLOG(1) << "Ingnoreing SysEx message due to the size limit"
158 << ", size = " << message.size();
159 return;
160 }
161
162 ScopedMIDIHDR midi_header(CreateMIDIHDR(message.size()));
163 std::copy(message.begin(), message.end(), midi_header->lpData);
164
165 MMRESULT result = midiOutPrepareHeader(midi_out_handle, midi_header.get(),
166 sizeof(*midi_header));
167 if (result != MMSYSERR_NOERROR) {
168 DLOG(ERROR) << "Failed to prepare output buffer: "
169 << GetOutErrorMessage(result);
170 return;
171 }
172
173 result =
174 midiOutLongMsg(midi_out_handle, midi_header.get(), sizeof(*midi_header));
175 if (result != MMSYSERR_NOERROR) {
176 DLOG(ERROR) << "Failed to output long message: "
177 << GetOutErrorMessage(result);
178 result = midiOutUnprepareHeader(midi_out_handle, midi_header.get(),
179 sizeof(*midi_header));
180 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
181 << "Failed to uninitialize output buffer: "
182 << GetOutErrorMessage(result);
183 return;
184 }
185
186 // The ownership of |midi_header| is moved to MOM_DONE event handler.
187 ignore_result(midi_header.release());
188 }
189
190 template <size_t array_size>
191 base::string16 AsString16(const wchar_t(&buffer)[array_size]) {
192 size_t len = 0;
193 for (len = 0; len < array_size; ++len) {
194 if (buffer[len] == L'\0')
195 break;
196 }
197 return base::string16(buffer, len);
198 }
199
200 struct MidiDeviceInfo final {
201 explicit MidiDeviceInfo(const MIDIINCAPS2W& caps)
202 : manufacturer_id(caps.wMid),
203 product_id(caps.wPid),
204 driver_version(caps.vDriverVersion),
205 product_name(AsString16(caps.szPname)),
206 usb_vendor_id(ExtractUsbVendorIdIfExists(caps)),
207 usb_product_id(ExtractUsbProductIdIfExists(caps)),
208 is_usb_device(IsUsbDevice(caps)),
209 is_software_synth(false) {}
210 explicit MidiDeviceInfo(const MIDIOUTCAPS2W& caps)
211 : manufacturer_id(caps.wMid),
212 product_id(caps.wPid),
213 driver_version(caps.vDriverVersion),
214 product_name(AsString16(caps.szPname)),
215 usb_vendor_id(ExtractUsbVendorIdIfExists(caps)),
216 usb_product_id(ExtractUsbProductIdIfExists(caps)),
217 is_usb_device(IsUsbDevice(caps)),
218 is_software_synth(IsSoftwareSynth(caps)) {}
219 explicit MidiDeviceInfo(const MidiDeviceInfo& info)
220 : manufacturer_id(info.manufacturer_id),
221 product_id(info.product_id),
222 driver_version(info.driver_version),
223 product_name(info.product_name),
224 usb_vendor_id(info.usb_vendor_id),
225 usb_product_id(info.usb_product_id),
226 is_usb_device(info.is_usb_device),
227 is_software_synth(info.is_software_synth) {}
228 // Currently only following entities are considered when testing the equality
229 // of two MIDI devices.
230 // TODO(toyoshim): Consider to calculate MIDIPort.id here and use it as the
231 // key. See crbug.com/467448. Then optimize the data for |MidiPortInfo|.
232 const uint16_t manufacturer_id;
233 const uint16_t product_id;
234 const uint32_t driver_version;
235 const base::string16 product_name;
236 const uint16_t usb_vendor_id;
237 const uint16_t usb_product_id;
238 const bool is_usb_device;
239 const bool is_software_synth;
240
241 // Required to be used as the key of base::hash_map.
242 bool operator==(const MidiDeviceInfo& that) const {
243 return manufacturer_id == that.manufacturer_id &&
244 product_id == that.product_id &&
245 driver_version == that.driver_version &&
246 product_name == that.product_name &&
247 is_usb_device == that.is_usb_device &&
248 (is_usb_device && usb_vendor_id == that.usb_vendor_id &&
249 usb_product_id == that.usb_product_id);
250 }
251
252 // Hash function to be used in base::hash_map.
253 struct Hasher {
254 size_t operator()(const MidiDeviceInfo& info) const {
255 size_t hash = info.manufacturer_id;
256 hash *= 131;
257 hash += info.product_id;
258 hash *= 131;
259 hash += info.driver_version;
260 hash *= 131;
261 hash += info.product_name.size();
262 hash *= 131;
263 if (!info.product_name.empty()) {
264 hash += info.product_name[0];
265 }
266 hash *= 131;
267 hash += info.usb_vendor_id;
268 hash *= 131;
269 hash += info.usb_product_id;
270 return hash;
271 }
272 };
273
274 private:
275 static bool IsUsbDevice(const MIDIINCAPS2W& caps) {
276 return IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid) &&
277 IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid);
278 }
279 static bool IsUsbDevice(const MIDIOUTCAPS2W& caps) {
280 return IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid) &&
281 IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid);
282 }
283 static bool IsSoftwareSynth(const MIDIOUTCAPS2W& caps) {
284 return caps.wTechnology == MOD_SWSYNTH;
285 }
286 static uint16_t ExtractUsbVendorIdIfExists(const MIDIINCAPS2W& caps) {
287 if (!IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid))
288 return 0;
289 return EXTRACT_USBAUDIO_MID(&caps.ManufacturerGuid);
290 }
291 static uint16_t ExtractUsbVendorIdIfExists(const MIDIOUTCAPS2W& caps) {
292 if (!IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid))
293 return 0;
294 return EXTRACT_USBAUDIO_MID(&caps.ManufacturerGuid);
295 }
296 static uint16_t ExtractUsbProductIdIfExists(const MIDIINCAPS2W& caps) {
297 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid))
298 return 0;
299 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid);
300 }
301 static uint16_t ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) {
302 if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid))
303 return 0;
304 return EXTRACT_USBAUDIO_PID(&caps.ProductGuid);
305 }
306 };
307
308 std::string GetManufacturerName(const MidiDeviceInfo& info) {
309 if (info.is_usb_device) {
310 const char* name = device::UsbIds::GetVendorName(info.usb_vendor_id);
311 return std::string(name ? name : "");
312 }
313
314 switch (info.manufacturer_id) {
315 case MM_MICROSOFT:
316 return "Microsoft Corporation";
317 default:
318 // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341.
319 return "";
320 }
321 }
322
323 bool IsUnsupportedDevice(const MidiDeviceInfo& info) {
324 return info.is_software_synth && info.manufacturer_id == MM_MICROSOFT &&
325 (info.product_id == MM_MSFT_WDMAUDIO_MIDIOUT ||
326 info.product_id == MM_MSFT_GENERIC_MIDISYNTH);
327 }
328
329 using PortNumberCache =
330 base::hash_map<MidiDeviceInfo,
331 std::priority_queue<uint32_t,
332 std::vector<uint32_t>,
333 std::greater<uint32_t>>,
334 MidiDeviceInfo::Hasher>;
335
336 struct MidiInputDeviceState final
337 : base::RefCountedThreadSafe<MidiInputDeviceState> {
338 explicit MidiInputDeviceState(const MidiDeviceInfo& device_info)
339 : device_info(device_info),
340 midi_handle(kInvalidMidiInHandle),
341 port_index(0),
342 port_age(0),
343 start_time_initialized(false) {}
344
345 const MidiDeviceInfo device_info;
346 HMIDIIN midi_handle;
347 ScopedMIDIHDR midi_header;
348 // Since Win32 multimedia system uses a relative time offset from when
349 // |midiInStart| API is called, we need to record when it is called.
350 base::TimeTicks start_time;
351 // 0-based port index. We will try to reuse the previous port index when the
352 // MIDI device is closed then reopened.
353 uint32_t port_index;
354 // A sequence number which represents how many times |port_index| is reused.
355 // We can remove this field if we decide not to clear unsent events
356 // when the device is disconnected.
357 // See https://github.com/WebAudio/web-midi-api/issues/133
358 uint64_t port_age;
359 // True if |start_time| is initialized. This field is not used so far, but
360 // kept for the debugging purpose.
361 bool start_time_initialized;
362
363 private:
364 friend class base::RefCountedThreadSafe<MidiInputDeviceState>;
365 ~MidiInputDeviceState() {}
366 };
367
368 struct MidiOutputDeviceState final
369 : base::RefCountedThreadSafe<MidiOutputDeviceState> {
370 explicit MidiOutputDeviceState(const MidiDeviceInfo& device_info)
371 : device_info(device_info),
372 midi_handle(kInvalidMidiOutHandle),
373 port_index(0),
374 port_age(0),
375 closed(false) {}
376
377 const MidiDeviceInfo device_info;
378 HMIDIOUT midi_handle;
379 // 0-based port index. We will try to reuse the previous port index when the
380 // MIDI device is closed then reopened.
381 uint32_t port_index;
382 // A sequence number which represents how many times |port_index| is reused.
383 // We can remove this field if we decide not to clear unsent events
384 // when the device is disconnected.
385 // See https://github.com/WebAudio/web-midi-api/issues/133
386 uint64_t port_age;
387 // True if the device is already closed and |midi_handle| is considered to be
388 // invalid.
389 // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium
390 // project.
391 volatile bool closed;
392
393 private:
394 friend class base::RefCountedThreadSafe<MidiOutputDeviceState>;
395 ~MidiOutputDeviceState() {}
396 };
397
398 // The core logic of MIDI device handling for Windows. Basically this class is
399 // shared among following 4 threads:
400 // 1. Chrome IO Thread
401 // 2. OS Multimedia Thread
402 // 3. Task Thread
403 // 4. Sender Thread
404 //
405 // Chrome IO Thread:
406 // MidiManager runs on Chrome IO thread. Device change notification is
407 // delivered to the thread through the SystemMonitor service.
408 // OnDevicesChanged() callback is invoked to update the MIDI device list.
409 // Note that in the current implementation we will try to open all the
410 // existing devices in practice. This is OK because trying to reopen a MIDI
411 // device that is already opened would simply fail, and there is no unwilling
412 // side effect.
413 //
414 // OS Multimedia Thread:
415 // This thread is maintained by the OS as a part of MIDI runtime, and
416 // responsible for receiving all the system initiated events such as device
417 // close, and receiving data. For performance reasons, most of potentially
418 // blocking operations will be dispatched into Task Thread.
419 //
420 // Task Thread:
421 // This thread will be used to call back following methods of MidiManager.
422 // - MidiManager::CompleteInitialization
423 // - MidiManager::AddInputPort
424 // - MidiManager::AddOutputPort
425 // - MidiManager::SetInputPortState
426 // - MidiManager::SetOutputPortState
427 // - MidiManager::ReceiveMidiData
428 //
429 // Sender Thread:
430 // This thread will be used to call Win32 APIs to send MIDI message at the
431 // specified time. We don't want to call MIDI send APIs on Task Thread
432 // because those APIs could be performed synchronously, hence they could block
433 // the caller thread for a while. See the comment in
434 // SendLongMidiMessageInternal for details. Currently we expect that the
435 // blocking time would be less than 1 second.
436 class MidiServiceWinImpl : public MidiServiceWin,
437 public base::SystemMonitor::DevicesChangedObserver {
438 public:
439 MidiServiceWinImpl()
440 : delegate_(nullptr),
441 sender_thread_("Windows MIDI sender thread"),
442 task_thread_("Windows MIDI task thread"),
443 destructor_started(false) {}
444
445 ~MidiServiceWinImpl() final {
446 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and
447 // RemoveDevicesChangeObserver() should be called on the same thread.
448 DCHECK(thread_checker_.CalledOnValidThread());
449
450 destructor_started = true;
451 base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this);
452 {
453 std::vector<HMIDIIN> input_devices;
454 {
455 base::AutoLock auto_lock(input_ports_lock_);
456 for (auto it : input_device_map_)
457 input_devices.push_back(it.first);
458 }
459 {
460 for (auto* handle : input_devices) {
461 MMRESULT result = midiInClose(handle);
462 if (result == MIDIERR_STILLPLAYING) {
463 result = midiInReset(handle);
464 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
465 << "midiInReset failed: " << GetInErrorMessage(result);
466 result = midiInClose(handle);
467 }
468 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
469 << "midiInClose failed: " << GetInErrorMessage(result);
470 }
471 }
472 }
473 {
474 std::vector<HMIDIOUT> output_devices;
475 {
476 base::AutoLock auto_lock(output_ports_lock_);
477 for (auto it : output_device_map_)
478 output_devices.push_back(it.first);
479 }
480 {
481 for (auto* handle : output_devices) {
482 MMRESULT result = midiOutClose(handle);
483 if (result == MIDIERR_STILLPLAYING) {
484 result = midiOutReset(handle);
485 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
486 << "midiOutReset failed: " << GetOutErrorMessage(result);
487 result = midiOutClose(handle);
488 }
489 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
490 << "midiOutClose failed: " << GetOutErrorMessage(result);
491 }
492 }
493 }
494 sender_thread_.Stop();
495 task_thread_.Stop();
496 }
497
498 // MidiServiceWin overrides:
499 void InitializeAsync(MidiServiceWinDelegate* delegate) final {
500 // Start() and Stop() of the threads, and AddDevicesChangeObserver() and
501 // RemoveDevicesChangeObserver() should be called on the same thread.
502 DCHECK(thread_checker_.CalledOnValidThread());
503
504 delegate_ = delegate;
505
506 sender_thread_.Start();
507 task_thread_.Start();
508
509 // Start monitoring device changes. This should start before the
510 // following UpdateDeviceList() call not to miss the event happening
511 // between the call and the observer registration.
512 base::SystemMonitor::Get()->AddDevicesChangedObserver(this);
513
514 UpdateDeviceList();
515
516 task_thread_.task_runner()->PostTask(
517 FROM_HERE,
518 base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread,
519 base::Unretained(this), Result::OK));
520 }
521
522 void SendMidiDataAsync(uint32_t port_number,
523 const std::vector<uint8_t>& data,
524 base::TimeTicks time) final {
525 if (destructor_started) {
526 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is "
527 "being destructed. port: " << port_number;
528 return;
529 }
530 auto state = GetOutputDeviceFromPort(port_number);
531 if (!state) {
532 LOG(ERROR) << "ThreadSafeSendData failed due to an invalid port number. "
533 << "port: " << port_number;
534 return;
535 }
536 if (state->closed) {
537 LOG(ERROR)
538 << "ThreadSafeSendData failed because target port is already closed."
539 << "port: " << port_number;
540 return;
541 }
542 const auto now = base::TimeTicks::Now();
543 if (now < time) {
544 sender_thread_.task_runner()->PostDelayedTask(
545 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread,
546 base::Unretained(this), port_number,
547 state->port_age, data, time),
548 time - now);
549 } else {
550 sender_thread_.task_runner()->PostTask(
551 FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread,
552 base::Unretained(this), port_number,
553 state->port_age, data, time));
554 }
555 }
556
557 // base::SystemMonitor::DevicesChangedObserver overrides:
558 void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) final {
559 DCHECK(thread_checker_.CalledOnValidThread());
560 if (destructor_started)
561 return;
562
563 switch (device_type) {
564 case base::SystemMonitor::DEVTYPE_AUDIO:
565 case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE:
566 // Add case of other unrelated device types here.
567 return;
568 case base::SystemMonitor::DEVTYPE_UNKNOWN:
569 // Interested in MIDI devices. Try updating the device list.
570 UpdateDeviceList();
571 break;
572 // No default here to capture new DeviceType by compile time.
573 }
574 }
575
576 private:
577 scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle(
578 HMIDIIN midi_handle) {
579 base::AutoLock auto_lock(input_ports_lock_);
580 const auto it = input_device_map_.find(midi_handle);
581 return (it != input_device_map_.end() ? it->second : nullptr);
582 }
583
584 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle(
585 HMIDIOUT midi_handle) {
586 base::AutoLock auto_lock(output_ports_lock_);
587 const auto it = output_device_map_.find(midi_handle);
588 return (it != output_device_map_.end() ? it->second : nullptr);
589 }
590
591 scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort(
592 uint32_t port_number) {
593 base::AutoLock auto_lock(output_ports_lock_);
594 if (output_ports_.size() <= port_number)
595 return nullptr;
596 return output_ports_[port_number];
597 }
598
599 void UpdateDeviceList() {
600 task_thread_.task_runner()->PostTask(
601 FROM_HERE, base::Bind(&MidiServiceWinImpl::UpdateDeviceListOnTaskThread,
602 base::Unretained(this)));
603 }
604
605 /////////////////////////////////////////////////////////////////////////////
606 // Callbacks on the OS multimedia thread.
607 /////////////////////////////////////////////////////////////////////////////
608
609 static void CALLBACK
610 OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle,
611 UINT message,
612 DWORD_PTR instance,
613 DWORD_PTR param1,
614 DWORD_PTR param2) {
615 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
616 if (!self)
617 return;
618 switch (message) {
619 case MIM_OPEN:
620 self->OnMidiInOpen(midi_in_handle);
621 break;
622 case MIM_DATA:
623 self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2);
624 break;
625 case MIM_LONGDATA:
626 self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1,
627 param2);
628 break;
629 case MIM_CLOSE:
630 self->OnMidiInCloseOnMultimediaThread(midi_in_handle);
631 break;
632 }
633 }
634
635 void OnMidiInOpen(HMIDIIN midi_in_handle) {
636 UINT device_id = 0;
637 MMRESULT result = midiInGetID(midi_in_handle, &device_id);
638 if (result != MMSYSERR_NOERROR) {
639 DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result);
640 return;
641 }
642 MIDIINCAPS2W caps = {};
643 result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps),
644 sizeof(caps));
645 if (result != MMSYSERR_NOERROR) {
646 DLOG(ERROR) << "midiInGetDevCaps failed: " << GetInErrorMessage(result);
647 return;
648 }
649 auto state =
650 make_scoped_refptr(new MidiInputDeviceState(MidiDeviceInfo(caps)));
651 state->midi_handle = midi_in_handle;
652 state->midi_header = CreateMIDIHDR(kBufferLength);
653 const auto& state_device_info = state->device_info;
654 bool add_new_port = false;
655 uint32_t port_number = 0;
656 {
657 base::AutoLock auto_lock(input_ports_lock_);
658 const auto it = unused_input_ports_.find(state_device_info);
659 if (it == unused_input_ports_.end()) {
660 port_number = static_cast<uint32_t>(input_ports_.size());
661 add_new_port = true;
662 input_ports_.push_back(nullptr);
663 input_ports_ages_.push_back(0);
664 } else {
665 port_number = it->second.top();
666 it->second.pop();
667 if (it->second.empty()) {
668 unused_input_ports_.erase(it);
669 }
670 }
671 input_ports_[port_number] = state;
672
673 input_ports_ages_[port_number] += 1;
674 input_device_map_[input_ports_[port_number]->midi_handle] =
675 input_ports_[port_number];
676 input_ports_[port_number]->port_index = port_number;
677 input_ports_[port_number]->port_age = input_ports_ages_[port_number];
678 }
679 // Several initial startup tasks cannot be done in MIM_OPEN handler.
680 task_thread_.task_runner()->PostTask(
681 FROM_HERE, base::Bind(&MidiServiceWinImpl::StartInputDeviceOnTaskThread,
682 base::Unretained(this), midi_in_handle));
683 if (add_new_port) {
684 const MidiPortInfo port_info(
685 // TODO(toyoshim): Use a hash ID insted crbug.com/467448
686 base::IntToString(static_cast<int>(port_number)),
687 GetManufacturerName(state_device_info),
688 base::WideToUTF8(state_device_info.product_name),
689 MmversionToString(state_device_info.driver_version),
690 PortState::OPENED);
691 task_thread_.task_runner()->PostTask(
692 FROM_HERE, base::Bind(&MidiServiceWinImpl::AddInputPortOnTaskThread,
693 base::Unretained(this), port_info));
694 } else {
695 task_thread_.task_runner()->PostTask(
696 FROM_HERE,
697 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread,
698 base::Unretained(this), port_number,
699 PortState::CONNECTED));
700 }
701 }
702
703 void OnMidiInDataOnMultimediaThread(HMIDIIN midi_in_handle,
704 DWORD_PTR param1,
705 DWORD_PTR param2) {
706 auto state = GetInputDeviceFromHandle(midi_in_handle);
707 if (!state)
708 return;
709 const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff);
710 const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff);
711 const uint8_t second_data_byte =
712 static_cast<uint8_t>((param1 >> 16) & 0xff);
713 const DWORD elapsed_ms = param2;
714 const size_t len = GetMessageLength(status_byte);
715 const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte};
716 std::vector<uint8_t> data;
717 data.assign(kData, kData + len);
718 DCHECK_LE(len, arraysize(kData));
719 // MIM_DATA/MIM_LONGDATA message treats the time when midiInStart() is
720 // called as the origin of |elapsed_ms|.
721 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757284.aspx
722 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757286.aspx
723 const base::TimeTicks event_time =
724 state->start_time + base::TimeDelta::FromMilliseconds(elapsed_ms);
725 task_thread_.task_runner()->PostTask(
726 FROM_HERE, base::Bind(&MidiServiceWinImpl::ReceiveMidiDataOnTaskThread,
727 base::Unretained(this), state->port_index, data,
728 event_time));
729 }
730
731 void OnMidiInLongDataOnMultimediaThread(HMIDIIN midi_in_handle,
732 DWORD_PTR param1,
733 DWORD_PTR param2) {
734 auto state = GetInputDeviceFromHandle(midi_in_handle);
735 if (!state)
736 return;
737 MIDIHDR* header = reinterpret_cast<MIDIHDR*>(param1);
738 const DWORD elapsed_ms = param2;
739 MMRESULT result = MMSYSERR_NOERROR;
740 if (destructor_started) {
741 if (state->midi_header &&
742 (state->midi_header->dwFlags & MHDR_PREPARED) == MHDR_PREPARED) {
743 result =
744 midiInUnprepareHeader(state->midi_handle, state->midi_header.get(),
745 sizeof(*state->midi_header));
746 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
747 << "Failed to uninitialize input buffer: "
748 << GetInErrorMessage(result);
749 }
750 return;
751 }
752 if (header->dwBytesRecorded > 0) {
753 const uint8_t* src = reinterpret_cast<const uint8_t*>(header->lpData);
754 std::vector<uint8_t> data;
755 data.assign(src, src + header->dwBytesRecorded);
756 // MIM_DATA/MIM_LONGDATA message treats the time when midiInStart() is
757 // called as the origin of |elapsed_ms|.
758 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757284.aspx
759 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757286.aspx
760 const base::TimeTicks event_time =
761 state->start_time + base::TimeDelta::FromMilliseconds(elapsed_ms);
762 task_thread_.task_runner()->PostTask(
763 FROM_HERE,
764 base::Bind(&MidiServiceWinImpl::ReceiveMidiDataOnTaskThread,
765 base::Unretained(this), state->port_index, data,
766 event_time));
767 }
768 result = midiInAddBuffer(state->midi_handle, header, sizeof(*header));
769 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
770 << "Failed to attach input buffer: " << GetInErrorMessage(result)
771 << "port number:" << state->port_index;
772 }
773
774 void OnMidiInCloseOnMultimediaThread(HMIDIIN midi_in_handle) {
775 auto state = GetInputDeviceFromHandle(midi_in_handle);
776 if (!state)
777 return;
778 const uint32_t port_number = state->port_index;
779 const auto device_info(state->device_info);
780 {
781 base::AutoLock auto_lock(input_ports_lock_);
782 input_device_map_.erase(state->midi_handle);
783 input_ports_[port_number] = nullptr;
784 input_ports_ages_[port_number] += 1;
785 unused_input_ports_[device_info].push(port_number);
786 }
787 task_thread_.task_runner()->PostTask(
788 FROM_HERE,
789 base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread,
790 base::Unretained(this), port_number,
791 PortState::DISCONNECTED));
792 }
793
794 static void CALLBACK
795 OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle,
796 UINT message,
797 DWORD_PTR instance,
798 DWORD_PTR param1,
799 DWORD_PTR param2) {
800 MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance);
801 if (!self)
802 return;
803 switch (message) {
804 case MOM_OPEN:
805 self->OnMidiOutOpen(midi_out_handle, param1, param2);
806 break;
807 case MOM_DONE:
808 self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1);
809 break;
810 case MOM_CLOSE:
811 self->OnMidiOutCloseOnMultimediaThread(midi_out_handle);
812 break;
813 }
814 }
815
816 void OnMidiOutOpen(HMIDIOUT midi_out_handle,
817 DWORD_PTR param1,
818 DWORD_PTR param2) {
819 UINT device_id = 0;
820 MMRESULT result = midiOutGetID(midi_out_handle, &device_id);
821 if (result != MMSYSERR_NOERROR) {
822 DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result);
823 return;
824 }
825 MIDIOUTCAPS2W caps = {};
826 result = midiOutGetDevCaps(
827 device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps));
828 if (result != MMSYSERR_NOERROR) {
829 DLOG(ERROR) << "midiInGetDevCaps failed: " << GetOutErrorMessage(result);
830 return;
831 }
832 auto state =
833 make_scoped_refptr(new MidiOutputDeviceState(MidiDeviceInfo(caps)));
834 state->midi_handle = midi_out_handle;
835 const auto& state_device_info = state->device_info;
836 if (IsUnsupportedDevice(state_device_info)) {
837 task_thread_.task_runner()->PostTask(
838 FROM_HERE, base::Bind(&CloseOutputPortOnTaskThread, midi_out_handle));
839 return;
840 }
841 bool add_new_port = false;
842 uint32_t port_number = 0;
843 {
844 base::AutoLock auto_lock(output_ports_lock_);
845 const auto it = unused_output_ports_.find(state_device_info);
846 if (it == unused_output_ports_.end()) {
847 port_number = static_cast<uint32_t>(output_ports_.size());
848 add_new_port = true;
849 output_ports_.push_back(nullptr);
850 output_ports_ages_.push_back(0);
851 } else {
852 port_number = it->second.top();
853 it->second.pop();
854 if (it->second.empty())
855 unused_output_ports_.erase(it);
856 }
857 output_ports_[port_number] = state;
858 output_ports_ages_[port_number] += 1;
859 output_device_map_[output_ports_[port_number]->midi_handle] =
860 output_ports_[port_number];
861 output_ports_[port_number]->port_index = port_number;
862 output_ports_[port_number]->port_age = output_ports_ages_[port_number];
863 }
864 if (add_new_port) {
865 const MidiPortInfo port_info(
866 // TODO(toyoshim): Use a hash ID insted. crbug.com/467448
867 base::IntToString(static_cast<int>(port_number)),
868 GetManufacturerName(state_device_info),
869 base::WideToUTF8(state_device_info.product_name),
870 MmversionToString(state_device_info.driver_version),
871 PortState::OPENED);
872 task_thread_.task_runner()->PostTask(
873 FROM_HERE, base::Bind(&MidiServiceWinImpl::AddOutputPortOnTaskThread,
874 base::Unretained(this), port_info));
875 } else {
876 task_thread_.task_runner()->PostTask(
877 FROM_HERE,
878 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread,
879 base::Unretained(this), port_number,
880 PortState::CONNECTED));
881 }
882 }
883
884 void OnMidiOutDoneOnMultimediaThread(HMIDIOUT midi_out_handle,
885 DWORD_PTR param1) {
886 auto state = GetOutputDeviceFromHandle(midi_out_handle);
887 if (!state)
888 return;
889 // Take ownership of the MIDIHDR object.
890 ScopedMIDIHDR header(reinterpret_cast<MIDIHDR*>(param1));
891 if (!header)
892 return;
893 MMRESULT result = midiOutUnprepareHeader(state->midi_handle, header.get(),
894 sizeof(*header));
895 DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
896 << "Failed to uninitialize output buffer: "
897 << GetOutErrorMessage(result);
898 }
899
900 void OnMidiOutCloseOnMultimediaThread(HMIDIOUT midi_out_handle) {
901 auto state = GetOutputDeviceFromHandle(midi_out_handle);
902 if (!state)
903 return;
904 const uint32_t port_number = state->port_index;
905 const auto device_info(state->device_info);
906 {
907 base::AutoLock auto_lock(output_ports_lock_);
908 output_device_map_.erase(state->midi_handle);
909 output_ports_[port_number] = nullptr;
910 output_ports_ages_[port_number] += 1;
911 unused_output_ports_[device_info].push(port_number);
912 state->closed = true;
913 }
914 task_thread_.task_runner()->PostTask(
915 FROM_HERE,
916 base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread,
917 base::Unretained(this), port_number,
918 PortState::DISCONNECTED));
919 }
920
921 /////////////////////////////////////////////////////////////////////////////
922 // Callbacks on the sender thread.
923 /////////////////////////////////////////////////////////////////////////////
924
925 void AssertOnSenderThread() {
926 DCHECK_EQ(sender_thread_.GetThreadId(), base::PlatformThread::CurrentId());
927 }
928
929 void SendOnSenderThread(uint32_t port_number,
930 uint64_t port_age,
931 const std::vector<uint8_t>& data,
932 base::TimeTicks time) {
933 AssertOnSenderThread();
934 if (destructor_started) {
935 LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is "
936 "being destructed. port: " << port_number;
937 }
938 auto state = GetOutputDeviceFromPort(port_number);
939 if (!state) {
940 LOG(ERROR) << "ThreadSafeSendData failed due to an invalid port number. "
941 << "port: " << port_number;
942 return;
943 }
944 if (state->closed) {
945 LOG(ERROR)
946 << "ThreadSafeSendData failed because target port is already closed."
947 << "port: " << port_number;
948 return;
949 }
950 if (state->port_age != port_age) {
951 LOG(ERROR)
952 << "ThreadSafeSendData failed because target port is being closed."
953 << "port: " << port_number << "expected port age: " << port_age
954 << "actual port age: " << state->port_age;
955 }
956
957 // MIDI Running status must be filtered out.
958 MidiMessageQueue message_queue(false);
959 message_queue.Add(data);
960 std::vector<uint8_t> message;
961 while (true) {
962 if (destructor_started)
963 break;
964 if (state->closed)
965 break;
966 message_queue.Get(&message);
967 if (message.empty())
968 break;
969 // SendShortMidiMessageInternal can send a MIDI message up to 3 bytes.
970 if (message.size() <= 3)
971 SendShortMidiMessageInternal(state->midi_handle, message);
972 else
973 SendLongMidiMessageInternal(state->midi_handle, message);
974 }
975 }
976
977 /////////////////////////////////////////////////////////////////////////////
978 // Callbacks on the task thread.
979 /////////////////////////////////////////////////////////////////////////////
980
981 void AssertOnTaskThread() {
982 DCHECK_EQ(task_thread_.GetThreadId(), base::PlatformThread::CurrentId());
983 }
984
985 void UpdateDeviceListOnTaskThread() {
986 AssertOnTaskThread();
987 const UINT num_in_devices = midiInGetNumDevs();
988 for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
989 // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA,
990 // MIM_OPEN, and MIM_CLOSE events.
991 // - MIM_DATA: This is the only way to get a short MIDI message with
992 // timestamp information.
993 // - MIM_LONGDATA: This is the only way to get a long MIDI message with
994 // timestamp information.
995 // - MIM_OPEN: This event is sent the input device is opened. Note that
996 // this message is called on the caller thread.
997 // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
998 // the MIDI device becomes unavailable for some reasons, e.g., the
999 // cable is disconnected. As for the former case, HMIDIOUT will be
1000 // invalidated soon after the callback is finished. As for the later
1001 // case, however, HMIDIOUT continues to be valid until midiInClose()
1002 // is called.
1003 HMIDIIN midi_handle = kInvalidMidiInHandle;
1004 const MMRESULT result = midiInOpen(
1005 &midi_handle, device_id,
1006 reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread),
1007 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
1008 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
1009 << "Failed to open output device. "
1010 << " id: " << device_id << " message: " << GetInErrorMessage(result);
1011 }
1012
1013 const UINT num_out_devices = midiOutGetNumDevs();
1014 for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
1015 // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and
1016 // MOM_CLOSE events.
1017 // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
1018 // up the backing store where a long MIDI message is stored.
1019 // - MOM_OPEN: This event is sent the output device is opened. Note that
1020 // this message is called on the caller thread.
1021 // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
1022 // the MIDI device becomes unavailable for some reasons, e.g., the
1023 // cable is disconnected. As for the former case, HMIDIOUT will be
1024 // invalidated soon after the callback is finished. As for the later
1025 // case, however, HMIDIOUT continues to be valid until midiOutClose()
1026 // is called.
1027 HMIDIOUT midi_handle = kInvalidMidiOutHandle;
1028 const MMRESULT result = midiOutOpen(
1029 &midi_handle, device_id,
1030 reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread),
1031 reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION);
1032 DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED)
1033 << "Failed to open output device. "
1034 << " id: " << device_id << " message: " << GetOutErrorMessage(result);
1035 }
1036 }
1037
1038 void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) {
1039 AssertOnTaskThread();
1040 auto state = GetInputDeviceFromHandle(midi_in_handle);
1041 if (!state)
1042 return;
1043 MMRESULT result =
1044 midiInPrepareHeader(state->midi_handle, state->midi_header.get(),
1045 sizeof(*state->midi_header));
1046 if (result != MMSYSERR_NOERROR) {
1047 DLOG(ERROR) << "Failed to initialize input buffer: "
1048 << GetInErrorMessage(result);
1049 return;
1050 }
1051 result = midiInAddBuffer(state->midi_handle, state->midi_header.get(),
1052 sizeof(*state->midi_header));
1053 if (result != MMSYSERR_NOERROR) {
1054 DLOG(ERROR) << "Failed to attach input buffer: "
1055 << GetInErrorMessage(result);
1056 return;
1057 }
1058 result = midiInStart(state->midi_handle);
1059 if (result != MMSYSERR_NOERROR) {
1060 DLOG(ERROR) << "Failed to start input port: "
1061 << GetInErrorMessage(result);
1062 return;
1063 }
1064 state->start_time = base::TimeTicks::Now();
1065 state->start_time_initialized = true;
1066 }
1067
1068 void CompleteInitializationOnTaskThread(Result result) {
1069 AssertOnTaskThread();
1070 delegate_->OnCompleteInitialization(result);
1071 }
1072
1073 void ReceiveMidiDataOnTaskThread(uint32_t port_index,
1074 std::vector<uint8_t> data,
1075 base::TimeTicks time) {
1076 AssertOnTaskThread();
1077 delegate_->OnReceiveMidiData(port_index, data, time);
1078 }
1079
1080 void AddInputPortOnTaskThread(MidiPortInfo info) {
1081 AssertOnTaskThread();
1082 delegate_->OnAddInputPort(info);
1083 }
1084
1085 void AddOutputPortOnTaskThread(MidiPortInfo info) {
1086 AssertOnTaskThread();
1087 delegate_->OnAddOutputPort(info);
1088 }
1089
1090 void SetInputPortStateOnTaskThread(uint32_t port_index, PortState state) {
1091 AssertOnTaskThread();
1092 delegate_->OnSetInputPortState(port_index, state);
1093 }
1094
1095 void SetOutputPortStateOnTaskThread(uint32_t port_index, PortState state) {
1096 AssertOnTaskThread();
1097 delegate_->OnSetOutputPortState(port_index, state);
1098 }
1099
1100 /////////////////////////////////////////////////////////////////////////////
1101 // Fields:
1102 /////////////////////////////////////////////////////////////////////////////
1103
1104 // Does not take ownership.
1105 MidiServiceWinDelegate* delegate_;
1106
1107 base::ThreadChecker thread_checker_;
1108
1109 base::Thread sender_thread_;
1110 base::Thread task_thread_;
1111
1112 base::Lock input_ports_lock_;
1113 base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>>
1114 input_device_map_; // GUARDED_BY(input_ports_lock_)
1115 PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_)
1116 std::vector<scoped_refptr<MidiInputDeviceState>>
1117 input_ports_; // GUARDED_BY(input_ports_lock_)
1118 std::vector<uint64_t> input_ports_ages_; // GUARDED_BY(input_ports_lock_)
1119
1120 base::Lock output_ports_lock_;
1121 base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>>
1122 output_device_map_; // GUARDED_BY(output_ports_lock_)
1123 PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_)
1124 std::vector<scoped_refptr<MidiOutputDeviceState>>
1125 output_ports_; // GUARDED_BY(output_ports_lock_)
1126 std::vector<uint64_t> output_ports_ages_; // GUARDED_BY(output_ports_lock_)
1127
1128 // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note
1129 // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until
1130 // |sender_thread_|, and |task_thread_| are stopped.
1131 // This flag can be used as the signal that when background tasks must be
1132 // interrupted.
1133 // TODO(toyoshim): Use std::atomic<bool> when it is allowed.
1134 volatile bool destructor_started;
1135
1136 DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl);
1137 };
1138
1139 } // namespace
1140
1141 MidiManagerWin::MidiManagerWin(MidiService* service) : MidiManager(service) {}
1142
1143 MidiManagerWin::~MidiManagerWin() {
1144 }
1145
1146 void MidiManagerWin::StartInitialization() {
1147 midi_service_.reset(new MidiServiceWinImpl);
1148 // Note that |CompleteInitialization()| will be called from the callback.
1149 midi_service_->InitializeAsync(this);
1150 }
1151
1152 void MidiManagerWin::Finalize() {
1153 midi_service_.reset();
1154 }
1155
1156 void MidiManagerWin::DispatchSendMidiData(MidiManagerClient* client,
1157 uint32_t port_index,
1158 const std::vector<uint8_t>& data,
1159 double timestamp) {
1160 if (!midi_service_)
1161 return;
1162
1163 base::TimeTicks time_to_send = base::TimeTicks::Now();
1164 if (timestamp != 0.0) {
1165 time_to_send =
1166 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
1167 timestamp * base::Time::kMicrosecondsPerSecond);
1168 }
1169 midi_service_->SendMidiDataAsync(port_index, data, time_to_send);
1170
1171 // TOOD(toyoshim): This calculation should be done when the date is actually
1172 // sent.
1173 client->AccumulateMidiBytesSent(data.size());
1174 }
1175
1176 void MidiManagerWin::OnCompleteInitialization(Result result) {
1177 CompleteInitialization(result);
1178 }
1179
1180 void MidiManagerWin::OnAddInputPort(MidiPortInfo info) {
1181 AddInputPort(info);
1182 }
1183
1184 void MidiManagerWin::OnAddOutputPort(MidiPortInfo info) {
1185 AddOutputPort(info);
1186 }
1187
1188 void MidiManagerWin::OnSetInputPortState(uint32_t port_index, PortState state) {
1189 SetInputPortState(port_index, state);
1190 }
1191
1192 void MidiManagerWin::OnSetOutputPortState(uint32_t port_index,
1193 PortState state) {
1194 SetOutputPortState(port_index, state);
1195 }
1196
1197 void MidiManagerWin::OnReceiveMidiData(uint32_t port_index,
1198 const std::vector<uint8_t>& data,
1199 base::TimeTicks time) {
1200 ReceiveMidiData(port_index, &data[0], data.size(), time);
1201 }
1202
1203 MidiManager* MidiManager::Create(MidiService* service) {
1204 if (base::FeatureList::IsEnabled(features::kMidiManagerWinrt) &&
1205 base::win::GetVersion() >= base::win::VERSION_WIN10)
1206 return new MidiManagerWinrt(service);
1207 if (base::FeatureList::IsEnabled(features::kMidiManagerDynamicInstantiation))
1208 return new DynamicallyInitializedMidiManagerWin(service);
1209 return new MidiManagerWin(service);
1210 }
1211
1212 } // namespace midi
OLDNEW
« no previous file with comments | « media/midi/midi_manager_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698