OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/midi/midi_manager_alsa.h" | 5 #include "media/midi/midi_manager_alsa.h" |
6 | 6 |
7 #include <alsa/asoundlib.h> | 7 #include <alsa/asoundlib.h> |
8 #include <stdlib.h> | 8 #include <stdlib.h> |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <string> | 10 #include <string> |
11 | 11 |
12 #include "base/bind.h" | 12 #include "base/bind.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/memory/ref_counted.h" | 14 #include "base/memory/ref_counted.h" |
15 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
16 #include "base/posix/eintr_wrapper.h" | 16 #include "base/posix/eintr_wrapper.h" |
17 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
18 #include "base/threading/thread.h" | 18 #include "base/threading/thread.h" |
19 #include "base/time/time.h" | 19 #include "base/time/time.h" |
20 #include "media/midi/midi_port_info.h" | 20 #include "media/midi/midi_port_info.h" |
21 | 21 |
22 namespace media { | 22 namespace media { |
23 | 23 |
24 namespace { | 24 namespace { |
25 | 25 |
26 const size_t kReceiveBufferSize = 4096; | 26 // Per-output buffer. This can be smaller, but then large sysex messages |
27 const unsigned short kPollEventMask = POLLIN | POLLERR | POLLNVAL; | 27 // will be (harmlessly) split across multiple seq events. This should |
28 // not have any real practical effect, except perhaps to slightly reorder | |
29 // realtime messages with respect to sysex. | |
30 const size_t kSendBufferSize = 256; | |
31 | |
32 // Constants for the capabilities we search for in inputs and outputs. | |
33 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. | |
34 const unsigned int kRequiredInputPortCaps = | |
35 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; | |
36 const unsigned int kRequiredOutputPortCaps = | |
37 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; | |
38 | |
39 int AddrToInt(const snd_seq_addr_t* addr) { | |
40 return (addr->client << 8) | addr->port; | |
41 } | |
28 | 42 |
29 } // namespace | 43 } // namespace |
30 | 44 |
31 class MidiManagerAlsa::MidiDeviceInfo | |
32 : public base::RefCounted<MidiDeviceInfo> { | |
33 public: | |
34 MidiDeviceInfo(MidiManagerAlsa* manager, | |
35 const std::string& bus_id, | |
36 snd_ctl_card_info_t* card, | |
37 const snd_rawmidi_info_t* midi, | |
38 int device) { | |
39 opened_ = !snd_rawmidi_open(&midi_in_, &midi_out_, bus_id.c_str(), 0); | |
40 if (!opened_) | |
41 return; | |
42 | |
43 const std::string id = base::StringPrintf("%s:%d", bus_id.c_str(), device); | |
44 const std::string name = snd_rawmidi_info_get_name(midi); | |
45 // We assume that card longname is in the format of | |
46 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect | |
47 // a manufacturer name here. | |
48 std::string manufacturer; | |
49 const std::string card_name = snd_ctl_card_info_get_longname(card); | |
50 size_t name_index = card_name.find(name); | |
51 if (std::string::npos != name_index) | |
52 manufacturer = card_name.substr(0, name_index - 1); | |
53 const std::string version = | |
54 base::StringPrintf("%s / ALSA library version %d.%d.%d", | |
55 snd_ctl_card_info_get_driver(card), | |
56 SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR); | |
57 port_info_ = MidiPortInfo(id, manufacturer, name, version); | |
58 } | |
59 | |
60 void Send(MidiManagerClient* client, const std::vector<uint8>& data) { | |
61 ssize_t result = snd_rawmidi_write( | |
62 midi_out_, reinterpret_cast<const void*>(&data[0]), data.size()); | |
63 if (static_cast<size_t>(result) != data.size()) { | |
64 // TODO(toyoshim): Handle device disconnection. | |
65 VLOG(1) << "snd_rawmidi_write fails: " << strerror(-result); | |
66 } | |
67 base::MessageLoop::current()->PostTask( | |
68 FROM_HERE, | |
69 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, | |
70 base::Unretained(client), data.size())); | |
71 } | |
72 | |
73 // Read input data from a MIDI input device which is ready to read through | |
74 // the ALSA library. Called from EventLoop() and read data will be sent to | |
75 // blink through MIDIManager base class. | |
76 size_t Receive(uint8* data, size_t length) { | |
77 return snd_rawmidi_read(midi_in_, reinterpret_cast<void*>(data), length); | |
78 } | |
79 | |
80 const MidiPortInfo& GetMidiPortInfo() const { return port_info_; } | |
81 | |
82 // Get the number of descriptors which is required to call poll() on the | |
83 // device. The ALSA library always returns 1 here now, but it may be changed | |
84 // in the future. | |
85 int GetPollDescriptorsCount() { | |
86 return snd_rawmidi_poll_descriptors_count(midi_in_); | |
87 } | |
88 | |
89 // Following API initializes pollfds for polling the device, and returns the | |
90 // number of descriptors they are initialized. It must be the same value with | |
91 // snd_rawmidi_poll_descriptors_count(). | |
92 int SetupPollDescriptors(struct pollfd* pfds, unsigned int count) { | |
93 return snd_rawmidi_poll_descriptors(midi_in_, pfds, count); | |
94 } | |
95 | |
96 unsigned short GetPollDescriptorsRevents(struct pollfd* pfds) { | |
97 unsigned short revents; | |
98 snd_rawmidi_poll_descriptors_revents(midi_in_, | |
99 pfds, | |
100 GetPollDescriptorsCount(), | |
101 &revents); | |
102 return revents; | |
103 } | |
104 | |
105 bool IsOpened() const { return opened_; } | |
106 | |
107 private: | |
108 friend class base::RefCounted<MidiDeviceInfo>; | |
109 virtual ~MidiDeviceInfo() { | |
110 if (opened_) { | |
111 snd_rawmidi_close(midi_in_); | |
112 snd_rawmidi_close(midi_out_); | |
113 } | |
114 } | |
115 | |
116 bool opened_; | |
117 MidiPortInfo port_info_; | |
118 snd_rawmidi_t* midi_in_; | |
119 snd_rawmidi_t* midi_out_; | |
120 | |
121 DISALLOW_COPY_AND_ASSIGN(MidiDeviceInfo); | |
122 }; | |
123 | |
124 MidiManagerAlsa::MidiManagerAlsa() | 45 MidiManagerAlsa::MidiManagerAlsa() |
125 : send_thread_("MidiSendThread"), | 46 : in_client_(NULL), |
126 event_thread_("MidiEventThread") { | 47 out_client_(NULL), |
127 for (size_t i = 0; i < arraysize(pipe_fd_); ++i) | 48 out_client_id_(-1), |
128 pipe_fd_[i] = -1; | 49 in_port_(-1), |
50 decoder_(NULL), | |
51 send_thread_("MidiSendThread"), | |
52 event_thread_("MidiEventThread"), | |
53 event_thread_shutdown_(false) { | |
54 // Initialize decoder. | |
55 snd_midi_event_new(0, &decoder_); | |
56 snd_midi_event_no_status(decoder_, 1); | |
129 } | 57 } |
130 | 58 |
131 void MidiManagerAlsa::StartInitialization() { | 59 void MidiManagerAlsa::StartInitialization() { |
132 // Enumerate only hardware MIDI devices because software MIDIs running in | 60 // TODO(agoode): Move off I/O thread. |
133 // the browser process is not secure. | 61 |
134 snd_ctl_card_info_t* card; | 62 // Create client handles. |
135 snd_rawmidi_info_t* midi_out; | 63 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0); |
136 snd_rawmidi_info_t* midi_in; | 64 if (err != 0) { |
137 snd_ctl_card_info_alloca(&card); | 65 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err); |
138 snd_rawmidi_info_alloca(&midi_out); | 66 // Nothing to close, just return failure. |
139 snd_rawmidi_info_alloca(&midi_in); | 67 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
140 for (int index = -1; !snd_card_next(&index) && index >= 0; ) { | 68 } |
141 const std::string id = base::StringPrintf("hw:CARD=%i", index); | 69 int in_client_id = snd_seq_client_id(in_client_); |
142 snd_ctl_t* handle; | 70 err = snd_seq_open(&out_client_, "hw", SND_SEQ_OPEN_OUTPUT, 0); |
143 int err = snd_ctl_open(&handle, id.c_str(), 0); | 71 if (err != 0) { |
144 if (err != 0) { | 72 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err); |
145 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); | 73 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
74 } | |
75 out_client_id_ = snd_seq_client_id(out_client_); | |
76 | |
77 // Name the clients. | |
78 err = snd_seq_set_client_name(in_client_, "Chrome (input)"); | |
79 if (err != 0) { | |
80 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err); | |
81 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | |
82 } | |
83 err = snd_seq_set_client_name(out_client_, "Chrome (output)"); | |
84 if (err != 0) { | |
85 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err); | |
86 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | |
87 } | |
88 | |
89 // Create input port. | |
90 in_port_ = snd_seq_create_simple_port(in_client_, NULL, | |
91 SND_SEQ_PORT_CAP_WRITE | | |
92 SND_SEQ_PORT_CAP_NO_EXPORT, | |
93 SND_SEQ_PORT_TYPE_MIDI_GENERIC | | |
94 SND_SEQ_PORT_TYPE_APPLICATION); | |
95 if (in_port_ < 0) { | |
96 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_); | |
97 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | |
98 } | |
99 | |
100 // Subscribe to the announce port. | |
101 snd_seq_port_subscribe_t* subs; | |
102 snd_seq_port_subscribe_alloca(&subs); | |
103 snd_seq_addr_t announce_sender; | |
104 snd_seq_addr_t announce_dest; | |
105 announce_sender.client = SND_SEQ_CLIENT_SYSTEM; | |
106 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; | |
107 announce_dest.client = in_client_id; | |
108 announce_dest.port = in_port_; | |
109 snd_seq_port_subscribe_set_sender(subs, &announce_sender); | |
110 snd_seq_port_subscribe_set_dest(subs, &announce_dest); | |
111 err = snd_seq_subscribe_port(in_client_, subs); | |
112 if (err != 0) { | |
113 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " | |
114 << snd_strerror(err); | |
115 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | |
116 } | |
117 | |
118 // Enumerate all ports in all clients. | |
119 snd_seq_client_info_t* client_info; | |
120 snd_seq_client_info_alloca(&client_info); | |
121 snd_seq_port_info_t* port_info; | |
122 snd_seq_port_info_alloca(&port_info); | |
123 | |
124 snd_seq_client_info_set_client(client_info, -1); | |
125 // Enumerate clients. | |
126 uint32 current_input = 0; | |
127 while (!snd_seq_query_next_client(in_client_, client_info)) { | |
128 int client_id = snd_seq_client_info_get_client(client_info); | |
129 if ((client_id == in_client_id) || (client_id == out_client_id_)) { | |
130 // Skip our own clients. | |
146 continue; | 131 continue; |
147 } | 132 } |
148 err = snd_ctl_card_info(handle, card); | 133 snd_seq_port_info_set_client(port_info, client_id); |
149 if (err != 0) { | 134 snd_seq_port_info_set_port(port_info, -1); |
150 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); | 135 // Enumerate ports. |
151 snd_ctl_close(handle); | 136 while (!snd_seq_query_next_port(in_client_, port_info)) { |
152 continue; | 137 unsigned int port_type = snd_seq_port_info_get_type(port_info); |
153 } | 138 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { |
154 for (int device = -1; | 139 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info); |
155 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { | 140 const std::string name = snd_seq_port_info_get_name(port_info); |
156 bool output; | 141 const std::string id = base::StringPrintf("%d:%d %s", |
157 bool input; | 142 addr->client, |
158 snd_rawmidi_info_set_device(midi_out, device); | 143 addr->port, |
159 snd_rawmidi_info_set_subdevice(midi_out, 0); | 144 name.c_str()); |
160 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); | 145 // TODO(agoode): Determine good values for manufacturer and version. |
Takashi Toyoshima
2014/05/16 19:31:52
If it takes time, filing a bug and adding the bug
Adam Goode
2014/05/25 04:57:06
I implemented it. And filed a bug to make it bette
| |
161 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; | 146 |
162 snd_rawmidi_info_set_device(midi_in, device); | 147 unsigned int caps = snd_seq_port_info_get_capability(port_info); |
163 snd_rawmidi_info_set_subdevice(midi_in, 0); | 148 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) { |
164 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); | 149 // Subscribe to this port. |
165 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; | 150 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info); |
166 if (!output && !input) | 151 snd_seq_addr_t dest; |
167 continue; | 152 dest.client = snd_seq_client_id(in_client_); |
168 scoped_refptr<MidiDeviceInfo> port = new MidiDeviceInfo( | 153 dest.port = in_port_; |
169 this, id, card, output ? midi_out : midi_in, device); | 154 snd_seq_port_subscribe_set_sender(subs, sender); |
170 if (!port->IsOpened()) { | 155 snd_seq_port_subscribe_set_dest(subs, &dest); |
171 VLOG(1) << "MidiDeviceInfo open fails"; | 156 err = snd_seq_subscribe_port(in_client_, subs); |
172 continue; | 157 if (err != 0) { |
173 } | 158 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); |
174 if (input) { | 159 } else { |
175 in_devices_.push_back(port); | 160 source_map_[AddrToInt(sender)] = current_input++; |
176 AddInputPort(port->GetMidiPortInfo()); | 161 AddInputPort(MidiPortInfo(id, "", name, "")); |
177 } | 162 } |
178 if (output) { | 163 } |
179 out_devices_.push_back(port); | 164 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) { |
180 AddOutputPort(port->GetMidiPortInfo()); | 165 // Create a port for us to send on. |
166 int out_port = | |
167 snd_seq_create_simple_port(out_client_, NULL, | |
168 SND_SEQ_PORT_CAP_READ | | |
169 SND_SEQ_PORT_CAP_NO_EXPORT, | |
170 SND_SEQ_PORT_TYPE_MIDI_GENERIC | | |
171 SND_SEQ_PORT_TYPE_APPLICATION); | |
172 if (out_port < 0) { | |
173 VLOG(1) << "snd_seq_create_simple_port fails: " | |
174 << snd_strerror(out_port); | |
175 // Skip this output port for now. | |
176 continue; | |
177 } | |
178 | |
179 // Activate port subscription. | |
180 snd_seq_addr_t sender; | |
181 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info); | |
182 sender.client = snd_seq_client_id(out_client_); | |
183 sender.port = out_port; | |
184 snd_seq_port_subscribe_set_sender(subs, &sender); | |
185 snd_seq_port_subscribe_set_dest(subs, dest); | |
186 err = snd_seq_subscribe_port(out_client_, subs); | |
187 if (err != 0) { | |
188 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); | |
189 snd_seq_delete_simple_port(out_client_, out_port); | |
190 } else { | |
191 snd_midi_event_t* encoder; | |
192 snd_midi_event_new(kSendBufferSize, &encoder); | |
193 encoders_.push_back(encoder); | |
194 out_ports_.push_back(out_port); | |
195 AddOutputPort(MidiPortInfo(id, "", name, "")); | |
196 } | |
197 } | |
181 } | 198 } |
182 } | 199 } |
183 snd_ctl_close(handle); | 200 } |
184 } | 201 |
185 | 202 event_thread_.Start(); |
186 if (pipe(pipe_fd_) < 0) { | 203 event_thread_.message_loop()->PostTask( |
187 VPLOG(1) << "pipe() failed"; | 204 FROM_HERE, |
188 CompleteInitialization(MIDI_INITIALIZATION_ERROR); | 205 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this))); |
189 } else { | 206 |
190 event_thread_.Start(); | 207 CompleteInitialization(MIDI_OK); |
191 event_thread_.message_loop()->PostTask( | |
192 FROM_HERE, | |
193 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this))); | |
194 CompleteInitialization(MIDI_OK); | |
195 } | |
196 } | 208 } |
197 | 209 |
198 MidiManagerAlsa::~MidiManagerAlsa() { | 210 MidiManagerAlsa::~MidiManagerAlsa() { |
199 // Send a shutdown message to awake |event_thread_| from poll(). | 211 // Tell the event thread it will soon be time to shut down. This gives |
200 if (pipe_fd_[1] >= 0) | 212 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT |
201 HANDLE_EINTR(write(pipe_fd_[1], "Q", 1)); | 213 // message is lost. |
202 | 214 { |
203 // Stop receiving messages. | 215 base::AutoLock lock(shutdown_lock_); |
216 event_thread_shutdown_ = true; | |
217 } | |
218 | |
219 // Stop the send thread. | |
220 send_thread_.Stop(); | |
221 | |
222 // Close the out client. This will trigger the event thread to stop, | |
223 // because of SND_SEQ_EVENT_CLIENT_EXIT. | |
224 if (out_client_) { | |
225 snd_seq_close(out_client_); | |
226 } | |
227 | |
228 // Wait for the event thread to stop. | |
204 event_thread_.Stop(); | 229 event_thread_.Stop(); |
205 | 230 |
206 for (int i = 0; i < 2; ++i) { | 231 // Close the in client. |
207 if (pipe_fd_[i] >= 0) | 232 if (in_client_) |
208 close(pipe_fd_[i]); | 233 snd_seq_close(in_client_); |
209 } | 234 |
210 send_thread_.Stop(); | 235 // Free the decoder. |
236 snd_midi_event_free(decoder_); | |
237 | |
238 // Free the encoders. | |
239 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i) | |
240 snd_midi_event_free(*i); | |
241 } | |
242 | |
243 void MidiManagerAlsa::SendMidiData(uint32 port_index, | |
244 const std::vector<uint8>& data) { | |
245 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
246 | |
247 snd_midi_event_t* encoder = encoders_[port_index]; | |
248 for (unsigned int i = 0; i < data.size(); i++) { | |
249 snd_seq_event_t event; | |
250 int result = snd_midi_event_encode_byte(encoder, data[i], &event); | |
251 if (result == 1) { | |
252 // Full event, send it. | |
253 snd_seq_ev_set_source(&event, out_ports_[port_index]); | |
254 snd_seq_ev_set_subs(&event); | |
255 snd_seq_ev_set_direct(&event); | |
256 snd_seq_event_output_direct(out_client_, &event); | |
257 } | |
258 } | |
211 } | 259 } |
212 | 260 |
213 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, | 261 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, |
214 uint32 port_index, | 262 uint32 port_index, |
215 const std::vector<uint8>& data, | 263 const std::vector<uint8>& data, |
216 double timestamp) { | 264 double timestamp) { |
217 if (out_devices_.size() <= port_index) | 265 if (out_ports_.size() <= port_index) |
218 return; | 266 return; |
219 | 267 |
268 // TODO(agoode): Verify that Stop() is called on the same thread. | |
269 if (!send_thread_.IsRunning()) | |
270 send_thread_.Start(); | |
271 | |
220 base::TimeDelta delay; | 272 base::TimeDelta delay; |
221 if (timestamp != 0.0) { | 273 if (timestamp != 0.0) { |
222 base::TimeTicks time_to_send = | 274 base::TimeTicks time_to_send = |
223 base::TimeTicks() + base::TimeDelta::FromMicroseconds( | 275 base::TimeTicks() + base::TimeDelta::FromMicroseconds( |
224 timestamp * base::Time::kMicrosecondsPerSecond); | 276 timestamp * base::Time::kMicrosecondsPerSecond); |
225 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); | 277 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); |
226 } | 278 } |
227 | 279 |
228 if (!send_thread_.IsRunning()) | |
229 send_thread_.Start(); | |
230 | |
231 scoped_refptr<MidiDeviceInfo> device = out_devices_[port_index]; | |
232 send_thread_.message_loop()->PostDelayedTask( | 280 send_thread_.message_loop()->PostDelayedTask( |
233 FROM_HERE, | 281 FROM_HERE, |
234 base::Bind(&MidiDeviceInfo::Send, device, client, data), | 282 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this), |
235 delay); | 283 port_index, data), delay); |
284 | |
285 // Acknowledge send. | |
286 send_thread_.message_loop()->PostTask( | |
287 FROM_HERE, | |
288 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, | |
289 base::Unretained(client), data.size())); | |
236 } | 290 } |
237 | 291 |
238 void MidiManagerAlsa::EventReset() { | 292 void MidiManagerAlsa::EventReset() { |
239 CHECK_GE(pipe_fd_[0], 0); | |
240 | |
241 // Sum up descriptors which are needed to poll input devices and a shutdown | |
242 // message. | |
243 // Keep the first one descriptor for a shutdown message. | |
244 size_t poll_fds_size = 1; | |
245 for (size_t i = 0; i < in_devices_.size(); ++i) | |
246 poll_fds_size += in_devices_[i]->GetPollDescriptorsCount(); | |
247 poll_fds_.resize(poll_fds_size); | |
248 | |
249 // Setup struct pollfd to poll input MIDI devices and a shutdown message. | |
250 // The first pollfd is for a shutdown message. | |
251 poll_fds_[0].fd = pipe_fd_[0]; | |
252 poll_fds_[0].events = kPollEventMask; | |
253 int fds_index = 1; | |
254 for (size_t i = 0; i < in_devices_.size(); ++i) { | |
255 fds_index += in_devices_[i]->SetupPollDescriptors( | |
256 &poll_fds_[fds_index], poll_fds_.size() - fds_index); | |
257 } | |
258 | |
259 event_thread_.message_loop()->PostTask( | 293 event_thread_.message_loop()->PostTask( |
260 FROM_HERE, | 294 FROM_HERE, |
261 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); | 295 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); |
262 } | 296 } |
263 | 297 |
264 void MidiManagerAlsa::EventLoop() { | 298 void MidiManagerAlsa::EventLoop() { |
265 if (HANDLE_EINTR(poll(&poll_fds_[0], poll_fds_.size(), -1)) < 0) { | 299 // Read available incoming MIDI data. |
266 VPLOG(1) << "Couldn't poll(). Stop to poll input MIDI devices."; | 300 snd_seq_event_t* event; |
267 // TODO(toyoshim): Handle device disconnection, and try to reconnect? | 301 int err = snd_seq_event_input(in_client_, &event); |
268 return; | 302 double timestamp = |
269 } | 303 (base::TimeTicks::HighResNow() - base::TimeTicks()).InSecondsF(); |
304 if (err == -ENOSPC) { | |
305 VLOG(1) << "snd_seq_event_input detected buffer overrun"; | |
270 | 306 |
271 // Check timestamp as soon as possible because the API requires accurate | 307 // We've lost events: check another way to see if we need to shut down. |
272 // timestamp as possible. It will be useful for recording MIDI events. | 308 base::AutoLock lock(shutdown_lock_); |
273 base::TimeTicks now = base::TimeTicks::HighResNow(); | 309 if (event_thread_shutdown_) { |
310 return; | |
311 } | |
312 } else if (err < 0) { | |
313 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err); | |
314 return; | |
315 } else { | |
316 // Check for disconnection of out client. This means "shut down". | |
317 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && | |
318 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE && | |
319 event->type == SND_SEQ_EVENT_CLIENT_EXIT && | |
320 event->data.addr.client == out_client_id_) { | |
321 return; | |
322 } | |
274 | 323 |
275 // Is this thread going to be shutdown? | 324 std::map<int, uint32>::iterator source_it = |
276 if (poll_fds_[0].revents & kPollEventMask) | 325 source_map_.find(AddrToInt(&event->source)); |
277 return; | 326 if (source_it != source_map_.end()) { |
278 | 327 uint32 source = source_it->second; |
279 // Read available incoming MIDI data. | 328 if (event->type == SND_SEQ_EVENT_SYSEX) { |
280 int fds_index = 1; | 329 // Special! Variable-length sysex. |
281 uint8 buffer[kReceiveBufferSize]; | 330 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), |
282 | 331 event->data.ext.len, |
283 for (size_t i = 0; i < in_devices_.size(); ++i) { | 332 timestamp); |
284 unsigned short revents = | 333 } else { |
285 in_devices_[i]->GetPollDescriptorsRevents(&poll_fds_[fds_index]); | 334 // Otherwise, decode this and send that on. |
286 if (revents & (POLLERR | POLLNVAL)) { | 335 unsigned char buf[12]; |
287 // TODO(toyoshim): Handle device disconnection. | 336 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event); |
288 VLOG(1) << "snd_rawmidi_descriptors_revents fails"; | 337 if (count <= 0) { |
289 poll_fds_[fds_index].events = 0; | 338 if (count != -ENOENT) { |
339 // ENOENT means that it's not a MIDI message, which is not an | |
340 // error, but other negative values are errors for us. | |
341 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count); | |
342 } | |
343 } else { | |
344 ReceiveMidiData(source, buf, count, timestamp); | |
345 } | |
346 } | |
290 } | 347 } |
291 if (revents & POLLIN) { | |
292 size_t read_size = in_devices_[i]->Receive(buffer, kReceiveBufferSize); | |
293 ReceiveMidiData(i, buffer, read_size, now); | |
294 } | |
295 fds_index += in_devices_[i]->GetPollDescriptorsCount(); | |
296 } | 348 } |
297 | 349 |
298 // Do again. | 350 // Do again. |
299 event_thread_.message_loop()->PostTask( | 351 event_thread_.message_loop()->PostTask( |
300 FROM_HERE, | 352 FROM_HERE, |
301 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); | 353 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); |
302 } | 354 } |
303 | 355 |
304 MidiManager* MidiManager::Create() { | 356 MidiManager* MidiManager::Create() { |
305 return new MidiManagerAlsa(); | 357 return new MidiManagerAlsa(); |
306 } | 358 } |
307 | 359 |
308 } // namespace media | 360 } // namespace media |
OLD | NEW |