1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// found in the LICENSE file.
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_manager_win.h"
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
7a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <windows.h>
8a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Prevent unnecessary functions from being included from <mmsystem.h>
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNODRV
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOSOUND
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOWAVE
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOAUX
14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOMIXER
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOTIMER
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOJOY
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOMCI
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#define MMNOMMIO
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include <mmsystem.h>
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <algorithm>
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <string>
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/bind.h"
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/message_loop/message_loop.h"
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/threading/thread.h"
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_message_queue.h"
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_message_util.h"
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_port_info.h"
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace media {
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace {
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)std::string GetInErrorMessage(MMRESULT result) {
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  wchar_t text[MAXERRORLENGTH];
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text));
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (get_result != MMSYSERR_NOERROR) {
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG(ERROR) << "Failed to get error message."
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << " original error: " << result
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << " midiInGetErrorText error: " << get_result;
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return std::string();
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return base::WideToUTF8(text);
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)std::string GetOutErrorMessage(MMRESULT result) {
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  wchar_t text[MAXERRORLENGTH];
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  MMRESULT get_result = midiOutGetErrorText(result, text, arraysize(text));
50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (get_result != MMSYSERR_NOERROR) {
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG(ERROR) << "Failed to get error message."
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << " original error: " << result
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << " midiOutGetErrorText error: " << get_result;
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return std::string();
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return base::WideToUTF8(text);
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)class MIDIHDRDeleter {
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) public:
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void operator()(MIDIHDR* header) {
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!header)
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    delete[] static_cast<char*>(header->lpData);
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    header->lpData = NULL;
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    header->dwBufferLength = 0;
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    delete header;
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)};
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)typedef scoped_ptr<MIDIHDR, MIDIHDRDeleter> ScopedMIDIHDR;
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ScopedMIDIHDR CreateMIDIHDR(size_t size) {
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ScopedMIDIHDR header(new MIDIHDR);
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ZeroMemory(header.get(), sizeof(*header));
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  header->lpData = new char[size];
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  header->dwBufferLength = size;
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return header.Pass();
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SendShortMidiMessageInternal(HMIDIOUT midi_out_handle,
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  const std::vector<uint8>& message) {
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (message.size() >= 4)
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DWORD packed_message = 0;
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (size_t i = 0; i < message.size(); ++i)
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    packed_message |= (static_cast<uint32>(message[i]) << (i * 8));
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  MMRESULT result = midiOutShortMsg(midi_out_handle, packed_message);
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      << "Failed to output short message: " << GetOutErrorMessage(result);
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void SendLongMidiMessageInternal(HMIDIOUT midi_out_handle,
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 const std::vector<uint8>& message) {
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Implementation note:
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Sending long MIDI message can be performed synchronously or asynchronously
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // depending on the driver. There are 2 options to support both cases:
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // 1) Call midiOutLongMsg() API and wait for its completion within this
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //   function. In this approach, we can avoid memory copy by directly pointing
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //   |message| as the data buffer to be sent.
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // 2) Allocate a buffer and copy |message| to it, then call midiOutLongMsg()
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //   API. The buffer will be freed in the MOM_DONE event hander, which tells
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  //   us that the task of midiOutLongMsg() API is completed.
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Here we choose option 2) in favor of asynchronous design.
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Note for built-in USB-MIDI driver:
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // From an observation on Windows 7/8.1 with a USB-MIDI keyboard,
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // midiOutLongMsg() will be always blocked. Sending 64 bytes or less data
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // takes roughly 300 usecs. Sending 2048 bytes or more data takes roughly
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // |message.size() / (75 * 1024)| secs in practice. Here we put 60 KB size
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // limit on SysEx message, with hoping that midiOutLongMsg will be blocked at
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // most 1 sec or so with a typical USB-MIDI device.
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const size_t kSysExSizeLimit = 60 * 1024;
115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (message.size() >= kSysExSizeLimit) {
116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DVLOG(1) << "Ingnoreing SysEx message due to the size limit"
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)             << ", size = " << message.size();
118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ScopedMIDIHDR midi_header(CreateMIDIHDR(message.size()));
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (size_t i = 0; i < message.size(); ++i)
123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    midi_header->lpData[i] = static_cast<char>(message[i]);
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  MMRESULT result = midiOutPrepareHeader(
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_out_handle, midi_header.get(), sizeof(*midi_header));
127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (result != MMSYSERR_NOERROR) {
128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG(ERROR) << "Failed to prepare output buffer: "
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << GetOutErrorMessage(result);
130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  result = midiOutLongMsg(
134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_out_handle, midi_header.get(), sizeof(*midi_header));
135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (result != MMSYSERR_NOERROR) {
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG(ERROR) << "Failed to output long message: "
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                << GetOutErrorMessage(result);
138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    result = midiOutUnprepareHeader(
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        midi_out_handle, midi_header.get(), sizeof(*midi_header));
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        << "Failed to uninitialize output buffer: "
142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        << GetOutErrorMessage(result);
143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // The ownership of |midi_header| is moved to MOM_DONE event handler.
147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  midi_header.release();
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}  // namespace
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class MidiManagerWin::InDeviceInfo {
153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) public:
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ~InDeviceInfo() {
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    Uninitialize();
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void set_port_index(int index) {
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    port_index_ = index;
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int port_index() const {
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return port_index_;
162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool device_to_be_closed() const {
164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return device_to_be_closed_;
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  HMIDIIN midi_handle() const {
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return midi_handle_;
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
169a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  static scoped_ptr<InDeviceInfo> Create(MidiManagerWin* manager,
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         UINT device_id) {
172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_ptr<InDeviceInfo> obj(new InDeviceInfo(manager));
173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!obj->Initialize(device_id))
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      obj.reset();
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return obj.Pass();
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) private:
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static const int kInvalidPortIndex = -1;
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static const size_t kBufferLength = 32 * 1024;
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  explicit InDeviceInfo(MidiManagerWin* manager)
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      : manager_(manager),
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        port_index_(kInvalidPortIndex),
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        midi_handle_(NULL),
186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        started_(false),
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        device_to_be_closed_(false) {
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
190a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool Initialize(DWORD device_id) {
191a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    Uninitialize();
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    midi_header_ = CreateMIDIHDR(kBufferLength);
193a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA, and
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // MIM_CLOSE events.
196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // - MIM_DATA: This is the only way to get a short MIDI message with
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     timestamp information.
198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // - MIM_LONGDATA: This is the only way to get a long MIDI message with
199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     timestamp information.
200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2)
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     the MIDI device becomes unavailable for some reasons, e.g., the cable
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     is disconnected. As for the former case, HMIDIOUT will be invalidated
203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     soon after the callback is finished. As for the later case, however,
204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     HMIDIOUT continues to be valid until midiInClose() is called.
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = midiInOpen(&midi_handle_,
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 device_id,
207a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 reinterpret_cast<DWORD_PTR>(&HandleMessage),
208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 reinterpret_cast<DWORD_PTR>(this),
209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                 CALLBACK_FUNCTION);
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to open output device. "
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << " id: " << device_id
213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << " message: " << GetInErrorMessage(result);
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    result = midiInPrepareHeader(
217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        midi_handle_, midi_header_.get(), sizeof(*midi_header_));
218a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to initialize input buffer: "
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << GetInErrorMessage(result);
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    result = midiInAddBuffer(
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        midi_handle_, midi_header_.get(), sizeof(*midi_header_));
225a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
226a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to attach input buffer: "
227a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << GetInErrorMessage(result);
228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
229a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
230a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    result = midiInStart(midi_handle_);
231a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
232a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to start input port: "
233a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << GetInErrorMessage(result);
234a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
235a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
236a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    started_ = true;
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    start_time_ = base::TimeTicks::Now();
238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return true;
239a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
241a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void Uninitialize() {
242a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = MMSYSERR_NOERROR;
243a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (midi_handle_ && started_) {
244a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      result = midiInStop(midi_handle_);
245a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
246a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          << "Failed to stop input port: " << GetInErrorMessage(result);
247a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      started_ = false;
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      start_time_ = base::TimeTicks();
249a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
250a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (midi_handle_) {
251a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      // midiInReset flushes pending messages. We ignore these messages.
252a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      device_to_be_closed_ = true;
253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      result = midiInReset(midi_handle_);
254a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          << "Failed to reset input port: " << GetInErrorMessage(result);
256a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      result = midiInClose(midi_handle_);
257a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      device_to_be_closed_ = false;
258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
259a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          << "Failed to close input port: " << GetInErrorMessage(result);
260a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_header_.reset();
261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_handle_ = NULL;
262a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      port_index_ = kInvalidPortIndex;
263a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
265a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static void CALLBACK HandleMessage(HMIDIIN midi_in_handle,
267a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     UINT message,
268a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR instance,
269a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR param1,
270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR param2) {
271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // This method can be called back on any thread depending on Windows
272a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // multimedia subsystem and underlying MIDI drivers.
273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    InDeviceInfo* self = reinterpret_cast<InDeviceInfo*>(instance);
274a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!self)
275a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (self->midi_handle() != midi_in_handle)
277a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
279a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    switch (message) {
280a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case MIM_DATA:
281a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        self->OnShortMessageReceived(static_cast<uint8>(param1 & 0xff),
282a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     static_cast<uint8>((param1 >> 8) & 0xff),
283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     static_cast<uint8>((param1 >> 16) & 0xff),
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     param2);
285a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return;
286a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case MIM_LONGDATA:
287a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        self->OnLongMessageReceived(reinterpret_cast<MIDIHDR*>(param1),
2885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                    param2);
289a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return;
290a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case MIM_CLOSE:
291a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // TODO(yukawa): Implement crbug.com/279097.
292a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return;
293a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
295a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
296a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void OnShortMessageReceived(uint8 status_byte,
297a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                              uint8 first_data_byte,
298a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                              uint8 second_data_byte,
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                              DWORD elapsed_ms) {
300a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (device_to_be_closed())
301a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const size_t len = GetMidiMessageLength(status_byte);
303a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (len == 0 || port_index() == kInvalidPortIndex)
304a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
305a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const uint8 kData[] = { status_byte, first_data_byte, second_data_byte };
306a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DCHECK_LE(len, arraysize(kData));
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    OnMessageReceived(kData, len, elapsed_ms);
308a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
309a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void OnLongMessageReceived(MIDIHDR* header, DWORD elapsed_ms) {
311a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (header != midi_header_.get())
312a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
313a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = MMSYSERR_NOERROR;
314a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (device_to_be_closed()) {
315a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (midi_header_ &&
316a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          (midi_header_->dwFlags & MHDR_PREPARED) == MHDR_PREPARED) {
317a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        result = midiInUnprepareHeader(
318a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            midi_handle_, midi_header_.get(), sizeof(*midi_header_));
319a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
320a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            << "Failed to uninitialize input buffer: "
321a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            << GetInErrorMessage(result);
322a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
323a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
324a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
325a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (header->dwBytesRecorded > 0 && port_index() != kInvalidPortIndex) {
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      OnMessageReceived(reinterpret_cast<const uint8*>(header->lpData),
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        header->dwBytesRecorded,
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        elapsed_ms);
329a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    result = midiInAddBuffer(midi_handle_, header, sizeof(*header));
331a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
332a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        << "Failed to attach input port: " << GetInErrorMessage(result);
333a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
334a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void OnMessageReceived(const uint8* data, size_t length, DWORD elapsed_ms) {
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // MIM_DATA/MIM_LONGDATA message treats the time when midiInStart() is
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // called as the origin of |elapsed_ms|.
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757284.aspx
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757286.aspx
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::TimeTicks event_time =
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        start_time_ + base::TimeDelta::FromMilliseconds(elapsed_ms);
3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    manager_->ReceiveMidiData(port_index_, data, length, event_time);
343a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
344a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  MidiManagerWin* manager_;
346a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  int port_index_;
347a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  HMIDIIN midi_handle_;
348a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ScopedMIDIHDR midi_header_;
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::TimeTicks start_time_;
350a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool started_;
351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool device_to_be_closed_;
352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(InDeviceInfo);
353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)};
354a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class MidiManagerWin::OutDeviceInfo {
356a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) public:
357a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  ~OutDeviceInfo() {
358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    Uninitialize();
359a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
360a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
361a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static scoped_ptr<OutDeviceInfo> Create(UINT device_id) {
362a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_ptr<OutDeviceInfo> obj(new OutDeviceInfo);
363a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!obj->Initialize(device_id))
364a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      obj.reset();
365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return obj.Pass();
366a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  HMIDIOUT midi_handle() const {
369a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return midi_handle_;
370a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
371a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void Quit() {
373a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    quitting_ = true;
374a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
375a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
376a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void Send(const std::vector<uint8>& data) {
377a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Check if the attached device is still available or not.
378a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!midi_handle_)
379a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
380a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
381a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Give up sending MIDI messages here if the device is already closed.
382a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Note that this check is optional. Regardless of that we check |closed_|
383a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // or not, nothing harmful happens as long as |midi_handle_| is still valid.
384a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (closed_)
385a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
386a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
387a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // MIDI Running status must be filtered out.
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MidiMessageQueue message_queue(false);
389a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    message_queue.Add(data);
390a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    std::vector<uint8> message;
391a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    while (!quitting_) {
392a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      message_queue.Get(&message);
393a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (message.empty())
394a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        break;
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // SendShortMidiMessageInternal can send a MIDI message up to 3 bytes.
396a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (message.size() <= 3)
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        SendShortMidiMessageInternal(midi_handle_, message);
398a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      else
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        SendLongMidiMessageInternal(midi_handle_, message);
400a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
401a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
402a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
403a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) private:
404a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  OutDeviceInfo()
405a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      : midi_handle_(NULL),
406a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        closed_(false),
407a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        quitting_(false) {}
408a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool Initialize(DWORD device_id) {
410a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    Uninitialize();
411a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE and MOM_CLOSE
412a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // events.
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean
414a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     up the backing store where a long MIDI message is stored.
415a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2)
416a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     the MIDI device becomes unavailable for some reasons, e.g., the cable
417a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     is disconnected. As for the former case, HMIDIOUT will be invalidated
418a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     soon after the callback is finished. As for the later case, however,
419a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    //     HMIDIOUT continues to be valid until midiOutClose() is called.
420a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = midiOutOpen(&midi_handle_,
421a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  device_id,
422a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  reinterpret_cast<DWORD_PTR>(&HandleMessage),
423a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  reinterpret_cast<DWORD_PTR>(this),
424a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                  CALLBACK_FUNCTION);
425a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
426a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to open output device. "
427a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << " id: " << device_id
428a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << " message: "<< GetOutErrorMessage(result);
429a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      midi_handle_ = NULL;
430a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;
431a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
432a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return true;
433a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
434a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
435a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  void Uninitialize() {
436a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!midi_handle_)
437a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
438a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
439a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = midiOutReset(midi_handle_);
440a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
441a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        << "Failed to reset output port: " << GetOutErrorMessage(result);
442a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    result = midiOutClose(midi_handle_);
443a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
444a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        << "Failed to close output port: " << GetOutErrorMessage(result);
445a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    midi_handle_ = NULL;
446a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    closed_ = true;
447a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
448a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
449a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  static void CALLBACK HandleMessage(HMIDIOUT midi_out_handle,
450a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     UINT message,
451a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR instance,
452a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR param1,
453a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                     DWORD_PTR param2) {
454a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // This method can be called back on any thread depending on Windows
455a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // multimedia subsystem and underlying MIDI drivers.
456a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
457a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    OutDeviceInfo* self = reinterpret_cast<OutDeviceInfo*>(instance);
458a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!self)
459a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
460a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (self->midi_handle() != midi_out_handle)
461a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return;
462a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    switch (message) {
463a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case MOM_DONE: {
464a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // Take ownership of the MIDIHDR object.
465a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        ScopedMIDIHDR header(reinterpret_cast<MIDIHDR*>(param1));
466a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        if (!header)
467a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)          return;
468a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        MMRESULT result = midiOutUnprepareHeader(
469a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            self->midi_handle(), header.get(), sizeof(*header));
470a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        DLOG_IF(ERROR, result != MMSYSERR_NOERROR)
471a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            << "Failed to uninitialize output buffer: "
472a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            << GetOutErrorMessage(result);
473a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return;
474a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      }
475a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      case MOM_CLOSE:
476a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // No lock is required since this flag is just a hint to avoid
477a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // unnecessary API calls that will result in failure anyway.
478a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        self->closed_ = true;
479a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        // TODO(yukawa): Implement crbug.com/279097.
480a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return;
481a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
482a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
483a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
484a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  HMIDIOUT midi_handle_;
485a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
486a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // True if the device is already closed.
487a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  volatile bool closed_;
488a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
4895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // True if the MidiManagerWin is trying to stop the sender thread.
490a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  volatile bool quitting_;
491a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
492a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(OutDeviceInfo);
493a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)};
494a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
4955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManagerWin::MidiManagerWin()
4965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : send_thread_("MidiSendThread") {
497a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
498a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
499010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void MidiManagerWin::StartInitialization() {
500a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const UINT num_in_devices = midiInGetNumDevs();
501a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  in_devices_.reserve(num_in_devices);
502a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (UINT device_id = 0; device_id < num_in_devices; ++device_id) {
503a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MIDIINCAPS caps = {};
504a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = midiInGetDevCaps(device_id, &caps, sizeof(caps));
505a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
506a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to obtain input device info: "
507a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << GetInErrorMessage(result);
508a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;
509a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
510a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_ptr<InDeviceInfo> in_device(InDeviceInfo::Create(this, device_id));
511a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!in_device)
512a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MidiPortInfo info(
514a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::IntToString(static_cast<int>(device_id)),
515a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "",
516a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::WideToUTF8(caps.szPname),
517a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::IntToString(static_cast<int>(caps.vDriverVersion)));
518a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    AddInputPort(info);
519010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    in_device->set_port_index(input_ports().size() - 1);
520a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    in_devices_.push_back(in_device.Pass());
521a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
522a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
523a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const UINT num_out_devices = midiOutGetNumDevs();
524a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  out_devices_.reserve(num_out_devices);
525a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (UINT device_id = 0; device_id < num_out_devices; ++device_id) {
526a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MIDIOUTCAPS caps = {};
527a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    MMRESULT result = midiOutGetDevCaps(device_id, &caps, sizeof(caps));
528a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
529a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      DLOG(ERROR) << "Failed to obtain output device info: "
530a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                  << GetOutErrorMessage(result);
531a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;
532a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
533a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    scoped_ptr<OutDeviceInfo> out_port(OutDeviceInfo::Create(device_id));
534a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (!out_port)
535a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;
5365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    MidiPortInfo info(
537a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::IntToString(static_cast<int>(device_id)),
538a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        "",
539a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::WideToUTF8(caps.szPname),
540a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::IntToString(static_cast<int>(caps.vDriverVersion)));
541a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    AddOutputPort(info);
542a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    out_devices_.push_back(out_port.Pass());
543a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
544a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
545010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  CompleteInitialization(MIDI_OK);
546a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
547a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManagerWin::~MidiManagerWin() {
549a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Cleanup order is important. |send_thread_| must be stopped before
550a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // |out_devices_| is cleared.
551010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  for (size_t i = 0; i < output_ports().size(); ++i)
552a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    out_devices_[i]->Quit();
553a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  send_thread_.Stop();
554a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
555a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  out_devices_.clear();
556a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  in_devices_.clear();
557a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
558a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerWin::DispatchSendMidiData(MidiManagerClient* client,
560a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          uint32 port_index,
561a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          const std::vector<uint8>& data,
562a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                          double timestamp) {
563a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (out_devices_.size() <= port_index)
564a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
565a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
566a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::TimeDelta delay;
567a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (timestamp != 0.0) {
568a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::TimeTicks time_to_send =
569a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::TimeTicks() + base::TimeDelta::FromMicroseconds(
570a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)            timestamp * base::Time::kMicrosecondsPerSecond);
571a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
572a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
573a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
574a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!send_thread_.IsRunning())
575a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    send_thread_.Start();
576a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
577a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  OutDeviceInfo* out_port = out_devices_[port_index].get();
578a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  send_thread_.message_loop()->PostDelayedTask(
579a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FROM_HERE,
580a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::Bind(&OutDeviceInfo::Send, base::Unretained(out_port), data),
581a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      delay);
582a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Call back AccumulateMidiBytesSent() on |send_thread_| to emulate the
5845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // behavior of MidiManagerMac::SendMidiData.
585a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // TODO(yukawa): Do this task in a platform-independent way if possible.
586a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // See crbug.com/325810.
587a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  send_thread_.message_loop()->PostTask(
588a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FROM_HERE,
5895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
590a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 base::Unretained(client), data.size()));
591a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
592a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
5935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManager* MidiManager::Create() {
5945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new MidiManagerWin();
595a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
596a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
597a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}  // namespace media
598