| Index: media/midi/midi_manager_alsa.cc
 | 
| diff --git a/media/midi/midi_manager_alsa.cc b/media/midi/midi_manager_alsa.cc
 | 
| index 6c91e13b476edd3dede2185abb1d16555134ec2a..74e2f421b11968e0fb64c487f9a176a2f6f99cfa 100644
 | 
| --- a/media/midi/midi_manager_alsa.cc
 | 
| +++ b/media/midi/midi_manager_alsa.cc
 | 
| @@ -30,6 +30,14 @@ namespace {
 | 
|  // realtime messages with respect to sysex.
 | 
|  const size_t kSendBufferSize = 256;
 | 
|  
 | 
| +// Minimum client id for which we will have ALSA card devices for. When we
 | 
| +// are searching for card devices (used to get the path, id, and manufacturer),
 | 
| +// we don't want to get confused by kernel clients that do not have a card.
 | 
| +// See seq_clientmgr.c in the ALSA code for this.
 | 
| +// TODO(agoode): Add proper client -> card export from the kernel to avoid
 | 
| +//               hardcoding.
 | 
| +const int kMinimumClientIdForCards = 16;
 | 
| +
 | 
|  // 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 =
 | 
| @@ -60,6 +68,38 @@ MidiManagerAlsa::MidiManagerAlsa()
 | 
|    snd_midi_event_no_status(decoder_, 1);
 | 
|  }
 | 
|  
 | 
| +MidiManagerAlsa::~MidiManagerAlsa() {
 | 
| +  // 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 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();
 | 
| +
 | 
| +  // 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::StartInitialization() {
 | 
|    // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
 | 
|  
 | 
| @@ -118,17 +158,150 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|      return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
 | 
|    }
 | 
|  
 | 
| -  // 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/431489.)
 | 
| -  ScopedVector<CardInfo> cards;
 | 
| +  EnumeratePorts();
 | 
| +
 | 
| +  event_thread_.Start();
 | 
| +  event_thread_.message_loop()->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
 | 
| +
 | 
| +  CompleteInitialization(MIDI_OK);
 | 
| +}
 | 
| +
 | 
| +void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
 | 
| +                                           uint32 port_index,
 | 
| +                                           const std::vector<uint8>& data,
 | 
| +                                           double timestamp) {
 | 
| +  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 =
 | 
| +        base::TimeTicks() + base::TimeDelta::FromMicroseconds(
 | 
| +                                timestamp * base::Time::kMicrosecondsPerSecond);
 | 
| +    delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
 | 
| +  }
 | 
| +
 | 
| +  send_thread_.message_loop()->PostDelayedTask(
 | 
| +      FROM_HERE, 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()));
 | 
| +}
 | 
| +
 | 
| +MidiManagerAlsa::MidiDevice::MidiDevice(const MidiManagerAlsa* outer,
 | 
| +                                        const std::string& alsa_name,
 | 
| +                                        const std::string& alsa_longname,
 | 
| +                                        const std::string& alsa_driver,
 | 
| +                                        int card_index)
 | 
| +    : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
 | 
| +  // Get udev properties if available.
 | 
| +  std::string udev_id_vendor;
 | 
| +  std::string udev_id_vendor_id;
 | 
| +  std::string udev_id_vendor_from_database;
 | 
| +
 | 
| +#if defined(USE_UDEV)
 | 
| +  const std::string sysname = base::StringPrintf("card%i", card_index);
 | 
| +  device::ScopedUdevDevicePtr udev_device(
 | 
| +      device::udev_device_new_from_subsystem_sysname(outer->udev_.get(),
 | 
| +                                                     "sound", sysname.c_str()));
 | 
| +  udev_id_vendor = device::UdevDecodeString(
 | 
| +      device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ENC"));
 | 
| +  udev_id_vendor_id =
 | 
| +      device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ID");
 | 
| +  udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue(
 | 
| +      udev_device.get(), "ID_VENDOR_FROM_DATABASE");
 | 
| +
 | 
| +  udev_id_path_ =
 | 
| +      device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_PATH");
 | 
| +  udev_id_id_ = device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_ID");
 | 
| +#endif  // defined(USE_UDEV)
 | 
| +
 | 
| +  manufacturer_ = ExtractManufacturerString(udev_id_vendor, udev_id_vendor_id,
 | 
| +                                            udev_id_vendor_from_database,
 | 
| +                                            alsa_name, alsa_longname);
 | 
| +}
 | 
