summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authortoyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-06 11:23:09 +0000
committertoyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-06 11:23:09 +0000
commit64c29a24182066e0271ea2d30cb23258ff140ac4 (patch)
tree37df28da38094a78722cac54b774e1d0ecd8d17e /media
parentc82d8a30294a151592bb3480562c6ec9e10380f3 (diff)
downloadchromium_src-64c29a24182066e0271ea2d30cb23258ff140ac4.zip
chromium_src-64c29a24182066e0271ea2d30cb23258ff140ac4.tar.gz
chromium_src-64c29a24182066e0271ea2d30cb23258ff140ac4.tar.bz2
Web MIDI: enable receiving functionality in Linux and Chrome OS
BUG=303601 Review URL: https://codereview.chromium.org/111103008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249359 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/midi/midi_manager_alsa.cc139
-rw-r--r--media/midi/midi_manager_alsa.h11
2 files changed, 148 insertions, 2 deletions
diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
index 6f389ca..4716db2 100644
--- a/media/midi/midi_manager_alsa.cc
+++ b/media/midi/midi_manager_alsa.cc
@@ -5,18 +5,28 @@
#include "media/midi/midi_manager_alsa.h"
#include <alsa/asoundlib.h>
+#include <stdlib.h>
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
+#include "base/time/time.h"
#include "media/midi/midi_port_info.h"
namespace media {
+namespace {
+
+const size_t kReceiveBufferSize = 4096;
+const unsigned short kPollEventMask = POLLIN | POLLERR | POLLNVAL;
+
+} // namespace
+
class MidiManagerAlsa::MidiDeviceInfo
: public base::RefCounted<MidiDeviceInfo> {
public:
@@ -50,7 +60,7 @@ class MidiManagerAlsa::MidiDeviceInfo
ssize_t result = snd_rawmidi_write(
midi_out_, reinterpret_cast<const void*>(&data[0]), data.size());
if (static_cast<size_t>(result) != data.size()) {
- // TODO(toyoshim): Disconnect and reopen the device.
+ // TODO(toyoshim): Handle device disconnection.
LOG(ERROR) << "snd_rawmidi_write fails: " << strerror(-result);
}
base::MessageLoop::current()->PostTask(
@@ -59,7 +69,38 @@ class MidiManagerAlsa::MidiDeviceInfo
base::Unretained(client), data.size()));
}
+ // Read input data from a MIDI input device which is ready to read through
+ // the ALSA library. Called from EventLoop() and read data will be sent to
+ // blink through MIDIManager base class.
+ size_t Receive(uint8* data, size_t length) {
+ return snd_rawmidi_read(midi_in_, reinterpret_cast<void*>(data), length);
+ }
+
const MidiPortInfo& GetMidiPortInfo() const { return port_info_; }
+
+ // Get the number of descriptors which is required to call poll() on the
+ // device. The ALSA library always returns 1 here now, but it may be changed
+ // in the future.
+ int GetPollDescriptorsCount() {
+ return snd_rawmidi_poll_descriptors_count(midi_in_);
+ }
+
+ // Following API initializes pollfds for polling the device, and returns the
+ // number of descriptors they are initialized. It must be the same value with
+ // snd_rawmidi_poll_descriptors_count().
+ int SetupPollDescriptors(struct pollfd* pfds, unsigned int count) {
+ return snd_rawmidi_poll_descriptors(midi_in_, pfds, count);
+ }
+
+ unsigned short GetPollDescriptorsRevents(struct pollfd* pfds) {
+ unsigned short revents;
+ snd_rawmidi_poll_descriptors_revents(midi_in_,
+ pfds,
+ GetPollDescriptorsCount(),
+ &revents);
+ return revents;
+ }
+
bool IsOpened() const { return opened_; }
private:
@@ -80,7 +121,10 @@ class MidiManagerAlsa::MidiDeviceInfo
};
MidiManagerAlsa::MidiManagerAlsa()
- : send_thread_("MidiSendThread") {
+ : send_thread_("MidiSendThread"),
+ event_thread_("MidiEventThread") {
+ for (size_t i = 0; i < arraysize(pipe_fd_); ++i)
+ pipe_fd_[i] = -1;
}
bool MidiManagerAlsa::Initialize() {
@@ -141,10 +185,31 @@ bool MidiManagerAlsa::Initialize() {
}
snd_ctl_close(handle);
}
+
+ if (pipe(pipe_fd_) < 0) {
+ DPLOG(ERROR) << "pipe() failed";
+ return false;
+ }
+ event_thread_.Start();
+ event_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
+
return true;
}
MidiManagerAlsa::~MidiManagerAlsa() {
+ // Send a shutdown message to awake |event_thread_| from poll().
+ if (pipe_fd_[1] >= 0)
+ HANDLE_EINTR(write(pipe_fd_[1], "Q", 1));
+
+ // Stop receiving messages.
+ event_thread_.Stop();
+
+ for (int i = 0; i < 2; ++i) {
+ if (pipe_fd_[i] >= 0)
+ close(pipe_fd_[i]);
+ }
send_thread_.Stop();
}
@@ -173,6 +238,76 @@ void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
delay);
}
+void MidiManagerAlsa::EventReset() {
+ CHECK(pipe_fd_[0] >= 0);
+
+ // Sum up descriptors which are needed to poll input devices and a shutdown
+ // message.
+ // Keep the first one descriptor for a shutdown message.
+ size_t poll_fds_size = 1;
+ for (size_t i = 0; i < in_devices_.size(); ++i)
+ poll_fds_size += in_devices_[i]->GetPollDescriptorsCount();
+ poll_fds_.resize(poll_fds_size);
+
+ // Setup struct pollfd to poll input MIDI devices and a shutdown message.
+ // The first pollfd is for a shutdown message.
+ poll_fds_[0].fd = pipe_fd_[0];
+ poll_fds_[0].events = kPollEventMask;
+ int fds_index = 1;
+ for (size_t i = 0; i < in_devices_.size(); ++i) {
+ fds_index += in_devices_[i]->SetupPollDescriptors(
+ &poll_fds_[fds_index], poll_fds_.size() - fds_index);
+ }
+
+ event_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
+}
+
+void MidiManagerAlsa::EventLoop() {
+ if (HANDLE_EINTR(poll(&poll_fds_[0], poll_fds_.size(), -1)) < 0) {
+ DPLOG(ERROR) << "Couldn't poll(). Stop to poll input MIDI devices.";
+ // TODO(toyoshim): Handle device disconnection, and try to reconnect?
+ return;
+ }
+
+ // Check timestamp as soon as possible because the API requires accurate
+ // timestamp as possible. It will be useful for recording MIDI events.
+ base::TimeTicks now = base::TimeTicks::HighResNow();
+
+ // Is this thread going to be shutdown?
+ if (poll_fds_[0].revents & kPollEventMask)
+ return;
+
+ // Read available incoming MIDI data.
+ int fds_index = 1;
+ uint8 buffer[kReceiveBufferSize];
+ // TODO(toyoshim): Revisit if the following conversion is the best way.
+ base::TimeDelta timestamp_delta =
+ base::TimeDelta::FromInternalValue(now.ToInternalValue());
+ double timestamp = timestamp_delta.InSecondsF();
+
+ for (size_t i = 0; i < in_devices_.size(); ++i) {
+ unsigned short revents =
+ in_devices_[i]->GetPollDescriptorsRevents(&poll_fds_[fds_index]);
+ if (revents & (POLLERR | POLLNVAL)) {
+ // TODO(toyoshim): Handle device disconnection.
+ LOG(ERROR) << "snd_rawmidi_descriptors_revents fails";
+ poll_fds_[fds_index].events = 0;
+ }
+ if (revents & POLLIN) {
+ size_t read_size = in_devices_[i]->Receive(buffer, kReceiveBufferSize);
+ ReceiveMidiData(i, buffer, read_size, timestamp);
+ }
+ fds_index += in_devices_[i]->GetPollDescriptorsCount();
+ }
+
+ // Do again.
+ event_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
+}
+
MidiManager* MidiManager::Create() {
return new MidiManagerAlsa();
}
diff --git a/media/midi/midi_manager_alsa.h b/media/midi/midi_manager_alsa.h
index d14107a..d97650c 100644
--- a/media/midi/midi_manager_alsa.h
+++ b/media/midi/midi_manager_alsa.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_MIDI_MIDI_MANAGER_ALSA_H_
#define MEDIA_MIDI_MIDI_MANAGER_ALSA_H_
+#include <poll.h>
#include <vector>
#include "base/basictypes.h"
@@ -27,10 +28,20 @@ class MidiManagerAlsa : public MidiManager {
double timestamp) OVERRIDE;
private:
+ void EventReset();
+ void EventLoop();
+
class MidiDeviceInfo;
std::vector<scoped_refptr<MidiDeviceInfo> > in_devices_;
std::vector<scoped_refptr<MidiDeviceInfo> > out_devices_;
base::Thread send_thread_;
+ base::Thread event_thread_;
+
+ // Used for shutting down the |event_thread_| safely.
+ int pipe_fd_[2];
+ // Used for polling input MIDI ports and |pipe_fd_| in |event_thread_|.
+ std::vector<struct pollfd> poll_fds_;
+
DISALLOW_COPY_AND_ASSIGN(MidiManagerAlsa);
};