Remove no longer needed toolbar layer method.
[chromium-blink-merge.git] / media / midi / midi_manager_alsa.cc
blob6c91e13b476edd3dede2185abb1d16555134ec2a
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
3 // found in the LICENSE file.
5 #include "media/midi/midi_manager_alsa.h"
7 #include <alsa/asoundlib.h>
8 #include <stdlib.h>
9 #include <algorithm>
10 #include <string>
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/thread.h"
20 #include "base/time/time.h"
21 #include "media/midi/midi_port_info.h"
23 namespace media {
25 namespace {
27 // Per-output buffer. This can be smaller, but then large sysex messages
28 // will be (harmlessly) split across multiple seq events. This should
29 // not have any real practical effect, except perhaps to slightly reorder
30 // realtime messages with respect to sysex.
31 const size_t kSendBufferSize = 256;
33 // Constants for the capabilities we search for in inputs and outputs.
34 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
35 const unsigned int kRequiredInputPortCaps =
36 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
37 const unsigned int kRequiredOutputPortCaps =
38 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
40 int AddrToInt(const snd_seq_addr_t* addr) {
41 return (addr->client << 8) | addr->port;
44 } // namespace
46 MidiManagerAlsa::MidiManagerAlsa()
47 : in_client_(NULL),
48 out_client_(NULL),
49 out_client_id_(-1),
50 in_port_(-1),
51 decoder_(NULL),
52 #if defined(USE_UDEV)
53 udev_(device::udev_new()),
54 #endif // defined(USE_UDEV)
55 send_thread_("MidiSendThread"),
56 event_thread_("MidiEventThread"),
57 event_thread_shutdown_(false) {
58 // Initialize decoder.
59 snd_midi_event_new(0, &decoder_);
60 snd_midi_event_no_status(decoder_, 1);
63 void MidiManagerAlsa::StartInitialization() {
64 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
66 // Create client handles.
67 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0);
68 if (err != 0) {
69 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
70 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
72 int in_client_id = snd_seq_client_id(in_client_);
73 err = snd_seq_open(&out_client_, "hw", SND_SEQ_OPEN_OUTPUT, 0);
74 if (err != 0) {
75 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
76 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
78 out_client_id_ = snd_seq_client_id(out_client_);
80 // Name the clients.
81 err = snd_seq_set_client_name(in_client_, "Chrome (input)");
82 if (err != 0) {
83 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
84 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
86 err = snd_seq_set_client_name(out_client_, "Chrome (output)");
87 if (err != 0) {
88 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
89 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
92 // Create input port.
93 in_port_ = snd_seq_create_simple_port(in_client_, NULL,
94 SND_SEQ_PORT_CAP_WRITE |
95 SND_SEQ_PORT_CAP_NO_EXPORT,
96 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
97 SND_SEQ_PORT_TYPE_APPLICATION);
98 if (in_port_ < 0) {
99 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_);
100 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
103 // Subscribe to the announce port.
104 snd_seq_port_subscribe_t* subs;
105 snd_seq_port_subscribe_alloca(&subs);
106 snd_seq_addr_t announce_sender;
107 snd_seq_addr_t announce_dest;
108 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
109 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
110 announce_dest.client = in_client_id;
111 announce_dest.port = in_port_;
112 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
113 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
114 err = snd_seq_subscribe_port(in_client_, subs);
115 if (err != 0) {
116 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
117 << snd_strerror(err);
118 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
121 // Extract the list of manufacturers for the hardware MIDI
122 // devices. This won't work for all devices. It is also brittle until
123 // hotplug is implemented. (See http://crbug.com/431489.)
124 ScopedVector<CardInfo> cards;
125 snd_ctl_card_info_t* card;
126 snd_rawmidi_info_t* midi_out;
127 snd_rawmidi_info_t* midi_in;
128 snd_ctl_card_info_alloca(&card);
129 snd_rawmidi_info_alloca(&midi_out);
130 snd_rawmidi_info_alloca(&midi_in);
131 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
132 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
133 snd_ctl_t* handle;
134 int err = snd_ctl_open(&handle, id.c_str(), 0);
135 if (err != 0) {
136 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
137 continue;
139 err = snd_ctl_card_info(handle, card);
140 if (err != 0) {
141 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
142 snd_ctl_close(handle);
143 continue;
145 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
146 for (int device = -1;
147 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
148 bool output;
149 bool input;
150 snd_rawmidi_info_set_device(midi_out, device);
151 snd_rawmidi_info_set_subdevice(midi_out, 0);
152 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
153 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
154 snd_rawmidi_info_set_device(midi_in, device);
155 snd_rawmidi_info_set_subdevice(midi_in, 0);
156 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
157 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
158 if (!output && !input)
159 continue;
161 // Compute and save Alsa and udev properties.
162 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
163 cards.push_back(new CardInfo(
164 this,
165 snd_rawmidi_info_get_name(midi),
166 snd_ctl_card_info_get_longname(card),
167 snd_ctl_card_info_get_driver(card),
168 card_index));
170 snd_ctl_close(handle);
173 // Enumerate all ports in all clients.
174 snd_seq_client_info_t* client_info;
175 snd_seq_client_info_alloca(&client_info);
176 snd_seq_port_info_t* port_info;
177 snd_seq_port_info_alloca(&port_info);
179 snd_seq_client_info_set_client(client_info, -1);
180 // Enumerate clients.
181 uint32 current_input = 0;
182 unsigned int current_card = 0;
183 while (!snd_seq_query_next_client(in_client_, client_info)) {
184 int client_id = snd_seq_client_info_get_client(client_info);
185 if ((client_id == in_client_id) || (client_id == out_client_id_)) {
186 // Skip our own clients.
187 continue;
189 const std::string client_name = snd_seq_client_info_get_name(client_info);
190 snd_seq_port_info_set_client(port_info, client_id);
191 snd_seq_port_info_set_port(port_info, -1);
193 std::string manufacturer;
194 std::string driver;
195 // In the current Alsa kernel implementation, hardware clients match the
196 // cards in the same order.
197 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
198 (current_card < cards.size())) {
199 const CardInfo* info = cards[current_card];
200 if (info->alsa_name() == client_name) {
201 manufacturer = info->manufacturer();
202 driver = info->alsa_driver();
203 current_card++;
206 // Enumerate ports.
207 while (!snd_seq_query_next_port(in_client_, port_info)) {
208 unsigned int port_type = snd_seq_port_info_get_type(port_info);
209 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
210 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
211 const std::string name = snd_seq_port_info_get_name(port_info);
212 const std::string id = base::StringPrintf("%d:%d %s",
213 addr->client,
214 addr->port,
215 name.c_str());
216 std::string version;
217 if (!driver.empty()) {
218 version = driver + " / ";
220 version += base::StringPrintf("ALSA library version %d.%d.%d",
221 SND_LIB_MAJOR,
222 SND_LIB_MINOR,
223 SND_LIB_SUBMINOR);
224 unsigned int caps = snd_seq_port_info_get_capability(port_info);
225 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
226 // Subscribe to this port.
227 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
228 snd_seq_addr_t dest;
229 dest.client = snd_seq_client_id(in_client_);
230 dest.port = in_port_;
231 snd_seq_port_subscribe_set_sender(subs, sender);
232 snd_seq_port_subscribe_set_dest(subs, &dest);
233 err = snd_seq_subscribe_port(in_client_, subs);
234 if (err != 0) {
235 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
236 } else {
237 source_map_[AddrToInt(sender)] = current_input++;
238 AddInputPort(MidiPortInfo(
239 id, manufacturer, name, version, MIDI_PORT_OPENED));
242 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
243 // Create a port for us to send on.
244 int out_port =
245 snd_seq_create_simple_port(out_client_, NULL,
246 SND_SEQ_PORT_CAP_READ |
247 SND_SEQ_PORT_CAP_NO_EXPORT,
248 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
249 SND_SEQ_PORT_TYPE_APPLICATION);
250 if (out_port < 0) {
251 VLOG(1) << "snd_seq_create_simple_port fails: "
252 << snd_strerror(out_port);
253 // Skip this output port for now.
254 continue;
257 // Activate port subscription.
258 snd_seq_addr_t sender;
259 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
260 sender.client = snd_seq_client_id(out_client_);
261 sender.port = out_port;
262 snd_seq_port_subscribe_set_sender(subs, &sender);
263 snd_seq_port_subscribe_set_dest(subs, dest);
264 err = snd_seq_subscribe_port(out_client_, subs);
265 if (err != 0) {
266 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
267 snd_seq_delete_simple_port(out_client_, out_port);
268 } else {
269 snd_midi_event_t* encoder;
270 snd_midi_event_new(kSendBufferSize, &encoder);
271 encoders_.push_back(encoder);
272 out_ports_.push_back(out_port);
273 AddOutputPort(MidiPortInfo(
274 id, manufacturer, name, version, MIDI_PORT_OPENED));
281 event_thread_.Start();
282 event_thread_.message_loop()->PostTask(
283 FROM_HERE,
284 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
286 CompleteInitialization(MIDI_OK);
289 MidiManagerAlsa::~MidiManagerAlsa() {
290 // Tell the event thread it will soon be time to shut down. This gives
291 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
292 // message is lost.
294 base::AutoLock lock(shutdown_lock_);
295 event_thread_shutdown_ = true;
298 // Stop the send thread.
299 send_thread_.Stop();
301 // Close the out client. This will trigger the event thread to stop,
302 // because of SND_SEQ_EVENT_CLIENT_EXIT.
303 if (out_client_)
304 snd_seq_close(out_client_);
306 // Wait for the event thread to stop.
307 event_thread_.Stop();
309 // Close the in client.
310 if (in_client_)
311 snd_seq_close(in_client_);
313 // Free the decoder.
314 snd_midi_event_free(decoder_);
316 // Free the encoders.
317 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
318 snd_midi_event_free(*i);
321 MidiManagerAlsa::CardInfo::CardInfo(
322 const MidiManagerAlsa* outer,
323 const std::string& alsa_name, const std::string& alsa_longname,
324 const std::string& alsa_driver, int card_index)
325 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
326 // Get udev properties if available.
327 std::string udev_id_vendor;
328 std::string udev_id_vendor_id;
329 std::string udev_id_vendor_from_database;
331 #if defined(USE_UDEV)
332 const std::string sysname = base::StringPrintf("card%i", card_index);
333 device::ScopedUdevDevicePtr udev_device(
334 device::udev_device_new_from_subsystem_sysname(
335 outer->udev_.get(), "sound", sysname.c_str()));
336 udev_id_vendor = device::UdevDecodeString(device::UdevDeviceGetPropertyValue(
337 udev_device.get(), "ID_VENDOR_ENC"));
338 udev_id_vendor_id = device::UdevDeviceGetPropertyValue(
339 udev_device.get(), "ID_VENDOR_ID");
340 udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue(
341 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
343 udev_id_path_ = device::UdevDeviceGetPropertyValue(
344 udev_device.get(), "ID_PATH");
345 udev_id_id_ = device::UdevDeviceGetPropertyValue(
346 udev_device.get(), "ID_ID");
347 #endif // defined(USE_UDEV)
349 manufacturer_ = ExtractManufacturerString(
350 udev_id_vendor, udev_id_vendor_id, udev_id_vendor_from_database,
351 alsa_name, alsa_longname);
354 MidiManagerAlsa::CardInfo::~CardInfo() {
357 const std::string MidiManagerAlsa::CardInfo::alsa_name() const {
358 return alsa_name_;
361 const std::string MidiManagerAlsa::CardInfo::manufacturer() const {
362 return manufacturer_;
365 const std::string MidiManagerAlsa::CardInfo::alsa_driver() const {
366 return alsa_driver_;
369 const std::string MidiManagerAlsa::CardInfo::udev_id_path() const {
370 return udev_id_path_;
373 const std::string MidiManagerAlsa::CardInfo::udev_id_id() const {
374 return udev_id_id_;
377 void MidiManagerAlsa::SendMidiData(uint32 port_index,
378 const std::vector<uint8>& data) {
379 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
381 snd_midi_event_t* encoder = encoders_[port_index];
382 for (unsigned int i = 0; i < data.size(); i++) {
383 snd_seq_event_t event;
384 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
385 if (result == 1) {
386 // Full event, send it.
387 snd_seq_ev_set_source(&event, out_ports_[port_index]);
388 snd_seq_ev_set_subs(&event);
389 snd_seq_ev_set_direct(&event);
390 snd_seq_event_output_direct(out_client_, &event);
395 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
396 uint32 port_index,
397 const std::vector<uint8>& data,
398 double timestamp) {
399 if (out_ports_.size() <= port_index)
400 return;
402 // Not correct right now. http://crbug.com/374341.
403 if (!send_thread_.IsRunning())
404 send_thread_.Start();
406 base::TimeDelta delay;
407 if (timestamp != 0.0) {
408 base::TimeTicks time_to_send =
409 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
410 timestamp * base::Time::kMicrosecondsPerSecond);
411 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
414 send_thread_.message_loop()->PostDelayedTask(
415 FROM_HERE,
416 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
417 port_index, data), delay);
419 // Acknowledge send.
420 send_thread_.message_loop()->PostTask(
421 FROM_HERE,
422 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
423 base::Unretained(client), data.size()));
426 void MidiManagerAlsa::EventReset() {
427 event_thread_.message_loop()->PostTask(
428 FROM_HERE,
429 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
432 void MidiManagerAlsa::EventLoop() {
433 // Read available incoming MIDI data.
434 snd_seq_event_t* event;
435 int err = snd_seq_event_input(in_client_, &event);
436 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
437 if (err == -ENOSPC) {
438 VLOG(1) << "snd_seq_event_input detected buffer overrun";
440 // We've lost events: check another way to see if we need to shut down.
441 base::AutoLock lock(shutdown_lock_);
442 if (event_thread_shutdown_) {
443 return;
445 } else if (err < 0) {
446 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
447 return;
448 } else {
449 // Check for disconnection of out client. This means "shut down".
450 if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
451 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE &&
452 event->type == SND_SEQ_EVENT_CLIENT_EXIT &&
453 event->data.addr.client == out_client_id_) {
454 return;
457 std::map<int, uint32>::iterator source_it =
458 source_map_.find(AddrToInt(&event->source));
459 if (source_it != source_map_.end()) {
460 uint32 source = source_it->second;
461 if (event->type == SND_SEQ_EVENT_SYSEX) {
462 // Special! Variable-length sysex.
463 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
464 event->data.ext.len,
465 timestamp);
466 } else {
467 // Otherwise, decode this and send that on.
468 unsigned char buf[12];
469 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
470 if (count <= 0) {
471 if (count != -ENOENT) {
472 // ENOENT means that it's not a MIDI message, which is not an
473 // error, but other negative values are errors for us.
474 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
476 } else {
477 ReceiveMidiData(source, buf, count, timestamp);
483 // Do again.
484 event_thread_.message_loop()->PostTask(
485 FROM_HERE,
486 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
489 // static
490 std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString(
491 const std::string& udev_id_vendor,
492 const std::string& udev_id_vendor_id,
493 const std::string& udev_id_vendor_from_database,
494 const std::string& alsa_name,
495 const std::string& alsa_longname) {
496 // Let's try to determine the manufacturer. Here is the ordered preference
497 // in extraction:
498 // 1. Vendor name from the USB device iManufacturer string, from
499 // the udev property ID_VENDOR_ENC.
500 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
501 // 3. Heuristic from ALSA.
503 // Is the vendor string not just the USB vendor hex id?
504 if (udev_id_vendor != udev_id_vendor_id) {
505 return udev_id_vendor;
508 // Is there a vendor string in the hardware database?
509 if (!udev_id_vendor_from_database.empty()) {
510 return udev_id_vendor_from_database;
513 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
514 // We assume that card longname is in the format of
515 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
516 // a manufacturer name here.
517 size_t at_index = alsa_longname.rfind(" at ");
518 if (std::string::npos != at_index) {
519 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
520 if (std::string::npos != name_index)
521 return alsa_longname.substr(0, name_index - 1);
524 // Failure.
525 return "";
528 MidiManager* MidiManager::Create() {
529 return new MidiManagerAlsa();
532 } // namespace media