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

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: 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
« no previous file with comments | « media/midi/dynamically_initialized_midi_manager_win.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 aae9d1274a788e5c850ca22caab0f58f414f2522..e29fee8c248447d3a266e5aa798e6fbb879abf57 100644
--- a/media/midi/dynamically_initialized_midi_manager_win.cc
+++ b/media/midi/dynamically_initialized_midi_manager_win.cc
@@ -32,12 +32,18 @@ namespace {
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;
int g_next_instance_id = 0;
DynamicallyInitializedMidiManagerWin* g_manager_instance = nullptr;
+// Global map to resolve MIDI input port index from HMIDIIN.
+std::map<HMIDIIN, size_t> g_hmidiin_to_index_map;
+
// Obtains base::Lock instance pointer to lock instance_id.
base::Lock* GetInstanceIdLock() {
static base::Lock* lock = new base::Lock;
@@ -70,8 +76,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
+ // 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);
}
@@ -85,7 +127,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;
+ }
+
+ auto found = g_hmidiin_to_index_map.find(hmi);
+ if (found == g_hmidiin_to_index_map.end())
+ return;
+ size_t index = found->second;
+
+ if (msg == MIM_DATA) {
+ 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 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->CalculateInEventTime(index, param2)));
+ } else { // msg == MIM_LONGDATA
+ 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->CalculateInEventTime(index, param2)));
+ }
+ // TODO(toyoshim): Call midiInAddBuffer outside the callback.
+ midiInAddBuffer(hmi, hdr, sizeof(*hdr));
+ }
}
// Handles MIDI output port callbacks that runs on a system provided thread.
@@ -190,24 +278,49 @@ class DynamicallyInitializedMidiManagerWin::InPort final : public Port {
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()));
}
}
+ base::TimeTicks CalculateInEventTime(uint32_t elapsed_ms) const {
+ return start_time_ + base::TimeDelta::FromMilliseconds(elapsed_ms);
+ }
+
// Port overrides:
void Open() override {
MMRESULT result =
midiInOpen(&in_handle_, device_id_,
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();
+ g_hmidiin_to_index_map[in_handle_] = index_;
Port::Open();
- else
+ } else {
+ if (in_handle_ != kInvalidInHandle) {
+ midiInUnprepareHeader(in_handle_, hdr_.get(), sizeof(*hdr_));
+ midiInClose(in_handle_);
+ in_handle_ = kInvalidInHandle;
Takashi Toyoshima 2017/02/24 06:34:28 Actually this was a real reason why Open() wasn't
+ }
Disconnect();
+ }
}
private:
HMIDIIN in_handle_;
+ ScopedMIDIHDR hdr_;
+ base::TimeTicks start_time_;
int instance_id_;
};
@@ -276,6 +389,21 @@ DynamicallyInitializedMidiManagerWin::~DynamicallyInitializedMidiManagerWin() {
CHECK(thread_runner_->BelongsToCurrentThread());
}
+base::TimeTicks DynamicallyInitializedMidiManagerWin::CalculateInEventTime(
+ size_t index,
+ uint32_t elapsed_ms) const {
+ GetTaskLock()->AssertAcquired();
+ CHECK_GT(input_ports_.size(), index);
+ return input_ports_[index]->CalculateInEventTime(elapsed_ms);
+}
+
+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::PostReplyTask(
const base::Closure& task) {
thread_runner_->PostTask(FROM_HERE, base::Bind(&RunTask, instance_id_, task));
« no previous file with comments | « media/midi/dynamically_initialized_midi_manager_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698