| Index: media/midi/midi_manager_alsa.cc
|
| diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
|
| index c4be32ffae29428b918cdae2707d4e92c920675d..a21f8919d37f75fad03cceda590a7a52826d8b28 100644
|
| --- a/media/midi/midi_manager_alsa.cc
|
| +++ b/media/midi/midi_manager_alsa.cc
|
| @@ -12,6 +12,7 @@
|
| #include "base/bind.h"
|
| #include "base/logging.h"
|
| #include "base/memory/ref_counted.h"
|
| +#include "base/memory/scoped_vector.h"
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/posix/eintr_wrapper.h"
|
| #include "base/strings/stringprintf.h"
|
| @@ -23,114 +24,114 @@ namespace media {
|
|
|
| namespace {
|
|
|
| -const size_t kReceiveBufferSize = 4096;
|
| -const unsigned short kPollEventMask = POLLIN | POLLERR | POLLNVAL;
|
| -
|
| -} // namespace
|
| +// Per-output buffer. This can be smaller, but then large sysex messages
|
| +// will be (harmlessly) split across multiple seq events. This should
|
| +// not have any real practical effect, except perhaps to slightly reorder
|
| +// realtime messages with respect to sysex.
|
| +const size_t kSendBufferSize = 256;
|
| +
|
| +// Constants for the capabilities we search for in inputs and outputs.
|
| +// See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
|
| +const unsigned int kRequiredInputPortCaps =
|
| + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
|
| +const unsigned int kRequiredOutputPortCaps =
|
| + SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
|
| +
|
| +int AddrToInt(const snd_seq_addr_t* addr) {
|
| + return (addr->client << 8) | addr->port;
|
| +}
|
|
|
| -class MidiManagerAlsa::MidiDeviceInfo
|
| - : public base::RefCounted<MidiDeviceInfo> {
|
| +class CardInfo {
|
| public:
|
| - MidiDeviceInfo(MidiManagerAlsa* manager,
|
| - const std::string& bus_id,
|
| - snd_ctl_card_info_t* card,
|
| - const snd_rawmidi_info_t* midi,
|
| - int device) {
|
| - opened_ = !snd_rawmidi_open(&midi_in_, &midi_out_, bus_id.c_str(), 0);
|
| - if (!opened_)
|
| - return;
|
| -
|
| - const std::string id = base::StringPrintf("%s:%d", bus_id.c_str(), device);
|
| - const std::string name = snd_rawmidi_info_get_name(midi);
|
| - // We assume that card longname is in the format of
|
| - // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
|
| - // a manufacturer name here.
|
| - std::string manufacturer;
|
| - const std::string card_name = snd_ctl_card_info_get_longname(card);
|
| - size_t name_index = card_name.find(name);
|
| - if (std::string::npos != name_index)
|
| - manufacturer = card_name.substr(0, name_index - 1);
|
| - const std::string version =
|
| - base::StringPrintf("%s / ALSA library version %d.%d.%d",
|
| - snd_ctl_card_info_get_driver(card),
|
| - SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR);
|
| - port_info_ = MidiPortInfo(id, manufacturer, name, version);
|
| + CardInfo(const std::string name, const std::string manufacturer,
|
| + const std::string driver)
|
| + : name_(name), manufacturer_(manufacturer), driver_(driver) {
|
| }
|
| + const std::string name_;
|
| + const std::string manufacturer_;
|
| + const std::string driver_;
|
| +};
|
|
|
| - void Send(MidiManagerClient* client, const std::vector<uint8>& data) {
|
| - 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): Handle device disconnection.
|
| - VLOG(1) << "snd_rawmidi_write fails: " << strerror(-result);
|
| - }
|
| - base::MessageLoop::current()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
|
| - base::Unretained(client), data.size()));
|
| - }
|
| +} // namespace
|
|
|
| - // 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);
|
| - }
|
| +MidiManagerAlsa::MidiManagerAlsa()
|
| + : in_client_(NULL),
|
| + out_client_(NULL),
|
| + out_client_id_(-1),
|
| + in_port_(-1),
|
| + decoder_(NULL),
|
| + send_thread_("MidiSendThread"),
|
| + event_thread_("MidiEventThread"),
|
| + event_thread_shutdown_(false) {
|
| + // Initialize decoder.
|
| + snd_midi_event_new(0, &decoder_);
|
| + snd_midi_event_no_status(decoder_, 1);
|
| +}
|
|
|
| - const MidiPortInfo& GetMidiPortInfo() const { return port_info_; }
|
| +void MidiManagerAlsa::StartInitialization() {
|
| + // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
|
|
|
| - // 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_);
|
| + // Create client handles.
|
| + int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0);
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| }
|
| -
|
| - // 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);
|
| + int in_client_id = snd_seq_client_id(in_client_);
|
| + err = snd_seq_open(&out_client_, "hw", SND_SEQ_OPEN_OUTPUT, 0);
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| }
|
| + out_client_id_ = snd_seq_client_id(out_client_);
|
|
|
| - unsigned short GetPollDescriptorsRevents(struct pollfd* pfds) {
|
| - unsigned short revents;
|
| - snd_rawmidi_poll_descriptors_revents(midi_in_,
|
| - pfds,
|
| - GetPollDescriptorsCount(),
|
| - &revents);
|
| - return revents;
|
| + // Name the clients.
|
| + err = snd_seq_set_client_name(in_client_, "Chrome (input)");
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| }
|
| -
|
| - bool IsOpened() const { return opened_; }
|
| -
|
| - private:
|
| - friend class base::RefCounted<MidiDeviceInfo>;
|
| - virtual ~MidiDeviceInfo() {
|
| - if (opened_) {
|
| - snd_rawmidi_close(midi_in_);
|
| - snd_rawmidi_close(midi_out_);
|
| - }
|
| + err = snd_seq_set_client_name(out_client_, "Chrome (output)");
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| }
|
|
|
| - bool opened_;
|
| - MidiPortInfo port_info_;
|
| - snd_rawmidi_t* midi_in_;
|
| - snd_rawmidi_t* midi_out_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(MidiDeviceInfo);
|
| -};
|
| + // Create input port.
|
| + in_port_ = snd_seq_create_simple_port(in_client_, NULL,
|
| + SND_SEQ_PORT_CAP_WRITE |
|
| + SND_SEQ_PORT_CAP_NO_EXPORT,
|
| + SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
| + SND_SEQ_PORT_TYPE_APPLICATION);
|
| + if (in_port_ < 0) {
|
| + VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| + }
|
|
|
| -MidiManagerAlsa::MidiManagerAlsa()
|
| - : send_thread_("MidiSendThread"),
|
| - event_thread_("MidiEventThread") {
|
| - for (size_t i = 0; i < arraysize(pipe_fd_); ++i)
|
| - pipe_fd_[i] = -1;
|
| -}
|
| + // Subscribe to the announce port.
|
| + snd_seq_port_subscribe_t* subs;
|
| + snd_seq_port_subscribe_alloca(&subs);
|
| + snd_seq_addr_t announce_sender;
|
| + snd_seq_addr_t announce_dest;
|
| + announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
|
| + announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
|
| + announce_dest.client = in_client_id;
|
| + announce_dest.port = in_port_;
|
| + snd_seq_port_subscribe_set_sender(subs, &announce_sender);
|
| + snd_seq_port_subscribe_set_dest(subs, &announce_dest);
|
| + err = snd_seq_subscribe_port(in_client_, subs);
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
|
| + << snd_strerror(err);
|
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| + }
|
|
|
| -void MidiManagerAlsa::StartInitialization() {
|
| - // Enumerate only hardware MIDI devices because software MIDIs running in
|
| - // the browser process is not secure.
|
| + // Use a heuristic to extract the list of manufacturers for the hardware MIDI
|
| + // devices. This won't work for all devices. It is also brittle until
|
| + // hotplug is implemented. (See http://crbug.com/279097.)
|
| + // TODO(agoode): Make manufacturer extraction simple and reliable.
|
| + // http://crbug.com/377250.
|
| + ScopedVector<CardInfo> cards;
|
| snd_ctl_card_info_t* card;
|
| snd_rawmidi_info_t* midi_out;
|
| snd_rawmidi_info_t* midi_in;
|
| @@ -151,8 +152,9 @@ void MidiManagerAlsa::StartInitialization() {
|
| snd_ctl_close(handle);
|
| continue;
|
| }
|
| + // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
|
| for (int device = -1;
|
| - !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
|
| + !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
|
| bool output;
|
| bool input;
|
| snd_rawmidi_info_set_device(midi_out, device);
|
| @@ -165,58 +167,200 @@ void MidiManagerAlsa::StartInitialization() {
|
| input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
|
| if (!output && !input)
|
| continue;
|
| - scoped_refptr<MidiDeviceInfo> port = new MidiDeviceInfo(
|
| - this, id, card, output ? midi_out : midi_in, device);
|
| - if (!port->IsOpened()) {
|
| - VLOG(1) << "MidiDeviceInfo open fails";
|
| - continue;
|
| +
|
| + snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
|
| + const std::string name = snd_rawmidi_info_get_name(midi);
|
| + // We assume that card longname is in the format of
|
| + // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
|
| + // a manufacturer name here.
|
| + std::string manufacturer;
|
| + const std::string card_name = snd_ctl_card_info_get_longname(card);
|
| + size_t at_index = card_name.rfind(" at ");
|
| + if (std::string::npos != at_index) {
|
| + size_t name_index = card_name.rfind(name, at_index - 1);
|
| + if (std::string::npos != name_index)
|
| + manufacturer = card_name.substr(0, name_index - 1);
|
| }
|
| - if (input) {
|
| - in_devices_.push_back(port);
|
| - AddInputPort(port->GetMidiPortInfo());
|
| + const std::string driver = snd_ctl_card_info_get_driver(card);
|
| + cards.push_back(new CardInfo(name, manufacturer, driver));
|
| + }
|
| + }
|
| +
|
| + // Enumerate all ports in all clients.
|
| + snd_seq_client_info_t* client_info;
|
| + snd_seq_client_info_alloca(&client_info);
|
| + snd_seq_port_info_t* port_info;
|
| + snd_seq_port_info_alloca(&port_info);
|
| +
|
| + snd_seq_client_info_set_client(client_info, -1);
|
| + // Enumerate clients.
|
| + uint32 current_input = 0;
|
| + unsigned int current_card = 0;
|
| + while (!snd_seq_query_next_client(in_client_, client_info)) {
|
| + int client_id = snd_seq_client_info_get_client(client_info);
|
| + if ((client_id == in_client_id) || (client_id == out_client_id_)) {
|
| + // Skip our own clients.
|
| + continue;
|
| + }
|
| + const std::string client_name = snd_seq_client_info_get_name(client_info);
|
| + snd_seq_port_info_set_client(port_info, client_id);
|
| + snd_seq_port_info_set_port(port_info, -1);
|
| +
|
| + std::string manufacturer;
|
| + std::string driver;
|
| + // In the current Alsa kernel implementation, hardware clients match the
|
| + // cards in the same order.
|
| + if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
|
| + (current_card < cards.size())) {
|
| + const CardInfo* info = cards[current_card];
|
| + if (info->name_ == client_name) {
|
| + manufacturer = info->manufacturer_;
|
| + driver = info->driver_;
|
| + current_card++;
|
| }
|
| - if (output) {
|
| - out_devices_.push_back(port);
|
| - AddOutputPort(port->GetMidiPortInfo());
|
| + }
|
| + // Enumerate ports.
|
| + while (!snd_seq_query_next_port(in_client_, port_info)) {
|
| + unsigned int port_type = snd_seq_port_info_get_type(port_info);
|
| + if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
|
| + const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
|
| + const std::string name = snd_seq_port_info_get_name(port_info);
|
| + const std::string id = base::StringPrintf("%d:%d %s",
|
| + addr->client,
|
| + addr->port,
|
| + name.c_str());
|
| + std::string version;
|
| + if (driver != "") {
|
| + version = driver + " / ";
|
| + }
|
| + version += base::StringPrintf("ALSA library version %d.%d.%d",
|
| + SND_LIB_MAJOR,
|
| + SND_LIB_MINOR,
|
| + SND_LIB_SUBMINOR);
|
| + unsigned int caps = snd_seq_port_info_get_capability(port_info);
|
| + if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
|
| + // Subscribe to this port.
|
| + const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
|
| + snd_seq_addr_t dest;
|
| + dest.client = snd_seq_client_id(in_client_);
|
| + dest.port = in_port_;
|
| + snd_seq_port_subscribe_set_sender(subs, sender);
|
| + snd_seq_port_subscribe_set_dest(subs, &dest);
|
| + err = snd_seq_subscribe_port(in_client_, subs);
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
|
| + } else {
|
| + source_map_[AddrToInt(sender)] = current_input++;
|
| + AddInputPort(MidiPortInfo(id, manufacturer, name, version));
|
| + }
|
| + }
|
| + if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
|
| + // Create a port for us to send on.
|
| + int out_port =
|
| + snd_seq_create_simple_port(out_client_, NULL,
|
| + SND_SEQ_PORT_CAP_READ |
|
| + SND_SEQ_PORT_CAP_NO_EXPORT,
|
| + SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
| + SND_SEQ_PORT_TYPE_APPLICATION);
|
| + if (out_port < 0) {
|
| + VLOG(1) << "snd_seq_create_simple_port fails: "
|
| + << snd_strerror(out_port);
|
| + // Skip this output port for now.
|
| + continue;
|
| + }
|
| +
|
| + // Activate port subscription.
|
| + snd_seq_addr_t sender;
|
| + const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
|
| + sender.client = snd_seq_client_id(out_client_);
|
| + sender.port = out_port;
|
| + snd_seq_port_subscribe_set_sender(subs, &sender);
|
| + snd_seq_port_subscribe_set_dest(subs, dest);
|
| + err = snd_seq_subscribe_port(out_client_, subs);
|
| + if (err != 0) {
|
| + VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
|
| + snd_seq_delete_simple_port(out_client_, out_port);
|
| + } else {
|
| + snd_midi_event_t* encoder;
|
| + snd_midi_event_new(kSendBufferSize, &encoder);
|
| + encoders_.push_back(encoder);
|
| + out_ports_.push_back(out_port);
|
| + AddOutputPort(MidiPortInfo(id, manufacturer, name, version));
|
| + }
|
| + }
|
| }
|
| }
|
| - snd_ctl_close(handle);
|
| }
|
|
|
| - if (pipe(pipe_fd_) < 0) {
|
| - VPLOG(1) << "pipe() failed";
|
| - CompleteInitialization(MIDI_INITIALIZATION_ERROR);
|
| - } else {
|
| - event_thread_.Start();
|
| - event_thread_.message_loop()->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
|
| - CompleteInitialization(MIDI_OK);
|
| - }
|
| + event_thread_.Start();
|
| + event_thread_.message_loop()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
|
| +
|
| + CompleteInitialization(MIDI_OK);
|
| }
|
|
|
| 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));
|
| + // Tell the event thread it will soon be time to shut down. This gives
|
| + // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
|
| + // message is lost.
|
| + {
|
| + base::AutoLock lock(shutdown_lock_);
|
| + event_thread_shutdown_ = true;
|
| + }
|
|
|
| - // Stop receiving messages.
|
| + // Stop the send thread.
|
| + send_thread_.Stop();
|
| +
|
| + // Close the out client. This will trigger the event thread to stop,
|
| + // because of SND_SEQ_EVENT_CLIENT_EXIT.
|
| + if (out_client_)
|
| + snd_seq_close(out_client_);
|
| +
|
| + // Wait for the event thread to stop.
|
| event_thread_.Stop();
|
|
|
| - for (int i = 0; i < 2; ++i) {
|
| - if (pipe_fd_[i] >= 0)
|
| - close(pipe_fd_[i]);
|
| + // Close the in client.
|
| + if (in_client_)
|
| + snd_seq_close(in_client_);
|
| +
|
| + // Free the decoder.
|
| + snd_midi_event_free(decoder_);
|
| +
|
| + // Free the encoders.
|
| + for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
|
| + snd_midi_event_free(*i);
|
| +}
|
| +
|
| +void MidiManagerAlsa::SendMidiData(uint32 port_index,
|
| + const std::vector<uint8>& data) {
|
| + DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
|
| +
|
| + snd_midi_event_t* encoder = encoders_[port_index];
|
| + for (unsigned int i = 0; i < data.size(); i++) {
|
| + snd_seq_event_t event;
|
| + int result = snd_midi_event_encode_byte(encoder, data[i], &event);
|
| + if (result == 1) {
|
| + // Full event, send it.
|
| + snd_seq_ev_set_source(&event, out_ports_[port_index]);
|
| + snd_seq_ev_set_subs(&event);
|
| + snd_seq_ev_set_direct(&event);
|
| + snd_seq_event_output_direct(out_client_, &event);
|
| + }
|
| }
|
| - send_thread_.Stop();
|
| }
|
|
|
| void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
|
| uint32 port_index,
|
| const std::vector<uint8>& data,
|
| double timestamp) {
|
| - if (out_devices_.size() <= port_index)
|
| + if (out_ports_.size() <= port_index)
|
| return;
|
|
|
| + // Not correct right now. http://crbug.com/374341.
|
| + if (!send_thread_.IsRunning())
|
| + send_thread_.Start();
|
| +
|
| base::TimeDelta delay;
|
| if (timestamp != 0.0) {
|
| base::TimeTicks time_to_send =
|
| @@ -225,74 +369,74 @@ void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
|
| delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
|
| }
|
|
|
| - if (!send_thread_.IsRunning())
|
| - send_thread_.Start();
|
| -
|
| - scoped_refptr<MidiDeviceInfo> device = out_devices_[port_index];
|
| send_thread_.message_loop()->PostDelayedTask(
|
| FROM_HERE,
|
| - base::Bind(&MidiDeviceInfo::Send, device, client, data),
|
| - delay);
|
| + base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
|
| + port_index, data), delay);
|
| +
|
| + // Acknowledge send.
|
| + send_thread_.message_loop()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
|
| + base::Unretained(client), data.size()));
|
| }
|
|
|
| void MidiManagerAlsa::EventReset() {
|
| - CHECK_GE(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) {
|
| - VPLOG(1) << "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];
|
| -
|
| - 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.
|
| - VLOG(1) << "snd_rawmidi_descriptors_revents fails";
|
| - poll_fds_[fds_index].events = 0;
|
| + snd_seq_event_t* event;
|
| + int err = snd_seq_event_input(in_client_, &event);
|
| + double timestamp =
|
| + (base::TimeTicks::HighResNow() - base::TimeTicks()).InSecondsF();
|
| + if (err == -ENOSPC) {
|
| + VLOG(1) << "snd_seq_event_input detected buffer overrun";
|
| +
|
| + // We've lost events: check another way to see if we need to shut down.
|
| + base::AutoLock lock(shutdown_lock_);
|
| + if (event_thread_shutdown_) {
|
| + return;
|
| + }
|
| + } else if (err < 0) {
|
| + VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
|
| + return;
|
| + } else {
|
| + // Check for disconnection of out client. This means "shut down".
|
| + if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
|
| + event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE &&
|
| + event->type == SND_SEQ_EVENT_CLIENT_EXIT &&
|
| + event->data.addr.client == out_client_id_) {
|
| + return;
|
| }
|
| - if (revents & POLLIN) {
|
| - size_t read_size = in_devices_[i]->Receive(buffer, kReceiveBufferSize);
|
| - ReceiveMidiData(i, buffer, read_size, now);
|
| +
|
| + std::map<int, uint32>::iterator source_it =
|
| + source_map_.find(AddrToInt(&event->source));
|
| + if (source_it != source_map_.end()) {
|
| + uint32 source = source_it->second;
|
| + if (event->type == SND_SEQ_EVENT_SYSEX) {
|
| + // Special! Variable-length sysex.
|
| + ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
|
| + event->data.ext.len,
|
| + timestamp);
|
| + } else {
|
| + // Otherwise, decode this and send that on.
|
| + unsigned char buf[12];
|
| + long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
|
| + if (count <= 0) {
|
| + if (count != -ENOENT) {
|
| + // ENOENT means that it's not a MIDI message, which is not an
|
| + // error, but other negative values are errors for us.
|
| + VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
|
| + }
|
| + } else {
|
| + ReceiveMidiData(source, buf, count, timestamp);
|
| + }
|
| + }
|
| }
|
| - fds_index += in_devices_[i]->GetPollDescriptorsCount();
|
| }
|
|
|
| // Do again.
|
|
|