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

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

Issue 968663004: Improve MidiManagerAlsa manufacturer reporting (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@udev
Patch Set: Undo incorrect use of MEDIA_IMPLEMENTATION Created 5 years, 9 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
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/memory/scoped_vector.h" 15 #include "base/memory/scoped_vector.h"
16 #include "base/message_loop/message_loop.h" 16 #include "base/message_loop/message_loop.h"
17 #include "base/posix/eintr_wrapper.h" 17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h" 19 #include "base/strings/stringprintf.h"
19 #include "base/threading/thread.h" 20 #include "base/threading/thread.h"
20 #include "base/time/time.h" 21 #include "base/time/time.h"
21 #include "media/midi/midi_port_info.h" 22 #include "media/midi/midi_port_info.h"
22 23
23 namespace media { 24 namespace media {
24 25
25 namespace { 26 namespace {
26 27
27 // Per-output buffer. This can be smaller, but then large sysex messages 28 // Per-output buffer. This can be smaller, but then large sysex messages
28 // will be (harmlessly) split across multiple seq events. This should 29 // will be (harmlessly) split across multiple seq events. This should
29 // not have any real practical effect, except perhaps to slightly reorder 30 // not have any real practical effect, except perhaps to slightly reorder
30 // realtime messages with respect to sysex. 31 // realtime messages with respect to sysex.
31 const size_t kSendBufferSize = 256; 32 const size_t kSendBufferSize = 256;
32 33
33 // Constants for the capabilities we search for in inputs and outputs. 34 // 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 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
35 const unsigned int kRequiredInputPortCaps = 36 const unsigned int kRequiredInputPortCaps =
36 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; 37 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
37 const unsigned int kRequiredOutputPortCaps = 38 const unsigned int kRequiredOutputPortCaps =
38 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; 39 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
39 40
40 int AddrToInt(const snd_seq_addr_t* addr) { 41 int AddrToInt(const snd_seq_addr_t* addr) {
41 return (addr->client << 8) | addr->port; 42 return (addr->client << 8) | addr->port;
42 } 43 }
43 44
45 #if defined(USE_UDEV)
46 // Copied from components/storage_monitor/udev_util_linux.cc.
47 std::string GetUdevDevicePropertyValue(udev_device* udev_device,
48 const char* key) {
49 const char* value = device::udev_device_get_property_value(udev_device, key);
50 return value ? value : std::string();
51 }
52 #endif // defined(USE_UDEV)
53
44 class CardInfo { 54 class CardInfo {
45 public: 55 public:
46 CardInfo(const std::string name, const std::string manufacturer, 56 CardInfo(const std::string name, const std::string manufacturer,
47 const std::string driver) 57 const std::string driver)
48 : name_(name), manufacturer_(manufacturer), driver_(driver) { 58 : name_(name), manufacturer_(manufacturer), driver_(driver) {
49 } 59 }
50 const std::string name_; 60 const std::string name_;
51 const std::string manufacturer_; 61 const std::string manufacturer_;
52 const std::string driver_; 62 const std::string driver_;
53 }; 63 };
54 64
55 } // namespace 65 } // namespace
56 66
67 namespace internal {
68
69 // Decode just \xXX in strings.
70 std::string UnescapeUdev(const std::string& s) {
71 std::string unescaped;
72 const size_t size = s.size();
73 for (size_t i = 0; i < size; ++i) {
74 char c = s[i];
75 if ((i + 3 < size) && c == '\\' && s[i + 1] == 'x') {
76 c = (HexDigitToInt(s[i + 2]) << 4) +
77 HexDigitToInt(s[i + 3]);
78 i += 3;
79 }
80 unescaped.push_back(c);
81 }
82 return unescaped;
83 }
84
85 std::string ExtractManufacturer(
86 const std::string& udev_id_vendor_enc,
87 const std::string& udev_id_vendor_id,
88 const std::string& udev_id_vendor_from_database,
89 const std::string& alsa_name,
90 const std::string& alsa_longname) {
91 // Let's try to determine the manufacturer. Here is the ordered preference
92 // in extraction:
93 // 1. Vendor name from the USB device iManufacturer string, stored in
94 // udev_id_vendor_enc.
95 // 2. Vendor name from the udev hwid database.
96 // 3. Heuristic from ALSA.
97
98 // Is the vendor string not just the USB vendor hex id?
99 std::string udev_id_vendor = UnescapeUdev(udev_id_vendor_enc);
100 if (udev_id_vendor != udev_id_vendor_id) {
101 return udev_id_vendor;
102 }
103
104 // Is there a vendor string in the hardware database?
105 if (!udev_id_vendor_from_database.empty()) {
106 return udev_id_vendor_from_database;
107 }
108
109 // Ok, udev gave us nothing useful. So try a heuristic.
110 // We assume that card longname is in the format of
111 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
112 // a manufacturer name here.
113 size_t at_index = alsa_longname.rfind(" at ");
114 if (std::string::npos != at_index) {
115 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
116 if (std::string::npos != name_index)
117 return alsa_longname.substr(0, name_index - 1);
118 }
119
120 // Failure.
121 return "";
122 }
123
124 } // namespace
125
57 MidiManagerAlsa::MidiManagerAlsa() 126 MidiManagerAlsa::MidiManagerAlsa()
58 : in_client_(NULL), 127 : in_client_(NULL),
59 out_client_(NULL), 128 out_client_(NULL),
60 out_client_id_(-1), 129 out_client_id_(-1),
61 in_port_(-1), 130 in_port_(-1),
62 decoder_(NULL), 131 decoder_(NULL),
132 #if defined(USE_UDEV)
133 udev_(device::udev_new()),
134 #endif // defined(USE_UDEV)
63 send_thread_("MidiSendThread"), 135 send_thread_("MidiSendThread"),
64 event_thread_("MidiEventThread"), 136 event_thread_("MidiEventThread"),
65 event_thread_shutdown_(false) { 137 event_thread_shutdown_(false) {
66 // Initialize decoder. 138 // Initialize decoder.
67 snd_midi_event_new(0, &decoder_); 139 snd_midi_event_new(0, &decoder_);
68 snd_midi_event_no_status(decoder_, 1); 140 snd_midi_event_no_status(decoder_, 1);
69 } 141 }
70 142
71 void MidiManagerAlsa::StartInitialization() { 143 void MidiManagerAlsa::StartInitialization() {
72 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. 144 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 announce_dest.port = in_port_; 191 announce_dest.port = in_port_;
120 snd_seq_port_subscribe_set_sender(subs, &announce_sender); 192 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
121 snd_seq_port_subscribe_set_dest(subs, &announce_dest); 193 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
122 err = snd_seq_subscribe_port(in_client_, subs); 194 err = snd_seq_subscribe_port(in_client_, subs);
123 if (err != 0) { 195 if (err != 0) {
124 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " 196 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
125 << snd_strerror(err); 197 << snd_strerror(err);
126 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); 198 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
127 } 199 }
128 200
129 // Use a heuristic to extract the list of manufacturers for the hardware MIDI 201 // Extract the list of manufacturers for the hardware MIDI
130 // devices. This won't work for all devices. It is also brittle until 202 // devices. This won't work for all devices. It is also brittle until
131 // hotplug is implemented. (See http://crbug.com/279097.) 203 // hotplug is implemented. (See http://crbug.com/279097.)
132 // TODO(agoode): Make manufacturer extraction simple and reliable.
133 // http://crbug.com/377250.
134 ScopedVector<CardInfo> cards; 204 ScopedVector<CardInfo> cards;
135 snd_ctl_card_info_t* card; 205 snd_ctl_card_info_t* card;
136 snd_rawmidi_info_t* midi_out; 206 snd_rawmidi_info_t* midi_out;
137 snd_rawmidi_info_t* midi_in; 207 snd_rawmidi_info_t* midi_in;
138 snd_ctl_card_info_alloca(&card); 208 snd_ctl_card_info_alloca(&card);
139 snd_rawmidi_info_alloca(&midi_out); 209 snd_rawmidi_info_alloca(&midi_out);
140 snd_rawmidi_info_alloca(&midi_in); 210 snd_rawmidi_info_alloca(&midi_in);
141 for (int index = -1; !snd_card_next(&index) && index >= 0; ) { 211 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
142 const std::string id = base::StringPrintf("hw:CARD=%i", index); 212 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
143 snd_ctl_t* handle; 213 snd_ctl_t* handle;
144 int err = snd_ctl_open(&handle, id.c_str(), 0); 214 int err = snd_ctl_open(&handle, id.c_str(), 0);
145 if (err != 0) { 215 if (err != 0) {
146 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); 216 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
147 continue; 217 continue;
148 } 218 }
149 err = snd_ctl_card_info(handle, card); 219 err = snd_ctl_card_info(handle, card);
150 if (err != 0) { 220 if (err != 0) {
151 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); 221 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
152 snd_ctl_close(handle); 222 snd_ctl_close(handle);
153 continue; 223 continue;
154 } 224 }
155 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo. 225 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
156 for (int device = -1; 226 for (int device = -1;
157 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { 227 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
158 bool output; 228 bool output;
159 bool input; 229 bool input;
160 snd_rawmidi_info_set_device(midi_out, device); 230 snd_rawmidi_info_set_device(midi_out, device);
161 snd_rawmidi_info_set_subdevice(midi_out, 0); 231 snd_rawmidi_info_set_subdevice(midi_out, 0);
162 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); 232 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
163 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; 233 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
164 snd_rawmidi_info_set_device(midi_in, device); 234 snd_rawmidi_info_set_device(midi_in, device);
165 snd_rawmidi_info_set_subdevice(midi_in, 0); 235 snd_rawmidi_info_set_subdevice(midi_in, 0);
166 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); 236 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
167 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; 237 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
168 if (!output && !input) 238 if (!output && !input)
169 continue; 239 continue;
170 240
241 // Get Alsa properties.
171 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; 242 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
172 const std::string name = snd_rawmidi_info_get_name(midi); 243 const std::string name = snd_rawmidi_info_get_name(midi);
173 // We assume that card longname is in the format of 244 const std::string longname = snd_ctl_card_info_get_longname(card);
174 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
175 // a manufacturer name here.
176 std::string manufacturer;
177 const std::string card_name = snd_ctl_card_info_get_longname(card);
178 size_t at_index = card_name.rfind(" at ");
179 if (std::string::npos != at_index) {
180 size_t name_index = card_name.rfind(name, at_index - 1);
181 if (std::string::npos != name_index)
182 manufacturer = card_name.substr(0, name_index - 1);
183 }
184 const std::string driver = snd_ctl_card_info_get_driver(card); 245 const std::string driver = snd_ctl_card_info_get_driver(card);
246
247 // Get udev properties if available.
248 std::string udev_id_vendor_enc;
249 std::string udev_id_vendor_id;
250 std::string udev_id_vendor_from_database;
251 #if defined(USE_UDEV)
252 const std::string sysname = base::StringPrintf("card%i", card_index);
253 device::ScopedUdevDevicePtr udev_device(
254 device::udev_device_new_from_subsystem_sysname(
255 udev_.get(), "sound", sysname.c_str()));
256 udev_id_vendor_enc = GetUdevDevicePropertyValue(
257 udev_device.get(), "ID_VENDOR_ENC");
258 udev_id_vendor_id = GetUdevDevicePropertyValue(
259 udev_device.get(), "ID_VENDOR_ID");
260 udev_id_vendor_from_database = GetUdevDevicePropertyValue(
261 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
262 #endif // defined(USE_UDEV)
263
264 const std::string manufacturer = internal::ExtractManufacturer(
265 udev_id_vendor_enc, udev_id_vendor_id, udev_id_vendor_from_database,
266 name, longname);
267
185 cards.push_back(new CardInfo(name, manufacturer, driver)); 268 cards.push_back(new CardInfo(name, manufacturer, driver));
186 } 269 }
187 snd_ctl_close(handle); 270 snd_ctl_close(handle);
188 } 271 }
189 272
190 // Enumerate all ports in all clients. 273 // Enumerate all ports in all clients.
191 snd_seq_client_info_t* client_info; 274 snd_seq_client_info_t* client_info;
192 snd_seq_client_info_alloca(&client_info); 275 snd_seq_client_info_alloca(&client_info);
193 snd_seq_port_info_t* port_info; 276 snd_seq_port_info_t* port_info;
194 snd_seq_port_info_alloca(&port_info); 277 snd_seq_port_info_alloca(&port_info);
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 event_thread_.message_loop()->PostTask( 528 event_thread_.message_loop()->PostTask(
446 FROM_HERE, 529 FROM_HERE,
447 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 530 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
448 } 531 }
449 532
450 MidiManager* MidiManager::Create() { 533 MidiManager* MidiManager::Create() {
451 return new MidiManagerAlsa(); 534 return new MidiManagerAlsa();
452 } 535 }
453 536
454 } // namespace media 537 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698