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

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: Fix embarrassing mistake that broke driver and manufacturer 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
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_alsa_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
44 class CardInfo { 45 #if defined(USE_UDEV)
45 public: 46 // Copied from components/storage_monitor/udev_util_linux.cc.
46 CardInfo(const std::string name, const std::string manufacturer, 47 // TODO(agoode): Move this into a common place. Maybe device/udev_linux?
47 const std::string driver) 48 std::string GetUdevDevicePropertyValue(udev_device* udev_device,
48 : name_(name), manufacturer_(manufacturer), driver_(driver) { 49 const char* key) {
49 } 50 const char* value = device::udev_device_get_property_value(udev_device, key);
50 const std::string name_; 51 return value ? value : std::string();
51 const std::string manufacturer_; 52 }
52 const std::string driver_; 53 #endif // defined(USE_UDEV)
53 };
54 54
55 } // namespace 55 } // namespace
56 56
57 MidiManagerAlsa::MidiManagerAlsa() 57 MidiManagerAlsa::MidiManagerAlsa()
58 : in_client_(NULL), 58 : in_client_(NULL),
59 out_client_(NULL), 59 out_client_(NULL),
60 out_client_id_(-1), 60 out_client_id_(-1),
61 in_port_(-1), 61 in_port_(-1),
62 decoder_(NULL), 62 decoder_(NULL),
63 #if defined(USE_UDEV)
64 udev_(device::udev_new()),
65 #endif // defined(USE_UDEV)
63 send_thread_("MidiSendThread"), 66 send_thread_("MidiSendThread"),
64 event_thread_("MidiEventThread"), 67 event_thread_("MidiEventThread"),
65 event_thread_shutdown_(false) { 68 event_thread_shutdown_(false) {
66 // Initialize decoder. 69 // Initialize decoder.
67 snd_midi_event_new(0, &decoder_); 70 snd_midi_event_new(0, &decoder_);
68 snd_midi_event_no_status(decoder_, 1); 71 snd_midi_event_no_status(decoder_, 1);
69 } 72 }
70 73
71 void MidiManagerAlsa::StartInitialization() { 74 void MidiManagerAlsa::StartInitialization() {
72 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. 75 // 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_; 122 announce_dest.port = in_port_;
120 snd_seq_port_subscribe_set_sender(subs, &announce_sender); 123 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
121 snd_seq_port_subscribe_set_dest(subs, &announce_dest); 124 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
122 err = snd_seq_subscribe_port(in_client_, subs); 125 err = snd_seq_subscribe_port(in_client_, subs);
123 if (err != 0) { 126 if (err != 0) {
124 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " 127 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
125 << snd_strerror(err); 128 << snd_strerror(err);
126 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); 129 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
127 } 130 }
128 131
129 // Use a heuristic to extract the list of manufacturers for the hardware MIDI 132 // Extract the list of manufacturers for the hardware MIDI
130 // devices. This won't work for all devices. It is also brittle until 133 // devices. This won't work for all devices. It is also brittle until
131 // hotplug is implemented. (See http://crbug.com/279097.) 134 // hotplug is implemented. (See http://crbug.com/431489.)
132 // TODO(agoode): Make manufacturer extraction simple and reliable.
133 // http://crbug.com/377250.
134 ScopedVector<CardInfo> cards; 135 ScopedVector<CardInfo> cards;
135 snd_ctl_card_info_t* card; 136 snd_ctl_card_info_t* card;
136 snd_rawmidi_info_t* midi_out; 137 snd_rawmidi_info_t* midi_out;
137 snd_rawmidi_info_t* midi_in; 138 snd_rawmidi_info_t* midi_in;
138 snd_ctl_card_info_alloca(&card); 139 snd_ctl_card_info_alloca(&card);
139 snd_rawmidi_info_alloca(&midi_out); 140 snd_rawmidi_info_alloca(&midi_out);
140 snd_rawmidi_info_alloca(&midi_in); 141 snd_rawmidi_info_alloca(&midi_in);
141 for (int index = -1; !snd_card_next(&index) && index >= 0; ) { 142 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
142 const std::string id = base::StringPrintf("hw:CARD=%i", index); 143 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
143 snd_ctl_t* handle; 144 snd_ctl_t* handle;
144 int err = snd_ctl_open(&handle, id.c_str(), 0); 145 int err = snd_ctl_open(&handle, id.c_str(), 0);
145 if (err != 0) { 146 if (err != 0) {
146 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); 147 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
147 continue; 148 continue;
148 } 149 }
149 err = snd_ctl_card_info(handle, card); 150 err = snd_ctl_card_info(handle, card);
150 if (err != 0) { 151 if (err != 0) {
151 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); 152 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
152 snd_ctl_close(handle); 153 snd_ctl_close(handle);
153 continue; 154 continue;
154 } 155 }
155 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo. 156 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
156 for (int device = -1; 157 for (int device = -1;
157 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { 158 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
158 bool output; 159 bool output;
159 bool input; 160 bool input;
160 snd_rawmidi_info_set_device(midi_out, device); 161 snd_rawmidi_info_set_device(midi_out, device);
161 snd_rawmidi_info_set_subdevice(midi_out, 0); 162 snd_rawmidi_info_set_subdevice(midi_out, 0);
162 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); 163 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
163 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; 164 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
164 snd_rawmidi_info_set_device(midi_in, device); 165 snd_rawmidi_info_set_device(midi_in, device);
165 snd_rawmidi_info_set_subdevice(midi_in, 0); 166 snd_rawmidi_info_set_subdevice(midi_in, 0);
166 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); 167 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
167 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; 168 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
168 if (!output && !input) 169 if (!output && !input)
169 continue; 170 continue;
170 171
172 // Compute and save Alsa and udev properties.
171 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; 173 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
172 const std::string name = snd_rawmidi_info_get_name(midi); 174 cards.push_back(new CardInfo(
173 // We assume that card longname is in the format of 175 this,
174 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect 176 snd_rawmidi_info_get_name(midi),
175 // a manufacturer name here. 177 snd_ctl_card_info_get_longname(card),
176 std::string manufacturer; 178 snd_ctl_card_info_get_driver(card),
177 const std::string card_name = snd_ctl_card_info_get_longname(card); 179 card_index));
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);
185 cards.push_back(new CardInfo(name, manufacturer, driver));
186 } 180 }
187 snd_ctl_close(handle); 181 snd_ctl_close(handle);
188 } 182 }
189 183
190 // Enumerate all ports in all clients. 184 // Enumerate all ports in all clients.
191 snd_seq_client_info_t* client_info; 185 snd_seq_client_info_t* client_info;
192 snd_seq_client_info_alloca(&client_info); 186 snd_seq_client_info_alloca(&client_info);
193 snd_seq_port_info_t* port_info; 187 snd_seq_port_info_t* port_info;
194 snd_seq_port_info_alloca(&port_info); 188 snd_seq_port_info_alloca(&port_info);
195 189
(...skipping 11 matching lines...) Expand all
207 snd_seq_port_info_set_client(port_info, client_id); 201 snd_seq_port_info_set_client(port_info, client_id);
208 snd_seq_port_info_set_port(port_info, -1); 202 snd_seq_port_info_set_port(port_info, -1);
209 203
210 std::string manufacturer; 204 std::string manufacturer;
211 std::string driver; 205 std::string driver;
212 // In the current Alsa kernel implementation, hardware clients match the 206 // In the current Alsa kernel implementation, hardware clients match the
213 // cards in the same order. 207 // cards in the same order.
214 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && 208 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
215 (current_card < cards.size())) { 209 (current_card < cards.size())) {
216 const CardInfo* info = cards[current_card]; 210 const CardInfo* info = cards[current_card];
217 if (info->name_ == client_name) { 211 if (info->alsa_name() == client_name) {
218 manufacturer = info->manufacturer_; 212 manufacturer = info->manufacturer();
219 driver = info->driver_; 213 driver = info->alsa_driver();
220 current_card++; 214 current_card++;
221 } 215 }
222 } 216 }
223 // Enumerate ports. 217 // Enumerate ports.
224 while (!snd_seq_query_next_port(in_client_, port_info)) { 218 while (!snd_seq_query_next_port(in_client_, port_info)) {
225 unsigned int port_type = snd_seq_port_info_get_type(port_info); 219 unsigned int port_type = snd_seq_port_info_get_type(port_info);
226 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { 220 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
227 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info); 221 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
228 const std::string name = snd_seq_port_info_get_name(port_info); 222 const std::string name = snd_seq_port_info_get_name(port_info);
229 const std::string id = base::StringPrintf("%d:%d %s", 223 const std::string id = base::StringPrintf("%d:%d %s",
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 snd_seq_close(in_client_); 322 snd_seq_close(in_client_);
329 323
330 // Free the decoder. 324 // Free the decoder.
331 snd_midi_event_free(decoder_); 325 snd_midi_event_free(decoder_);
332 326
333 // Free the encoders. 327 // Free the encoders.
334 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i) 328 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
335 snd_midi_event_free(*i); 329 snd_midi_event_free(*i);
336 } 330 }
337 331
332 MidiManagerAlsa::CardInfo::CardInfo(
333 const MidiManagerAlsa* outer,
334 const std::string& alsa_name, const std::string& alsa_longname,
335 const std::string& alsa_driver, int card_index)
336 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
337 // Get udev properties if available.
338 std::string udev_id_vendor_enc;
339 std::string udev_id_vendor_id;
340 std::string udev_id_vendor_from_database;
341
342 #if defined(USE_UDEV)
343 const std::string sysname = base::StringPrintf("card%i", card_index);
344 device::ScopedUdevDevicePtr udev_device(
345 device::udev_device_new_from_subsystem_sysname(
346 outer->udev_.get(), "sound", sysname.c_str()));
347 udev_id_vendor_enc = GetUdevDevicePropertyValue(
348 udev_device.get(), "ID_VENDOR_ENC");
349 udev_id_vendor_id = GetUdevDevicePropertyValue(
350 udev_device.get(), "ID_VENDOR_ID");
351 udev_id_vendor_from_database = GetUdevDevicePropertyValue(
352 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
353
354 udev_id_path_ = GetUdevDevicePropertyValue(
355 udev_device.get(), "ID_PATH");
356 udev_id_id_ = GetUdevDevicePropertyValue(
357 udev_device.get(), "ID_ID");
358 #endif // defined(USE_UDEV)
359
360 manufacturer_ = ExtractManufacturerString(
361 udev_id_vendor_enc, udev_id_vendor_id, udev_id_vendor_from_database,
362 alsa_name, alsa_longname);
363 }
364
365 MidiManagerAlsa::CardInfo::~CardInfo() {
366 }
367
368 const std::string MidiManagerAlsa::CardInfo::alsa_name() const {
369 return alsa_name_;
370 }
371
372 const std::string MidiManagerAlsa::CardInfo::manufacturer() const {
373 return manufacturer_;
374 }
375
376 const std::string MidiManagerAlsa::CardInfo::alsa_driver() const {
377 return alsa_driver_;
378 }
379
380 const std::string MidiManagerAlsa::CardInfo::udev_id_path() const {
381 return udev_id_path_;
382 }
383
384 const std::string MidiManagerAlsa::CardInfo::udev_id_id() const {
385 return udev_id_id_;
386 }
387
338 void MidiManagerAlsa::SendMidiData(uint32 port_index, 388 void MidiManagerAlsa::SendMidiData(uint32 port_index,
339 const std::vector<uint8>& data) { 389 const std::vector<uint8>& data) {
340 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); 390 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
341 391
342 snd_midi_event_t* encoder = encoders_[port_index]; 392 snd_midi_event_t* encoder = encoders_[port_index];
343 for (unsigned int i = 0; i < data.size(); i++) { 393 for (unsigned int i = 0; i < data.size(); i++) {
344 snd_seq_event_t event; 394 snd_seq_event_t event;
345 int result = snd_midi_event_encode_byte(encoder, data[i], &event); 395 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
346 if (result == 1) { 396 if (result == 1) {
347 // Full event, send it. 397 // Full event, send it.
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 } 490 }
441 } 491 }
442 } 492 }
443 493
444 // Do again. 494 // Do again.
445 event_thread_.message_loop()->PostTask( 495 event_thread_.message_loop()->PostTask(
446 FROM_HERE, 496 FROM_HERE,
447 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 497 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
448 } 498 }
449 499
500 // static
501 std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString(
502 const std::string& udev_id_vendor_enc,
503 const std::string& udev_id_vendor_id,
504 const std::string& udev_id_vendor_from_database,
505 const std::string& alsa_name,
506 const std::string& alsa_longname) {
507 // Let's try to determine the manufacturer. Here is the ordered preference
508 // in extraction:
509 // 1. Vendor name from the USB device iManufacturer string, stored in
510 // udev_id_vendor_enc.
511 // 2. Vendor name from the udev hwid database.
512 // 3. Heuristic from ALSA.
513
514 // Is the vendor string not just the USB vendor hex id?
515 std::string udev_id_vendor = UnescapeUdev(udev_id_vendor_enc);
516 if (udev_id_vendor != udev_id_vendor_id) {
517 return udev_id_vendor;
518 }
519
520 // Is there a vendor string in the hardware database?
521 if (!udev_id_vendor_from_database.empty()) {
522 return udev_id_vendor_from_database;
523 }
524
525 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
526 // We assume that card longname is in the format of
527 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
528 // a manufacturer name here.
529 size_t at_index = alsa_longname.rfind(" at ");
530 if (std::string::npos != at_index) {
531 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
532 if (std::string::npos != name_index)
533 return alsa_longname.substr(0, name_index - 1);
534 }
535
536 // Failure.
537 return "";
538 }
539
540 // static
541 std::string MidiManagerAlsa::CardInfo::UnescapeUdev(const std::string& s) {
542 std::string unescaped;
543 const size_t size = s.size();
544 for (size_t i = 0; i < size; ++i) {
545 char c = s[i];
546 if ((i + 3 < size) && c == '\\' && s[i + 1] == 'x') {
547 c = (HexDigitToInt(s[i + 2]) << 4) +
548 HexDigitToInt(s[i + 3]);
549 i += 3;
550 }
551 unescaped.push_back(c);
552 }
553 return unescaped;
554 }
555
450 MidiManager* MidiManager::Create() { 556 MidiManager* MidiManager::Create() {
451 return new MidiManagerAlsa(); 557 return new MidiManagerAlsa();
452 } 558 }
453 559
454 } // namespace media 560 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_alsa_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698