Chromium Code Reviews| 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..95c7e8ba70c5bbf3a041fdcb32fd799983507ee0 100644 |
| --- a/media/midi/midi_manager_alsa.cc |
| +++ b/media/midi/midi_manager_alsa.cc |
| @@ -23,276 +23,321 @@ namespace media { |
| namespace { |
| -const size_t kReceiveBufferSize = 4096; |
| -const unsigned short kPollEventMask = POLLIN | POLLERR | POLLNVAL; |
| +const size_t kSendBufferSize = 256; |
|
Takashi Toyoshima
2014/05/07 13:06:13
If larger buffer size improves sysex performance,
Adam Goode
2014/05/08 03:00:54
I put a note here. I don't think a large buffer is
|
| +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; |
| +} |
| } // namespace |
| -class MidiManagerAlsa::MidiDeviceInfo |
| - : public base::RefCounted<MidiDeviceInfo> { |
| - 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); |
| - } |
| +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); |
| +} |
| - 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())); |
| +void MidiManagerAlsa::StartInitialization() { |
| + // 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); |
| + // Nothing to close, just return failure. |
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
| } |
| - |
| - // 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); |
| + 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_); |
| - 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_); |
| + // Name the clients. |
| + err = snd_seq_set_client_name(in_client_, "Google Chrome (input)"); |
| + if (err != 0) { |
| + VLOG(1) << "snd_seq_set_client_name 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); |
| + err = snd_seq_set_client_name(out_client_, "Google Chrome (output)"); |
| + if (err != 0) { |
| + VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err); |
| + return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
| } |
| - unsigned short GetPollDescriptorsRevents(struct pollfd* pfds) { |
| - unsigned short revents; |
| - snd_rawmidi_poll_descriptors_revents(midi_in_, |
| - pfds, |
| - GetPollDescriptorsCount(), |
| - &revents); |
| - return revents; |
| + // 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); |
| } |
| - 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_); |
| - } |
| + // 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); |
| } |
| - bool opened_; |
| - MidiPortInfo port_info_; |
| - snd_rawmidi_t* midi_in_; |
| - snd_rawmidi_t* midi_out_; |
| - |
| - DISALLOW_COPY_AND_ASSIGN(MidiDeviceInfo); |
| -}; |
| - |
| -MidiManagerAlsa::MidiManagerAlsa() |
| - : send_thread_("MidiSendThread"), |
| - event_thread_("MidiEventThread") { |
| - for (size_t i = 0; i < arraysize(pipe_fd_); ++i) |
| - pipe_fd_[i] = -1; |
| -} |
| - |
| -void MidiManagerAlsa::StartInitialization() { |
| - // Enumerate only hardware MIDI devices because software MIDIs running in |
| - // the browser process is not secure. |
| - snd_ctl_card_info_t* card; |
| - snd_rawmidi_info_t* midi_out; |
| - snd_rawmidi_info_t* midi_in; |
| - snd_ctl_card_info_alloca(&card); |
| - snd_rawmidi_info_alloca(&midi_out); |
| - snd_rawmidi_info_alloca(&midi_in); |
| - for (int index = -1; !snd_card_next(&index) && index >= 0; ) { |
| - const std::string id = base::StringPrintf("hw:CARD=%i", index); |
| - snd_ctl_t* handle; |
| - int err = snd_ctl_open(&handle, id.c_str(), 0); |
| - if (err != 0) { |
| - VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); |
| - continue; |
| - } |
| - err = snd_ctl_card_info(handle, card); |
| - if (err != 0) { |
| - VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); |
| - snd_ctl_close(handle); |
| + // 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; |
| + 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; |
| } |
| - for (int device = -1; |
| - !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { |
| - bool output; |
| - bool input; |
| - snd_rawmidi_info_set_device(midi_out, device); |
| - snd_rawmidi_info_set_subdevice(midi_out, 0); |
| - snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); |
| - output = snd_ctl_rawmidi_info(handle, midi_out) == 0; |
| - snd_rawmidi_info_set_device(midi_in, device); |
| - snd_rawmidi_info_set_subdevice(midi_in, 0); |
| - snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); |
| - 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; |
| - } |
| - if (input) { |
| - in_devices_.push_back(port); |
| - AddInputPort(port->GetMidiPortInfo()); |
| - } |
| - if (output) { |
| - out_devices_.push_back(port); |
| - AddOutputPort(port->GetMidiPortInfo()); |
| + snd_seq_port_info_set_client(port_info, client_id); |
| + snd_seq_port_info_set_port(port_info, -1); |
| + // 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()); |
| + |
| + 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, "", name, "")); |
| + } |
| + } |
| + 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, "", name, "")); |
| + } |
| + } |
| } |
| } |
| - 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. |
| + shutdown_mu_.Acquire(); |
| + event_thread_shutdown_ = true; |
| + shutdown_mu_.Release(); |
| + |
| + // 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_); |
| + } |
| - // Stop receiving messages. |
| + // 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; |
| + if (!send_thread_.IsRunning()) |
| + send_thread_.Start(); |
| + |
| + // Compute delay. |
| base::TimeDelta delay; |
| if (timestamp != 0.0) { |
| - base::TimeTicks time_to_send = |
| - base::TimeTicks() + base::TimeDelta::FromMicroseconds( |
| - timestamp * base::Time::kMicrosecondsPerSecond); |
| - delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); |
| + delay = |
| + base::TimeDelta::FromSecondsD(timestamp) - |
| + (base::TimeTicks::HighResNow() - base::TimeTicks()); |
| } |
| - if (!send_thread_.IsRunning()) |
| - send_thread_.Start(); |
| + // Avoid negative delay. This can happen: because of timer jitter? |
| + if (delay < base::TimeDelta()) { |
| + delay = base::TimeDelta(); |
| + } |
|
Takashi Toyoshima
2014/05/07 13:06:13
Just a question, but why do you want to change thi
Adam Goode
2014/05/08 03:00:54
Thanks for pointing this out. I actually think my
|
| - 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_mu_); |
| + 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. |