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 26a4c835e181e62691f21b8853a0c7fa84fa9c44..2cb1fc1d2ad435f353c4904442423794d51f278b 100644 |
| --- a/media/midi/dynamically_initialized_midi_manager_win.cc |
| +++ b/media/midi/dynamically_initialized_midi_manager_win.cc |
| @@ -26,6 +26,10 @@ namespace midi { |
| namespace { |
| +// Assumes that nullptr represents an invalid MIDI handle. |
| +constexpr HMIDIIN kInvalidInHandle = nullptr; |
| +constexpr HMIDIOUT kInvalidOutHandle = nullptr; |
| + |
| // Global variables to identify MidiManager instance. |
| constexpr int kInvalidInstanceId = -1; |
| int g_active_instance_id = kInvalidInstanceId; |
| @@ -69,6 +73,33 @@ void RunTask(int instance_id, const base::Closure& task) { |
| // TODO(toyoshim): Factor out TaskRunner related functionaliries above, and |
| // deprecate MidiScheduler. It should be available via MidiManager::scheduler(). |
| +// Helper functions to close MIDI device handles on TaskRunner asynchronously. |
| +void FinalizeInPort(HMIDIIN handle) { |
| + midiInClose(handle); |
| +} |
| + |
| +void FinalizeOutPort(HMIDIOUT handle) { |
| + midiOutClose(handle); |
| +} |
| + |
| +// Handles MIDI input port callbacks that runs on a system provided thread. |
| +void CALLBACK HandleMidiInCallback(HMIDIIN hmi, |
| + UINT msg, |
| + DWORD_PTR instance, |
| + DWORD_PTR param1, |
| + DWORD_PTR param2) { |
| + // TODO(toyoshim): Following patches will implement actual functions. |
| +} |
| + |
| +// 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. |
| +} |
| + |
| class Port { |
| public: |
| Port(const std::string& type, |
| @@ -162,7 +193,8 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| caps.wPid, |
| caps.vDriverVersion, |
| base::WideToUTF8( |
| - base::string16(caps.szPname, wcslen(caps.szPname)))) {} |
| + base::string16(caps.szPname, wcslen(caps.szPname)))), |
| + in_handle_(kInvalidInHandle) {} |
| static std::vector<std::unique_ptr<InPort>> EnumerateActivePorts() { |
| std::vector<std::unique_ptr<InPort>> ports; |
| @@ -180,6 +212,12 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| return ports; |
| } |
| + void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) { |
|
Takashi Toyoshima
2017/02/22 10:00:23
Finalize() runs on the I/O thread, and others run
Takashi Toyoshima
2017/02/23 02:42:45
Ah, no. Now I remember that this was intentional.
Takashi Toyoshima
2017/02/23 03:01:42
https://codereview.chromium.org/2686043003/diff/14
|
| + if (in_handle_ != kInvalidInHandle) { |
| + runner->PostTask(FROM_HERE, base::Bind(&FinalizeInPort, in_handle_)); |
| + } |
| + } |
| + |
| void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) { |
| manager->PostReplyTask( |
| base::Bind(&DynamicallyInitializedMidiManagerWin::SetInputPortState, |
| @@ -191,6 +229,32 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port { |
| base::Bind(&DynamicallyInitializedMidiManagerWin::AddInputPort, |
| base::Unretained(manager), info_)); |
| } |
| + |
| + // Port overrides: |
| + bool Disconnect() override { |
| + if (in_handle_ != kInvalidInHandle) { |
| + // Following API call may fail because device was already disconnected. |
| + // But just in case. |
| + midiInClose(in_handle_); |
| + in_handle_ = kInvalidInHandle; |
| + } |
| + return Port::Disconnect(); |
| + } |
| + |
| + void Open() override { |
| + // TODO(toyoshim): Pass instance_id to implement HandleMidiInCallback. |
| + MMRESULT result = |
| + midiInOpen(&in_handle_, device_id_, |
| + reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback), 0, |
| + CALLBACK_FUNCTION); |
| + if (result == MMSYSERR_NOERROR) |
| + Port::Open(); |
| + else |
| + Disconnect(); |
| + } |
| + |
| + private: |
| + HMIDIIN in_handle_; |
| }; |
| // TODO(toyoshim): Following patches will implement actual functions. |
| @@ -204,7 +268,8 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| caps.vDriverVersion, |
| base::WideToUTF8( |
| base::string16(caps.szPname, wcslen(caps.szPname)))), |
| - software_(caps.wTechnology == MOD_SWSYNTH) {} |
| + software_(caps.wTechnology == MOD_SWSYNTH), |
| + out_handle_(kInvalidOutHandle) {} |
| static std::vector<std::unique_ptr<OutPort>> EnumerateActivePorts() { |
| std::vector<std::unique_ptr<OutPort>> ports; |
| @@ -222,6 +287,23 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| return ports; |
| } |
| + void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) { |
| + if (out_handle_ != kInvalidOutHandle) |
| + runner->PostTask(FROM_HERE, base::Bind(&FinalizeOutPort, out_handle_)); |
| + } |
| + |
| + void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) { |
| + manager->PostReplyTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState, |
| + base::Unretained(manager), index_, info_.state)); |
| + } |
| + |
| + void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) { |
| + manager->PostReplyTask( |
| + base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort, |
| + base::Unretained(manager), info_)); |
| + } |
| + |
| // Port overrides: |
| bool Connect() override { |
| // Until |software| option is supported, disable Microsoft GS Wavetable |
| @@ -234,20 +316,31 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port { |
| return Port::Connect(); |
| } |
| - // Port Overrides: |
| - void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) { |
| - manager->PostReplyTask( |
| - base::Bind(&DynamicallyInitializedMidiManagerWin::SetOutputPortState, |
| - base::Unretained(manager), index_, info_.state)); |
| + bool Disconnect() override { |
| + if (out_handle_ != kInvalidOutHandle) { |
| + // Following API call may fail because device was already disconnected. |
| + // But just in case. |
| + midiOutClose(out_handle_); |
| + out_handle_ = kInvalidOutHandle; |
| + } |
| + return Port::Disconnect(); |
| } |
| - void NotifyPortAdded(DynamicallyInitializedMidiManagerWin* manager) { |
| - manager->PostReplyTask( |
| - base::Bind(&DynamicallyInitializedMidiManagerWin::AddOutputPort, |
| - base::Unretained(manager), info_)); |
| + void Open() override { |
| + MMRESULT result = |
| + midiOutOpen(&out_handle_, device_id_, |
| + reinterpret_cast<DWORD_PTR>(&HandleMidiOutCallback), 0, |
| + CALLBACK_FUNCTION); |
| + if (result == MMSYSERR_NOERROR) { |
| + Port::Open(); |
| + } else { |
| + out_handle_ = kInvalidOutHandle; |
| + Disconnect(); |
| + } |
| } |
| const bool software_; |
| + HMIDIOUT out_handle_; |
| }; |
| DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin( |
| @@ -305,6 +398,15 @@ void DynamicallyInitializedMidiManagerWin::Finalize() { |
| // Tasks that did not started yet will do nothing after invalidate the |
| // instance ID above. |
| base::AutoLock lock(*GetTaskLock()); |
| + |
| + // Posts tasks that finalize each device port without MidiManager instance |
| + // on TaskRunner. If another MidiManager instance is created, its |
| + // initialization runs on the same task runner after all tasks posted here |
| + // finish. |
| + for (const auto& port : input_ports_) |
| + port->Finalize(service()->GetTaskRunner(kTaskRunner)); |
| + for (const auto& port : output_ports_) |
| + port->Finalize(service()->GetTaskRunner(kTaskRunner)); |
| } |
| void DynamicallyInitializedMidiManagerWin::DispatchSendMidiData( |