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