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 <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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |