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

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: Remove use of banned R"(...)" 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
45 #if defined(USE_UDEV)
46 // Copied from components/storage_monitor/udev_util_linux.cc.
Takashi Toyoshima 2015/03/05 07:23:22 I'm not an udev expert, but should we have a TODO
Adam Goode 2015/03/05 21:46:58 Done.
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
57 MidiManagerAlsa::MidiManagerAlsa() 67 MidiManagerAlsa::MidiManagerAlsa()
58 : in_client_(NULL), 68 : in_client_(NULL),
59 out_client_(NULL), 69 out_client_(NULL),
60 out_client_id_(-1), 70 out_client_id_(-1),
61 in_port_(-1), 71 in_port_(-1),
62 decoder_(NULL), 72 decoder_(NULL),
73 #if defined(USE_UDEV)
74 udev_(device::udev_new()),
75 #endif // defined(USE_UDEV)
63 send_thread_("MidiSendThread"), 76 send_thread_("MidiSendThread"),
64 event_thread_("MidiEventThread"), 77 event_thread_("MidiEventThread"),
65 event_thread_shutdown_(false) { 78 event_thread_shutdown_(false) {
66 // Initialize decoder. 79 // Initialize decoder.
67 snd_midi_event_new(0, &decoder_); 80 snd_midi_event_new(0, &decoder_);
68 snd_midi_event_no_status(decoder_, 1); 81 snd_midi_event_no_status(decoder_, 1);
69 } 82 }
70 83
71 void MidiManagerAlsa::StartInitialization() { 84 void MidiManagerAlsa::StartInitialization() {
72 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. 85 // 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_; 132 announce_dest.port = in_port_;
120 snd_seq_port_subscribe_set_sender(subs, &announce_sender); 133 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
121 snd_seq_port_subscribe_set_dest(subs, &announce_dest); 134 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
122 err = snd_seq_subscribe_port(in_client_, subs); 135 err = snd_seq_subscribe_port(in_client_, subs);
123 if (err != 0) { 136 if (err != 0) {
124 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " 137 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
125 << snd_strerror(err); 138 << snd_strerror(err);
126 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); 139 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
127 } 140 }
128 141
129 // Use a heuristic to extract the list of manufacturers for the hardware MIDI 142 // Extract the list of manufacturers for the hardware MIDI
130 // devices. This won't work for all devices. It is also brittle until 143 // devices. This won't work for all devices. It is also brittle until
131 // hotplug is implemented. (See http://crbug.com/279097.) 144 // hotplug is implemented. (See http://crbug.com/279097.)
Takashi Toyoshima 2015/03/05 07:23:22 Maybe 431489 is better bug id for now, since 27909
Adam Goode 2015/03/05 21:46:58 Done.
132 // TODO(agoode): Make manufacturer extraction simple and reliable.
133 // http://crbug.com/377250.
134 ScopedVector<CardInfo> cards; 145 ScopedVector<CardInfo> cards;
135 snd_ctl_card_info_t* card; 146 snd_ctl_card_info_t* card;
136 snd_rawmidi_info_t* midi_out; 147 snd_rawmidi_info_t* midi_out;
137 snd_rawmidi_info_t* midi_in; 148 snd_rawmidi_info_t* midi_in;
138 snd_ctl_card_info_alloca(&card); 149 snd_ctl_card_info_alloca(&card);
139 snd_rawmidi_info_alloca(&midi_out); 150 snd_rawmidi_info_alloca(&midi_out);
140 snd_rawmidi_info_alloca(&midi_in); 151 snd_rawmidi_info_alloca(&midi_in);
141 for (int index = -1; !snd_card_next(&index) && index >= 0; ) { 152 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) {
142 const std::string id = base::StringPrintf("hw:CARD=%i", index); 153 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
143 snd_ctl_t* handle; 154 snd_ctl_t* handle;
144 int err = snd_ctl_open(&handle, id.c_str(), 0); 155 int err = snd_ctl_open(&handle, id.c_str(), 0);
145 if (err != 0) { 156 if (err != 0) {
146 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); 157 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
147 continue; 158 continue;
148 } 159 }
149 err = snd_ctl_card_info(handle, card); 160 err = snd_ctl_card_info(handle, card);
150 if (err != 0) { 161 if (err != 0) {
151 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); 162 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
152 snd_ctl_close(handle); 163 snd_ctl_close(handle);
153 continue; 164 continue;
154 } 165 }
155 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo. 166 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo.
156 for (int device = -1; 167 for (int device = -1;
157 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { 168 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) {
158 bool output; 169 bool output;
159 bool input; 170 bool input;
160 snd_rawmidi_info_set_device(midi_out, device); 171 snd_rawmidi_info_set_device(midi_out, device);
161 snd_rawmidi_info_set_subdevice(midi_out, 0); 172 snd_rawmidi_info_set_subdevice(midi_out, 0);
162 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); 173 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
163 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; 174 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
164 snd_rawmidi_info_set_device(midi_in, device); 175 snd_rawmidi_info_set_device(midi_in, device);
165 snd_rawmidi_info_set_subdevice(midi_in, 0); 176 snd_rawmidi_info_set_subdevice(midi_in, 0);
166 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); 177 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
167 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; 178 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
168 if (!output && !input) 179 if (!output && !input)
169 continue; 180 continue;
170 181
182 // Get Alsa properties.
171 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; 183 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
172 const std::string name = snd_rawmidi_info_get_name(midi); 184 const std::string name = snd_rawmidi_info_get_name(midi);
173 // We assume that card longname is in the format of 185 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); 186 const std::string driver = snd_ctl_card_info_get_driver(card);
187
188 // Get udev properties if available.
189 std::string udev_id_vendor_enc;
Takashi Toyoshima 2015/03/05 07:23:22 I slightly feel it's better to replace following u
Adam Goode 2015/03/05 21:46:58 I refactored a bit. Let me know what you think. It
190 std::string udev_id_vendor_id;
191 std::string udev_id_vendor_from_database;
192 #if defined(USE_UDEV)
193 const std::string sysname = base::StringPrintf("card%i", card_index);
194 device::ScopedUdevDevicePtr udev_device(
195 device::udev_device_new_from_subsystem_sysname(
196 udev_.get(), "sound", sysname.c_str()));
197 udev_id_vendor_enc = GetUdevDevicePropertyValue(
198 udev_device.get(), "ID_VENDOR_ENC");
199 udev_id_vendor_id = GetUdevDevicePropertyValue(
200 udev_device.get(), "ID_VENDOR_ID");
201 udev_id_vendor_from_database = GetUdevDevicePropertyValue(
202 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
203 #endif // defined(USE_UDEV)
204
205 const std::string manufacturer = ExtractManufacturer(
206 udev_id_vendor_enc, udev_id_vendor_id, udev_id_vendor_from_database,
207 name, longname);
208
185 cards.push_back(new CardInfo(name, manufacturer, driver)); 209 cards.push_back(new CardInfo(name, manufacturer, driver));
186 } 210 }
187 snd_ctl_close(handle); 211 snd_ctl_close(handle);
188 } 212 }
189 213
190 // Enumerate all ports in all clients. 214 // Enumerate all ports in all clients.
191 snd_seq_client_info_t* client_info; 215 snd_seq_client_info_t* client_info;
192 snd_seq_client_info_alloca(&client_info); 216 snd_seq_client_info_alloca(&client_info);
193 snd_seq_port_info_t* port_info; 217 snd_seq_port_info_t* port_info;
194 snd_seq_port_info_alloca(&port_info); 218 snd_seq_port_info_alloca(&port_info);
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 } 464 }
441 } 465 }
442 } 466 }
443 467
444 // Do again. 468 // Do again.
445 event_thread_.message_loop()->PostTask( 469 event_thread_.message_loop()->PostTask(
446 FROM_HERE, 470 FROM_HERE,
447 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 471 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
448 } 472 }
449 473
474 std::string MidiManagerAlsa::ExtractManufacturer(
Takashi Toyoshima 2015/03/05 07:23:22 It isn't mandatory, but usually we add an one line
Adam Goode 2015/03/05 21:46:58 Done.
475 const std::string& udev_id_vendor_enc,
476 const std::string& udev_id_vendor_id,
477 const std::string& udev_id_vendor_from_database,
478 const std::string& alsa_name,
479 const std::string& alsa_longname) {
480 // Let's try to determine the manufacturer. Here is the ordered preference
Takashi Toyoshima 2015/03/05 07:23:22 I prefer to use #if defined(USE_UDEV) - #endif fro
Adam Goode 2015/03/05 21:46:58 Acknowledged.
481 // in extraction:
482 // 1. Vendor name from the USB device iManufacturer string, stored in
483 // udev_id_vendor_enc.
484 // 2. Vendor name from the udev hwid database.
485 // 3. Heuristic from ALSA.
486
487 // Is the vendor string not just the USB vendor hex id?
488 std::string udev_id_vendor = UnescapeUdev(udev_id_vendor_enc);
489 if (udev_id_vendor != udev_id_vendor_id) {
490 return udev_id_vendor;
491 }
492
493 // Is there a vendor string in the hardware database?
494 if (!udev_id_vendor_from_database.empty()) {
495 return udev_id_vendor_from_database;
496 }
497
498 // Ok, udev gave us nothing useful. So try a heuristic.
499 // We assume that card longname is in the format of
500 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
501 // a manufacturer name here.
502 size_t at_index = alsa_longname.rfind(" at ");
503 if (std::string::npos != at_index) {
504 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
505 if (std::string::npos != name_index)
506 return alsa_longname.substr(0, name_index - 1);
507 }
508
509 // Failure.
510 return "";
511 }
512
513 std::string MidiManagerAlsa::UnescapeUdev(const std::string& s) {
514 std::string unescaped;
515 const size_t size = s.size();
516 for (size_t i = 0; i < size; ++i) {
517 char c = s[i];
518 if ((i + 3 < size) && c == '\\' && s[i + 1] == 'x') {
519 c = (HexDigitToInt(s[i + 2]) << 4) +
520 HexDigitToInt(s[i + 3]);
521 i += 3;
522 }
523 unescaped.push_back(c);
524 }
525 return unescaped;
526 }
527
450 MidiManager* MidiManager::Create() { 528 MidiManager* MidiManager::Create() {
451 return new MidiManagerAlsa(); 529 return new MidiManagerAlsa();
452 } 530 }
453 531
454 } // namespace media 532 } // 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