| +
 | 
| +MidiManagerAlsa::MidiDevice::~MidiDevice() {
 | 
| +}
 | 
| +
 | 
| +const std::string MidiManagerAlsa::MidiDevice::alsa_name() const {
 | 
| +  return alsa_name_;
 | 
| +}
 | 
| +
 | 
| +const std::string MidiManagerAlsa::MidiDevice::manufacturer() const {
 | 
| +  return manufacturer_;
 | 
| +}
 | 
| +
 | 
| +const std::string MidiManagerAlsa::MidiDevice::alsa_driver() const {
 | 
| +  return alsa_driver_;
 | 
| +}
 | 
| +
 | 
| +const std::string MidiManagerAlsa::MidiDevice::udev_id_path() const {
 | 
| +  return udev_id_path_;
 | 
| +}
 | 
| +
 | 
| +const std::string MidiManagerAlsa::MidiDevice::udev_id_id() const {
 | 
| +  return udev_id_id_;
 | 
| +}
 | 
| +
 | 
| +// static
 | 
| +std::string MidiManagerAlsa::MidiDevice::ExtractManufacturerString(
 | 
| +    const std::string& udev_id_vendor,
 | 
| +    const std::string& udev_id_vendor_id,
 | 
| +    const std::string& udev_id_vendor_from_database,
 | 
| +    const std::string& alsa_name,
 | 
| +    const std::string& alsa_longname) {
 | 
| +  // Let's try to determine the manufacturer. Here is the ordered preference
 | 
| +  // in extraction:
 | 
| +  //  1. Vendor name from the USB device iManufacturer string, from
 | 
| +  //     the udev property ID_VENDOR_ENC.
 | 
| +  //  2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
 | 
| +  //  3. Heuristic from ALSA.
 | 
| +
 | 
| +  // Is the vendor string not just the USB vendor hex id?
 | 
| +  if (udev_id_vendor != udev_id_vendor_id) {
 | 
| +    return udev_id_vendor;
 | 
| +  }
 | 
| +
 | 
| +  // Is there a vendor string in the hardware database?
 | 
| +  if (!udev_id_vendor_from_database.empty()) {
 | 
| +    return udev_id_vendor_from_database;
 | 
| +  }
 | 
| +
 | 
| +  // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
 | 
| +  // We assume that card longname is in the format of
 | 
| +  // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
 | 
| +  // a manufacturer name here.
 | 
| +  size_t at_index = alsa_longname.rfind(" at ");
 | 
| +  if (std::string::npos != at_index) {
 | 
| +    size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
 | 
| +    if (std::string::npos != name_index)
 | 
| +      return alsa_longname.substr(0, name_index - 1);
 | 
| +  }
 | 
| +
 | 
| +  // Failure.
 | 
| +  return "";
 | 
| +}
 | 
| +
 | 
| +ScopedVector<MidiManagerAlsa::MidiDevice> MidiManagerAlsa::AllMidiDevices() {
 | 
| +  ScopedVector<MidiDevice> devices;
 | 
|    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 card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
 | 
| +  for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) {
 | 
|      const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
 | 
|      snd_ctl_t* handle;
 | 
|      int err = snd_ctl_open(&handle, id.c_str(), 0);
 | 
| @@ -142,9 +315,9 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|        snd_ctl_close(handle);
 | 
|        continue;
 | 
|      }
 | 
| -    // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
 | 
| +    // Enumerate any rawmidi devices (not subdevices) and extract MidiDevice.
 | 
|      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);
 | 
| @@ -158,50 +331,67 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|        if (!output && !input)
 | 
|          continue;
 | 
|  
 | 
| -      // Compute and save Alsa and udev properties.
 | 
| +      // Compute and save ALSA and udev properties.
 | 
|        snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
 | 
| -      cards.push_back(new CardInfo(
 | 
| -          this,
 | 
| -          snd_rawmidi_info_get_name(midi),
 | 
| -          snd_ctl_card_info_get_longname(card),
 | 
| -          snd_ctl_card_info_get_driver(card),
 | 
| -          card_index));
 | 
