blob: b9e0c588a9f5eebdbfb0ce21c7e8bd5c2bb1fc2f [file] [log] [blame]
Prashant Malanifd1e2002017-08-09 13:22:59 -07001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "midis/seq_handler.h"
6
7#include <map>
Ben Chand496e612017-09-29 00:20:50 -07008#include <memory>
Prashant Malanifd1e2002017-08-09 13:22:59 -07009#include <string>
10#include <utility>
Prashant Malanie6e6b272018-05-02 10:16:30 -070011#include <vector>
Prashant Malanifd1e2002017-08-09 13:22:59 -070012
13#include <base/bind.h>
Prashant Malanifd1e2002017-08-09 13:22:59 -070014#include <poll.h>
15
Prashant Malanie6e6b272018-05-02 10:16:30 -070016#include "media/midi/message_util.h"
Prashant Malani3c540362017-09-28 13:35:14 -070017#include "midis/constants.h"
18
Prashant Malanifd1e2002017-08-09 13:22:59 -070019namespace {
20
21const unsigned int kCreateInputPortCaps =
22 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT;
23const unsigned int kCreateOutputPortCaps =
24 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT;
25const unsigned int kCreatePortType =
26 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION;
27const char kSndSeqName[] = "hw";
28
29} // namespace
30
31namespace midis {
32
Prashant Malani3c540362017-09-28 13:35:14 -070033SeqHandler::SeqHandler() : weak_factory_(this) {}
34
Prashant Malanifd1e2002017-08-09 13:22:59 -070035SeqHandler::SeqHandler(AddDeviceCallback add_device_cb,
36 RemoveDeviceCallback remove_device_cb,
37 HandleReceiveDataCallback handle_rx_data_cb,
38 IsDevicePresentCallback is_device_present_cb,
39 IsPortPresentCallback is_port_present_cb)
40 : add_device_cb_(add_device_cb),
41 remove_device_cb_(remove_device_cb),
42 handle_rx_data_cb_(handle_rx_data_cb),
43 is_device_present_cb_(is_device_present_cb),
44 is_port_present_cb_(is_port_present_cb),
45 weak_factory_(this) {}
46
47bool SeqHandler::InitSeq() {
48 // Create client handles.
49 snd_seq_t* tmp_seq = nullptr;
50 int err =
51 snd_seq_open(&tmp_seq, kSndSeqName, SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK);
52 if (err != 0) {
53 LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err);
54 return false;
55 }
56 ScopedSeqPtr in_client(tmp_seq);
57 tmp_seq = nullptr;
58 in_client_id_ = snd_seq_client_id(in_client.get());
59
60 err = snd_seq_open(&tmp_seq, kSndSeqName, SND_SEQ_OPEN_OUTPUT, 0);
61 if (err != 0) {
62 LOG(ERROR) << "snd_seq_open fails: " << snd_strerror(err);
63 return false;
64 }
65
66 ScopedSeqPtr out_client(tmp_seq);
67 tmp_seq = nullptr;
68 out_client_id_ = snd_seq_client_id(out_client.get());
69
70 // Name the clients.
71 err = snd_seq_set_client_name(in_client.get(), "midis (input)");
72 if (err != 0) {
73 LOG(ERROR) << "snd_seq_set_client_name fails: " << snd_strerror(err);
74 return false;
75 }
76 err = snd_seq_set_client_name(out_client.get(), "midis (output)");
77 if (err != 0) {
78 LOG(ERROR) << "snd_seq_set_client_name fails: " << snd_strerror(err);
79 return false;
80 }
81
82 // Create input port.
83 in_port_id_ = snd_seq_create_simple_port(
84 in_client.get(), NULL, kCreateInputPortCaps, kCreatePortType);
85 if (in_port_id_ < 0) {
86 LOG(ERROR) << "snd_seq_create_simple_port fails: "
87 << snd_strerror(in_port_id_);
88 return false;
89 }
90
91 // Subscribe to the announce port.
92 snd_seq_port_subscribe_t* subs;
93 snd_seq_port_subscribe_alloca(&subs);
94 snd_seq_addr_t announce_sender;
95 snd_seq_addr_t announce_dest;
96 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
97 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
98 announce_dest.client = in_client_id_;
99 announce_dest.port = in_port_id_;
100 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
101 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
102 err = snd_seq_subscribe_port(in_client.get(), subs);
103 if (err != 0) {
104 LOG(ERROR) << "snd_seq_subscribe_port on the announce port fails: "
105 << snd_strerror(err);
106 return false;
107 }
108
Prashant Malani78b59c32017-10-24 22:54:51 -0700109 in_client_ = std::move(in_client);
110 out_client_ = std::move(out_client);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700111
Prashant Malani78b59c32017-10-24 22:54:51 -0700112 // Initialize decoder.
113 decoder_ = CreateMidiEvent(0);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700114
Prashant Malani8ca4ab32017-10-03 15:12:49 -0700115 EnumerateExistingDevices();
116
Prashant Malanifd1e2002017-08-09 13:22:59 -0700117 // Obtain the poll file descriptor to watch.
Ben Chand496e612017-09-29 00:20:50 -0700118 pfd_ = std::make_unique<pollfd>();
Prashant Malanifd1e2002017-08-09 13:22:59 -0700119 snd_seq_poll_descriptors(in_client_.get(), pfd_.get(), 1, POLLIN);
120
Hidehiko Abeb9eb3692019-09-19 01:15:11 +0900121 watcher_ = base::FileDescriptorWatcher::WatchReadable(
Tom Hughesaebcdce2020-08-27 15:18:39 -0700122 pfd_->fd, base::BindRepeating(&SeqHandler::ProcessAlsaClientFd,
123 weak_factory_.GetWeakPtr()));
Hidehiko Abeb9eb3692019-09-19 01:15:11 +0900124 if (!watcher_) {
Prashant Malanifd1e2002017-08-09 13:22:59 -0700125 in_client_.reset();
126 out_client_.reset();
127 decoder_.reset();
128 pfd_.reset();
129 return false;
130 }
131
132 return true;
133}
134
135void SeqHandler::ProcessAlsaClientFd() {
136 int remaining;
137 do {
138 snd_seq_event_t* event;
Prashant Malania9bbf542017-10-19 18:27:18 -0700139 int err = SndSeqEventInput(in_client_.get(), &event);
140 remaining = SndSeqEventInputPending(in_client_.get(), 0);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700141
142 if (err == -ENOSPC) {
143 // Handle out of space error.
144 LOG(ERROR) << "snd_seq_event_input detected buffer overrun";
145 // We've lost events: check another way to see if we need to shut
146 // down.
147 } else if (err == -EAGAIN) {
148 // We've read all the data.
149 } else if (err < 0) {
150 // Handle other errors.
151 LOG(ERROR) << "snd_seq_event_input fails: " << snd_strerror(err);
152 // TODO(pmalani): Stop the message loop here then.
153 } else if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
154 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
155 // Handle announce events.
156 switch (event->type) {
157 case SND_SEQ_EVENT_PORT_START:
158 // Don't use SND_SEQ_EVENT_CLIENT_START because the
159 // client name may not be set by the time we query
160 // it. It should be set by the time ports are made.
161 AddSeqDevice(event->data.addr.client);
162 AddSeqPort(event->data.addr.client, event->data.addr.port);
163 break;
164 case SND_SEQ_EVENT_CLIENT_EXIT:
165 // Check for disconnection of our "out" client. This means "shut
166 // down".
167 if (event->data.addr.client == out_client_id_) {
168 // TODO(pmalani): Stop the message loop here then.
169 remaining = 0;
170 } else {
171 RemoveSeqDevice(event->data.addr.client);
172 }
173 break;
174 case SND_SEQ_EVENT_PORT_EXIT:
175 RemoveSeqPort(event->data.addr.client, event->data.addr.port);
176 break;
177 }
178 } else {
179 // Normal operation.
180 ProcessMidiEvent(event);
181 }
182 } while (remaining > 0);
183}
184
185void SeqHandler::AddSeqDevice(uint32_t device_id) {
Prashant Malani3c540362017-09-28 13:35:14 -0700186 if (is_device_present_cb_.Run(0 /* TODO(pmalani): Remove card number */,
187 device_id)) {
Prashant Malanifd1e2002017-08-09 13:22:59 -0700188 LOG(INFO) << "Device: " << device_id << " already exists.";
189 return;
190 }
191
192 // Check that the device isn't our own in/our client.
193 if (device_id == in_client_id_ || device_id == out_client_id_) {
194 return;
195 }
196
197 snd_seq_client_info_t* client_info;
198 snd_seq_client_info_alloca(&client_info);
199 int err =
200 snd_seq_get_any_client_info(in_client_.get(), device_id, client_info);
201 if (err != 0) {
202 LOG(ERROR) << "Failed to get client info.";
203 return;
204 }
205
206 std::string name(snd_seq_client_info_get_name(client_info));
Prashant Malanifd1e2002017-08-09 13:22:59 -0700207
208 // Store the list of MIDI ports and corresponding capabilities in a map.
209 std::map<uint32_t, unsigned int> port_caps;
210 snd_seq_port_info_t* port_info;
211 snd_seq_port_info_alloca(&port_info);
212 snd_seq_port_info_set_client(port_info, device_id);
213 snd_seq_port_info_set_port(port_info, -1);
214 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
215 if (!(snd_seq_port_info_get_type(port_info) &
216 SND_SEQ_PORT_TYPE_MIDI_GENERIC)) {
217 LOG(INFO) << "Skipping non-MIDI port.";
218 continue;
219 }
220 port_caps.emplace(snd_seq_port_info_get_port(port_info),
221 snd_seq_port_info_get_capability(port_info));
222 }
223
Prashant Malani06d54652018-03-08 18:58:15 -0800224 // If the number of MIDI ports is 0, there is no use in creating
225 // a device.
226 if (port_caps.size() == 0) {
227 LOG(INFO) << "Connected device: " << name << " has no MIDI ports.";
228 return;
229 }
230
Ben Chan2a3a8e32017-10-05 11:15:11 -0700231 auto dev = std::make_unique<Device>(
Prashant Malani3c540362017-09-28 13:35:14 -0700232 name, std::string(),
233 0 /* card number; TODO(pmalani) remove card number */, device_id,
Prashant Malani06d54652018-03-08 18:58:15 -0800234 port_caps.size(), 0 /* device flags TODO(pmalani): flags not needed. */,
Prashant Malanifd1e2002017-08-09 13:22:59 -0700235 base::Bind(&SeqHandler::SubscribeInPort, base::Unretained(this)),
236 base::Bind(&SeqHandler::SubscribeOutPort, base::Unretained(this)),
237 base::Bind(&SeqHandler::UnsubscribeInPort, weak_factory_.GetWeakPtr()),
238 base::Bind(&SeqHandler::UnsubscribeOutPort, weak_factory_.GetWeakPtr()),
239 base::Bind(&SeqHandler::SendMidiData, weak_factory_.GetWeakPtr()),
240 std::move(port_caps));
241 add_device_cb_.Run(std::move(dev));
242}
243
244void SeqHandler::AddSeqPort(uint32_t device_id, uint32_t port_id) {
245 if (!is_port_present_cb_.Run(0, device_id, port_id)) {
246 LOG(WARNING) << "Received port start event for new port: " << port_id
247 << " on device: " << device_id << "; ignoring";
248 }
249}
250
251void SeqHandler::RemoveSeqDevice(uint32_t device_id) {
252 remove_device_cb_.Run(0 /* FIXME remove card number */, device_id);
253}
254
255void SeqHandler::RemoveSeqPort(uint32_t device_id, uint32_t port_id) {
256 if (!is_port_present_cb_.Run(0, device_id, port_id)) {
257 LOG(WARNING) << "Received port start event for new port: " << port_id
258 << " on device: " << device_id << "; ignoring";
259 }
260}
261
262bool SeqHandler::SubscribeInPort(uint32_t device_id, uint32_t port_id) {
263 snd_seq_port_subscribe_t* subs;
264 snd_seq_port_subscribe_alloca(&subs);
265 snd_seq_addr_t sender;
266 sender.client = device_id;
267 sender.port = port_id;
268 snd_seq_port_subscribe_set_sender(subs, &sender);
269
270 snd_seq_addr_t dest;
271 dest.client = in_client_id_;
272 dest.port = in_port_id_;
273 snd_seq_port_subscribe_set_dest(subs, &dest);
274
275 int err = snd_seq_subscribe_port(in_client_.get(), subs);
276 if (err != 0) {
277 LOG(ERROR) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
278 return false;
279 }
280
281 return true;
282}
283
284int SeqHandler::SubscribeOutPort(uint32_t device_id, uint32_t port_id) {
285 int out_port;
Prashant Malani3c540362017-09-28 13:35:14 -0700286 out_port = snd_seq_create_simple_port(out_client_.get(), NULL,
287 kCreateOutputPortCaps, kCreatePortType);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700288 if (out_port < 0) {
289 LOG(INFO) << "snd_seq_creat_simple_port (output) failed: "
290 << snd_strerror(out_port);
291 return -1;
292 }
293
294 snd_seq_port_subscribe_t* subs;
295 snd_seq_port_subscribe_alloca(&subs);
296 snd_seq_addr_t sender;
297 sender.client = out_client_id_;
298 sender.port = out_port;
299 snd_seq_port_subscribe_set_sender(subs, &sender);
300
301 snd_seq_addr_t dest;
302 dest.client = device_id;
303 dest.port = port_id;
304 snd_seq_port_subscribe_set_dest(subs, &dest);
305
306 int err = snd_seq_subscribe_port(out_client_.get(), subs);
307 if (err != 0) {
308 snd_seq_delete_simple_port(out_client_.get(), out_port);
309 LOG(ERROR) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
310 return -1;
311 }
312
313 return out_port;
314}
315
316void SeqHandler::UnsubscribeInPort(uint32_t device_id, uint32_t port_id) {
317 snd_seq_port_subscribe_t* subs;
318 snd_seq_port_subscribe_alloca(&subs);
319 snd_seq_addr_t sender;
320 sender.client = device_id;
321 sender.port = port_id;
322 snd_seq_port_subscribe_set_sender(subs, &sender);
323 snd_seq_addr_t dest;
324 dest.client = in_client_id_;
325 dest.port = in_port_id_;
326 snd_seq_port_subscribe_set_dest(subs, &dest);
327
328 int err = snd_seq_unsubscribe_port(in_client_.get(), subs);
329 if (err != 0) {
330 LOG(WARNING) << "snd_seq_unsubscribe_port fails: " << snd_strerror(err);
331 return;
332 }
333}
334
335void SeqHandler::UnsubscribeOutPort(int out_port_id) {
336 snd_seq_delete_simple_port(out_client_.get(), out_port_id);
337}
338
Prashant Malani3c540362017-09-28 13:35:14 -0700339bool SeqHandler::EncodeMidiBytes(int out_port_id,
340 snd_seq_t* out_client,
341 const uint8_t* buffer,
342 size_t buf_len,
343 snd_midi_event_t* encoder) {
344 if (buf_len == 0 || buf_len > kMaxBufSize) {
345 return false;
346 }
347
Prashant Malanifd1e2002017-08-09 13:22:59 -0700348 for (int i = 0; i < buf_len; i++) {
349 snd_seq_event_t event;
350 int result = snd_midi_event_encode_byte(encoder, buffer[i], &event);
Prashant Malani3c540362017-09-28 13:35:14 -0700351 if (result < 0) {
352 LOG(ERROR) << "Error snd_midi_event_encode_byte(): " << result;
353 return false;
354 }
Prashant Malanifd1e2002017-08-09 13:22:59 -0700355 if (result == 1) {
356 // Send the message.
357 snd_seq_ev_set_source(&event, out_port_id);
358 snd_seq_ev_set_subs(&event);
359 snd_seq_ev_set_direct(&event);
Prashant Malani3c540362017-09-28 13:35:14 -0700360 int expected_length = snd_seq_event_length(&event);
361 result = SndSeqEventOutputDirect(out_client, &event);
362 if (result != expected_length) {
Prashant Malanie6e6b272018-05-02 10:16:30 -0700363 LOG(WARNING) << "Error in snd_seq_event_output_direct(): " << result;
Prashant Malani3c540362017-09-28 13:35:14 -0700364 return false;
365 }
366 return true;
Prashant Malanifd1e2002017-08-09 13:22:59 -0700367 }
368 }
Prashant Malani3c540362017-09-28 13:35:14 -0700369
370 // If we reached here, something went wrong.
371 return false;
372}
373
374void SeqHandler::SendMidiData(int out_port_id,
375 const uint8_t* buffer,
376 size_t buf_len) {
Prashant Malanie6e6b272018-05-02 10:16:30 -0700377 std::vector<uint8_t> v(buffer, buffer + buf_len);
378 if (!midi::IsValidWebMIDIData(v)) {
379 LOG(WARNING) << "Received invalid MIDI Data.";
380 return;
381 }
382
Prashant Malani3c540362017-09-28 13:35:14 -0700383 snd_midi_event_t* encoder;
384 int ret = snd_midi_event_new(buf_len, &encoder);
385 if (ret != 0) {
386 LOG(ERROR) << "Error snd_midi_event_new(): " << ret;
387 return;
388 }
389 bool success =
390 EncodeMidiBytes(out_port_id, out_client_.get(), buffer, buf_len, encoder);
391 if (!success) {
Prashant Malanie6e6b272018-05-02 10:16:30 -0700392 LOG(WARNING) << "Failed to send MIDI data to output port: " << out_port_id;
Prashant Malani3c540362017-09-28 13:35:14 -0700393 }
Prashant Malanifd1e2002017-08-09 13:22:59 -0700394 snd_midi_event_free(encoder);
395}
396
397void SeqHandler::ProcessMidiEvent(snd_seq_event_t* event) {
398 uint32_t device_id = event->source.client;
399 uint32_t subdevice_num = event->source.port;
400
401 if (event->type == SND_SEQ_EVENT_SYSEX) {
402 // SysEX, so pass it through without decoding.
Prashant Malani3c540362017-09-28 13:35:14 -0700403 handle_rx_data_cb_.Run(0, device_id, subdevice_num,
Prashant Malanifd1e2002017-08-09 13:22:59 -0700404 static_cast<char*>(event->data.ext.ptr),
405 event->data.ext.len);
406 } else {
407 // Normal message, so decode and send.
408 unsigned char buf[12];
409 int64_t count =
410 snd_midi_event_decode(decoder_.get(), buf, sizeof(buf), event);
411 if (count <= 0) {
412 if (count != -ENOENT) {
413 LOG(ERROR) << "snd_midi_event_decoder failed: " << snd_strerror(count);
414 }
415 } else {
Prashant Malani3c540362017-09-28 13:35:14 -0700416 handle_rx_data_cb_.Run(0, device_id, subdevice_num,
417 reinterpret_cast<char*>(buf), count);
Prashant Malanifd1e2002017-08-09 13:22:59 -0700418 }
419 }
420}
421
Prashant Malani3c540362017-09-28 13:35:14 -0700422int SeqHandler::SndSeqEventOutputDirect(snd_seq_t* out_client,
423 snd_seq_event_t* event) {
424 return snd_seq_event_output_direct(out_client, event);
425}
426
Prashant Malania9bbf542017-10-19 18:27:18 -0700427int SeqHandler::SndSeqEventInput(snd_seq_t* in_client, snd_seq_event_t** ev) {
428 return snd_seq_event_input(in_client, ev);
429}
430
431int SeqHandler::SndSeqEventInputPending(snd_seq_t* in_client,
432 int fetch_sequencer) {
433 return snd_seq_event_input_pending(in_client, fetch_sequencer);
434}
435
Prashant Malani8ca4ab32017-10-03 15:12:49 -0700436void SeqHandler::EnumerateExistingDevices() {
437 snd_seq_client_info_t* client_info;
438 snd_seq_client_info_alloca(&client_info);
439 snd_seq_port_info_t* port_info;
440 snd_seq_port_info_alloca(&port_info);
441
442 snd_seq_client_info_set_client(client_info, -1);
443 while (!snd_seq_query_next_client(in_client_.get(), client_info)) {
444 int device_id = snd_seq_client_info_get_client(client_info);
445 AddSeqDevice(device_id);
446
447 // Call AddSeqPort to make sure we "process" all the ports of a client.
448 // Note that currently we don't support the dynamic addition / deletion
449 // of ports.
450 snd_seq_port_info_set_client(port_info, device_id);
451 snd_seq_port_info_set_port(port_info, -1);
452 while (!snd_seq_query_next_port(in_client_.get(), port_info)) {
453 int port_id = snd_seq_port_info_get_port(port_info);
454 AddSeqPort(device_id, port_id);
455 }
456 }
457}
458
Prashant Malani78b59c32017-10-24 22:54:51 -0700459SeqHandler::ScopedMidiEventPtr SeqHandler::CreateMidiEvent(size_t buf_size) {
460 snd_midi_event_t* tmp = nullptr;
461 snd_midi_event_new(buf_size, &tmp);
462 ScopedMidiEventPtr ev(tmp);
463 tmp = nullptr;
464 snd_midi_event_no_status(ev.get(), 1);
465
466 return ev;
467}
468
Prashant Malanifd1e2002017-08-09 13:22:59 -0700469} // namespace midis