Index: media/midi/midi_manager_win.cc |
diff --git a/media/midi/midi_manager_win.cc b/media/midi/midi_manager_win.cc |
deleted file mode 100644 |
index 2a7265ced4c68bd0219614b8b661bd50c97274ed..0000000000000000000000000000000000000000 |
--- a/media/midi/midi_manager_win.cc |
+++ /dev/null |
@@ -1,1212 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "media/midi/midi_manager_win.h" |
- |
-#include <windows.h> |
-#include <ks.h> |
-#include <ksmedia.h> |
-#include <mmreg.h> |
-// Prevent unnecessary functions from being included from <mmsystem.h> |
-#define MMNODRV |
-#define MMNOSOUND |
-#define MMNOWAVE |
-#define MMNOAUX |
-#define MMNOMIXER |
-#define MMNOTIMER |
-#define MMNOJOY |
-#define MMNOMCI |
-#define MMNOMMIO |
-#include <mmsystem.h> |
-#include <stddef.h> |
- |
-#include <algorithm> |
-#include <functional> |
-#include <queue> |
-#include <string> |
- |
-#include "base/bind.h" |
-#include "base/containers/hash_tables.h" |
-#include "base/feature_list.h" |
-#include "base/macros.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/strings/string16.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/string_piece.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/system_monitor/system_monitor.h" |
-#include "base/threading/thread_checker.h" |
-#include "base/timer/timer.h" |
-#include "base/win/message_window.h" |
-#include "base/win/windows_version.h" |
-#include "device/usb/usb_ids.h" |
-#include "media/midi/dynamically_initialized_midi_manager_win.h" |
-#include "media/midi/message_util.h" |
-#include "media/midi/midi_manager_winrt.h" |
-#include "media/midi/midi_message_queue.h" |
-#include "media/midi/midi_port_info.h" |
-#include "media/midi/midi_switches.h" |
- |
-namespace midi { |
-namespace { |
- |
-using mojom::PortState; |
-using mojom::Result; |
- |
-static const size_t kBufferLength = 32 * 1024; |
- |
-// We assume that nullpter represents an invalid MIDI handle. |
-const HMIDIIN kInvalidMidiInHandle = nullptr; |
-const HMIDIOUT kInvalidMidiOutHandle = nullptr; |
- |
-std::string GetInErrorMessage(MMRESULT result) { |
- wchar_t text[MAXERRORLENGTH]; |
- MMRESULT get_result = midiInGetErrorText(result, text, arraysize(text)); |
- if (get_result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to get error message." |
- << " original error: " << result |
- << " midiInGetErrorText error: " << get_result; |
- return std::string(); |
- } |
- return base::WideToUTF8(text); |
-} |
- |
-std::string GetOutErrorMessage(MMRESULT result) { |
- wchar_t text[MAXERRORLENGTH]; |
- MMRESULT get_result = midiOutGetErrorText(result, text, arraysize(text)); |
- if (get_result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to get error message." |
- << " original error: " << result |
- << " midiOutGetErrorText error: " << get_result; |
- return std::string(); |
- } |
- return base::WideToUTF8(text); |
-} |
- |
-std::string MmversionToString(MMVERSION version) { |
- return base::StringPrintf("%d.%d", HIBYTE(version), LOBYTE(version)); |
-} |
- |
-void CloseOutputPortOnTaskThread(HMIDIOUT midi_out_handle) { |
- midiOutClose(midi_out_handle); |
-} |
- |
-class MIDIHDRDeleter { |
- public: |
- void operator()(MIDIHDR* header) { |
- if (!header) |
- return; |
- delete[] static_cast<char*>(header->lpData); |
- header->lpData = NULL; |
- header->dwBufferLength = 0; |
- delete header; |
- } |
-}; |
- |
-using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>; |
- |
-ScopedMIDIHDR CreateMIDIHDR(size_t size) { |
- ScopedMIDIHDR header(new MIDIHDR); |
- ZeroMemory(header.get(), sizeof(*header)); |
- header->lpData = new char[size]; |
- header->dwBufferLength = static_cast<DWORD>(size); |
- return header; |
-} |
- |
-void SendShortMidiMessageInternal(HMIDIOUT midi_out_handle, |
- const std::vector<uint8_t>& message) { |
- DCHECK_LE(message.size(), static_cast<size_t>(3)) |
- << "A short MIDI message should be up to 3 bytes."; |
- |
- DWORD packed_message = 0; |
- for (size_t i = 0; i < message.size(); ++i) |
- packed_message |= (static_cast<uint32_t>(message[i]) << (i * 8)); |
- MMRESULT result = midiOutShortMsg(midi_out_handle, packed_message); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "Failed to output short message: " << GetOutErrorMessage(result); |
-} |
- |
-void SendLongMidiMessageInternal(HMIDIOUT midi_out_handle, |
- const std::vector<uint8_t>& message) { |
- // Implementation note: |
- // Sending a long MIDI message can be performed synchronously or |
- // asynchronously depending on the driver. There are 2 options to support both |
- // cases: |
- // 1) Call midiOutLongMsg() API and wait for its completion within this |
- // function. In this approach, we can avoid memory copy by directly pointing |
- // |message| as the data buffer to be sent. |
- // 2) Allocate a buffer and copy |message| to it, then call midiOutLongMsg() |
- // API. The buffer will be freed in the MOM_DONE event hander, which tells |
- // us that the task of midiOutLongMsg() API is completed. |
- // Here we choose option 2) in favor of asynchronous design. |
- |
- // Note for built-in USB-MIDI driver: |
- // From an observation on Windows 7/8.1 with a USB-MIDI keyboard, |
- // midiOutLongMsg() will be always blocked. Sending 64 bytes or less data |
- // takes roughly 300 usecs. Sending 2048 bytes or more data takes roughly |
- // |message.size() / (75 * 1024)| secs in practice. Here we put 256 KB size |
- // limit on SysEx message, with hoping that midiOutLongMsg will be blocked at |
- // most 4 sec or so with a typical USB-MIDI device. |
- // TODO(crbug.com/383578): This restriction should be removed once Web MIDI |
- // defines a standardized way to handle large sysex messages. |
- const size_t kSysExSizeLimit = 256 * 1024; |
- if (message.size() >= kSysExSizeLimit) { |
- DVLOG(1) << "Ingnoreing SysEx message due to the size limit" |
- << ", size = " << message.size(); |
- return; |
- } |
- |
- ScopedMIDIHDR midi_header(CreateMIDIHDR(message.size())); |
- std::copy(message.begin(), message.end(), midi_header->lpData); |
- |
- MMRESULT result = midiOutPrepareHeader(midi_out_handle, midi_header.get(), |
- sizeof(*midi_header)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to prepare output buffer: " |
- << GetOutErrorMessage(result); |
- return; |
- } |
- |
- result = |
- midiOutLongMsg(midi_out_handle, midi_header.get(), sizeof(*midi_header)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to output long message: " |
- << GetOutErrorMessage(result); |
- result = midiOutUnprepareHeader(midi_out_handle, midi_header.get(), |
- sizeof(*midi_header)); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "Failed to uninitialize output buffer: " |
- << GetOutErrorMessage(result); |
- return; |
- } |
- |
- // The ownership of |midi_header| is moved to MOM_DONE event handler. |
- ignore_result(midi_header.release()); |
-} |
- |
-template <size_t array_size> |
-base::string16 AsString16(const wchar_t(&buffer)[array_size]) { |
- size_t len = 0; |
- for (len = 0; len < array_size; ++len) { |
- if (buffer[len] == L'\0') |
- break; |
- } |
- return base::string16(buffer, len); |
-} |
- |
-struct MidiDeviceInfo final { |
- explicit MidiDeviceInfo(const MIDIINCAPS2W& caps) |
- : manufacturer_id(caps.wMid), |
- product_id(caps.wPid), |
- driver_version(caps.vDriverVersion), |
- product_name(AsString16(caps.szPname)), |
- usb_vendor_id(ExtractUsbVendorIdIfExists(caps)), |
- usb_product_id(ExtractUsbProductIdIfExists(caps)), |
- is_usb_device(IsUsbDevice(caps)), |
- is_software_synth(false) {} |
- explicit MidiDeviceInfo(const MIDIOUTCAPS2W& caps) |
- : manufacturer_id(caps.wMid), |
- product_id(caps.wPid), |
- driver_version(caps.vDriverVersion), |
- product_name(AsString16(caps.szPname)), |
- usb_vendor_id(ExtractUsbVendorIdIfExists(caps)), |
- usb_product_id(ExtractUsbProductIdIfExists(caps)), |
- is_usb_device(IsUsbDevice(caps)), |
- is_software_synth(IsSoftwareSynth(caps)) {} |
- explicit MidiDeviceInfo(const MidiDeviceInfo& info) |
- : manufacturer_id(info.manufacturer_id), |
- product_id(info.product_id), |
- driver_version(info.driver_version), |
- product_name(info.product_name), |
- usb_vendor_id(info.usb_vendor_id), |
- usb_product_id(info.usb_product_id), |
- is_usb_device(info.is_usb_device), |
- is_software_synth(info.is_software_synth) {} |
- // Currently only following entities are considered when testing the equality |
- // of two MIDI devices. |
- // TODO(toyoshim): Consider to calculate MIDIPort.id here and use it as the |
- // key. See crbug.com/467448. Then optimize the data for |MidiPortInfo|. |
- const uint16_t manufacturer_id; |
- const uint16_t product_id; |
- const uint32_t driver_version; |
- const base::string16 product_name; |
- const uint16_t usb_vendor_id; |
- const uint16_t usb_product_id; |
- const bool is_usb_device; |
- const bool is_software_synth; |
- |
- // Required to be used as the key of base::hash_map. |
- bool operator==(const MidiDeviceInfo& that) const { |
- return manufacturer_id == that.manufacturer_id && |
- product_id == that.product_id && |
- driver_version == that.driver_version && |
- product_name == that.product_name && |
- is_usb_device == that.is_usb_device && |
- (is_usb_device && usb_vendor_id == that.usb_vendor_id && |
- usb_product_id == that.usb_product_id); |
- } |
- |
- // Hash function to be used in base::hash_map. |
- struct Hasher { |
- size_t operator()(const MidiDeviceInfo& info) const { |
- size_t hash = info.manufacturer_id; |
- hash *= 131; |
- hash += info.product_id; |
- hash *= 131; |
- hash += info.driver_version; |
- hash *= 131; |
- hash += info.product_name.size(); |
- hash *= 131; |
- if (!info.product_name.empty()) { |
- hash += info.product_name[0]; |
- } |
- hash *= 131; |
- hash += info.usb_vendor_id; |
- hash *= 131; |
- hash += info.usb_product_id; |
- return hash; |
- } |
- }; |
- |
- private: |
- static bool IsUsbDevice(const MIDIINCAPS2W& caps) { |
- return IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid) && |
- IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid); |
- } |
- static bool IsUsbDevice(const MIDIOUTCAPS2W& caps) { |
- return IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid) && |
- IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid); |
- } |
- static bool IsSoftwareSynth(const MIDIOUTCAPS2W& caps) { |
- return caps.wTechnology == MOD_SWSYNTH; |
- } |
- static uint16_t ExtractUsbVendorIdIfExists(const MIDIINCAPS2W& caps) { |
- if (!IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid)) |
- return 0; |
- return EXTRACT_USBAUDIO_MID(&caps.ManufacturerGuid); |
- } |
- static uint16_t ExtractUsbVendorIdIfExists(const MIDIOUTCAPS2W& caps) { |
- if (!IS_COMPATIBLE_USBAUDIO_MID(&caps.ManufacturerGuid)) |
- return 0; |
- return EXTRACT_USBAUDIO_MID(&caps.ManufacturerGuid); |
- } |
- static uint16_t ExtractUsbProductIdIfExists(const MIDIINCAPS2W& caps) { |
- if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid)) |
- return 0; |
- return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); |
- } |
- static uint16_t ExtractUsbProductIdIfExists(const MIDIOUTCAPS2W& caps) { |
- if (!IS_COMPATIBLE_USBAUDIO_PID(&caps.ProductGuid)) |
- return 0; |
- return EXTRACT_USBAUDIO_PID(&caps.ProductGuid); |
- } |
-}; |
- |
-std::string GetManufacturerName(const MidiDeviceInfo& info) { |
- if (info.is_usb_device) { |
- const char* name = device::UsbIds::GetVendorName(info.usb_vendor_id); |
- return std::string(name ? name : ""); |
- } |
- |
- switch (info.manufacturer_id) { |
- case MM_MICROSOFT: |
- return "Microsoft Corporation"; |
- default: |
- // TODO(toyoshim): Support other manufacture IDs. crbug.com/472341. |
- return ""; |
- } |
-} |
- |
-bool IsUnsupportedDevice(const MidiDeviceInfo& info) { |
- return info.is_software_synth && info.manufacturer_id == MM_MICROSOFT && |
- (info.product_id == MM_MSFT_WDMAUDIO_MIDIOUT || |
- info.product_id == MM_MSFT_GENERIC_MIDISYNTH); |
-} |
- |
-using PortNumberCache = |
- base::hash_map<MidiDeviceInfo, |
- std::priority_queue<uint32_t, |
- std::vector<uint32_t>, |
- std::greater<uint32_t>>, |
- MidiDeviceInfo::Hasher>; |
- |
-struct MidiInputDeviceState final |
- : base::RefCountedThreadSafe<MidiInputDeviceState> { |
- explicit MidiInputDeviceState(const MidiDeviceInfo& device_info) |
- : device_info(device_info), |
- midi_handle(kInvalidMidiInHandle), |
- port_index(0), |
- port_age(0), |
- start_time_initialized(false) {} |
- |
- const MidiDeviceInfo device_info; |
- HMIDIIN midi_handle; |
- ScopedMIDIHDR midi_header; |
- // Since Win32 multimedia system uses a relative time offset from when |
- // |midiInStart| API is called, we need to record when it is called. |
- base::TimeTicks start_time; |
- // 0-based port index. We will try to reuse the previous port index when the |
- // MIDI device is closed then reopened. |
- uint32_t port_index; |
- // A sequence number which represents how many times |port_index| is reused. |
- // We can remove this field if we decide not to clear unsent events |
- // when the device is disconnected. |
- // See https://github.com/WebAudio/web-midi-api/issues/133 |
- uint64_t port_age; |
- // True if |start_time| is initialized. This field is not used so far, but |
- // kept for the debugging purpose. |
- bool start_time_initialized; |
- |
- private: |
- friend class base::RefCountedThreadSafe<MidiInputDeviceState>; |
- ~MidiInputDeviceState() {} |
-}; |
- |
-struct MidiOutputDeviceState final |
- : base::RefCountedThreadSafe<MidiOutputDeviceState> { |
- explicit MidiOutputDeviceState(const MidiDeviceInfo& device_info) |
- : device_info(device_info), |
- midi_handle(kInvalidMidiOutHandle), |
- port_index(0), |
- port_age(0), |
- closed(false) {} |
- |
- const MidiDeviceInfo device_info; |
- HMIDIOUT midi_handle; |
- // 0-based port index. We will try to reuse the previous port index when the |
- // MIDI device is closed then reopened. |
- uint32_t port_index; |
- // A sequence number which represents how many times |port_index| is reused. |
- // We can remove this field if we decide not to clear unsent events |
- // when the device is disconnected. |
- // See https://github.com/WebAudio/web-midi-api/issues/133 |
- uint64_t port_age; |
- // True if the device is already closed and |midi_handle| is considered to be |
- // invalid. |
- // TODO(toyoshim): Use std::atomic<bool> when it is allowed in Chromium |
- // project. |
- volatile bool closed; |
- |
- private: |
- friend class base::RefCountedThreadSafe<MidiOutputDeviceState>; |
- ~MidiOutputDeviceState() {} |
-}; |
- |
-// The core logic of MIDI device handling for Windows. Basically this class is |
-// shared among following 4 threads: |
-// 1. Chrome IO Thread |
-// 2. OS Multimedia Thread |
-// 3. Task Thread |
-// 4. Sender Thread |
-// |
-// Chrome IO Thread: |
-// MidiManager runs on Chrome IO thread. Device change notification is |
-// delivered to the thread through the SystemMonitor service. |
-// OnDevicesChanged() callback is invoked to update the MIDI device list. |
-// Note that in the current implementation we will try to open all the |
-// existing devices in practice. This is OK because trying to reopen a MIDI |
-// device that is already opened would simply fail, and there is no unwilling |
-// side effect. |
-// |
-// OS Multimedia Thread: |
-// This thread is maintained by the OS as a part of MIDI runtime, and |
-// responsible for receiving all the system initiated events such as device |
-// close, and receiving data. For performance reasons, most of potentially |
-// blocking operations will be dispatched into Task Thread. |
-// |
-// Task Thread: |
-// This thread will be used to call back following methods of MidiManager. |
-// - MidiManager::CompleteInitialization |
-// - MidiManager::AddInputPort |
-// - MidiManager::AddOutputPort |
-// - MidiManager::SetInputPortState |
-// - MidiManager::SetOutputPortState |
-// - MidiManager::ReceiveMidiData |
-// |
-// Sender Thread: |
-// This thread will be used to call Win32 APIs to send MIDI message at the |
-// specified time. We don't want to call MIDI send APIs on Task Thread |
-// because those APIs could be performed synchronously, hence they could block |
-// the caller thread for a while. See the comment in |
-// SendLongMidiMessageInternal for details. Currently we expect that the |
-// blocking time would be less than 1 second. |
-class MidiServiceWinImpl : public MidiServiceWin, |
- public base::SystemMonitor::DevicesChangedObserver { |
- public: |
- MidiServiceWinImpl() |
- : delegate_(nullptr), |
- sender_thread_("Windows MIDI sender thread"), |
- task_thread_("Windows MIDI task thread"), |
- destructor_started(false) {} |
- |
- ~MidiServiceWinImpl() final { |
- // Start() and Stop() of the threads, and AddDevicesChangeObserver() and |
- // RemoveDevicesChangeObserver() should be called on the same thread. |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- destructor_started = true; |
- base::SystemMonitor::Get()->RemoveDevicesChangedObserver(this); |
- { |
- std::vector<HMIDIIN> input_devices; |
- { |
- base::AutoLock auto_lock(input_ports_lock_); |
- for (auto it : input_device_map_) |
- input_devices.push_back(it.first); |
- } |
- { |
- for (auto* handle : input_devices) { |
- MMRESULT result = midiInClose(handle); |
- if (result == MIDIERR_STILLPLAYING) { |
- result = midiInReset(handle); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "midiInReset failed: " << GetInErrorMessage(result); |
- result = midiInClose(handle); |
- } |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "midiInClose failed: " << GetInErrorMessage(result); |
- } |
- } |
- } |
- { |
- std::vector<HMIDIOUT> output_devices; |
- { |
- base::AutoLock auto_lock(output_ports_lock_); |
- for (auto it : output_device_map_) |
- output_devices.push_back(it.first); |
- } |
- { |
- for (auto* handle : output_devices) { |
- MMRESULT result = midiOutClose(handle); |
- if (result == MIDIERR_STILLPLAYING) { |
- result = midiOutReset(handle); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "midiOutReset failed: " << GetOutErrorMessage(result); |
- result = midiOutClose(handle); |
- } |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "midiOutClose failed: " << GetOutErrorMessage(result); |
- } |
- } |
- } |
- sender_thread_.Stop(); |
- task_thread_.Stop(); |
- } |
- |
- // MidiServiceWin overrides: |
- void InitializeAsync(MidiServiceWinDelegate* delegate) final { |
- // Start() and Stop() of the threads, and AddDevicesChangeObserver() and |
- // RemoveDevicesChangeObserver() should be called on the same thread. |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- delegate_ = delegate; |
- |
- sender_thread_.Start(); |
- task_thread_.Start(); |
- |
- // Start monitoring device changes. This should start before the |
- // following UpdateDeviceList() call not to miss the event happening |
- // between the call and the observer registration. |
- base::SystemMonitor::Get()->AddDevicesChangedObserver(this); |
- |
- UpdateDeviceList(); |
- |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::CompleteInitializationOnTaskThread, |
- base::Unretained(this), Result::OK)); |
- } |
- |
- void SendMidiDataAsync(uint32_t port_number, |
- const std::vector<uint8_t>& data, |
- base::TimeTicks time) final { |
- if (destructor_started) { |
- LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " |
- "being destructed. port: " << port_number; |
- return; |
- } |
- auto state = GetOutputDeviceFromPort(port_number); |
- if (!state) { |
- LOG(ERROR) << "ThreadSafeSendData failed due to an invalid port number. " |
- << "port: " << port_number; |
- return; |
- } |
- if (state->closed) { |
- LOG(ERROR) |
- << "ThreadSafeSendData failed because target port is already closed." |
- << "port: " << port_number; |
- return; |
- } |
- const auto now = base::TimeTicks::Now(); |
- if (now < time) { |
- sender_thread_.task_runner()->PostDelayedTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, |
- base::Unretained(this), port_number, |
- state->port_age, data, time), |
- time - now); |
- } else { |
- sender_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::SendOnSenderThread, |
- base::Unretained(this), port_number, |
- state->port_age, data, time)); |
- } |
- } |
- |
- // base::SystemMonitor::DevicesChangedObserver overrides: |
- void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) final { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- if (destructor_started) |
- return; |
- |
- switch (device_type) { |
- case base::SystemMonitor::DEVTYPE_AUDIO: |
- case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE: |
- // Add case of other unrelated device types here. |
- return; |
- case base::SystemMonitor::DEVTYPE_UNKNOWN: |
- // Interested in MIDI devices. Try updating the device list. |
- UpdateDeviceList(); |
- break; |
- // No default here to capture new DeviceType by compile time. |
- } |
- } |
- |
- private: |
- scoped_refptr<MidiInputDeviceState> GetInputDeviceFromHandle( |
- HMIDIIN midi_handle) { |
- base::AutoLock auto_lock(input_ports_lock_); |
- const auto it = input_device_map_.find(midi_handle); |
- return (it != input_device_map_.end() ? it->second : nullptr); |
- } |
- |
- scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromHandle( |
- HMIDIOUT midi_handle) { |
- base::AutoLock auto_lock(output_ports_lock_); |
- const auto it = output_device_map_.find(midi_handle); |
- return (it != output_device_map_.end() ? it->second : nullptr); |
- } |
- |
- scoped_refptr<MidiOutputDeviceState> GetOutputDeviceFromPort( |
- uint32_t port_number) { |
- base::AutoLock auto_lock(output_ports_lock_); |
- if (output_ports_.size() <= port_number) |
- return nullptr; |
- return output_ports_[port_number]; |
- } |
- |
- void UpdateDeviceList() { |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::UpdateDeviceListOnTaskThread, |
- base::Unretained(this))); |
- } |
- |
- ///////////////////////////////////////////////////////////////////////////// |
- // Callbacks on the OS multimedia thread. |
- ///////////////////////////////////////////////////////////////////////////// |
- |
- static void CALLBACK |
- OnMidiInEventOnMainlyMultimediaThread(HMIDIIN midi_in_handle, |
- UINT message, |
- DWORD_PTR instance, |
- DWORD_PTR param1, |
- DWORD_PTR param2) { |
- MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); |
- if (!self) |
- return; |
- switch (message) { |
- case MIM_OPEN: |
- self->OnMidiInOpen(midi_in_handle); |
- break; |
- case MIM_DATA: |
- self->OnMidiInDataOnMultimediaThread(midi_in_handle, param1, param2); |
- break; |
- case MIM_LONGDATA: |
- self->OnMidiInLongDataOnMultimediaThread(midi_in_handle, param1, |
- param2); |
- break; |
- case MIM_CLOSE: |
- self->OnMidiInCloseOnMultimediaThread(midi_in_handle); |
- break; |
- } |
- } |
- |
- void OnMidiInOpen(HMIDIIN midi_in_handle) { |
- UINT device_id = 0; |
- MMRESULT result = midiInGetID(midi_in_handle, &device_id); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "midiInGetID failed: " << GetInErrorMessage(result); |
- return; |
- } |
- MIDIINCAPS2W caps = {}; |
- result = midiInGetDevCaps(device_id, reinterpret_cast<LPMIDIINCAPSW>(&caps), |
- sizeof(caps)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "midiInGetDevCaps failed: " << GetInErrorMessage(result); |
- return; |
- } |
- auto state = |
- make_scoped_refptr(new MidiInputDeviceState(MidiDeviceInfo(caps))); |
- state->midi_handle = midi_in_handle; |
- state->midi_header = CreateMIDIHDR(kBufferLength); |
- const auto& state_device_info = state->device_info; |
- bool add_new_port = false; |
- uint32_t port_number = 0; |
- { |
- base::AutoLock auto_lock(input_ports_lock_); |
- const auto it = unused_input_ports_.find(state_device_info); |
- if (it == unused_input_ports_.end()) { |
- port_number = static_cast<uint32_t>(input_ports_.size()); |
- add_new_port = true; |
- input_ports_.push_back(nullptr); |
- input_ports_ages_.push_back(0); |
- } else { |
- port_number = it->second.top(); |
- it->second.pop(); |
- if (it->second.empty()) { |
- unused_input_ports_.erase(it); |
- } |
- } |
- input_ports_[port_number] = state; |
- |
- input_ports_ages_[port_number] += 1; |
- input_device_map_[input_ports_[port_number]->midi_handle] = |
- input_ports_[port_number]; |
- input_ports_[port_number]->port_index = port_number; |
- input_ports_[port_number]->port_age = input_ports_ages_[port_number]; |
- } |
- // Several initial startup tasks cannot be done in MIM_OPEN handler. |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::StartInputDeviceOnTaskThread, |
- base::Unretained(this), midi_in_handle)); |
- if (add_new_port) { |
- const MidiPortInfo port_info( |
- // TODO(toyoshim): Use a hash ID insted crbug.com/467448 |
- base::IntToString(static_cast<int>(port_number)), |
- GetManufacturerName(state_device_info), |
- base::WideToUTF8(state_device_info.product_name), |
- MmversionToString(state_device_info.driver_version), |
- PortState::OPENED); |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::AddInputPortOnTaskThread, |
- base::Unretained(this), port_info)); |
- } else { |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, |
- base::Unretained(this), port_number, |
- PortState::CONNECTED)); |
- } |
- } |
- |
- void OnMidiInDataOnMultimediaThread(HMIDIIN midi_in_handle, |
- DWORD_PTR param1, |
- DWORD_PTR param2) { |
- auto state = GetInputDeviceFromHandle(midi_in_handle); |
- if (!state) |
- return; |
- const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff); |
- const uint8_t first_data_byte = static_cast<uint8_t>((param1 >> 8) & 0xff); |
- const uint8_t second_data_byte = |
- static_cast<uint8_t>((param1 >> 16) & 0xff); |
- const DWORD elapsed_ms = param2; |
- const size_t len = GetMessageLength(status_byte); |
- const uint8_t kData[] = {status_byte, first_data_byte, second_data_byte}; |
- std::vector<uint8_t> data; |
- data.assign(kData, kData + len); |
- DCHECK_LE(len, arraysize(kData)); |
- // MIM_DATA/MIM_LONGDATA message treats the time when midiInStart() is |
- // called as the origin of |elapsed_ms|. |
- // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757284.aspx |
- // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757286.aspx |
- const base::TimeTicks event_time = |
- state->start_time + base::TimeDelta::FromMilliseconds(elapsed_ms); |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::ReceiveMidiDataOnTaskThread, |
- base::Unretained(this), state->port_index, data, |
- event_time)); |
- } |
- |
- void OnMidiInLongDataOnMultimediaThread(HMIDIIN midi_in_handle, |
- DWORD_PTR param1, |
- DWORD_PTR param2) { |
- auto state = GetInputDeviceFromHandle(midi_in_handle); |
- if (!state) |
- return; |
- MIDIHDR* header = reinterpret_cast<MIDIHDR*>(param1); |
- const DWORD elapsed_ms = param2; |
- MMRESULT result = MMSYSERR_NOERROR; |
- if (destructor_started) { |
- if (state->midi_header && |
- (state->midi_header->dwFlags & MHDR_PREPARED) == MHDR_PREPARED) { |
- result = |
- midiInUnprepareHeader(state->midi_handle, state->midi_header.get(), |
- sizeof(*state->midi_header)); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "Failed to uninitialize input buffer: " |
- << GetInErrorMessage(result); |
- } |
- return; |
- } |
- if (header->dwBytesRecorded > 0) { |
- const uint8_t* src = reinterpret_cast<const uint8_t*>(header->lpData); |
- std::vector<uint8_t> data; |
- data.assign(src, src + header->dwBytesRecorded); |
- // MIM_DATA/MIM_LONGDATA message treats the time when midiInStart() is |
- // called as the origin of |elapsed_ms|. |
- // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757284.aspx |
- // http://msdn.microsoft.com/en-us/library/windows/desktop/dd757286.aspx |
- const base::TimeTicks event_time = |
- state->start_time + base::TimeDelta::FromMilliseconds(elapsed_ms); |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::ReceiveMidiDataOnTaskThread, |
- base::Unretained(this), state->port_index, data, |
- event_time)); |
- } |
- result = midiInAddBuffer(state->midi_handle, header, sizeof(*header)); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "Failed to attach input buffer: " << GetInErrorMessage(result) |
- << "port number:" << state->port_index; |
- } |
- |
- void OnMidiInCloseOnMultimediaThread(HMIDIIN midi_in_handle) { |
- auto state = GetInputDeviceFromHandle(midi_in_handle); |
- if (!state) |
- return; |
- const uint32_t port_number = state->port_index; |
- const auto device_info(state->device_info); |
- { |
- base::AutoLock auto_lock(input_ports_lock_); |
- input_device_map_.erase(state->midi_handle); |
- input_ports_[port_number] = nullptr; |
- input_ports_ages_[port_number] += 1; |
- unused_input_ports_[device_info].push(port_number); |
- } |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::SetInputPortStateOnTaskThread, |
- base::Unretained(this), port_number, |
- PortState::DISCONNECTED)); |
- } |
- |
- static void CALLBACK |
- OnMidiOutEventOnMainlyMultimediaThread(HMIDIOUT midi_out_handle, |
- UINT message, |
- DWORD_PTR instance, |
- DWORD_PTR param1, |
- DWORD_PTR param2) { |
- MidiServiceWinImpl* self = reinterpret_cast<MidiServiceWinImpl*>(instance); |
- if (!self) |
- return; |
- switch (message) { |
- case MOM_OPEN: |
- self->OnMidiOutOpen(midi_out_handle, param1, param2); |
- break; |
- case MOM_DONE: |
- self->OnMidiOutDoneOnMultimediaThread(midi_out_handle, param1); |
- break; |
- case MOM_CLOSE: |
- self->OnMidiOutCloseOnMultimediaThread(midi_out_handle); |
- break; |
- } |
- } |
- |
- void OnMidiOutOpen(HMIDIOUT midi_out_handle, |
- DWORD_PTR param1, |
- DWORD_PTR param2) { |
- UINT device_id = 0; |
- MMRESULT result = midiOutGetID(midi_out_handle, &device_id); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "midiOutGetID failed: " << GetOutErrorMessage(result); |
- return; |
- } |
- MIDIOUTCAPS2W caps = {}; |
- result = midiOutGetDevCaps( |
- device_id, reinterpret_cast<LPMIDIOUTCAPSW>(&caps), sizeof(caps)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "midiInGetDevCaps failed: " << GetOutErrorMessage(result); |
- return; |
- } |
- auto state = |
- make_scoped_refptr(new MidiOutputDeviceState(MidiDeviceInfo(caps))); |
- state->midi_handle = midi_out_handle; |
- const auto& state_device_info = state->device_info; |
- if (IsUnsupportedDevice(state_device_info)) { |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&CloseOutputPortOnTaskThread, midi_out_handle)); |
- return; |
- } |
- bool add_new_port = false; |
- uint32_t port_number = 0; |
- { |
- base::AutoLock auto_lock(output_ports_lock_); |
- const auto it = unused_output_ports_.find(state_device_info); |
- if (it == unused_output_ports_.end()) { |
- port_number = static_cast<uint32_t>(output_ports_.size()); |
- add_new_port = true; |
- output_ports_.push_back(nullptr); |
- output_ports_ages_.push_back(0); |
- } else { |
- port_number = it->second.top(); |
- it->second.pop(); |
- if (it->second.empty()) |
- unused_output_ports_.erase(it); |
- } |
- output_ports_[port_number] = state; |
- output_ports_ages_[port_number] += 1; |
- output_device_map_[output_ports_[port_number]->midi_handle] = |
- output_ports_[port_number]; |
- output_ports_[port_number]->port_index = port_number; |
- output_ports_[port_number]->port_age = output_ports_ages_[port_number]; |
- } |
- if (add_new_port) { |
- const MidiPortInfo port_info( |
- // TODO(toyoshim): Use a hash ID insted. crbug.com/467448 |
- base::IntToString(static_cast<int>(port_number)), |
- GetManufacturerName(state_device_info), |
- base::WideToUTF8(state_device_info.product_name), |
- MmversionToString(state_device_info.driver_version), |
- PortState::OPENED); |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, base::Bind(&MidiServiceWinImpl::AddOutputPortOnTaskThread, |
- base::Unretained(this), port_info)); |
- } else { |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, |
- base::Unretained(this), port_number, |
- PortState::CONNECTED)); |
- } |
- } |
- |
- void OnMidiOutDoneOnMultimediaThread(HMIDIOUT midi_out_handle, |
- DWORD_PTR param1) { |
- auto state = GetOutputDeviceFromHandle(midi_out_handle); |
- if (!state) |
- return; |
- // Take ownership of the MIDIHDR object. |
- ScopedMIDIHDR header(reinterpret_cast<MIDIHDR*>(param1)); |
- if (!header) |
- return; |
- MMRESULT result = midiOutUnprepareHeader(state->midi_handle, header.get(), |
- sizeof(*header)); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR) |
- << "Failed to uninitialize output buffer: " |
- << GetOutErrorMessage(result); |
- } |
- |
- void OnMidiOutCloseOnMultimediaThread(HMIDIOUT midi_out_handle) { |
- auto state = GetOutputDeviceFromHandle(midi_out_handle); |
- if (!state) |
- return; |
- const uint32_t port_number = state->port_index; |
- const auto device_info(state->device_info); |
- { |
- base::AutoLock auto_lock(output_ports_lock_); |
- output_device_map_.erase(state->midi_handle); |
- output_ports_[port_number] = nullptr; |
- output_ports_ages_[port_number] += 1; |
- unused_output_ports_[device_info].push(port_number); |
- state->closed = true; |
- } |
- task_thread_.task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind(&MidiServiceWinImpl::SetOutputPortStateOnTaskThread, |
- base::Unretained(this), port_number, |
- PortState::DISCONNECTED)); |
- } |
- |
- ///////////////////////////////////////////////////////////////////////////// |
- // Callbacks on the sender thread. |
- ///////////////////////////////////////////////////////////////////////////// |
- |
- void AssertOnSenderThread() { |
- DCHECK_EQ(sender_thread_.GetThreadId(), base::PlatformThread::CurrentId()); |
- } |
- |
- void SendOnSenderThread(uint32_t port_number, |
- uint64_t port_age, |
- const std::vector<uint8_t>& data, |
- base::TimeTicks time) { |
- AssertOnSenderThread(); |
- if (destructor_started) { |
- LOG(ERROR) << "ThreadSafeSendData failed because MidiServiceWinImpl is " |
- "being destructed. port: " << port_number; |
- } |
- auto state = GetOutputDeviceFromPort(port_number); |
- if (!state) { |
- LOG(ERROR) << "ThreadSafeSendData failed due to an invalid port number. " |
- << "port: " << port_number; |
- return; |
- } |
- if (state->closed) { |
- LOG(ERROR) |
- << "ThreadSafeSendData failed because target port is already closed." |
- << "port: " << port_number; |
- return; |
- } |
- if (state->port_age != port_age) { |
- LOG(ERROR) |
- << "ThreadSafeSendData failed because target port is being closed." |
- << "port: " << port_number << "expected port age: " << port_age |
- << "actual port age: " << state->port_age; |
- } |
- |
- // MIDI Running status must be filtered out. |
- MidiMessageQueue message_queue(false); |
- message_queue.Add(data); |
- std::vector<uint8_t> message; |
- while (true) { |
- if (destructor_started) |
- break; |
- if (state->closed) |
- break; |
- message_queue.Get(&message); |
- if (message.empty()) |
- break; |
- // SendShortMidiMessageInternal can send a MIDI message up to 3 bytes. |
- if (message.size() <= 3) |
- SendShortMidiMessageInternal(state->midi_handle, message); |
- else |
- SendLongMidiMessageInternal(state->midi_handle, message); |
- } |
- } |
- |
- ///////////////////////////////////////////////////////////////////////////// |
- // Callbacks on the task thread. |
- ///////////////////////////////////////////////////////////////////////////// |
- |
- void AssertOnTaskThread() { |
- DCHECK_EQ(task_thread_.GetThreadId(), base::PlatformThread::CurrentId()); |
- } |
- |
- void UpdateDeviceListOnTaskThread() { |
- AssertOnTaskThread(); |
- const UINT num_in_devices = midiInGetNumDevs(); |
- for (UINT device_id = 0; device_id < num_in_devices; ++device_id) { |
- // Here we use |CALLBACK_FUNCTION| to subscribe MIM_DATA, MIM_LONGDATA, |
- // MIM_OPEN, and MIM_CLOSE events. |
- // - MIM_DATA: This is the only way to get a short MIDI message with |
- // timestamp information. |
- // - MIM_LONGDATA: This is the only way to get a long MIDI message with |
- // timestamp information. |
- // - MIM_OPEN: This event is sent the input device is opened. Note that |
- // this message is called on the caller thread. |
- // - MIM_CLOSE: This event is sent when 1) midiInClose() is called, or 2) |
- // the MIDI device becomes unavailable for some reasons, e.g., the |
- // cable is disconnected. As for the former case, HMIDIOUT will be |
- // invalidated soon after the callback is finished. As for the later |
- // case, however, HMIDIOUT continues to be valid until midiInClose() |
- // is called. |
- HMIDIIN midi_handle = kInvalidMidiInHandle; |
- const MMRESULT result = midiInOpen( |
- &midi_handle, device_id, |
- reinterpret_cast<DWORD_PTR>(&OnMidiInEventOnMainlyMultimediaThread), |
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) |
- << "Failed to open output device. " |
- << " id: " << device_id << " message: " << GetInErrorMessage(result); |
- } |
- |
- const UINT num_out_devices = midiOutGetNumDevs(); |
- for (UINT device_id = 0; device_id < num_out_devices; ++device_id) { |
- // Here we use |CALLBACK_FUNCTION| to subscribe MOM_DONE, MOM_OPEN, and |
- // MOM_CLOSE events. |
- // - MOM_DONE: SendLongMidiMessageInternal() relies on this event to clean |
- // up the backing store where a long MIDI message is stored. |
- // - MOM_OPEN: This event is sent the output device is opened. Note that |
- // this message is called on the caller thread. |
- // - MOM_CLOSE: This event is sent when 1) midiOutClose() is called, or 2) |
- // the MIDI device becomes unavailable for some reasons, e.g., the |
- // cable is disconnected. As for the former case, HMIDIOUT will be |
- // invalidated soon after the callback is finished. As for the later |
- // case, however, HMIDIOUT continues to be valid until midiOutClose() |
- // is called. |
- HMIDIOUT midi_handle = kInvalidMidiOutHandle; |
- const MMRESULT result = midiOutOpen( |
- &midi_handle, device_id, |
- reinterpret_cast<DWORD_PTR>(&OnMidiOutEventOnMainlyMultimediaThread), |
- reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION); |
- DLOG_IF(ERROR, result != MMSYSERR_NOERROR && result != MMSYSERR_ALLOCATED) |
- << "Failed to open output device. " |
- << " id: " << device_id << " message: " << GetOutErrorMessage(result); |
- } |
- } |
- |
- void StartInputDeviceOnTaskThread(HMIDIIN midi_in_handle) { |
- AssertOnTaskThread(); |
- auto state = GetInputDeviceFromHandle(midi_in_handle); |
- if (!state) |
- return; |
- MMRESULT result = |
- midiInPrepareHeader(state->midi_handle, state->midi_header.get(), |
- sizeof(*state->midi_header)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to initialize input buffer: " |
- << GetInErrorMessage(result); |
- return; |
- } |
- result = midiInAddBuffer(state->midi_handle, state->midi_header.get(), |
- sizeof(*state->midi_header)); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to attach input buffer: " |
- << GetInErrorMessage(result); |
- return; |
- } |
- result = midiInStart(state->midi_handle); |
- if (result != MMSYSERR_NOERROR) { |
- DLOG(ERROR) << "Failed to start input port: " |
- << GetInErrorMessage(result); |
- return; |
- } |
- state->start_time = base::TimeTicks::Now(); |
- state->start_time_initialized = true; |
- } |
- |
- void CompleteInitializationOnTaskThread(Result result) { |
- AssertOnTaskThread(); |
- delegate_->OnCompleteInitialization(result); |
- } |
- |
- void ReceiveMidiDataOnTaskThread(uint32_t port_index, |
- std::vector<uint8_t> data, |
- base::TimeTicks time) { |
- AssertOnTaskThread(); |
- delegate_->OnReceiveMidiData(port_index, data, time); |
- } |
- |
- void AddInputPortOnTaskThread(MidiPortInfo info) { |
- AssertOnTaskThread(); |
- delegate_->OnAddInputPort(info); |
- } |
- |
- void AddOutputPortOnTaskThread(MidiPortInfo info) { |
- AssertOnTaskThread(); |
- delegate_->OnAddOutputPort(info); |
- } |
- |
- void SetInputPortStateOnTaskThread(uint32_t port_index, PortState state) { |
- AssertOnTaskThread(); |
- delegate_->OnSetInputPortState(port_index, state); |
- } |
- |
- void SetOutputPortStateOnTaskThread(uint32_t port_index, PortState state) { |
- AssertOnTaskThread(); |
- delegate_->OnSetOutputPortState(port_index, state); |
- } |
- |
- ///////////////////////////////////////////////////////////////////////////// |
- // Fields: |
- ///////////////////////////////////////////////////////////////////////////// |
- |
- // Does not take ownership. |
- MidiServiceWinDelegate* delegate_; |
- |
- base::ThreadChecker thread_checker_; |
- |
- base::Thread sender_thread_; |
- base::Thread task_thread_; |
- |
- base::Lock input_ports_lock_; |
- base::hash_map<HMIDIIN, scoped_refptr<MidiInputDeviceState>> |
- input_device_map_; // GUARDED_BY(input_ports_lock_) |
- PortNumberCache unused_input_ports_; // GUARDED_BY(input_ports_lock_) |
- std::vector<scoped_refptr<MidiInputDeviceState>> |
- input_ports_; // GUARDED_BY(input_ports_lock_) |
- std::vector<uint64_t> input_ports_ages_; // GUARDED_BY(input_ports_lock_) |
- |
- base::Lock output_ports_lock_; |
- base::hash_map<HMIDIOUT, scoped_refptr<MidiOutputDeviceState>> |
- output_device_map_; // GUARDED_BY(output_ports_lock_) |
- PortNumberCache unused_output_ports_; // GUARDED_BY(output_ports_lock_) |
- std::vector<scoped_refptr<MidiOutputDeviceState>> |
- output_ports_; // GUARDED_BY(output_ports_lock_) |
- std::vector<uint64_t> output_ports_ages_; // GUARDED_BY(output_ports_lock_) |
- |
- // True if one thread reached MidiServiceWinImpl::~MidiServiceWinImpl(). Note |
- // that MidiServiceWinImpl::~MidiServiceWinImpl() is blocked until |
- // |sender_thread_|, and |task_thread_| are stopped. |
- // This flag can be used as the signal that when background tasks must be |
- // interrupted. |
- // TODO(toyoshim): Use std::atomic<bool> when it is allowed. |
- volatile bool destructor_started; |
- |
- DISALLOW_COPY_AND_ASSIGN(MidiServiceWinImpl); |
-}; |
- |
-} // namespace |
- |
-MidiManagerWin::MidiManagerWin(MidiService* service) : MidiManager(service) {} |
- |
-MidiManagerWin::~MidiManagerWin() { |
-} |
- |
-void MidiManagerWin::StartInitialization() { |
- midi_service_.reset(new MidiServiceWinImpl); |
- // Note that |CompleteInitialization()| will be called from the callback. |
- midi_service_->InitializeAsync(this); |
-} |
- |
-void MidiManagerWin::Finalize() { |
- midi_service_.reset(); |
-} |
- |
-void MidiManagerWin::DispatchSendMidiData(MidiManagerClient* client, |
- uint32_t port_index, |
- const std::vector<uint8_t>& data, |
- double timestamp) { |
- if (!midi_service_) |
- return; |
- |
- base::TimeTicks time_to_send = base::TimeTicks::Now(); |
- if (timestamp != 0.0) { |
- time_to_send = |
- base::TimeTicks() + base::TimeDelta::FromMicroseconds( |
- timestamp * base::Time::kMicrosecondsPerSecond); |
- } |
- midi_service_->SendMidiDataAsync(port_index, data, time_to_send); |
- |
- // TOOD(toyoshim): This calculation should be done when the date is actually |
- // sent. |
- client->AccumulateMidiBytesSent(data.size()); |
-} |
- |
-void MidiManagerWin::OnCompleteInitialization(Result result) { |
- CompleteInitialization(result); |
-} |
- |
-void MidiManagerWin::OnAddInputPort(MidiPortInfo info) { |
- AddInputPort(info); |
-} |
- |
-void MidiManagerWin::OnAddOutputPort(MidiPortInfo info) { |
- AddOutputPort(info); |
-} |
- |
-void MidiManagerWin::OnSetInputPortState(uint32_t port_index, PortState state) { |
- SetInputPortState(port_index, state); |
-} |
- |
-void MidiManagerWin::OnSetOutputPortState(uint32_t port_index, |
- PortState state) { |
- SetOutputPortState(port_index, state); |
-} |
- |
-void MidiManagerWin::OnReceiveMidiData(uint32_t port_index, |
- const std::vector<uint8_t>& data, |
- base::TimeTicks time) { |
- ReceiveMidiData(port_index, &data[0], data.size(), time); |
-} |
- |
-MidiManager* MidiManager::Create(MidiService* service) { |
- if (base::FeatureList::IsEnabled(features::kMidiManagerWinrt) && |
- base::win::GetVersion() >= base::win::VERSION_WIN10) |
- return new MidiManagerWinrt(service); |
- if (base::FeatureList::IsEnabled(features::kMidiManagerDynamicInstantiation)) |
- return new DynamicallyInitializedMidiManagerWin(service); |
- return new MidiManagerWin(service); |
-} |
- |
-} // namespace midi |