| +      devices.push_back(new MidiDevice(this, snd_rawmidi_info_get_name(midi),
 | 
| +                                       snd_ctl_card_info_get_longname(card),
 | 
| +                                       snd_ctl_card_info_get_driver(card),
 | 
| +                                       card_index));
 | 
|      }
 | 
|      snd_ctl_close(handle);
 | 
|    }
 | 
|  
 | 
| -  // Enumerate all ports in all clients.
 | 
| +  return devices.Pass();
 | 
| +}
 | 
| +
 | 
| +void MidiManagerAlsa::EnumeratePorts() {
 | 
| +  ScopedVector<MidiDevice> devices = AllMidiDevices();
 | 
| +
 | 
| +  snd_seq_port_subscribe_t* subs;
 | 
| +  snd_seq_port_subscribe_alloca(&subs);
 | 
| +
 | 
| +  int in_client_id = snd_seq_client_id(in_client_);
 | 
| +
 | 
| +  // Enumerate 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.
 | 
| +  snd_seq_client_info_set_client(client_info, -1);
 | 
|    uint32 current_input = 0;
 | 
| -  unsigned int current_card = 0;
 | 
| +  unsigned int current_device = 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;
 | 
|      }
 | 
| +
 | 
| +    // Get client metadata.
 | 
|      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.
 | 
| +    std::string udev_id_path;
 | 
| +    std::string udev_id_id;
 | 
| +
 | 
| +    // Join kernel clients against the list of MidiDevices.
 | 
| +    // In the current ALSA kernel implementation, kernel clients match the
 | 
| +    // kernel devices in the same order, for devices with client_id over
 | 
| +    // kMinimumClientIdForCards.
 | 
|      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->alsa_name() == client_name) {
 | 
| -        manufacturer = info->manufacturer();
 | 
| -        driver = info->alsa_driver();
 | 
| -        current_card++;
 | 
| -      }
 | 
| +        (current_device < devices.size()) &&
 | 
| +        (client_id >= kMinimumClientIdForCards)) {
 | 
| +      const MidiDevice* device = devices[current_device];
 | 
| +      manufacturer = device->manufacturer();
 | 
| +      driver = device->alsa_driver();
 | 
| +      udev_id_path = device->udev_id_path();
 | 
| +      udev_id_id = device->udev_id_id();
 | 
| +      current_device++;
 | 
|      }
 | 
|      // Enumerate ports.
 | 
|      while (!snd_seq_query_next_port(in_client_, port_info)) {
 | 
| @@ -209,10 +399,9 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|        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());
 | 
| +        const std::string id_suffix =
 | 
| +            base::StringPrintf("%d:%d/%s/%s", addr->client, addr->port,
 | 
| +                               udev_id_path.c_str(), udev_id_id.c_str());
 | 
|          std::string version;
 | 
|          if (!driver.empty()) {
 | 
|            version = driver + " / ";
 | 
| @@ -230,13 +419,14 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|            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);
 | 
| +          int 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, MIDI_PORT_OPENED));
 | 
| +            AddInputPort(
 | 
| +                MidiPortInfo(base::StringPrintf("input/%s", id_suffix.c_str()),
 | 
| +                             manufacturer, name, version, MIDI_PORT_OPENED));
 | 
|            }
 | 
|          }
 | 
|          if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
 | 
| @@ -261,7 +451,7 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|            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);
 | 
| +          int 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);
 | 
| @@ -270,108 +460,14 @@ void MidiManagerAlsa::StartInitialization() {
 | 
|              snd_midi_event_new(kSendBufferSize, &encoder);
 | 
|              encoders_.push_back(encoder);
 | 
|              out_ports_.push_back(out_port);
 | 
| -            AddOutputPort(MidiPortInfo(
 | 
| -                id, manufacturer, name, version, MIDI_PORT_OPENED));
 | 
| +            AddOutputPort(
 | 
| +                MidiPortInfo(base::StringPrintf("output/%s", id_suffix.c_str()),
 | 
| +                             manufacturer, name, version, MIDI_PORT_OPENED));
 | 
|            }
 | 
|          }
 | 
|        }
 | 
|      }
 | 
|    }
 | 
| -
 | 
| -  event_thread_.Start();
 | 
| -  event_thread_.message_loop()->PostTask(
 | 
| -      FROM_HERE,
 | 
| -      base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
 | 
| -
 | 
| -  CompleteInitialization(MIDI_OK);
 | 
| -}
 | 
