Chromium Code Reviews| 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 <poll.h> | 8 #include <poll.h> |
| 9 #include <stdlib.h> | 9 #include <stdlib.h> |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 namespace midi { | 30 namespace midi { |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 // Per-output buffer. This can be smaller, but then large sysex messages | 34 // Per-output buffer. This can be smaller, but then large sysex messages |
| 35 // will be (harmlessly) split across multiple seq events. This should | 35 // will be (harmlessly) split across multiple seq events. This should |
| 36 // not have any real practical effect, except perhaps to slightly reorder | 36 // not have any real practical effect, except perhaps to slightly reorder |
| 37 // realtime messages with respect to sysex. | 37 // realtime messages with respect to sysex. |
| 38 const size_t kSendBufferSize = 256; | 38 const size_t kSendBufferSize = 256; |
| 39 | 39 |
| 40 // Minimum client id for which we will have ALSA card devices for. When we | |
| 41 // are searching for card devices (used to get the path, id, and manufacturer), | |
| 42 // we don't want to get confused by kernel clients that do not have a card. | |
| 43 // See seq_clientmgr.c in the ALSA code for this. | |
| 44 // TODO(agoode): Add proper client -> card export from the kernel to avoid | |
| 45 // hardcoding. | |
| 46 const int kMinimumClientIdForCards = 16; | |
| 47 | |
| 40 // ALSA constants. | 48 // ALSA constants. |
| 41 const char kAlsaHw[] = "hw"; | 49 const char kAlsaHw[] = "hw"; |
| 42 | 50 |
| 43 // udev constants. | 51 // udev constants. |
| 44 const char kUdev[] = "udev"; | 52 const char kUdev[] = "udev"; |
| 45 const char kUdevSubsystemSound[] = "sound"; | 53 const char kUdevSubsystemSound[] = "sound"; |
| 46 const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED"; | 54 const char kUdevPropertySoundInitialized[] = "SOUND_INITIALIZED"; |
| 47 const char kUdevActionChange[] = "change"; | 55 const char kUdevActionChange[] = "change"; |
| 48 const char kUdevActionRemove[] = "remove"; | 56 const char kUdevActionRemove[] = "remove"; |
| 49 | 57 |
| 58 const char kUdevIdVendor[] = "ID_VENDOR"; | |
| 59 const char kUdevIdVendorEnc[] = "ID_VENDOR_ENC"; | |
| 60 const char kUdevIdVendorFromDatabase[] = "ID_VENDOR_FROM_DATABASE"; | |
| 61 const char kUdevIdVendorId[] = "ID_VENDOR_ID"; | |
| 62 const char kUdevIdModelId[] = "ID_MODEL_ID"; | |
| 63 const char kUdevIdBus[] = "ID_BUS"; | |
| 64 const char kUdevIdPath[] = "ID_PATH"; | |
| 65 const char kUdevIdUsbInterfaceNum[] = "ID_USB_INTERFACE_NUM"; | |
| 66 const char kUdevIdSerialShort[] = "ID_SERIAL_SHORT"; | |
| 67 | |
| 68 const char kSysattrVendorName[] = "vendor_name"; | |
| 69 const char kSysattrVendor[] = "vendor"; | |
| 70 const char kSysattrModel[] = "model"; | |
| 71 const char kSysattrGuid[] = "guid"; | |
| 72 | |
| 73 const char kCardSyspath[] = "/card"; | |
| 74 | |
| 50 // Constants for the capabilities we search for in inputs and outputs. | 75 // Constants for the capabilities we search for in inputs and outputs. |
| 51 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. | 76 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. |
| 52 const unsigned int kRequiredInputPortCaps = | 77 const unsigned int kRequiredInputPortCaps = |
| 53 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; | 78 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; |
| 54 const unsigned int kRequiredOutputPortCaps = | 79 const unsigned int kRequiredOutputPortCaps = |
| 55 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; | 80 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; |
| 56 | 81 |
| 57 const unsigned int kCreateOutputPortCaps = | 82 const unsigned int kCreateOutputPortCaps = |
| 58 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT; | 83 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_NO_EXPORT; |
| 59 const unsigned int kCreateInputPortCaps = | 84 const unsigned int kCreateInputPortCaps = |
| 60 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT; | 85 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_NO_EXPORT; |
| 61 const unsigned int kCreatePortType = | 86 const unsigned int kCreatePortType = |
| 62 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION; | 87 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION; |
| 63 | 88 |
| 64 int AddrToInt(int client, int port) { | 89 int AddrToInt(int client, int port) { |
| 65 return (client << 8) | port; | 90 return (client << 8) | port; |
| 66 } | 91 } |
| 67 | 92 |
| 93 bool IsCardKernelClient(snd_seq_client_type_t type, int client_id) { | |
| 94 return (type == SND_SEQ_KERNEL_CLIENT) && | |
| 95 (client_id >= kMinimumClientIdForCards); | |
| 96 } | |
| 97 | |
| 98 // TODO(agoode): Move this to device/udev_linux. | |
| 99 const std::string UdevDeviceGetPropertyOrSysattr( | |
| 100 struct udev_device* udev_device, | |
| 101 const char* property_key, | |
| 102 const char* sysattr_key) { | |
| 103 // First try the property. | |
| 104 std::string value = | |
| 105 device::UdevDeviceGetPropertyValue(udev_device, property_key); | |
| 106 | |
| 107 // If no property, look for sysattrs and walk up the parent devices too. | |
| 108 while (value.empty() && udev_device) { | |
| 109 value = device::UdevDeviceGetSysattrValue(udev_device, sysattr_key); | |
| 110 udev_device = device::udev_device_get_parent(udev_device); | |
| 111 } | |
| 112 return value; | |
| 113 } | |
| 114 | |
| 115 int GetCardNumber(udev_device* dev) { | |
| 116 const char* syspath = device::udev_device_get_syspath(dev); | |
| 117 if (!syspath) | |
| 118 return -1; | |
| 119 | |
| 120 std::string syspath_str(syspath); | |
| 121 size_t i = syspath_str.rfind(kCardSyspath); | |
| 122 if (i == std::string::npos) | |
| 123 return -1; | |
| 124 | |
| 125 int number; | |
| 126 if (!base::StringToInt(syspath_str.substr(i + strlen(kCardSyspath)), &number)) | |
| 127 return -1; | |
| 128 return number; | |
| 129 } | |
| 130 | |
| 68 void SetStringIfNonEmpty(base::DictionaryValue* value, | 131 void SetStringIfNonEmpty(base::DictionaryValue* value, |
| 69 const std::string& path, | 132 const std::string& path, |
| 70 const std::string& in_value) { | 133 const std::string& in_value) { |
| 71 if (!in_value.empty()) | 134 if (!in_value.empty()) |
| 72 value->SetString(path, in_value); | 135 value->SetString(path, in_value); |
| 73 } | 136 } |
| 74 | 137 |
| 75 } // namespace | 138 } // namespace |
| 76 | 139 |
| 77 MidiManagerAlsa::MidiManagerAlsa() | 140 MidiManagerAlsa::MidiManagerAlsa() |
| 78 : in_client_(NULL), | 141 : in_client_(NULL), |
| 79 out_client_(NULL), | 142 out_client_(NULL), |
| 80 out_client_id_(-1), | 143 out_client_id_(-1), |
| 81 in_port_id_(-1), | 144 in_port_id_(-1), |
| 145 alsa_cards_deleter_(&alsa_cards_), | |
| 146 alsa_card_midi_count_(0), | |
| 82 decoder_(NULL), | 147 decoder_(NULL), |
| 83 udev_(device::udev_new()), | 148 udev_(device::udev_new()), |
| 84 send_thread_("MidiSendThread"), | 149 send_thread_("MidiSendThread"), |
| 85 event_thread_("MidiEventThread"), | 150 event_thread_("MidiEventThread"), |
| 86 event_thread_shutdown_(false) { | 151 event_thread_shutdown_(false) { |
| 87 // Initialize decoder. | 152 // Initialize decoder. |
| 88 snd_midi_event_new(0, &decoder_); | 153 snd_midi_event_new(0, &decoder_); |
| 89 snd_midi_event_no_status(decoder_, 1); | 154 snd_midi_event_no_status(decoder_, 1); |
| 90 } | 155 } |
| 91 | 156 |
| (...skipping 396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 488 break; | 553 break; |
| 489 case MidiPort::Type::kOutput: | 554 case MidiPort::Type::kOutput: |
| 490 web_port_index = num_output_ports_++; | 555 web_port_index = num_output_ports_++; |
| 491 break; | 556 break; |
| 492 } | 557 } |
| 493 port->set_web_port_index(web_port_index); | 558 port->set_web_port_index(web_port_index); |
| 494 ports()->push_back(port.Pass()); | 559 ports()->push_back(port.Pass()); |
| 495 return web_port_index; | 560 return web_port_index; |
| 496 } | 561 } |
| 497 | 562 |
| 498 MidiManagerAlsa::AlsaSeqState::AlsaSeqState() : clients_deleter_(&clients_) { | 563 MidiManagerAlsa::AlsaSeqState::AlsaSeqState() |
| 564 : clients_deleter_(&clients_), kernel_client_count_(0) { | |
| 499 } | 565 } |
| 500 | 566 |
| 501 MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() { | 567 MidiManagerAlsa::AlsaSeqState::~AlsaSeqState() { |
| 502 } | 568 } |
| 503 | 569 |
| 504 void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id, | 570 void MidiManagerAlsa::AlsaSeqState::ClientStart(int client_id, |
| 505 const std::string& client_name, | 571 const std::string& client_name, |
| 506 snd_seq_client_type_t type) { | 572 snd_seq_client_type_t type) { |
| 507 ClientExit(client_id); | 573 ClientExit(client_id); |
| 508 clients_[client_id] = new Client(client_name, type); | 574 clients_[client_id] = new Client(client_name, type); |
| 575 if (IsCardKernelClient(type, client_id)) | |
| 576 ++kernel_client_count_; | |
| 509 } | 577 } |
| 510 | 578 |
| 511 bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) { | 579 bool MidiManagerAlsa::AlsaSeqState::ClientStarted(int client_id) { |
| 512 return clients_.find(client_id) != clients_.end(); | 580 return clients_.find(client_id) != clients_.end(); |
| 513 } | 581 } |
| 514 | 582 |
| 515 void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) { | 583 void MidiManagerAlsa::AlsaSeqState::ClientExit(int client_id) { |
| 516 auto it = clients_.find(client_id); | 584 auto it = clients_.find(client_id); |
| 517 if (it != clients_.end()) { | 585 if (it != clients_.end()) { |
| 586 if (IsCardKernelClient(it->second->type(), client_id)) | |
| 587 --kernel_client_count_; | |
| 518 delete it->second; | 588 delete it->second; |
| 519 clients_.erase(it); | 589 clients_.erase(it); |
| 520 } | 590 } |
| 521 } | 591 } |
| 522 | 592 |
| 523 void MidiManagerAlsa::AlsaSeqState::PortStart( | 593 void MidiManagerAlsa::AlsaSeqState::PortStart( |
| 524 int client_id, | 594 int client_id, |
| 525 int port_id, | 595 int port_id, |
| 526 const std::string& port_name, | 596 const std::string& port_name, |
| 527 MidiManagerAlsa::AlsaSeqState::PortDirection direction, | 597 MidiManagerAlsa::AlsaSeqState::PortDirection direction, |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 540 | 610 |
| 541 snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType( | 611 snd_seq_client_type_t MidiManagerAlsa::AlsaSeqState::ClientType( |
| 542 int client_id) const { | 612 int client_id) const { |
| 543 auto it = clients_.find(client_id); | 613 auto it = clients_.find(client_id); |
| 544 if (it == clients_.end()) | 614 if (it == clients_.end()) |
| 545 return SND_SEQ_USER_CLIENT; | 615 return SND_SEQ_USER_CLIENT; |
| 546 return it->second->type(); | 616 return it->second->type(); |
| 547 } | 617 } |
| 548 | 618 |
| 549 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState> | 619 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState> |
| 550 MidiManagerAlsa::AlsaSeqState::ToMidiPortState() { | 620 MidiManagerAlsa::AlsaSeqState::ToMidiPortState(const AlsaCardMap& alsa_cards) { |
| 551 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports( | 621 scoped_ptr<MidiManagerAlsa::TemporaryMidiPortState> midi_ports( |
| 552 new TemporaryMidiPortState); | 622 new TemporaryMidiPortState); |
| 553 // TODO(agoode): Use information from udev as well. | 623 // TODO(agoode): Use more information from udev, to allow hardware matching. |
| 624 // See http://crbug.com/486471. | |
| 625 auto card_it = alsa_cards.begin(); | |
| 554 | 626 |
| 627 int card_midi_device = -1; | |
| 555 for (const auto& client_pair : clients_) { | 628 for (const auto& client_pair : clients_) { |
| 556 int client_id = client_pair.first; | 629 int client_id = client_pair.first; |
| 557 const auto& client = client_pair.second; | 630 const auto& client = client_pair.second; |
| 558 | 631 |
| 559 // Get client metadata. | 632 // Get client metadata. |
| 560 const std::string client_name = client->name(); | 633 const std::string client_name = client->name(); |
| 561 std::string manufacturer; | 634 std::string manufacturer; |
| 562 std::string driver; | 635 std::string driver; |
| 563 std::string path; | 636 std::string path; |
| 564 std::string id; | 637 std::string id; |
| 565 std::string serial; | 638 std::string serial; |
| 566 std::string card_name; | 639 std::string card_name; |
| 567 std::string card_longname; | 640 std::string card_longname; |
| 568 int midi_device = -1; | 641 int midi_device = -1; |
| 569 | 642 |
| 643 if (IsCardKernelClient(client->type(), client_id)) { | |
| 644 auto& card = card_it->second; | |
| 645 if (card_midi_device == -1) | |
| 646 card_midi_device = 0; | |
| 647 | |
| 648 manufacturer = card->manufacturer(); | |
| 649 midi_device = card_midi_device; | |
| 650 | |
| 651 ++card_midi_device; | |
| 652 if (card_midi_device >= card->midi_device_count()) { | |
| 653 card_midi_device = -1; | |
| 654 ++card_it; | |
| 655 } | |
| 656 } | |
| 657 | |
| 570 for (const auto& port_pair : *client) { | 658 for (const auto& port_pair : *client) { |
| 571 int port_id = port_pair.first; | 659 int port_id = port_pair.first; |
| 572 const auto& port = port_pair.second; | 660 const auto& port = port_pair.second; |
| 573 | 661 |
| 574 if (port->midi()) { | 662 if (port->midi()) { |
| 575 std::string version; | 663 std::string version; |
| 576 if (!driver.empty()) { | 664 if (!driver.empty()) { |
| 577 version = driver + " / "; | 665 version = driver + " / "; |
| 578 } | 666 } |
| 579 version += | 667 version += |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 655 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator | 743 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator |
| 656 MidiManagerAlsa::AlsaSeqState::Client::begin() const { | 744 MidiManagerAlsa::AlsaSeqState::Client::begin() const { |
| 657 return ports_.begin(); | 745 return ports_.begin(); |
| 658 } | 746 } |
| 659 | 747 |
| 660 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator | 748 MidiManagerAlsa::AlsaSeqState::Client::PortMap::const_iterator |
| 661 MidiManagerAlsa::AlsaSeqState::Client::end() const { | 749 MidiManagerAlsa::AlsaSeqState::Client::end() const { |
| 662 return ports_.end(); | 750 return ports_.end(); |
| 663 } | 751 } |
| 664 | 752 |
| 753 MidiManagerAlsa::AlsaCard::AlsaCard(udev_device* dev, | |
| 754 const std::string& alsa_name, | |
| 755 const std::string& alsa_longname, | |
| 756 const std::string& alsa_driver, | |
| 757 int midi_device_count) | |
| 758 : alsa_name_(alsa_name), | |
| 759 alsa_longname_(alsa_longname), | |
| 760 alsa_driver_(alsa_driver), | |
| 761 midi_device_count_(midi_device_count) { | |
| 762 // Try to get the vendor string. Sometimes it is encoded. | |
| 763 std::string vendor = device::UdevDecodeString( | |
| 764 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorEnc)); | |
| 765 // Sometimes it is not encoded. | |
| 766 if (vendor.empty()) | |
| 767 vendor = | |
| 768 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendor, kSysattrVendorName); | |
| 769 // Also get the vendor string from the hardware database. | |
| 770 std::string vendor_from_database = | |
| 771 device::UdevDeviceGetPropertyValue(dev, kUdevIdVendorFromDatabase); | |
| 772 | |
| 773 // Get the device path. | |
| 774 path_ = device::UdevDeviceGetPropertyValue(dev, kUdevIdPath); | |
| 775 // Get the bus. | |
| 776 bus_ = device::UdevDeviceGetPropertyValue(dev, kUdevIdBus); | |
| 777 | |
| 778 // Get the "serial" number. (Often untrustable or missing.) | |
| 779 serial_ = | |
| 780 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdSerialShort, kSysattrGuid); | |
| 781 | |
| 782 // Get the vendor id, by either property or sysattr. | |
| 783 vendor_id_ = | |
| 784 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdVendorId, kSysattrVendor); | |
| 785 // Get the model id, by either property or sysattr. | |
| 786 model_id_ = | |
| 787 UdevDeviceGetPropertyOrSysattr(dev, kUdevIdModelId, kSysattrModel); | |
| 788 // Get the usb interface number. | |
| 789 usb_interface_num_ = | |
| 790 device::UdevDeviceGetPropertyValue(dev, kUdevIdUsbInterfaceNum); | |
| 791 manufacturer_ = ExtractManufacturerString( | |
| 792 vendor, vendor_id_, vendor_from_database, alsa_name, alsa_longname); | |
| 793 } | |
| 794 | |
| 795 MidiManagerAlsa::AlsaCard::~AlsaCard() { | |
| 796 } | |
| 797 | |
| 665 // static | 798 // static |
| 666 std::string MidiManagerAlsa::ExtractManufacturerString( | 799 std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString( |
| 667 const std::string& udev_id_vendor, | 800 const std::string& udev_id_vendor, |
| 668 const std::string& udev_id_vendor_id, | 801 const std::string& udev_id_vendor_id, |
| 669 const std::string& udev_id_vendor_from_database, | 802 const std::string& udev_id_vendor_from_database, |
| 670 const std::string& alsa_name, | 803 const std::string& alsa_name, |
| 671 const std::string& alsa_longname) { | 804 const std::string& alsa_longname) { |
| 672 // Let's try to determine the manufacturer. Here is the ordered preference | 805 // Let's try to determine the manufacturer. Here is the ordered preference |
| 673 // in extraction: | 806 // in extraction: |
| 674 // 1. Vendor name from the hardware device string, from udev properties | 807 // 1. Vendor name from the hardware device string, from udev properties |
| 675 // or sysattrs. | 808 // or sysattrs. |
| 676 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). | 809 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 916 kUdevPropertySoundInitialized)) | 1049 kUdevPropertySoundInitialized)) |
| 917 return; | 1050 return; |
| 918 | 1051 |
| 919 // Get the action. If no action, then we are doing first time enumeration | 1052 // Get the action. If no action, then we are doing first time enumeration |
| 920 // and the device is treated as new. | 1053 // and the device is treated as new. |
| 921 const char* action = device::udev_device_get_action(dev); | 1054 const char* action = device::udev_device_get_action(dev); |
| 922 if (!action) | 1055 if (!action) |
| 923 action = kUdevActionChange; | 1056 action = kUdevActionChange; |
| 924 | 1057 |
| 925 if (strcmp(action, kUdevActionChange) == 0) { | 1058 if (strcmp(action, kUdevActionChange) == 0) { |
| 926 // TODO(agoode): add | 1059 AddCard(dev); |
| 1060 // Generate Web MIDI events. | |
| 1061 UpdatePortStateAndGenerateEvents(); | |
| 927 } else if (strcmp(action, kUdevActionRemove) == 0) { | 1062 } else if (strcmp(action, kUdevActionRemove) == 0) { |
| 928 // TODO(agoode): remove | 1063 RemoveCard(GetCardNumber(dev)); |
| 1064 // Generate Web MIDI events. | |
| 1065 UpdatePortStateAndGenerateEvents(); | |
| 929 } | 1066 } |
| 930 } | 1067 } |
| 931 | 1068 |
| 1069 void MidiManagerAlsa::AddCard(udev_device* dev) { | |
| 1070 int number = GetCardNumber(dev); | |
| 1071 if (number == -1) | |
| 1072 return; | |
| 1073 | |
| 1074 RemoveCard(number); | |
| 1075 | |
| 1076 snd_ctl_card_info_t* card; | |
| 1077 snd_hwdep_info_t* hwdep; | |
| 1078 snd_ctl_card_info_alloca(&card); | |
| 1079 snd_hwdep_info_alloca(&hwdep); | |
| 1080 const std::string id = base::StringPrintf("hw:CARD=%i", number); | |
| 1081 snd_ctl_t* handle; | |
| 1082 int err = snd_ctl_open(&handle, id.c_str(), 0); | |
| 1083 if (err != 0) { | |
| 1084 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); | |
| 1085 return; | |
| 1086 } | |
| 1087 err = snd_ctl_card_info(handle, card); | |
| 1088 if (err != 0) { | |
| 1089 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); | |
| 1090 snd_ctl_close(handle); | |
| 1091 return; | |
| 1092 } | |
| 1093 std::string name = snd_ctl_card_info_get_name(card); | |
| 1094 std::string longname = snd_ctl_card_info_get_longname(card); | |
| 1095 std::string driver = snd_ctl_card_info_get_driver(card); | |
| 1096 | |
| 1097 // Count rawmidi devices (not subdevices). | |
| 1098 int midi_count = 0; | |
| 1099 for (int device = -1; | |
| 1100 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) | |
| 1101 ++midi_count; | |
| 1102 | |
| 1103 // Count any hwdep synths that become MIDI devices outside of rawmidi. | |
| 1104 // | |
| 1105 // Explanation: | |
| 1106 // Any kernel driver can create an ALSA client (visible to us). | |
| 1107 // With modern hardware, only rawmidi devices do this. Kernel | |
| 1108 // drivers create rawmidi devices and the rawmidi subsystem makes | |
| 1109 // the seq clients. But the OPL3 driver is special, it does not | |
| 1110 // make a rawmidi device but a seq client directly. (This is the | |
| 1111 // only one to worry about in the kernel code, as of 2015-03-23.) | |
| 1112 // | |
| 1113 // OPL3 is very old (but still possible to get in new | |
| 1114 // hardware). It is unlikely that new drivers would not use | |
| 1115 // rawmidi and defeat our heuristic. | |
| 1116 // | |
| 1117 // Longer term, support should be added in the kernel to expose a | |
| 1118 // direct link from card->client (or client->card) so that all | |
| 1119 // these heuristics will be obsolete. Once that is there, we can | |
| 1120 // assume our old heuristics will work on old kernels and the new | |
| 1121 // robust code will be used on new. Then we will not need to worry | |
| 1122 // about changes to kernel internals breaking our code. | |
| 1123 // See the TODO above at kMinimumClientIdForCards. | |
| 1124 for (int device = -1; | |
| 1125 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) { | |
| 1126 err = snd_ctl_hwdep_info(handle, hwdep); | |
| 1127 if (err != 0) { | |
| 1128 VLOG(1) << "snd_ctl_hwdep_info fails: " << snd_strerror(err); | |
| 1129 continue; | |
| 1130 } | |
| 1131 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep); | |
| 1132 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 || | |
| 1133 iface == SND_HWDEP_IFACE_OPL4) | |
| 1134 ++midi_count; | |
| 1135 } | |
| 1136 snd_ctl_close(handle); | |
| 1137 | |
| 1138 if (midi_count > 0) | |
| 1139 alsa_cards_[number] = new AlsaCard(dev, name, longname, driver, midi_count); | |
| 1140 alsa_card_midi_count_ += midi_count; | |
| 1141 } | |
| 1142 | |
| 1143 void MidiManagerAlsa::RemoveCard(int number) { | |
| 1144 auto it = alsa_cards_.find(number); | |
| 1145 if (it == alsa_cards_.end()) | |
| 1146 return; | |
| 1147 | |
| 1148 alsa_card_midi_count_ -= it->second->midi_device_count(); | |
| 1149 delete it->second; | |
| 1150 alsa_cards_.erase(it); | |
| 1151 } | |
| 1152 | |
| 932 void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() { | 1153 void MidiManagerAlsa::UpdatePortStateAndGenerateEvents() { |
| 1154 if (alsa_card_midi_count_ != alsa_seq_state_.kernel_client_count()) | |
|
Takashi Toyoshima
2015/05/11 12:32:04
Can you add some comments to know what this check
Adam Goode
2015/05/11 15:17:25
Yes. I also renamed the variables to be more clear
| |
| 1155 return; | |
| 1156 | |
| 933 // Generate new port state. | 1157 // Generate new port state. |
| 934 auto new_port_state = alsa_seq_state_.ToMidiPortState(); | 1158 auto new_port_state = alsa_seq_state_.ToMidiPortState(alsa_cards_); |
| 935 | 1159 |
| 936 // Disconnect any connected old ports that are now missing. | 1160 // Disconnect any connected old ports that are now missing. |
| 937 for (auto* old_port : port_state_) { | 1161 for (auto* old_port : port_state_) { |
| 938 if (old_port->connected() && | 1162 if (old_port->connected() && |
| 939 (new_port_state->FindConnected(*old_port) == new_port_state->end())) { | 1163 (new_port_state->FindConnected(*old_port) == new_port_state->end())) { |
| 940 old_port->set_connected(false); | 1164 old_port->set_connected(false); |
| 941 uint32 web_port_index = old_port->web_port_index(); | 1165 uint32 web_port_index = old_port->web_port_index(); |
| 942 switch (old_port->type()) { | 1166 switch (old_port->type()) { |
| 943 case MidiPort::Type::kInput: | 1167 case MidiPort::Type::kInput: |
| 944 source_map_.erase( | 1168 source_map_.erase( |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1132 source_map_[AddrToInt(client_id, port_id)] = port_index; | 1356 source_map_[AddrToInt(client_id, port_id)] = port_index; |
| 1133 return true; | 1357 return true; |
| 1134 } | 1358 } |
| 1135 | 1359 |
| 1136 MidiManager* MidiManager::Create() { | 1360 MidiManager* MidiManager::Create() { |
| 1137 return new MidiManagerAlsa(); | 1361 return new MidiManagerAlsa(); |
| 1138 } | 1362 } |
| 1139 | 1363 |
| 1140 } // namespace midi | 1364 } // namespace midi |
| 1141 } // namespace media | 1365 } // namespace media |
| OLD | NEW |