Simplify an IPC handler which doesn't need _DELAY_REPLY
[chromium-blink-merge.git] / media / midi / midi_manager_alsa.cc
blob89b0da8cb815aa605df8863cfb60a709cb6a7baa
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/json/json_string_value_serializer.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/posix/eintr_wrapper.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/threading/thread.h"
22 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "crypto/sha2.h"
25 #include "media/midi/midi_port_info.h"
27 namespace media {
29 namespace {
31 // Per-output buffer. This can be smaller, but then large sysex messages
32 // will be (harmlessly) split across multiple seq events. This should
33 // not have any real practical effect, except perhaps to slightly reorder
34 // realtime messages with respect to sysex.
35 const size_t kSendBufferSize = 256;
37 // Minimum client id for which we will have ALSA card devices for. When we
38 // are searching for card devices (used to get the path, id, and manufacturer),
39 // we don't want to get confused by kernel clients that do not have a card.
40 // See seq_clientmgr.c in the ALSA code for this.
41 // TODO(agoode): Add proper client -> card export from the kernel to avoid
42 // hardcoding.
43 const int kMinimumClientIdForCards = 16;
45 // udev key constants.
46 #if defined(USE_UDEV)
47 const char kSoundClass[] = "sound";
48 const char kIdVendor[] = "ID_VENDOR";
49 const char kIdVendorEnc[] = "ID_VENDOR_ENC";
50 const char kIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE";
51 const char kSysattrVendorName[] = "vendor_name";
52 const char kIdVendorId[] = "ID_VENDOR_ID";
53 const char kSysattrVendor[] = "vendor";
54 const char kIdModelId[] = "ID_MODEL_ID";
55 const char kSysattrModel[] = "model";
56 const char kIdBus[] = "ID_BUS";
57 const char kIdPath[] = "ID_PATH";
58 const char kUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM";
59 #endif // defined(USE_UDEV)
61 // ALSA constants.
62 const char kAlsaHw[] = "hw";
64 // Constants for the capabilities we search for in inputs and outputs.
65 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
66 const unsigned int kRequiredInputPortCaps =
67 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
68 const unsigned int kRequiredOutputPortCaps =
69 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
71 int AddrToInt(const snd_seq_addr_t* addr) {
72 return (addr->client << 8) | addr->port;
75 // TODO(agoode): Move this to device/udev_linux.
76 #if defined(USE_UDEV)
77 const std::string UdevDeviceGetPropertyOrSysattr(
78 struct udev_device* udev_device,
79 const char* property_key,
80 const char* sysattr_key) {
81 // First try the property.
82 std::string value =
83 device::UdevDeviceGetPropertyValue(udev_device, property_key);
85 // If no property, look for sysattrs and walk up the parent devices too.
86 while (value.empty() && udev_device) {
87 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key);
88 udev_device = device::udev_device_get_parent(udev_device);
90 return value;
92 #endif // defined(USE_UDEV)
94 void SetStringIfNonEmpty(base::DictionaryValue* value,
95 const std::string& path,
96 const std::string& in_value) {
97 if (!in_value.empty())
98 value->SetString(path, in_value);
101 } // namespace
103 MidiManagerAlsa::MidiManagerAlsa()
104 : in_client_(NULL),
105 out_client_(NULL),
106 out_client_id_(-1),
107 in_port_(-1),
108 decoder_(NULL),
109 #if defined(USE_UDEV)
110 udev_(device::udev_new()),
111 #endif // defined(USE_UDEV)
112 send_thread_("MidiSendThread"),
113 event_thread_("MidiEventThread"),
114 event_thread_shutdown_(false) {
115 // Initialize decoder.
116 snd_midi_event_new(0, &decoder_);
117 snd_midi_event_no_status(decoder_, 1);
120 MidiManagerAlsa::~MidiManagerAlsa() {
121 // Tell the event thread it will soon be time to shut down. This gives
122 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
123 // message is lost.
125 base::AutoLock lock(shutdown_lock_);
126 event_thread_shutdown_ = true;
129 // Stop the send thread.
130 send_thread_.Stop();
132 // Close the out client. This will trigger the event thread to stop,
133 // because of SND_SEQ_EVENT_CLIENT_EXIT.
134 if (out_client_)
135 snd_seq_close(out_client_);
137 // Wait for the event thread to stop.
138 event_thread_.Stop();
140 // Close the in client.
141 if (in_client_)
142 snd_seq_close(in_client_);
144 // Free the decoder.
145 snd_midi_event_free(decoder_);
148 void MidiManagerAlsa::StartInitialization() {
149 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
151 // Create client handles.
152 int err = snd_seq_open(&in_client_, kAlsaHw, SND_SEQ_OPEN_INPUT, 0);
153 if (err != 0) {
154 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
155 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
157 int in_client_id = snd_seq_client_id(in_client_);
158 err = snd_seq_open(&out_client_, kAlsaHw, SND_SEQ_OPEN_OUTPUT, 0);
159 if (err != 0) {
160 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
161 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
163 out_client_id_ = snd_seq_client_id(out_client_);
165 // Name the clients.
166 err = snd_seq_set_client_name(in_client_, "Chrome (input)");
167 if (err != 0) {
168 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
169 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
171 err = snd_seq_set_client_name(out_client_, "Chrome (output)");
172 if (err != 0) {
173 VLOG(1) << "snd_seq_set_client_name fails: " << snd_strerror(err);
174 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
177 // Create input port.
178 in_port_ = snd_seq_create_simple_port(in_client_, NULL,
179 SND_SEQ_PORT_CAP_WRITE |
180 SND_SEQ_PORT_CAP_NO_EXPORT,
181 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
182 SND_SEQ_PORT_TYPE_APPLICATION);
183 if (in_port_ < 0) {
184 VLOG(1) << "snd_seq_create_simple_port fails: " << snd_strerror(in_port_);
185 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
188 // Subscribe to the announce port.
189 snd_seq_port_subscribe_t* subs;
190 snd_seq_port_subscribe_alloca(&subs);
191 snd_seq_addr_t announce_sender;
192 snd_seq_addr_t announce_dest;
193 announce_sender.client = SND_SEQ_CLIENT_SYSTEM;
194 announce_sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
195 announce_dest.client = in_client_id;
196 announce_dest.port = in_port_;
197 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
198 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
199 err = snd_seq_subscribe_port(in_client_, subs);
200 if (err != 0) {
201 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
202 << snd_strerror(err);
203 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
206 EnumeratePorts();
208 event_thread_.Start();
209 event_thread_.message_loop()->PostTask(
210 FROM_HERE,
211 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
213 CompleteInitialization(MIDI_OK);
216 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
217 uint32 port_index,
218 const std::vector<uint8>& data,
219 double timestamp) {
220 if (out_ports_.size() <= port_index)
221 return;
223 // Not correct right now. http://crbug.com/374341.
224 if (!send_thread_.IsRunning())
225 send_thread_.Start();
227 base::TimeDelta delay;
228 if (timestamp != 0.0) {
229 base::TimeTicks time_to_send =
230 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
231 timestamp * base::Time::kMicrosecondsPerSecond);
232 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
235 send_thread_.message_loop()->PostDelayedTask(
236 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
237 base::Unretained(this), port_index, data),
238 delay);
240 // Acknowledge send.
241 send_thread_.message_loop()->PostTask(
242 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
243 base::Unretained(client), data.size()));
246 MidiManagerAlsa::AlsaCard::AlsaCard(const MidiManagerAlsa* outer,
247 const std::string& alsa_name,
248 const std::string& alsa_longname,
249 const std::string& alsa_driver,
250 int card_index,
251 int midi_count)
252 : alsa_name_(alsa_name),
253 alsa_longname_(alsa_longname),
254 alsa_driver_(alsa_driver),
255 midi_count_(midi_count) {
256 // Get udev properties if available.
257 std::string vendor;
258 std::string vendor_from_database;
260 #if defined(USE_UDEV)
261 const std::string sysname = base::StringPrintf("card%i", card_index);
262 device::ScopedUdevDevicePtr udev_device(
263 device::udev_device_new_from_subsystem_sysname(
264 outer->udev_.get(), kSoundClass, sysname.c_str()));
266 // TODO(agoode): Move this to a new utility class in device/udev_linux?
268 // Try to get the vendor string. Sometimes it is encoded.
269 vendor = device::UdevDecodeString(
270 device::UdevDeviceGetPropertyValue(udev_device.get(), kIdVendorEnc));
271 // Sometimes it is not encoded.
272 if (vendor.empty())
273 vendor = UdevDeviceGetPropertyOrSysattr(udev_device.get(), kIdVendor,
274 kSysattrVendorName);
275 // Also get the vendor string from the hardware database.
276 vendor_from_database = device::UdevDeviceGetPropertyValue(
277 udev_device.get(), kIdVendorFromDatabase);
279 // Get the device path.
280 path_ = device::UdevDeviceGetPropertyValue(udev_device.get(), kIdPath);
282 // Get the bus.
283 bus_ = device::UdevDeviceGetPropertyValue(udev_device.get(), kIdBus);
285 // Get the vendor id, by either property or sysattr.
286 vendor_id_ = UdevDeviceGetPropertyOrSysattr(udev_device.get(), kIdVendorId,
287 kSysattrVendor);
289 // Get the model id, by either property or sysattr.
290 model_id_ = UdevDeviceGetPropertyOrSysattr(udev_device.get(), kIdModelId,
291 kSysattrModel);
293 // Get the usb interface number.
294 usb_interface_num_ =
295 device::UdevDeviceGetPropertyValue(udev_device.get(), kUsbInterfaceNum);
296 #endif // defined(USE_UDEV)
298 manufacturer_ = ExtractManufacturerString(
299 vendor, vendor_id_, vendor_from_database, alsa_name, alsa_longname);
302 MidiManagerAlsa::AlsaCard::~AlsaCard() {
305 const std::string MidiManagerAlsa::AlsaCard::alsa_name() const {
306 return alsa_name_;
309 const std::string MidiManagerAlsa::AlsaCard::alsa_longname() const {
310 return alsa_longname_;
313 const std::string MidiManagerAlsa::AlsaCard::manufacturer() const {
314 return manufacturer_;
317 const std::string MidiManagerAlsa::AlsaCard::alsa_driver() const {
318 return alsa_driver_;
321 const std::string MidiManagerAlsa::AlsaCard::path() const {
322 return path_;
325 const std::string MidiManagerAlsa::AlsaCard::bus() const {
326 return bus_;
329 const std::string MidiManagerAlsa::AlsaCard::vendor_id() const {
330 return vendor_id_;
333 const std::string MidiManagerAlsa::AlsaCard::id() const {
334 std::string id = vendor_id_;
335 if (!model_id_.empty())
336 id += ":" + model_id_;
337 if (!usb_interface_num_.empty())
338 id += ":" + usb_interface_num_;
339 return id;
342 const int MidiManagerAlsa::AlsaCard::midi_count() const {
343 return midi_count_;
346 // static
347 std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString(
348 const std::string& udev_id_vendor,
349 const std::string& udev_id_vendor_id,
350 const std::string& udev_id_vendor_from_database,
351 const std::string& alsa_name,
352 const std::string& alsa_longname) {
353 // Let's try to determine the manufacturer. Here is the ordered preference
354 // in extraction:
355 // 1. Vendor name from the hardware device string, from udev properties
356 // or sysattrs.
357 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
358 // 3. Heuristic from ALSA.
360 // Is the vendor string present and not just the vendor hex id?
361 if (!udev_id_vendor.empty() && (udev_id_vendor != udev_id_vendor_id)) {
362 return udev_id_vendor;
365 // Is there a vendor string in the hardware database?
366 if (!udev_id_vendor_from_database.empty()) {
367 return udev_id_vendor_from_database;
370 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
371 // We assume that card longname is in the format of
372 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
373 // a manufacturer name here.
374 size_t at_index = alsa_longname.rfind(" at ");
375 if (at_index && at_index != std::string::npos) {
376 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
377 if (name_index && name_index != std::string::npos)
378 return alsa_longname.substr(0, name_index - 1);
381 // Failure.
382 return "";
385 MidiManagerAlsa::AlsaPortMetadata::AlsaPortMetadata(
386 const std::string& path,
387 const std::string& bus,
388 const std::string& id,
389 const snd_seq_addr_t* address,
390 const std::string& client_name,
391 const std::string& port_name,
392 const std::string& card_name,
393 const std::string& card_longname,
394 Type type)
395 : path_(path),
396 bus_(bus),
397 id_(id),
398 client_addr_(address->client),
399 port_addr_(address->port),
400 client_name_(client_name),
401 port_name_(port_name),
402 card_name_(card_name),
403 card_longname_(card_longname),
404 type_(type) {
407 MidiManagerAlsa::AlsaPortMetadata::~AlsaPortMetadata() {
410 scoped_ptr<base::Value> MidiManagerAlsa::AlsaPortMetadata::Value() const {
411 scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue);
412 SetStringIfNonEmpty(value.get(), "path", path_);
413 SetStringIfNonEmpty(value.get(), "bus", bus_);
414 SetStringIfNonEmpty(value.get(), "id", id_);
415 value->SetInteger("clientAddr", client_addr_);
416 value->SetInteger("portAddr", port_addr_);
417 SetStringIfNonEmpty(value.get(), "clientName", client_name_);
418 SetStringIfNonEmpty(value.get(), "portName", port_name_);
419 SetStringIfNonEmpty(value.get(), "cardName", card_name_);
420 SetStringIfNonEmpty(value.get(), "cardLongname", card_longname_);
421 std::string type;
422 switch (type_) {
423 case Type::kInput:
424 type = "input";
425 break;
427 case Type::kOutput:
428 type = "output";
429 break;
431 SetStringIfNonEmpty(value.get(), "type", type);
433 return value.Pass();
436 std::string MidiManagerAlsa::AlsaPortMetadata::JSONValue() const {
437 std::string json;
438 JSONStringValueSerializer serializer(&json);
439 serializer.Serialize(*Value().get());
440 return json;
443 // TODO(agoode): Do not use SHA256 here. Instead store a persistent
444 // mapping and just use a UUID or other random string.
445 // http://crbug.com/465320
446 std::string MidiManagerAlsa::AlsaPortMetadata::OpaqueKey() const {
447 uint8 hash[crypto::kSHA256Length];
448 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash));
449 return base::HexEncode(&hash, sizeof(hash));
452 // TODO(agoode): Add a client->card/rawmidi mapping to the kernel to avoid
453 // needing to probe in this way.
454 ScopedVector<MidiManagerAlsa::AlsaCard> MidiManagerAlsa::AllMidiCards() {
455 ScopedVector<AlsaCard> devices;
456 snd_ctl_card_info_t* card;
457 snd_hwdep_info_t* hwdep;
458 snd_ctl_card_info_alloca(&card);
459 snd_hwdep_info_alloca(&hwdep);
460 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) {
461 int midi_count = 0;
462 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
463 snd_ctl_t* handle;
464 int err = snd_ctl_open(&handle, id.c_str(), 0);
465 if (err != 0) {
466 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
467 continue;
469 err = snd_ctl_card_info(handle, card);
470 if (err != 0) {
471 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
472 snd_ctl_close(handle);
473 continue;
475 std::string name = snd_ctl_card_info_get_name(card);
476 std::string longname = snd_ctl_card_info_get_longname(card);
477 std::string driver = snd_ctl_card_info_get_driver(card);
479 // Count rawmidi devices (not subdevices).
480 for (int device = -1;
481 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;)
482 ++midi_count;
484 // Count any hwdep synths that become MIDI devices outside of rawmidi.
486 // Explanation:
487 // Any kernel driver can create an ALSA client (visible to us).
488 // With modern hardware, only rawmidi devices do this. Kernel
489 // drivers create rawmidi devices and the rawmidi subsystem makes
490 // the seq clients. But the OPL3 driver is special, it does not
491 // make a rawmidi device but a seq client directly. (This is the
492 // only one to worry about in the kernel code, as of 2015-03-23.)
494 // OPL3 is very old (but still possible to get in new
495 // hardware). It is unlikely that new drivers would not use
496 // rawmidi and defeat our heuristic.
498 // Longer term, support should be added in the kernel to expose a
499 // direct link from card->client (or client->card) so that all
500 // these heuristics will be obsolete. Once that is there, we can
501 // assume our old heuristics will work on old kernels and the new
502 // robust code will be used on new. Then we will not need to worry
503 // about changes to kernel internals breaking our code.
504 // See the TODO above at kMinimumClientIdForCards.
505 for (int device = -1;
506 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) {
507 snd_ctl_hwdep_info(handle, hwdep);
508 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep);
509 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 ||
510 iface == SND_HWDEP_IFACE_OPL4)
511 ++midi_count;
514 if (midi_count > 0)
515 devices.push_back(
516 new AlsaCard(this, name, longname, driver, card_index, midi_count));
517 snd_ctl_close(handle);
520 return devices.Pass();
523 void MidiManagerAlsa::EnumeratePorts() {
524 ScopedVector<AlsaCard> cards = AllMidiCards();
525 std::vector<const AlsaCard*> devices;
526 for (const auto* card : cards) {
527 // Insert 1 AlsaCard per number of MIDI devices.
528 for (int n = 0; n < card->midi_count(); ++n)
529 devices.push_back(card);
532 snd_seq_port_subscribe_t* subs;
533 snd_seq_port_subscribe_alloca(&subs);
535 int in_client_id = snd_seq_client_id(in_client_);
537 // Enumerate all clients.
538 snd_seq_client_info_t* client_info;
539 snd_seq_client_info_alloca(&client_info);
540 snd_seq_port_info_t* port_info;
541 snd_seq_port_info_alloca(&port_info);
543 // Enumerate clients.
544 snd_seq_client_info_set_client(client_info, -1);
545 uint32 current_input = 0;
546 unsigned int current_device = 0;
547 while (!snd_seq_query_next_client(in_client_, client_info)) {
548 int client_id = snd_seq_client_info_get_client(client_info);
549 if ((client_id == in_client_id) || (client_id == out_client_id_)) {
550 // Skip our own clients.
551 continue;
554 // Get client metadata.
555 const std::string client_name = snd_seq_client_info_get_name(client_info);
556 snd_seq_port_info_set_client(port_info, client_id);
557 snd_seq_port_info_set_port(port_info, -1);
559 std::string manufacturer;
560 std::string driver;
561 std::string path;
562 std::string bus;
563 std::string vendor_id;
564 std::string id;
565 std::string card_name;
566 std::string card_longname;
568 // Join kernel clients against the list of AlsaCards.
569 // In the current ALSA kernel implementation, kernel clients match the
570 // kernel devices in the same order, for clients with client_id over
571 // kMinimumClientIdForCards.
572 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
573 (current_device < devices.size()) &&
574 (client_id >= kMinimumClientIdForCards)) {
575 const AlsaCard* device = devices[current_device];
576 manufacturer = device->manufacturer();
577 driver = device->alsa_driver();
578 path = device->path();
579 bus = device->bus();
580 vendor_id = device->vendor_id();
581 id = device->id();
582 card_name = device->alsa_name();
583 card_longname = device->alsa_longname();
584 current_device++;
586 // Enumerate ports.
587 while (!snd_seq_query_next_port(in_client_, port_info)) {
588 unsigned int port_type = snd_seq_port_info_get_type(port_info);
589 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
590 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
591 const std::string name = snd_seq_port_info_get_name(port_info);
592 std::string version;
593 if (!driver.empty()) {
594 version = driver + " / ";
596 version += base::StringPrintf("ALSA library version %d.%d.%d",
597 SND_LIB_MAJOR,
598 SND_LIB_MINOR,
599 SND_LIB_SUBMINOR);
600 unsigned int caps = snd_seq_port_info_get_capability(port_info);
601 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
602 // Subscribe to this port.
603 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
604 snd_seq_addr_t dest;
605 dest.client = snd_seq_client_id(in_client_);
606 dest.port = in_port_;
607 snd_seq_port_subscribe_set_sender(subs, sender);
608 snd_seq_port_subscribe_set_dest(subs, &dest);
609 int err = snd_seq_subscribe_port(in_client_, subs);
610 if (err != 0) {
611 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
612 } else {
613 source_map_[AddrToInt(sender)] = current_input++;
614 const AlsaPortMetadata metadata(path, bus, id, addr, client_name,
615 name, card_name, card_longname,
616 AlsaPortMetadata::Type::kInput);
617 const std::string id = metadata.OpaqueKey();
618 AddInputPort(MidiPortInfo(id.c_str(), manufacturer, name, version,
619 MIDI_PORT_OPENED));
622 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
623 // Create a port for us to send on.
624 int out_port =
625 snd_seq_create_simple_port(out_client_, NULL,
626 SND_SEQ_PORT_CAP_READ |
627 SND_SEQ_PORT_CAP_NO_EXPORT,
628 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
629 SND_SEQ_PORT_TYPE_APPLICATION);
630 if (out_port < 0) {
631 VLOG(1) << "snd_seq_create_simple_port fails: "
632 << snd_strerror(out_port);
633 // Skip this output port for now.
634 continue;
637 // Activate port subscription.
638 snd_seq_addr_t sender;
639 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
640 sender.client = snd_seq_client_id(out_client_);
641 sender.port = out_port;
642 snd_seq_port_subscribe_set_sender(subs, &sender);
643 snd_seq_port_subscribe_set_dest(subs, dest);
644 int err = snd_seq_subscribe_port(out_client_, subs);
645 if (err != 0) {
646 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
647 snd_seq_delete_simple_port(out_client_, out_port);
648 } else {
649 out_ports_.push_back(out_port);
650 const AlsaPortMetadata metadata(path, bus, id, addr, client_name,
651 name, card_name, card_longname,
652 AlsaPortMetadata::Type::kOutput);
653 const std::string id = metadata.OpaqueKey();
654 AddOutputPort(MidiPortInfo(id.c_str(), manufacturer, name, version,
655 MIDI_PORT_OPENED));
663 void MidiManagerAlsa::SendMidiData(uint32 port_index,
664 const std::vector<uint8>& data) {
665 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
667 snd_midi_event_t* encoder;
668 snd_midi_event_new(kSendBufferSize, &encoder);
669 for (unsigned int i = 0; i < data.size(); i++) {
670 snd_seq_event_t event;
671 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
672 if (result == 1) {
673 // Full event, send it.
674 snd_seq_ev_set_source(&event, out_ports_[port_index]);
675 snd_seq_ev_set_subs(&event);
676 snd_seq_ev_set_direct(&event);
677 snd_seq_event_output_direct(out_client_, &event);
680 snd_midi_event_free(encoder);
683 void MidiManagerAlsa::ScheduleEventLoop() {
684 event_thread_.message_loop()->PostTask(
685 FROM_HERE,
686 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
689 void MidiManagerAlsa::EventLoop() {
690 // Read available incoming MIDI data.
691 snd_seq_event_t* event;
692 int err = snd_seq_event_input(in_client_, &event);
693 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
695 // Handle errors.
696 if (err == -ENOSPC) {
697 VLOG(1) << "snd_seq_event_input detected buffer overrun";
698 // We've lost events: check another way to see if we need to shut down.
699 base::AutoLock lock(shutdown_lock_);
700 if (!event_thread_shutdown_)
701 ScheduleEventLoop();
702 return;
703 } else if (err < 0) {
704 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
705 // TODO(agoode): Use RecordAction() or similar to log this.
706 return;
709 // Handle announce events.
710 if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
711 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
712 switch (event->type) {
713 case SND_SEQ_EVENT_CLIENT_START:
714 // TODO(agoode): rescan hardware devices.
715 break;
717 case SND_SEQ_EVENT_PORT_START:
718 // TODO(agoode): add port.
719 break;
721 case SND_SEQ_EVENT_CLIENT_EXIT:
722 // Check for disconnection of our "out" client. This means "shut down".
723 if (event->data.addr.client == out_client_id_)
724 return;
726 // TODO(agoode): remove all ports for a client.
727 break;
729 case SND_SEQ_EVENT_PORT_EXIT:
730 // TODO(agoode): remove port.
731 break;
733 } else {
734 ProcessSingleEvent(event, timestamp);
737 // Do again.
738 ScheduleEventLoop();
741 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
742 double timestamp) {
743 auto source_it = source_map_.find(AddrToInt(&event->source));
744 if (source_it != source_map_.end()) {
745 uint32 source = source_it->second;
746 if (event->type == SND_SEQ_EVENT_SYSEX) {
747 // Special! Variable-length sysex.
748 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
749 event->data.ext.len, timestamp);
750 } else {
751 // Otherwise, decode this and send that on.
752 unsigned char buf[12];
753 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
754 if (count <= 0) {
755 if (count != -ENOENT) {
756 // ENOENT means that it's not a MIDI message, which is not an
757 // error, but other negative values are errors for us.
758 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
759 // TODO(agoode): Record this failure.
761 } else {
762 ReceiveMidiData(source, buf, count, timestamp);
768 MidiManager* MidiManager::Create() {
769 return new MidiManagerAlsa();
772 } // namespace media