| -
 | 
| -MidiManagerAlsa::~MidiManagerAlsa() {
 | 
| -  // 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 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();
 | 
| -
 | 
| -  // 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);
 | 
| -}
 | 
| -
 | 
| -MidiManagerAlsa::CardInfo::CardInfo(
 | 
| -    const MidiManagerAlsa* outer,
 | 
| -    const std::string& alsa_name, const std::string& alsa_longname,
 | 
| -    const std::string& alsa_driver, int card_index)
 | 
| -    : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
 | 
| -  // Get udev properties if available.
 | 
| -  std::string udev_id_vendor;
 | 
| -  std::string udev_id_vendor_id;
 | 
| -  std::string udev_id_vendor_from_database;
 | 
| -
 | 
| -#if defined(USE_UDEV)
 | 
| -  const std::string sysname = base::StringPrintf("card%i", card_index);
 | 
| -  device::ScopedUdevDevicePtr udev_device(
 | 
| -      device::udev_device_new_from_subsystem_sysname(
 | 
| -          outer->udev_.get(), "sound", sysname.c_str()));
 | 
| -  udev_id_vendor = device::UdevDecodeString(device::UdevDeviceGetPropertyValue(
 | 
| -      udev_device.get(), "ID_VENDOR_ENC"));
 | 
| -  udev_id_vendor_id = device::UdevDeviceGetPropertyValue(
 | 
| -      udev_device.get(), "ID_VENDOR_ID");
 | 
| -  udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue(
 | 
| -      udev_device.get(), "ID_VENDOR_FROM_DATABASE");
 | 
| -
 | 
| -  udev_id_path_ = device::UdevDeviceGetPropertyValue(
 | 
| -      udev_device.get(), "ID_PATH");
 | 
| -  udev_id_id_ = device::UdevDeviceGetPropertyValue(
 | 
| -      udev_device.get(), "ID_ID");
 | 
| -#endif  // defined(USE_UDEV)
 | 
| -
 | 
| -  manufacturer_ = ExtractManufacturerString(
 | 
| -      udev_id_vendor, udev_id_vendor_id, udev_id_vendor_from_database,
 | 
| -      alsa_name, alsa_longname);
 | 
| -}
 | 
| -
 | 
| -MidiManagerAlsa::CardInfo::~CardInfo() {
 | 
| -}
 | 
| -
 | 
| -const std::string MidiManagerAlsa::CardInfo::alsa_name() const {
 | 
| -  return alsa_name_;
 | 
| -}
 | 
| -
 | 
| -const std::string MidiManagerAlsa::CardInfo::manufacturer() const {
 | 
| -  return manufacturer_;
 | 
| -}
 | 
| -
 | 
| -const std::string MidiManagerAlsa::CardInfo::alsa_driver() const {
 | 
| -  return alsa_driver_;
 | 
| -}
 | 
| -
 | 
| -const std::string MidiManagerAlsa::CardInfo::udev_id_path() const {
 | 
| -  return udev_id_path_;
 | 
| -}
 | 
| -
 | 
| -const std::string MidiManagerAlsa::CardInfo::udev_id_id() const {
 | 
| -  return udev_id_id_;
 | 
|  }
 | 
|  
 | 
|  void MidiManagerAlsa::SendMidiData(uint32 port_index,
 | 
| @@ -392,38 +488,7 @@ void MidiManagerAlsa::SendMidiData(uint32 port_index,
 | 
|    }
 | 
|  }
 | 
|  
 | 
| -void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
 | 
| -                                           uint32 port_index,
 | 
| -                                           const std::vector<uint8>& data,
 | 
| -                                           double timestamp) {
 | 
| -  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 =
 | 
| -        base::TimeTicks() + base::TimeDelta::FromMicroseconds(
 | 
| -            timestamp * base::Time::kMicrosecondsPerSecond);
 | 
| -    delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
 | 
| -  }
 | 
| -
 | 
