15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "media/midi/midi_manager_alsa.h"
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <alsa/asoundlib.h>
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <stdlib.h>
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <algorithm>
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <string>
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/bind.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/logging.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/memory/ref_counted.h"
15f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/memory/scoped_vector.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/message_loop/message_loop.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/posix/eintr_wrapper.h"
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/stringprintf.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/threading/thread.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/time/time.h"
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "media/midi/midi_port_info.h"
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace media {
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Per-output buffer. This can be smaller, but then large sysex messages
28f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// will be (harmlessly) split across multiple seq events. This should
29f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// not have any real practical effect, except perhaps to slightly reorder
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// realtime messages with respect to sysex.
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)const size_t kSendBufferSize = 256;
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Constants for the capabilities we search for in inputs and outputs.
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)const unsigned int kRequiredInputPortCaps =
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)const unsigned int kRequiredOutputPortCaps =
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)int AddrToInt(const snd_seq_addr_t* addr) {
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return (addr->client << 8) | addr->port;
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
44f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)class CardInfo {
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public:
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  CardInfo(const std::string name, const std::string manufacturer,
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)           const std::string driver)
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      : name_(name), manufacturer_(manufacturer), driver_(driver) {
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const std::string name_;
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const std::string manufacturer_;
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  const std::string driver_;
53f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)MidiManagerAlsa::MidiManagerAlsa()
58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    : in_client_(NULL),
59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      out_client_(NULL),
60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      out_client_id_(-1),
61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      in_port_(-1),
62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      decoder_(NULL),
63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      send_thread_("MidiSendThread"),
64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      event_thread_("MidiEventThread"),
65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      event_thread_shutdown_(false) {
66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Initialize decoder.
67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_midi_event_new(0, &decoder_);
68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_midi_event_no_status(decoder_, 1);
69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void MidiManagerAlsa::StartInitialization() {
72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Create client handles.
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0);
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err != 0) {
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int in_client_id = snd_seq_client_id(in_client_);
81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  err = snd_seq_open(&out_client_, "hw", SND_SEQ_OPEN_OUTPUT, 0);
82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err != 0) {
83f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
84f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  out_client_id_ = snd_seq_client_id(out_client_);
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
88f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Name the clients.
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  err = snd_seq_set_client_name(in_client_, "Chrome (input)");
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err != 0) {
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
92f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
94f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  err = snd_seq_set_client_name(out_client_, "Chrome (output)");
95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err != 0) {
96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
97f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
100f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Create input port.
101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  in_port_ = snd_seq_create_simple_port(in_client_, NULL,
102f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                        SND_SEQ_PORT_CAP_WRITE |
103f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                        SND_SEQ_PORT_CAP_NO_EXPORT,
104f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                        SND_SEQ_PORT_TYPE_MIDI_GENERIC |
105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                        SND_SEQ_PORT_TYPE_APPLICATION);
106f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (in_port_ < 0) {
107f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_);
108f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
111f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Subscribe to the announce port.
112f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_subscribe_t* subs;
113f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_subscribe_alloca(&subs);
114f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_addr_t announce_sender;
115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_addr_t announce_dest;
116f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
117f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
118f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  announce_dest.client = in_client_id;
119f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  announce_dest.port = in_port_;
120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_subscribe_set_sender(subs, &announce_sender);
121f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_subscribe_set_dest(subs, &announce_dest);
122f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  err = snd_seq_subscribe_port(in_client_, subs);
123f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err != 0) {
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            << snd_strerror(err);
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
127f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
129f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Use a heuristic to extract the list of manufacturers for the hardware MIDI
130f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // devices. This won't work for all devices. It is also brittle until
131f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // hotplug is implemented. (See http://crbug.com/279097.)
132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // TODO(agoode): Make manufacturer extraction simple and reliable.
133f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // http://crbug.com/377250.
134f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ScopedVector<CardInfo> cards;
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_ctl_card_info_t* card;
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_rawmidi_info_t* midi_out;
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_rawmidi_info_t* midi_in;
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_ctl_card_info_alloca(&card);
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_rawmidi_info_alloca(&midi_out);
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  snd_rawmidi_info_alloca(&midi_in);
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (int index = -1; !snd_card_next(&index) && index >= 0; ) {
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string id = base::StringPrintf("hw:CARD=%i", index);
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    snd_ctl_t* handle;
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int err = snd_ctl_open(&handle, id.c_str(), 0);
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (err != 0) {
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    err = snd_ctl_card_info(handle, card);
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (err != 0) {
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_ctl_close(handle);
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
155f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for (int device = -1;
157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)         !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bool output;
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      bool input;
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_device(midi_out, device);
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_subdevice(midi_out, 0);
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_device(midi_in, device);
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_subdevice(midi_in, 0);
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!output && !input)
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        continue;
170f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
171f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
172f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      const std::string name = snd_rawmidi_info_get_name(midi);
173f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // We assume that card longname is in the format of
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // a manufacturer name here.
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      std::string manufacturer;
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      const std::string card_name = snd_ctl_card_info_get_longname(card);
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      size_t at_index = card_name.rfind(" at ");
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (std::string::npos != at_index) {
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        size_t name_index = card_name.rfind(name, at_index - 1);
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (std::string::npos != name_index)
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          manufacturer = card_name.substr(0, name_index - 1);
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      const std::string driver = snd_ctl_card_info_get_driver(card);
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      cards.push_back(new CardInfo(name, manufacturer, driver));
186f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
187f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
188f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
189f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Enumerate all ports in all clients.
190f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_client_info_t* client_info;
191f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_client_info_alloca(&client_info);
192f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_info_t* port_info;
193f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_port_info_alloca(&port_info);
194f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
195f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_client_info_set_client(client_info, -1);
196f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Enumerate clients.
197f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  uint32 current_input = 0;
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  unsigned int current_card = 0;
199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  while (!snd_seq_query_next_client(in_client_, client_info)) {
200f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int client_id = snd_seq_client_info_get_client(client_info);
201f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if ((client_id == in_client_id) || (client_id == out_client_id_)) {
202f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // Skip our own clients.
203f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      continue;
204f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
205f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const std::string client_name = snd_seq_client_info_get_name(client_info);
206f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_seq_port_info_set_client(port_info, client_id);
207f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_seq_port_info_set_port(port_info, -1);
208f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
209f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    std::string manufacturer;
210f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    std::string driver;
211f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // In the current Alsa kernel implementation, hardware clients match the
212f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // cards in the same order.
213f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        (current_card < cards.size())) {
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      const CardInfo* info = cards[current_card];
216f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (info->name_ == client_name) {
217f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        manufacturer = info->manufacturer_;
218f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        driver = info->driver_;
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        current_card++;
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
222f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Enumerate ports.
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    while (!snd_seq_query_next_port(in_client_, port_info)) {
224f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      unsigned int port_type = snd_seq_port_info_get_type(port_info);
225f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        const std::string name = snd_seq_port_info_get_name(port_info);
228f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        const std::string id = base::StringPrintf("%d:%d %s",
229f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                  addr->client,
230f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                  addr->port,
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                  name.c_str());
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        std::string version;
233f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (driver != "") {
234f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          version = driver + " / ";
235f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        }
236f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        version += base::StringPrintf("ALSA library version %d.%d.%d",
237f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                      SND_LIB_MAJOR,
238f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                      SND_LIB_MINOR,
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                      SND_LIB_SUBMINOR);
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        unsigned int caps = snd_seq_port_info_get_capability(port_info);
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          // Subscribe to this port.
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_addr_t dest;
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          dest.client = snd_seq_client_id(in_client_);
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          dest.port = in_port_;
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_port_subscribe_set_sender(subs, sender);
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_port_subscribe_set_dest(subs, &dest);
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          err = snd_seq_subscribe_port(in_client_, subs);
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          if (err != 0) {
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          } else {
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            source_map_[AddrToInt(sender)] = current_input++;
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            AddInputPort(MidiPortInfo(id, manufacturer, name, version));
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          }
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        }
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          // Create a port for us to send on.
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          int out_port =
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)              snd_seq_create_simple_port(out_client_, NULL,
261f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                         SND_SEQ_PORT_CAP_READ |
262f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                         SND_SEQ_PORT_CAP_NO_EXPORT,
263f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                         SND_SEQ_PORT_TYPE_MIDI_GENERIC |
264f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                         SND_SEQ_PORT_TYPE_APPLICATION);
265f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          if (out_port < 0) {
266f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            VLOG(1) << "snd_seq_create_simple_port fails: "
267f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                    << snd_strerror(out_port);
268f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            // Skip this output port for now.
269f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            continue;
270f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          }
271f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          // Activate port subscription.
273f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_addr_t sender;
274f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
275f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          sender.client = snd_seq_client_id(out_client_);
276f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          sender.port = out_port;
277f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_port_subscribe_set_sender(subs, &sender);
278f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          snd_seq_port_subscribe_set_dest(subs, dest);
279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          err = snd_seq_subscribe_port(out_client_, subs);
280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          if (err != 0) {
281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            snd_seq_delete_simple_port(out_client_, out_port);
283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          } else {
284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            snd_midi_event_t* encoder;
285f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            snd_midi_event_new(kSendBufferSize, &encoder);
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            encoders_.push_back(encoder);
287f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            out_ports_.push_back(out_port);
288f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            AddOutputPort(MidiPortInfo(id, manufacturer, name, version));
289f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          }
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        }
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
295f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  event_thread_.Start();
296f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  event_thread_.message_loop()->PostTask(
297f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      FROM_HERE,
298f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
299f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
300f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  CompleteInitialization(MIDI_OK);
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManagerAlsa::~MidiManagerAlsa() {
304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Tell the event thread it will soon be time to shut down. This gives
305f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // message is lost.
307f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  {
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    base::AutoLock lock(shutdown_lock_);
309f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    event_thread_shutdown_ = true;
310f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Stop the send thread.
313f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  send_thread_.Stop();
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
315f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Close the out client. This will trigger the event thread to stop,
316f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // because of SND_SEQ_EVENT_CLIENT_EXIT.
317f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (out_client_)
318f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_seq_close(out_client_);
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Wait for the event thread to stop.
3215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  event_thread_.Stop();
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
323f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Close the in client.
324f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (in_client_)
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_seq_close(in_client_);
326f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
327f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Free the decoder.
328f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_midi_event_free(decoder_);
329f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
330f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Free the encoders.
331f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_midi_event_free(*i);
333f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
334f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
335f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void MidiManagerAlsa::SendMidiData(uint32 port_index,
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                   const std::vector<uint8>& data) {
337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
339f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_midi_event_t* encoder = encoders_[port_index];
340f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (unsigned int i = 0; i < data.size(); i++) {
341f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    snd_seq_event_t event;
342f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    int result = snd_midi_event_encode_byte(encoder, data[i], &event);
343f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (result == 1) {
344f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // Full event, send it.
345f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      snd_seq_ev_set_source(&event, out_ports_[port_index]);
346f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      snd_seq_ev_set_subs(&event);
347f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      snd_seq_ev_set_direct(&event);
348f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      snd_seq_event_output_direct(out_client_, &event);
349f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           uint32 port_index,
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           const std::vector<uint8>& data,
3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           double timestamp) {
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (out_ports_.size() <= port_index)
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Not correct right now. http://crbug.com/374341.
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!send_thread_.IsRunning())
362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    send_thread_.Start();
363f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::TimeDelta delay;
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (timestamp != 0.0) {
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::TimeTicks time_to_send =
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        base::TimeTicks() + base::TimeDelta::FromMicroseconds(
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            timestamp * base::Time::kMicrosecondsPerSecond);
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  send_thread_.message_loop()->PostDelayedTask(
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE,
374f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 port_index, data), delay);
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Acknowledge send.
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  send_thread_.message_loop()->PostTask(
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      FROM_HERE,
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
381f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                 base::Unretained(client), data.size()));
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerAlsa::EventReset() {
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  event_thread_.message_loop()->PostTask(
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE,
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiManagerAlsa::EventLoop() {
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Read available incoming MIDI data.
392f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  snd_seq_event_t* event;
393f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  int err = snd_seq_event_input(in_client_, &event);
394f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  double timestamp =
395f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      (base::TimeTicks::HighResNow() - base::TimeTicks()).InSecondsF();
396f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (err == -ENOSPC) {
397f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    VLOG(1) << "snd_seq_event_input detected buffer overrun";
398f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
399f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // We've lost events: check another way to see if we need to shut down.
400f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      base::AutoLock lock(shutdown_lock_);
401f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (event_thread_shutdown_) {
402f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        return;
403f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
404f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else if (err < 0) {
405f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
406f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return;
407f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
408f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Check for disconnection of out client. This means "shut down".
409f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
410f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE &&
411f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        event->type == SND_SEQ_EVENT_CLIENT_EXIT &&
412f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        event->data.addr.client == out_client_id_) {
413f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return;
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
415f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
416f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    std::map<int, uint32>::iterator source_it =
417f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        source_map_.find(AddrToInt(&event->source));
418f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (source_it != source_map_.end()) {
419f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uint32 source = source_it->second;
420f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (event->type == SND_SEQ_EVENT_SYSEX) {
421f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        // Special! Variable-length sysex.
422f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
423f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                        event->data.ext.len,
424f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                        timestamp);
425f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      } else {
426f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        // Otherwise, decode this and send that on.
427f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        unsigned char buf[12];
428f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
429f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (count <= 0) {
430f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          if (count != -ENOENT) {
431f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            // ENOENT means that it's not a MIDI message, which is not an
432f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            // error, but other negative values are errors for us.
433f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
434f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          }
435f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        } else {
436f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          ReceiveMidiData(source, buf, count, timestamp);
437f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        }
438f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      }
4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Do again.
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  event_thread_.message_loop()->PostTask(
4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE,
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiManager* MidiManager::Create() {
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return new MidiManagerAlsa();
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace media
453