Chromium Code Reviews| Index: media/midi/dynamically_initialized_midi_manager_win.cc |
| diff --git a/media/midi/dynamically_initialized_midi_manager_win.cc b/media/midi/dynamically_initialized_midi_manager_win.cc |
| index 7439b19ed777f8d781e1d7828209a517d3d07d96..88af3810f57baea92b99e3d638e36f94d20517f5 100644 |
| --- a/media/midi/dynamically_initialized_midi_manager_win.cc |
| +++ b/media/midi/dynamically_initialized_midi_manager_win.cc |
| @@ -39,7 +39,7 @@ class DynamicallyInitializedMidiManagerWin::PortManager { |
| void UnregisterInHandle(HMIDIIN handle); |
| // Finds HMIDIIN handle and fullfil |out_index| with the port index. |
| - bool FindHandle(HMIDIIN hmi, size_t* out_index); |
| + bool FindInHandle(HMIDIIN hmi, size_t* out_index); |
| // Restores used input buffer for the next data receive. |
| void RestoreInBuffer(size_t index); |
| @@ -55,6 +55,13 @@ class DynamicallyInitializedMidiManagerWin::PortManager { |
| DWORD_PTR param1, |
| DWORD_PTR param2); |
| + // Handles MIDI output port callbacks that runs on a system provided thread. |
| + static void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, |
| + UINT msg, |
| + DWORD_PTR instance, |
| + DWORD_PTR param1, |
| + DWORD_PTR param2); |
| + |
| private: |
| // Holds all MIDI input or output ports connected once. |
| std::vector<std::unique_ptr<InPort>> input_ports_; |
| @@ -70,6 +77,20 @@ namespace { |
| constexpr HMIDIIN kInvalidInHandle = nullptr; |
| constexpr HMIDIOUT kInvalidOutHandle = nullptr; |
| +// Defines SysEx message size limit. |
| +// TODO(crbug.com/383578): This restriction should be removed once Web MIDI |
| +// defines a standardized way to handle large sysex messages. |
| +// 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(toyoshim): Consider to use linked small buffers so that midiOutReset() |
| +// can abort sending unhandled following buffers. |
| +constexpr size_t kSysExSizeLimit = 256 * 1024; |
| + |
| // Defines input buffer size. |
| constexpr size_t kBufferLength = 32 * 1024; |
| @@ -161,18 +182,12 @@ void FinalizeInPort(HMIDIIN handle, LPMIDIHDR lphdr) { |
| } |
| void FinalizeOutPort(HMIDIOUT handle) { |
| + // Resets inflight buffers. This will cancel sending data that system |
| + // holds and were not sent yet. |
| + midiOutReset(handle); |
| midiOutClose(handle); |
| } |
| -// Handles MIDI output port callbacks that runs on a system provided thread. |
| -void CALLBACK HandleMidiOutCallback(HMIDIOUT hmo, |
| - UINT msg, |
| - DWORD_PTR instance, |
| - DWORD_PTR param1, |
| - DWORD_PTR param2) { |
| - // TODO(toyoshim): Following patches will implement actual functions. |
| -} |
| - |
| // All instances of Port subclasses are always accessed behind a lock of |
| // *GetTaskLock(). Port and subclasses implementation do not need to |
| // consider thread safety. |
| @@ -259,7 +274,6 @@ class Port { |
| } // namespace |
| -// TODO(toyoshim): Following patches will implement actual functions. |
| class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| public: |
| InPort(DynamicallyInitializedMidiManagerWin* manager, |
| @@ -377,7 +391,6 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| int instance_id_; |
| }; |
| -// TODO(toyoshim): Following patches will implement actual functions. |
| class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| public: |
| OutPort(UINT device_id, const MIDIOUTCAPS2W& caps) |
| @@ -426,6 +439,36 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| base::Unretained(manager), info_)); |
| } |
| + void Send(const std::vector<uint8_t>& data) { |
| + if (out_handle_ == kInvalidOutHandle) |
| + return; |
| + |
| + if (data.size() <= 3) { |
| + DWORD message = 0; |
|
yhirano
2017/03/01 01:48:49
[optional] I would prefer defining |message| as a
Takashi Toyoshima
2017/03/01 03:38:02
changed to uint32_t.
cast is not needed to pass it
|
| + for (size_t i = 0; i < data.size(); ++i) |
| + message |= (static_cast<uint32_t>(data[i]) << (i * 8)); |
| + midiOutShortMsg(out_handle_, message); |
| + } else { |
| + if (data.size() > kSysExSizeLimit) { |
| + LOG(ERROR) << "Ignoring SysEx message due to the size limit" |
| + << ", size = " << data.size(); |
| + // TODO(toyoshim): Consider to report metrics here. |
| + return; |
| + } |
| + ScopedMIDIHDR hdr(CreateMIDIHDR(data)); |
| + MMRESULT result = |
| + midiOutPrepareHeader(out_handle_, hdr.get(), sizeof(*hdr)); |
| + if (result != MMSYSERR_NOERROR) |
| + return; |
| + result = midiOutLongMsg(out_handle_, hdr.get(), sizeof(*hdr)); |
| + if (result != MMSYSERR_NOERROR) |
| + midiOutUnprepareHeader(out_handle_, hdr.get(), sizeof(*hdr)); |
| + else |
| + ignore_result(hdr.release()); |
| + // MIDIHDR will be released on MOM_DONE. |
| + } |
| + } |
| + |
| // Port overrides: |
| bool Connect() override { |
| // Until |software| option is supported, disable Microsoft GS Wavetable |
| @@ -449,10 +492,10 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| } |
| void Open() override { |
| - MMRESULT result = |
| - midiOutOpen(&out_handle_, device_id_, |
| - reinterpret_cast<DWORD_PTR>(&HandleMidiOutCallback), 0, |
| - CALLBACK_FUNCTION); |
| + MMRESULT result = midiOutOpen( |
| + &out_handle_, device_id_, |
| + reinterpret_cast<DWORD_PTR>(&PortManager::HandleMidiOutCallback), 0, |
| + CALLBACK_FUNCTION); |
| if (result == MMSYSERR_NOERROR) { |
| Port::Open(); |
| } else { |
| @@ -487,7 +530,7 @@ void DynamicallyInitializedMidiManagerWin::PortManager::UnregisterInHandle( |
| hmidiin_to_index_map_.erase(handle); |
| } |
| -bool DynamicallyInitializedMidiManagerWin::PortManager::FindHandle( |
| +bool DynamicallyInitializedMidiManagerWin::PortManager::FindInHandle( |
| HMIDIIN hmi, |
| size_t* out_index) { |
| GetTaskLock()->AssertAcquired(); |
| @@ -528,7 +571,7 @@ DynamicallyInitializedMidiManagerWin::PortManager::HandleMidiInCallback( |
| } |
| size_t index; |
| - if (!manager->port_manager()->FindHandle(hmi, &index)) |
| + if (!manager->port_manager()->FindInHandle(hmi, &index)) |
| return; |
| DCHECK(msg == MIM_DATA || msg == MIM_LONGDATA); |
| @@ -563,6 +606,26 @@ DynamicallyInitializedMidiManagerWin::PortManager::HandleMidiInCallback( |
| } |
| } |
| +void CALLBACK |
| +DynamicallyInitializedMidiManagerWin::PortManager::HandleMidiOutCallback( |
| + HMIDIOUT hmo, |
| + UINT msg, |
| + DWORD_PTR instance, |
| + DWORD_PTR param1, |
| + DWORD_PTR param2) { |
| + if (msg == MOM_DONE) { |
| + ScopedMIDIHDR hdr(reinterpret_cast<LPMIDIHDR>(param1)); |
| + if (!hdr) |
| + return; |
| + // TODO(toyoshim): Call midiOutUnprepareHeader outside the callback. |
| + // Since this callback may be invoked after the manager is destructed, |
| + // and can not send a task to the TaskRunner in such case, we need to |
| + // consider to track MIDIHDR per port, and clean it in port finalization |
| + // steps, too. |
| + midiOutUnprepareHeader(hmo, hdr.get(), sizeof(*hdr)); |
| + } |
| +} |
| + |
| DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin( |
| MidiService* service) |
| : MidiManager(service), |
| @@ -633,7 +696,21 @@ void DynamicallyInitializedMidiManagerWin::DispatchSendMidiData( |
| uint32_t port_index, |
| const std::vector<uint8_t>& data, |
| double timestamp) { |
| - // TODO(toyoshim): Following patches will implement. |
| + if (timestamp != 0.0) { |
| + base::TimeTicks time = base::TimeTicks() + |
| + base::TimeDelta::FromMicroseconds( |
| + timestamp * base::Time::kMicrosecondsPerSecond); |
| + base::TimeTicks now = base::TimeTicks::Now(); |
| + if (now < time) { |
| + PostDelayedTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::SendOnTaskRunner, |
| + base::Unretained(this), client, port_index, data), |
| + time - now); |
| + return; |
| + } |
| + } |
| + PostTask(base::Bind(&DynamicallyInitializedMidiManagerWin::SendOnTaskRunner, |
| + base::Unretained(this), client, port_index, data)); |
| } |
| void DynamicallyInitializedMidiManagerWin::OnDevicesChanged( |
| @@ -673,6 +750,15 @@ void DynamicallyInitializedMidiManagerWin::PostReplyTask( |
| thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task)); |
| } |
| +void DynamicallyInitializedMidiManagerWin::PostDelayedTask( |
| + const base::Closure& task, |
| + base::TimeDelta delay) { |
| + service() |
| + ->GetTaskRunner(kTaskRunner) |
| + ->PostDelayedTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task), |
| + delay); |
| +} |
| + |
| void DynamicallyInitializedMidiManagerWin::InitializeOnTaskRunner() { |
| UpdateDeviceListOnTaskRunner(); |
| PostReplyTask( |
| @@ -730,4 +816,16 @@ void DynamicallyInitializedMidiManagerWin::ReflectActiveDeviceList( |
| } |
| } |
| +void DynamicallyInitializedMidiManagerWin::SendOnTaskRunner( |
| + MidiManagerClient* client, |
| + uint32_t port_index, |
| + const std::vector<uint8_t>& data) { |
| + CHECK_GT(port_manager_->outputs()->size(), port_index); |
| + (*port_manager_->outputs())[port_index]->Send(data); |
| + // |client| will be checked inside MidiManager::AccumulateMidiBytesSent. |
| + PostReplyTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::AccumulateMidiBytesSent, |
| + base::Unretained(this), client, data.size())); |
| +} |
| + |
| } // namespace midi |