| -  send_thread_.message_loop()->PostDelayedTask(
 | 
| -      FROM_HERE,
 | 
| -      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() {
 | 
| +void MidiManagerAlsa::ScheduleEventLoop() {
 | 
|    event_thread_.message_loop()->PostTask(
 | 
|        FROM_HERE,
 | 
|        base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
 | 
| @@ -434,95 +499,79 @@ void MidiManagerAlsa::EventLoop() {
 | 
|    snd_seq_event_t* event;
 | 
|    int err = snd_seq_event_input(in_client_, &event);
 | 
|    double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
 | 
| +
 | 
| +  // Handle errors.
 | 
|    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;
 | 
| -      }
 | 
| +    // We've lost events: check another way to see if we need to shut down.
 | 
| +    base::AutoLock lock(shutdown_lock_);
 | 
| +    if (!event_thread_shutdown_)
 | 
| +      ScheduleEventLoop();
 | 
| +    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;
 | 
| -    }
 | 
| +    VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
 | 
| +    // TODO(agoode): Use RecordAction() or similar to log this.
 | 
| +    return;
 | 
| +  }
 | 
|  
 | 
| -    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);
 | 
| -        }
 | 
| -      }
 | 
| +  // Handle announce events.
 | 
| +  if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
 | 
| +      event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
 | 
| +    switch (event->type) {
 | 
| +      case SND_SEQ_EVENT_CLIENT_START:
 | 
| +        // TODO(agoode): rescan hardware devices.
 | 
| +        break;
 | 
| +
 | 
| +      case SND_SEQ_EVENT_CLIENT_EXIT:
 | 
| +        // Check for disconnection of our "out" client. This means "shut down".
 | 
| +        if (event->data.addr.client == out_client_id_)
 | 
| +          return;
 | 
| +
 | 
| +        // TODO(agoode): remove all ports for a client.
 | 
| +        break;
 | 
| +
 | 
| +      case SND_SEQ_EVENT_PORT_START:
 | 
| +        // TODO(agoode): add port.
 | 
| +        break;
 | 
| +
 | 
| +      case SND_SEQ_EVENT_PORT_EXIT:
 | 
| +        // TODO(agoode): remove port.
 | 
| +        break;
 | 
|      }
 | 
|    }
 | 
|  
 | 
| +  ProcessSingleEvent(event, timestamp);
 | 
| +
 | 
|    // Do again.
 | 
| -  event_thread_.message_loop()->PostTask(
 | 
| -      FROM_HERE,
 | 
| -      base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
 | 
| +  ScheduleEventLoop();
 | 
|  }
 | 
|  
 | 
| -// static
 | 
| -std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString(
 | 
| -    const std::string& udev_id_vendor,
 | 
| -    const std::string& udev_id_vendor_id,
 | 
| -    const std::string& udev_id_vendor_from_database,
 | 
| -    const std::string& alsa_name,
 | 
| -    const std::string& alsa_longname) {
 | 
| -  // Let's try to determine the manufacturer. Here is the ordered preference
 | 
| -  // in extraction:
 | 
| -  //  1. Vendor name from the USB device iManufacturer string, from
 | 
| -  //     the udev property ID_VENDOR_ENC.
 | 
| -  //  2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
 | 
| -  //  3. Heuristic from ALSA.
 | 
| -
 | 
| -  // Is the vendor string not just the USB vendor hex id?
 | 
| -  if (udev_id_vendor != udev_id_vendor_id) {
 | 
| -    return udev_id_vendor;
 | 
| -  }
 | 
| -
 | 
| -  // Is there a vendor string in the hardware database?
 | 
| -  if (!udev_id_vendor_from_database.empty()) {
 | 
| -    return udev_id_vendor_from_database;
 | 
| -  }
 | 
| -
 | 
| -  // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
 | 
| -  // We assume that card longname is in the format of
 | 
| -  // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
 | 
| -  // a manufacturer name here.
 | 
| -  size_t at_index = alsa_longname.rfind(" at ");
 | 
| -  if (std::string::npos != at_index) {
 | 
| -    size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
 | 
| -    if (std::string::npos != name_index)
 | 
| -      return alsa_longname.substr(0, name_index - 1);
 | 
| +void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
 | 
| +                                         double timestamp) {
 | 
| +  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);
 | 
| +          // TODO(agoode): Record this failure.
 | 
| +        }
 | 
| +      } else {
 | 
| +        ReceiveMidiData(source, buf, count, timestamp);
 | 
| +      }
 | 
| +    }
 | 
|    }
 | 
| -
 | 
| -  // Failure.
 | 
| -  return "";
 | 
|  }
 | 
|  
 | 
|  MidiManager* MidiManager::Create() {
 | 
| 
 |