Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(380)

Side by Side Diff: media/midi/midi_manager_alsa.cc

Issue 264973012: Use seq instead of rawmidi for MidiManagerAlsa (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix small mistake in midi_manager_unittest Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 }
42
43 class CardInfo {
44 public:
45 CardInfo(std::string name, std::string manufacturer,
Takashi Toyoshima 2014/06/04 20:28:17 can't we make these arguments const?
Adam Goode 2014/06/08 02:52:58 Yes, by changing to ScopedVector below.
46 std::string driver)
47 : name_(name), manufacturer_(manufacturer), driver_(driver) {
48 }
49 std::string name_;
50 std::string manufacturer_;
51 std::string driver_;
52 };
28 53
29 } // namespace 54 } // namespace
30 55
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() 56 MidiManagerAlsa::MidiManagerAlsa()
125 : send_thread_("MidiSendThread"), 57 : in_client_(NULL),
126 event_thread_("MidiEventThread") { 58 out_client_(NULL),
127 for (size_t i = 0; i < arraysize(pipe_fd_); ++i) 59 out_client_id_(-1),
128 pipe_fd_[i] = -1; 60 in_port_(-1),
61 decoder_(NULL),
62 send_thread_("MidiSendThread"),
63 event_thread_("MidiEventThread"),
64 event_thread_shutdown_(false) {
65 // Initialize decoder.
66 snd_midi_event_new(0, &decoder_);
67 snd_midi_event_no_status(decoder_, 1);
129 } 68 }
130 69
131 void MidiManagerAlsa::StartInitialization() { 70 void MidiManagerAlsa::StartInitialization() {
132 // Enumerate only hardware MIDI devices because software MIDIs running in 71 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
133 // the browser process is not secure. 72
73 // Create client handles.
74 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0);
75 if (err != 0) {
76 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
77 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
78 }
79 int in_client_id = snd_seq_client_id(in_client_);
80 err = snd_seq_open(&out_client_, "hw", SND_SEQ_OPEN_OUTPUT, 0);
81 if (err != 0) {
82 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
83 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
84 }
85 out_client_id_ = snd_seq_client_id(out_client_);
86
87 // Name the clients.
88 err = snd_seq_set_client_name(in_client_, "Chrome (input)");
89 if (err != 0) {
90 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
91 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
92 }
93 err = snd_seq_set_client_name(out_client_, "Chrome (output)");
94 if (err != 0) {
95 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
96 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
97 }
98
99 // Create input port.
100 in_port_ = snd_seq_create_simple_port(in_client_, NULL,
101 SND_SEQ_PORT_CAP_WRITE |
102 SND_SEQ_PORT_CAP_NO_EXPORT,
103 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
104 SND_SEQ_PORT_TYPE_APPLICATION);
105 if (in_port_ < 0) {
106 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_);
107 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
108 }
109
110 // Subscribe to the announce port.
111 snd_seq_port_subscribe_t* subs;
112 snd_seq_port_subscribe_alloca(&subs);
113 snd_seq_addr_t announce_sender;
114 snd_seq_addr_t announce_dest;
115 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
116 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
117 announce_dest.client = in_client_id;
118 announce_dest.port = in_port_;
119 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
120 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
121 err = snd_seq_subscribe_port(in_client_, subs);
122 if (err != 0) {
123 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
124 << snd_strerror(err);
125 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
126 }
127
128 // Use a heuristic to extract the list of manufacturers for the hardware MIDI
129 // devices. This won't work for all devices. It is also brittle until
130 // hotplug is implemented. (See http://crbug.com/279097.)
131 // TODO(agoode): Make manufacturer extraction simple and reliable.
132 // http://crbug.com/377250.
133 std::vector<CardInfo> cards;
134 snd_ctl_card_info_t* card; 134 snd_ctl_card_info_t* card;
135 snd_rawmidi_info_t* midi_out; 135 snd_rawmidi_info_t* midi_out;
136 snd_rawmidi_info_t* midi_in; 136 snd_rawmidi_info_t* midi_in;
137 snd_ctl_card_info_alloca(&card); 137 snd_ctl_card_info_alloca(&card);
138 snd_rawmidi_info_alloca(&midi_out); 138 snd_rawmidi_info_alloca(&midi_out);
139 snd_rawmidi_info_alloca(&midi_in); 139 snd_rawmidi_info_alloca(&midi_in);
140 for (int index = -1; !snd_card_next(&index) && index >= 0; ) { 140 for (int index = -1; !snd_card_next(&index) && index >= 0; ) {
141 const std::string id = base::StringPrintf("hw:CARD=%i", index); 141 const std::string id = base::StringPrintf("hw:CARD=%i", index);
142 snd_ctl_t* handle; 142 snd_ctl_t* handle;
143 int err = snd_ctl_open(&handle, id.c_str(), 0); 143 int err = snd_ctl_open(&handle, id.c_str(), 0);
144 if (err != 0) { 144 if (err != 0) {
145 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); 145 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
146 continue; 146 continue;
147 } 147 }
148 err = snd_ctl_card_info(handle, card); 148 err = snd_ctl_card_info(handle, card);
149 if (err != 0) { 149 if (err != 0) {
150 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); 150 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
151 snd_ctl_close(handle); 151 snd_ctl_close(handle);
152 continue; 152 continue;
153 } 153 }
154 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
154 for (int device = -1; 155 for (int device = -1;
155 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { 156 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
156 bool output; 157 bool output;
157 bool input; 158 bool input;
158 snd_rawmidi_info_set_device(midi_out, device); 159 snd_rawmidi_info_set_device(midi_out, device);
159 snd_rawmidi_info_set_subdevice(midi_out, 0); 160 snd_rawmidi_info_set_subdevice(midi_out, 0);
160 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); 161 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
161 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; 162 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
162 snd_rawmidi_info_set_device(midi_in, device); 163 snd_rawmidi_info_set_device(midi_in, device);
163 snd_rawmidi_info_set_subdevice(midi_in, 0); 164 snd_rawmidi_info_set_subdevice(midi_in, 0);
164 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); 165 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
165 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; 166 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
166 if (!output && !input) 167 if (!output && !input)
167 continue; 168 continue;
168 scoped_refptr<MidiDeviceInfo> port = new MidiDeviceInfo( 169
169 this, id, card, output ? midi_out : midi_in, device); 170 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
170 if (!port->IsOpened()) { 171 const std::string name = snd_rawmidi_info_get_name(midi);
171 VLOG(1) << "MidiDeviceInfo open fails"; 172 // We assume that card longname is in the format of
172 continue; 173 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
174 // a manufacturer name here.
175 std::string manufacturer;
176 const std::string card_name = snd_ctl_card_info_get_longname(card);
177 size_t at_index = card_name.rfind(" at ");
178 if (std::string::npos != at_index) {
179 size_t name_index = card_name.rfind(name, at_index - 1);
180 if (std::string::npos != name_index)
181 manufacturer = card_name.substr(0, name_index - 1);
173 } 182 }
174 if (input) { 183 const std::string driver = snd_ctl_card_info_get_driver(card);
175 in_devices_.push_back(port); 184 cards.push_back(CardInfo(name, manufacturer, driver));
176 AddInputPort(port->GetMidiPortInfo()); 185 }
186 }
187
188 // Enumerate all ports in all clients.
189 snd_seq_client_info_t* client_info;
190 snd_seq_client_info_alloca(&client_info);
191 snd_seq_port_info_t* port_info;
192 snd_seq_port_info_alloca(&port_info);
193
194 snd_seq_client_info_set_client(client_info, -1);
195 // Enumerate clients.
196 uint32 current_input = 0;
197 unsigned int current_card = 0;
198 while (!snd_seq_query_next_client(in_client_, client_info)) {
199 int client_id = snd_seq_client_info_get_client(client_info);
200 if ((client_id == in_client_id) || (client_id == out_client_id_)) {
201 // Skip our own clients.
202 continue;
203 }
204 const std::string client_name = snd_seq_client_info_get_name(client_info);
205 snd_seq_port_info_set_client(port_info, client_id);
206 snd_seq_port_info_set_port(port_info, -1);
207
208 std::string manufacturer;
209 std::string driver;
210 // In the current Alsa kernel implementation, hardware clients match the
211 // cards in the same order.
212 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
213 (current_card < cards.size())) {
214 CardInfo info = cards[current_card];
Takashi Toyoshima 2014/06/04 20:28:17 const CardInfo
Adam Goode 2014/06/08 02:52:58 Done.
215 if (info.name_ == client_name) {
216 manufacturer = info.manufacturer_;
217 driver = info.driver_;
218 current_card++;
177 } 219 }
178 if (output) { 220 }
179 out_devices_.push_back(port); 221 // Enumerate ports.
180 AddOutputPort(port->GetMidiPortInfo()); 222 while (!snd_seq_query_next_port(in_client_, port_info)) {
223 unsigned int port_type = snd_seq_port_info_get_type(port_info);
224 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
225 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
226 const std::string name = snd_seq_port_info_get_name(port_info);
227 const std::string id = base::StringPrintf("%d:%d %s",
228 addr->client,
229 addr->port,
230 name.c_str());
231 std::string version;
232 if (driver != "") {
233 version = driver + " / ";
234 }
235 version += base::StringPrintf("ALSA library version %d.%d.%d",
236 SND_LIB_MAJOR,
237 SND_LIB_MINOR,
238 SND_LIB_SUBMINOR);
239 unsigned int caps = snd_seq_port_info_get_capability(port_info);
240 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
241 // Subscribe to this port.
242 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
243 snd_seq_addr_t dest;
244 dest.client = snd_seq_client_id(in_client_);
245 dest.port = in_port_;
246 snd_seq_port_subscribe_set_sender(subs, sender);
247 snd_seq_port_subscribe_set_dest(subs, &dest);
248 err = snd_seq_subscribe_port(in_client_, subs);
249 if (err != 0) {
250 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
251 } else {
252 source_map_[AddrToInt(sender)] = current_input++;
253 AddInputPort(MidiPortInfo(id, manufacturer, name, version));
254 }
255 }
256 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
257 // Create a port for us to send on.
258 int out_port =
259 snd_seq_create_simple_port(out_client_, NULL,
260 SND_SEQ_PORT_CAP_READ |
261 SND_SEQ_PORT_CAP_NO_EXPORT,
262 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
263 SND_SEQ_PORT_TYPE_APPLICATION);
264 if (out_port < 0) {
265 VLOG(1) << "snd_seq_create_simple_port fails: "
266 << snd_strerror(out_port);
267 // Skip this output port for now.
268 continue;
269 }
270
271 // Activate port subscription.
272 snd_seq_addr_t sender;
273 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
274 sender.client = snd_seq_client_id(out_client_);
275 sender.port = out_port;
276 snd_seq_port_subscribe_set_sender(subs, &sender);
277 snd_seq_port_subscribe_set_dest(subs, dest);
278 err = snd_seq_subscribe_port(out_client_, subs);
279 if (err != 0) {
280 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
281 snd_seq_delete_simple_port(out_client_, out_port);
282 } else {
283 snd_midi_event_t* encoder;
284 snd_midi_event_new(kSendBufferSize, &encoder);
285 encoders_.push_back(encoder);
286 out_ports_.push_back(out_port);
287 AddOutputPort(MidiPortInfo(id, manufacturer, name, version));
288 }
289 }
181 } 290 }
182 } 291 }
183 snd_ctl_close(handle); 292 }
184 } 293
185 294 event_thread_.Start();
186 if (pipe(pipe_fd_) < 0) { 295 event_thread_.message_loop()->PostTask(
187 VPLOG(1) << "pipe() failed"; 296 FROM_HERE,
188 CompleteInitialization(MIDI_INITIALIZATION_ERROR); 297 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
189 } else { 298
190 event_thread_.Start(); 299 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 } 300 }
197 301
198 MidiManagerAlsa::~MidiManagerAlsa() { 302 MidiManagerAlsa::~MidiManagerAlsa() {
199 // Send a shutdown message to awake |event_thread_| from poll(). 303 // Tell the event thread it will soon be time to shut down. This gives
200 if (pipe_fd_[1] >= 0) 304 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
201 HANDLE_EINTR(write(pipe_fd_[1], "Q", 1)); 305 // message is lost.
202 306 {
203 // Stop receiving messages. 307 base::AutoLock lock(shutdown_lock_);
308 event_thread_shutdown_ = true;
309 }
310
311 // Stop the send thread.
312 send_thread_.Stop();
313
314 // Close the out client. This will trigger the event thread to stop,
315 // because of SND_SEQ_EVENT_CLIENT_EXIT.
316 if (out_client_)
317 snd_seq_close(out_client_);
318
319 // Wait for the event thread to stop.
204 event_thread_.Stop(); 320 event_thread_.Stop();
205 321
206 for (int i = 0; i < 2; ++i) { 322 // Close the in client.
207 if (pipe_fd_[i] >= 0) 323 if (in_client_)
208 close(pipe_fd_[i]); 324 snd_seq_close(in_client_);
209 } 325
210 send_thread_.Stop(); 326 // Free the decoder.
327 snd_midi_event_free(decoder_);
328
329 // Free the encoders.
330 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
331 snd_midi_event_free(*i);
332 }
333
334 void MidiManagerAlsa::SendMidiData(uint32 port_index,
335 const std::vector<uint8>& data) {
336 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
337
338 snd_midi_event_t* encoder = encoders_[port_index];
339 for (unsigned int i = 0; i < data.size(); i++) {
340 snd_seq_event_t event;
341 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
342 if (result == 1) {
343 // Full event, send it.
344 snd_seq_ev_set_source(&event, out_ports_[port_index]);
345 snd_seq_ev_set_subs(&event);
346 snd_seq_ev_set_direct(&event);
347 snd_seq_event_output_direct(out_client_, &event);
348 }
349 }
211 } 350 }
212 351
213 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, 352 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
214 uint32 port_index, 353 uint32 port_index,
215 const std::vector<uint8>& data, 354 const std::vector<uint8>& data,
216 double timestamp) { 355 double timestamp) {
217 if (out_devices_.size() <= port_index) 356 if (out_ports_.size() <= port_index)
218 return; 357 return;
219 358
359 // Not correct right now. http://crbug.com/374341.
360 if (!send_thread_.IsRunning())
361 send_thread_.Start();
362
220 base::TimeDelta delay; 363 base::TimeDelta delay;
221 if (timestamp != 0.0) { 364 if (timestamp != 0.0) {
222 base::TimeTicks time_to_send = 365 base::TimeTicks time_to_send =
223 base::TimeTicks() + base::TimeDelta::FromMicroseconds( 366 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
224 timestamp * base::Time::kMicrosecondsPerSecond); 367 timestamp * base::Time::kMicrosecondsPerSecond);
225 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); 368 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
226 } 369 }
227 370
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( 371 send_thread_.message_loop()->PostDelayedTask(
233 FROM_HERE, 372 FROM_HERE,
234 base::Bind(&MidiDeviceInfo::Send, device, client, data), 373 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
235 delay); 374 port_index, data), delay);
375
376 // Acknowledge send.
377 send_thread_.message_loop()->PostTask(
378 FROM_HERE,
379 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
380 base::Unretained(client), data.size()));
236 } 381 }
237 382
238 void MidiManagerAlsa::EventReset() { 383 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( 384 event_thread_.message_loop()->PostTask(
260 FROM_HERE, 385 FROM_HERE,
261 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 386 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
262 } 387 }
263 388
264 void MidiManagerAlsa::EventLoop() { 389 void MidiManagerAlsa::EventLoop() {
265 if (HANDLE_EINTR(poll(&poll_fds_[0], poll_fds_.size(), -1)) < 0) { 390 // Read available incoming MIDI data.
266 VPLOG(1) << "Couldn't poll(). Stop to poll input MIDI devices."; 391 snd_seq_event_t* event;
267 // TODO(toyoshim): Handle device disconnection, and try to reconnect? 392 int err = snd_seq_event_input(in_client_, &event);
268 return; 393 double timestamp =
269 } 394 (base::TimeTicks::HighResNow() - base::TimeTicks()).InSecondsF();
395 if (err == -ENOSPC) {
396 VLOG(1) << "snd_seq_event_input detected buffer overrun";
270 397
271 // Check timestamp as soon as possible because the API requires accurate 398 // 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. 399 base::AutoLock lock(shutdown_lock_);
273 base::TimeTicks now = base::TimeTicks::HighResNow(); 400 if (event_thread_shutdown_) {
401 return;
402 }
403 } else if (err < 0) {
404 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
405 return;
406 } else {
407 // Check for disconnection of out client. This means "shut down".
408 if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
409 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE &&
410 event->type == SND_SEQ_EVENT_CLIENT_EXIT &&
411 event->data.addr.client == out_client_id_) {
412 return;
413 }
274 414
275 // Is this thread going to be shutdown? 415 std::map<int, uint32>::iterator source_it =
276 if (poll_fds_[0].revents & kPollEventMask) 416 source_map_.find(AddrToInt(&event->source));
277 return; 417 if (source_it != source_map_.end()) {
278 418 uint32 source = source_it->second;
279 // Read available incoming MIDI data. 419 if (event->type == SND_SEQ_EVENT_SYSEX) {
280 int fds_index = 1; 420 // Special! Variable-length sysex.
281 uint8 buffer[kReceiveBufferSize]; 421 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
282 422 event->data.ext.len,
283 for (size_t i = 0; i < in_devices_.size(); ++i) { 423 timestamp);
284 unsigned short revents = 424 } else {
285 in_devices_[i]->GetPollDescriptorsRevents(&poll_fds_[fds_index]); 425 // Otherwise, decode this and send that on.
286 if (revents & (POLLERR | POLLNVAL)) { 426 unsigned char buf[12];
287 // TODO(toyoshim): Handle device disconnection. 427 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
288 VLOG(1) << "snd_rawmidi_descriptors_revents fails"; 428 if (count <= 0) {
289 poll_fds_[fds_index].events = 0; 429 if (count != -ENOENT) {
430 // ENOENT means that it's not a MIDI message, which is not an
431 // error, but other negative values are errors for us.
432 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
433 }
434 } else {
435 ReceiveMidiData(source, buf, count, timestamp);
436 }
437 }
290 } 438 }
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 } 439 }
297 440
298 // Do again. 441 // Do again.
299 event_thread_.message_loop()->PostTask( 442 event_thread_.message_loop()->PostTask(
300 FROM_HERE, 443 FROM_HERE,
301 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 444 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
302 } 445 }
303 446
304 MidiManager* MidiManager::Create() { 447 MidiManager* MidiManager::Create() {
305 return new MidiManagerAlsa(); 448 return new MidiManagerAlsa();
306 } 449 }
307 450
308 } // namespace media 451 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698