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( |