Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Unified Diff: media/midi/dynamically_initialized_midi_manager_win.cc

Issue 2701783003: Web MIDI: implement receiving for dynamic manager instantiation on Windows (Closed)
Patch Set: make map non-global/statically initialized object Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 2a83b006fd0ae8e2d8597b6cfd4df6b09aba4f7c..cef899a3ac2a0332073ea3ecd22e9f1d9f924fa0 100644
--- a/media/midi/dynamically_initialized_midi_manager_win.cc
+++ b/media/midi/dynamically_initialized_midi_manager_win.cc
@@ -19,17 +19,53 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
+#include "media/midi/message_util.h"
#include "media/midi/midi_port_info.h"
#include "media/midi/midi_service.h"
namespace midi {
+// Forward declaration of PortManager for anonymous functions and internal
+// classes to use it.
+class DynamicallyInitializedMidiManagerWin::PortManager {
+ public:
+ // Calculates event time from elapsed time that system provides.
+ base::TimeTicks CalculateInEventTime(size_t index, uint32_t elapsed_ms) const;
+
+ // Registers HMIDIIN handle to resolve port index.
+ void RegisterInHandle(HMIDIIN handle, size_t index);
+
+ // Unregisters HMIDIIN handle.
+ void UnregisterInHandle(HMIDIIN handle);
+
+ // Finds HMIDIIN handle and fullfil |out_index| with the port index.
+ bool FindHandle(HMIDIIN hmi, size_t* out_index);
+
+ // Restores used input buffer for the next data receive.
+ void RestoreInBuffer(size_t index);
+
+ // Ports accessors.
+ std::vector<std::unique_ptr<InPort>>* inputs() { return &input_ports_; }
+ std::vector<std::unique_ptr<OutPort>>* outputs() { return &output_ports_; }
+
+ private:
+ // Holds all MIDI input or output ports connected once.
+ std::vector<std::unique_ptr<InPort>> input_ports_;
+ std::vector<std::unique_ptr<OutPort>> output_ports_;
+
+ // Map to resolve MIDI input port index from HMIDIIN.
+ std::map<HMIDIIN, size_t> hmidiin_to_index_map_;
+};
+
namespace {
// Assumes that nullptr represents an invalid MIDI handle.
constexpr HMIDIIN kInvalidInHandle = nullptr;
constexpr HMIDIOUT kInvalidOutHandle = nullptr;
+// Defines input buffer size.
+constexpr size_t kBufferLength = 32 * 1024;
+
// Global variables to identify MidiManager instance.
constexpr int kInvalidInstanceId = -1;
int g_active_instance_id = kInvalidInstanceId;
@@ -76,8 +112,44 @@ 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().
+// Utility class to handle MIDIHDR struct safely.
+class MIDIHDRDeleter {
+ public:
+ void operator()(LPMIDIHDR header) {
+ if (!header)
+ return;
+ delete[] static_cast<char*>(header->lpData);
+ delete header;
+ }
+};
+
+using ScopedMIDIHDR = std::unique_ptr<MIDIHDR, MIDIHDRDeleter>;
+
+ScopedMIDIHDR CreateMIDIHDR(size_t size) {
+ ScopedMIDIHDR hdr(new MIDIHDR);
+ ZeroMemory(hdr.get(), sizeof(*hdr));
+ hdr->lpData = new char[size];
+ hdr->dwBufferLength = static_cast<DWORD>(size);
+ return hdr;
+}
+
+ScopedMIDIHDR CreateMIDIHDR(const std::vector<uint8_t>& data) {
+ ScopedMIDIHDR hdr(CreateMIDIHDR(data.size()));
+ std::copy(data.begin(), data.end(), hdr->lpData);
+ return hdr;
+}
+
// Helper functions to close MIDI device handles on TaskRunner asynchronously.
-void FinalizeInPort(HMIDIIN handle) {
+void FinalizeInPort(HMIDIIN handle, LPMIDIHDR lphdr) {
+ // Resets the device. This stops receiving messages, and allows to release
Takashi Toyoshima 2017/02/24 08:53:34 Actually, our legacy code of midi_manager_win.cc d
+ // registered buffer headers. Otherwise, midiInUnprepareHeader() and
+ // midiInClose() will fail with MIDIERR_STILLPLAYING.
+ midiInReset(handle);
+
+ // Obtains MIDIHDR ownership to release allocated buffers.
+ ScopedMIDIHDR hdr(lphdr);
+ if (hdr)
+ midiInUnprepareHeader(handle, hdr.get(), sizeof(*hdr));
midiInClose(handle);
}
@@ -91,7 +163,53 @@ void CALLBACK HandleMidiInCallback(HMIDIIN hmi,
DWORD_PTR instance,
DWORD_PTR param1,
DWORD_PTR param2) {
- // TODO(toyoshim): Following patches will implement actual functions.
+ if (msg != MIM_DATA && msg != MIM_LONGDATA)
+ return;
+ int instance_id = static_cast<int>(instance);
+ DynamicallyInitializedMidiManagerWin* manager = nullptr;
+
+ // Use |g_task_lock| so to ensure the instance can keep alive while running,
+ // and to access member variables that are used on TaskRunner.
+ base::AutoLock task_lock(*GetTaskLock());
+ {
+ base::AutoLock lock(*GetInstanceIdLock());
+ if (instance_id != g_active_instance_id)
+ return;
+ manager = g_manager_instance;
+ }
+
+ size_t index;
+ if (!manager->port_manager()->FindHandle(hmi, &index))
+ return;
+
+ if (msg == MIM_DATA) {
+ const uint8_t status_byte = static_cast<uint8_t>(param1 & 0xff);
Takashi Toyoshima 2017/02/24 08:53:34 Following code through PostReplyTask is same with
+ 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 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);
+ manager->PostReplyTask(base::Bind(
+ &DynamicallyInitializedMidiManagerWin::ReceiveMidiData,
+ base::Unretained(manager), index, data,
+ manager->port_manager()->CalculateInEventTime(index, param2)));
+ } else { // msg == MIM_LONGDATA
yhirano 2017/02/24 12:22:58 Is this true? In the old code, the corresponding s
Takashi Toyoshima 2017/02/27 06:44:49 See line 166. But, it would be nicer to have DCHEC
+ LPMIDIHDR hdr = reinterpret_cast<LPMIDIHDR>(param1);
+ if (hdr->dwBytesRecorded > 0) {
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(hdr->lpData);
+ std::vector<uint8_t> data;
+ data.assign(src, src + hdr->dwBytesRecorded);
+ manager->PostReplyTask(base::Bind(
+ &DynamicallyInitializedMidiManagerWin::ReceiveMidiData,
+ base::Unretained(manager), index, data,
+ manager->port_manager()->CalculateInEventTime(index, param2)));
+ }
+ manager->PostTask(base::Bind(
+ &DynamicallyInitializedMidiManagerWin::PortManager::RestoreInBuffer,
+ base::Unretained(manager->port_manager()), index));
+ }
}
// Handles MIDI output port callbacks that runs on a system provided thread.
@@ -192,7 +310,10 @@ class Port {
// TODO(toyoshim): Following patches will implement actual functions.
class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
public:
- InPort(UINT device_id, const MIDIINCAPS2W& caps)
+ InPort(DynamicallyInitializedMidiManagerWin* manager,
+ int instance_id,
+ UINT device_id,
+ const MIDIINCAPS2W& caps)
: Port("input",
device_id,
caps.wMid,
@@ -200,9 +321,13 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
caps.vDriverVersion,
base::WideToUTF8(
base::string16(caps.szPname, wcslen(caps.szPname)))),
- in_handle_(kInvalidInHandle) {}
+ manager_(manager),
+ in_handle_(kInvalidInHandle),
+ instance_id_(instance_id) {}
- static std::vector<std::unique_ptr<InPort>> EnumerateActivePorts() {
+ static std::vector<std::unique_ptr<InPort>> EnumerateActivePorts(
+ DynamicallyInitializedMidiManagerWin* manager,
+ int instance_id) {
std::vector<std::unique_ptr<InPort>> ports;
const UINT num_devices = midiInGetNumDevs();
for (UINT device_id = 0; device_id < num_devices; ++device_id) {
@@ -213,18 +338,31 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
LOG(ERROR) << "midiInGetDevCaps fails on device " << device_id;
continue;
}
- ports.push_back(base::MakeUnique<InPort>(device_id, caps));
+ ports.push_back(
+ base::MakeUnique<InPort>(manager, instance_id, device_id, caps));
}
return ports;
}
void Finalize(scoped_refptr<base::SingleThreadTaskRunner> runner) {
if (in_handle_ != kInvalidInHandle) {
- runner->PostTask(FROM_HERE, base::Bind(&FinalizeInPort, in_handle_));
+ runner->PostTask(FROM_HERE,
+ base::Bind(&FinalizeInPort, in_handle_, hdr_.release()));
+ manager_->port_manager()->UnregisterInHandle(in_handle_);
in_handle_ = kInvalidInHandle;
}
}
+ base::TimeTicks CalculateInEventTime(uint32_t elapsed_ms) const {
+ return start_time_ + base::TimeDelta::FromMilliseconds(elapsed_ms);
+ }
+
+ void RestoreBuffer() {
+ if (in_handle_ == kInvalidInHandle || !hdr_)
+ return;
+ midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_));
+ }
+
void NotifyPortStateSet(DynamicallyInitializedMidiManagerWin* manager) {
manager->PostReplyTask(
base::Bind(&DynamicallyInitializedMidiManagerWin::SetInputPortState,
@@ -243,27 +381,48 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
// Following API call may fail because device was already disconnected.
// But just in case.
midiInClose(in_handle_);
+ manager_->port_manager()->UnregisterInHandle(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);
+ reinterpret_cast<DWORD_PTR>(&HandleMidiInCallback),
+ instance_id_, CALLBACK_FUNCTION);
if (result == MMSYSERR_NOERROR) {
+ hdr_ = CreateMIDIHDR(kBufferLength);
+ result = midiInPrepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_));
+ }
+ if (result != MMSYSERR_NOERROR)
+ in_handle_ = kInvalidInHandle;
+ if (result == MMSYSERR_NOERROR)
+ result = midiInAddBuffer(in_handle_, hdr_.get(), sizeof(*hdr_));
+ if (result == MMSYSERR_NOERROR)
+ result = midiInStart(in_handle_);
+ if (result == MMSYSERR_NOERROR) {
+ start_time_ = base::TimeTicks::Now();
+ manager_->port_manager()->RegisterInHandle(in_handle_, index_);
Port::Open();
} else {
- in_handle_ = kInvalidInHandle;
+ if (in_handle_ != kInvalidInHandle) {
+ midiInUnprepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_));
+ hdr_.reset();
+ midiInClose(in_handle_);
+ in_handle_ = kInvalidInHandle;
+ }
Disconnect();
}
}
private:
+ DynamicallyInitializedMidiManagerWin* manager_;
HMIDIIN in_handle_;
+ ScopedMIDIHDR hdr_;
+ base::TimeTicks start_time_;
+ int instance_id_;
};
// TODO(toyoshim): Following patches will implement actual functions.
@@ -354,9 +513,51 @@ class DynamicallyInitializedMidiManagerWin::OutPort final : public Port {
HMIDIOUT out_handle_;
};
+base::TimeTicks
+DynamicallyInitializedMidiManagerWin::PortManager::CalculateInEventTime(
+ size_t index,
+ uint32_t elapsed_ms) const {
+ GetTaskLock()->AssertAcquired();
Takashi Toyoshima 2017/02/24 08:53:34 task lock should be obtained outside this method,
+ CHECK_GT(input_ports_.size(), index);
+ return input_ports_[index]->CalculateInEventTime(elapsed_ms);
+}
+
+void DynamicallyInitializedMidiManagerWin::PortManager::RegisterInHandle(
+ HMIDIIN handle,
+ size_t index) {
+ GetTaskLock()->AssertAcquired();
+ hmidiin_to_index_map_[handle] = index;
+}
+
+void DynamicallyInitializedMidiManagerWin::PortManager::UnregisterInHandle(
+ HMIDIIN handle) {
+ GetTaskLock()->AssertAcquired();
+ hmidiin_to_index_map_.erase(handle);
+}
+
+bool DynamicallyInitializedMidiManagerWin::PortManager::FindHandle(
+ HMIDIIN hmi,
+ size_t* out_index) {
+ GetTaskLock()->AssertAcquired();
+ auto found = hmidiin_to_index_map_.find(hmi);
+ if (found == hmidiin_to_index_map_.end())
+ return false;
+ *out_index = found->second;
+ return true;
+}
+
+void DynamicallyInitializedMidiManagerWin::PortManager::RestoreInBuffer(
+ size_t index) {
+ GetTaskLock()->AssertAcquired();
+ CHECK_GT(input_ports_.size(), index);
+ input_ports_[index]->RestoreBuffer();
+}
+
DynamicallyInitializedMidiManagerWin::DynamicallyInitializedMidiManagerWin(
MidiService* service)
- : MidiManager(service), instance_id_(IssueNextInstanceId()) {
+ : MidiManager(service),
+ instance_id_(IssueNextInstanceId()),
+ port_manager_(base::MakeUnique<PortManager>()) {
base::AutoLock lock(*GetInstanceIdLock());
CHECK_EQ(kInvalidInstanceId, g_active_instance_id);
@@ -370,6 +571,19 @@ DynamicallyInitializedMidiManagerWin::~DynamicallyInitializedMidiManagerWin() {
CHECK(thread_runner_->BelongsToCurrentThread());
}
+void DynamicallyInitializedMidiManagerWin::ReceiveMidiData(
+ uint32_t index,
+ const std::vector<uint8_t>& data,
+ base::TimeTicks time) {
+ MidiManager::ReceiveMidiData(index, data.data(), data.size(), time);
+}
+
+void DynamicallyInitializedMidiManagerWin::PostTask(const base::Closure& task) {
+ service()
+ ->GetTaskRunner(kTaskRunner)
+ ->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task));
+}
+
void DynamicallyInitializedMidiManagerWin::PostReplyTask(
const base::Closure& task) {
thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task));
@@ -416,9 +630,9 @@ void DynamicallyInitializedMidiManagerWin::Finalize() {
// 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_)
+ for (const auto& port : *port_manager_->inputs())
port->Finalize(service()->GetTaskRunner(kTaskRunner));
- for (const auto& port : output_ports_)
+ for (const auto& port : *port_manager_->outputs())
port->Finalize(service()->GetTaskRunner(kTaskRunner));
}
@@ -449,12 +663,6 @@ void DynamicallyInitializedMidiManagerWin::OnDevicesChanged(
}
}
-void DynamicallyInitializedMidiManagerWin::PostTask(const base::Closure& task) {
- service()
- ->GetTaskRunner(kTaskRunner)
- ->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task));
-}
-
void DynamicallyInitializedMidiManagerWin::InitializeOnTaskRunner() {
UpdateDeviceListOnTaskRunner();
PostReplyTask(
@@ -464,12 +672,12 @@ void DynamicallyInitializedMidiManagerWin::InitializeOnTaskRunner() {
void DynamicallyInitializedMidiManagerWin::UpdateDeviceListOnTaskRunner() {
std::vector<std::unique_ptr<InPort>> active_input_ports =
- InPort::EnumerateActivePorts();
- ReflectActiveDeviceList(this, &input_ports_, &active_input_ports);
+ InPort::EnumerateActivePorts(this, instance_id_);
+ ReflectActiveDeviceList(this, port_manager_->inputs(), &active_input_ports);
std::vector<std::unique_ptr<OutPort>> active_output_ports =
OutPort::EnumerateActivePorts();
- ReflectActiveDeviceList(this, &output_ports_, &active_output_ports);
+ ReflectActiveDeviceList(this, port_manager_->outputs(), &active_output_ports);
// TODO(toyoshim): This method may run before internal MIDI device lists that
// Windows manages were updated. This may be because MIDI driver may be loaded

Powered by Google App Engine
This is Rietveld 408576698