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> |
(...skipping 12 matching lines...) Expand all Loading... |
23 namespace media { | 23 namespace media { |
24 | 24 |
25 namespace { | 25 namespace { |
26 | 26 |
27 // Per-output buffer. This can be smaller, but then large sysex messages | 27 // Per-output buffer. This can be smaller, but then large sysex messages |
28 // will be (harmlessly) split across multiple seq events. This should | 28 // will be (harmlessly) split across multiple seq events. This should |
29 // not have any real practical effect, except perhaps to slightly reorder | 29 // not have any real practical effect, except perhaps to slightly reorder |
30 // realtime messages with respect to sysex. | 30 // realtime messages with respect to sysex. |
31 const size_t kSendBufferSize = 256; | 31 const size_t kSendBufferSize = 256; |
32 | 32 |
| 33 // Minimum client id for which we will have ALSA card devices for. When we |
| 34 // are searching for card devices (used to get the path, id, and manufacturer), |
| 35 // we don't want to get confused by kernel clients that do not have a card. |
| 36 // See seq_clientmgr.c in the ALSA code for this. |
| 37 // TODO(agoode): Add proper client -> card export from the kernel to avoid |
| 38 // hardcoding. |
| 39 const int kMinimumClientIdForCards = 16; |
| 40 |
33 // Constants for the capabilities we search for in inputs and outputs. | 41 // Constants for the capabilities we search for in inputs and outputs. |
34 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. | 42 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. |
35 const unsigned int kRequiredInputPortCaps = | 43 const unsigned int kRequiredInputPortCaps = |
36 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; | 44 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; |
37 const unsigned int kRequiredOutputPortCaps = | 45 const unsigned int kRequiredOutputPortCaps = |
38 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; | 46 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; |
39 | 47 |
40 int AddrToInt(const snd_seq_addr_t* addr) { | 48 int AddrToInt(const snd_seq_addr_t* addr) { |
41 return (addr->client << 8) | addr->port; | 49 return (addr->client << 8) | addr->port; |
42 } | 50 } |
(...skipping 10 matching lines...) Expand all Loading... |
53 udev_(device::udev_new()), | 61 udev_(device::udev_new()), |
54 #endif // defined(USE_UDEV) | 62 #endif // defined(USE_UDEV) |
55 send_thread_("MidiSendThread"), | 63 send_thread_("MidiSendThread"), |
56 event_thread_("MidiEventThread"), | 64 event_thread_("MidiEventThread"), |
57 event_thread_shutdown_(false) { | 65 event_thread_shutdown_(false) { |
58 // Initialize decoder. | 66 // Initialize decoder. |
59 snd_midi_event_new(0, &decoder_); | 67 snd_midi_event_new(0, &decoder_); |
60 snd_midi_event_no_status(decoder_, 1); | 68 snd_midi_event_no_status(decoder_, 1); |
61 } | 69 } |
62 | 70 |
| 71 MidiManagerAlsa::~MidiManagerAlsa() { |
| 72 // Tell the event thread it will soon be time to shut down. This gives |
| 73 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT |
| 74 // message is lost. |
| 75 { |
| 76 base::AutoLock lock(shutdown_lock_); |
| 77 event_thread_shutdown_ = true; |
| 78 } |
| 79 |
| 80 // Stop the send thread. |
| 81 send_thread_.Stop(); |
| 82 |
| 83 // Close the out client. This will trigger the event thread to stop, |
| 84 // because of SND_SEQ_EVENT_CLIENT_EXIT. |
| 85 if (out_client_) |
| 86 snd_seq_close(out_client_); |
| 87 |
| 88 // Wait for the event thread to stop. |
| 89 event_thread_.Stop(); |
| 90 |
| 91 // Close the in client. |
| 92 if (in_client_) |
| 93 snd_seq_close(in_client_); |
| 94 |
| 95 // Free the decoder. |
| 96 snd_midi_event_free(decoder_); |
| 97 |
| 98 // Free the encoders. |
| 99 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i) |
| 100 snd_midi_event_free(*i); |
| 101 } |
| 102 |
63 void MidiManagerAlsa::StartInitialization() { | 103 void MidiManagerAlsa::StartInitialization() { |
64 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. | 104 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. |
65 | 105 |
66 // Create client handles. | 106 // Create client handles. |
67 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0); | 107 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0); |
68 if (err != 0) { | 108 if (err != 0) { |
69 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err); | 109 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err); |
70 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | 110 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
71 } | 111 } |
72 int in_client_id = snd_seq_client_id(in_client_); | 112 int in_client_id = snd_seq_client_id(in_client_); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 announce_dest.port = in_port_; | 151 announce_dest.port = in_port_; |
112 snd_seq_port_subscribe_set_sender(subs, &announce_sender); | 152 snd_seq_port_subscribe_set_sender(subs, &announce_sender); |
113 snd_seq_port_subscribe_set_dest(subs, &announce_dest); | 153 snd_seq_port_subscribe_set_dest(subs, &announce_dest); |
114 err = snd_seq_subscribe_port(in_client_, subs); | 154 err = snd_seq_subscribe_port(in_client_, subs); |
115 if (err != 0) { | 155 if (err != 0) { |
116 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " | 156 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " |
117 << snd_strerror(err); | 157 << snd_strerror(err); |
118 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); | 158 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); |
119 } | 159 } |
120 | 160 |
121 // Extract the list of manufacturers for the hardware MIDI | 161 EnumeratePorts(); |
122 // devices. This won't work for all devices. It is also brittle until | 162 |
123 // hotplug is implemented. (See http://crbug.com/431489.) | 163 event_thread_.Start(); |
124 ScopedVector<CardInfo> cards; | 164 event_thread_.message_loop()->PostTask( |
| 165 FROM_HERE, |
| 166 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this))); |
| 167 |
| 168 CompleteInitialization(MIDI_OK); |
| 169 } |
| 170 |
| 171 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, |
| 172 uint32 port_index, |
| 173 const std::vector<uint8>& data, |
| 174 double timestamp) { |
| 175 if (out_ports_.size() <= port_index) |
| 176 return; |
| 177 |
| 178 // Not correct right now. http://crbug.com/374341. |
| 179 if (!send_thread_.IsRunning()) |
| 180 send_thread_.Start(); |
| 181 |
| 182 base::TimeDelta delay; |
| 183 if (timestamp != 0.0) { |
| 184 base::TimeTicks time_to_send = |
| 185 base::TimeTicks() + base::TimeDelta::FromMicroseconds( |
| 186 timestamp * base::Time::kMicrosecondsPerSecond); |
| 187 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); |
| 188 } |
| 189 |
| 190 send_thread_.message_loop()->PostDelayedTask( |
| 191 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData, |
| 192 base::Unretained(this), port_index, data), |
| 193 delay); |
| 194 |
| 195 // Acknowledge send. |
| 196 send_thread_.message_loop()->PostTask( |
| 197 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, |
| 198 base::Unretained(client), data.size())); |
| 199 } |
| 200 |
| 201 MidiManagerAlsa::MidiDevice::MidiDevice(const MidiManagerAlsa* outer, |
| 202 const std::string& alsa_name, |
| 203 const std::string& alsa_longname, |
| 204 const std::string& alsa_driver, |
| 205 int card_index) |
| 206 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) { |
| 207 // Get udev properties if available. |
| 208 std::string udev_id_vendor; |
| 209 std::string udev_id_vendor_id; |
| 210 std::string udev_id_vendor_from_database; |
| 211 |
| 212 #if defined(USE_UDEV) |
| 213 const std::string sysname = base::StringPrintf("card%i", card_index); |
| 214 device::ScopedUdevDevicePtr udev_device( |
| 215 device::udev_device_new_from_subsystem_sysname(outer->udev_.get(), |
| 216 "sound", sysname.c_str())); |
| 217 udev_id_vendor = device::UdevDecodeString( |
| 218 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ENC")); |
| 219 udev_id_vendor_id = |
| 220 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ID"); |
| 221 udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue( |
| 222 udev_device.get(), "ID_VENDOR_FROM_DATABASE"); |
| 223 |
| 224 udev_id_path_ = |
| 225 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_PATH"); |
| 226 udev_id_id_ = device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_ID"); |
| 227 #endif // defined(USE_UDEV) |
| 228 |
| 229 manufacturer_ = ExtractManufacturerString(udev_id_vendor, udev_id_vendor_id, |
| 230 udev_id_vendor_from_database, |
| 231 alsa_name, alsa_longname); |
| 232 } |
| 233 |
| 234 MidiManagerAlsa::MidiDevice::~MidiDevice() { |
| 235 } |
| 236 |
| 237 const std::string MidiManagerAlsa::MidiDevice::alsa_name() const { |
| 238 return alsa_name_; |
| 239 } |
| 240 |
| 241 const std::string MidiManagerAlsa::MidiDevice::manufacturer() const { |
| 242 return manufacturer_; |
| 243 } |
| 244 |
| 245 const std::string MidiManagerAlsa::MidiDevice::alsa_driver() const { |
| 246 return alsa_driver_; |
| 247 } |
| 248 |
| 249 const std::string MidiManagerAlsa::MidiDevice::udev_id_path() const { |
| 250 return udev_id_path_; |
| 251 } |
| 252 |
| 253 const std::string MidiManagerAlsa::MidiDevice::udev_id_id() const { |
| 254 return udev_id_id_; |
| 255 } |
| 256 |
| 257 // static |
| 258 std::string MidiManagerAlsa::MidiDevice::ExtractManufacturerString( |
| 259 const std::string& udev_id_vendor, |
| 260 const std::string& udev_id_vendor_id, |
| 261 const std::string& udev_id_vendor_from_database, |
| 262 const std::string& alsa_name, |
| 263 const std::string& alsa_longname) { |
| 264 // Let's try to determine the manufacturer. Here is the ordered preference |
| 265 // in extraction: |
| 266 // 1. Vendor name from the USB device iManufacturer string, from |
| 267 // the udev property ID_VENDOR_ENC. |
| 268 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). |
| 269 // 3. Heuristic from ALSA. |
| 270 |
| 271 // Is the vendor string not just the USB vendor hex id? |
| 272 if (udev_id_vendor != udev_id_vendor_id) { |
| 273 return udev_id_vendor; |
| 274 } |
| 275 |
| 276 // Is there a vendor string in the hardware database? |
| 277 if (!udev_id_vendor_from_database.empty()) { |
| 278 return udev_id_vendor_from_database; |
| 279 } |
| 280 |
| 281 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic. |
| 282 // We assume that card longname is in the format of |
| 283 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect |
| 284 // a manufacturer name here. |
| 285 size_t at_index = alsa_longname.rfind(" at "); |
| 286 if (std::string::npos != at_index) { |
| 287 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1); |
| 288 if (std::string::npos != name_index) |
| 289 return alsa_longname.substr(0, name_index - 1); |
| 290 } |
| 291 |
| 292 // Failure. |
| 293 return ""; |
| 294 } |
| 295 |
| 296 ScopedVector<MidiManagerAlsa::MidiDevice> MidiManagerAlsa::AllMidiDevices() { |
| 297 ScopedVector<MidiDevice> devices; |
125 snd_ctl_card_info_t* card; | 298 snd_ctl_card_info_t* card; |
126 snd_rawmidi_info_t* midi_out; | 299 snd_rawmidi_info_t* midi_out; |
127 snd_rawmidi_info_t* midi_in; | 300 snd_rawmidi_info_t* midi_in; |
128 snd_ctl_card_info_alloca(&card); | 301 snd_ctl_card_info_alloca(&card); |
129 snd_rawmidi_info_alloca(&midi_out); | 302 snd_rawmidi_info_alloca(&midi_out); |
130 snd_rawmidi_info_alloca(&midi_in); | 303 snd_rawmidi_info_alloca(&midi_in); |
131 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) { | 304 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) { |
132 const std::string id = base::StringPrintf("hw:CARD=%i", card_index); | 305 const std::string id = base::StringPrintf("hw:CARD=%i", card_index); |
133 snd_ctl_t* handle; | 306 snd_ctl_t* handle; |
134 int err = snd_ctl_open(&handle, id.c_str(), 0); | 307 int err = snd_ctl_open(&handle, id.c_str(), 0); |
135 if (err != 0) { | 308 if (err != 0) { |
136 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); | 309 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); |
137 continue; | 310 continue; |
138 } | 311 } |
139 err = snd_ctl_card_info(handle, card); | 312 err = snd_ctl_card_info(handle, card); |
140 if (err != 0) { | 313 if (err != 0) { |
141 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); | 314 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); |
142 snd_ctl_close(handle); | 315 snd_ctl_close(handle); |
143 continue; | 316 continue; |
144 } | 317 } |
145 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo. | 318 // Enumerate any rawmidi devices (not subdevices) and extract MidiDevice. |
146 for (int device = -1; | 319 for (int device = -1; |
147 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { | 320 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) { |
148 bool output; | 321 bool output; |
149 bool input; | 322 bool input; |
150 snd_rawmidi_info_set_device(midi_out, device); | 323 snd_rawmidi_info_set_device(midi_out, device); |
151 snd_rawmidi_info_set_subdevice(midi_out, 0); | 324 snd_rawmidi_info_set_subdevice(midi_out, 0); |
152 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); | 325 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); |
153 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; | 326 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; |
154 snd_rawmidi_info_set_device(midi_in, device); | 327 snd_rawmidi_info_set_device(midi_in, device); |
155 snd_rawmidi_info_set_subdevice(midi_in, 0); | 328 snd_rawmidi_info_set_subdevice(midi_in, 0); |
156 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); | 329 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); |
157 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; | 330 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; |
158 if (!output && !input) | 331 if (!output && !input) |
159 continue; | 332 continue; |
160 | 333 |
161 // Compute and save Alsa and udev properties. | 334 // Compute and save ALSA and udev properties. |
162 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; | 335 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; |
163 cards.push_back(new CardInfo( | 336 devices.push_back(new MidiDevice(this, snd_rawmidi_info_get_name(midi), |
164 this, | 337 snd_ctl_card_info_get_longname(card), |
165 snd_rawmidi_info_get_name(midi), | 338 snd_ctl_card_info_get_driver(card), |
166 snd_ctl_card_info_get_longname(card), | 339 card_index)); |
167 snd_ctl_card_info_get_driver(card), | |
168 card_index)); | |
169 } | 340 } |
170 snd_ctl_close(handle); | 341 snd_ctl_close(handle); |
171 } | 342 } |
172 | 343 |
173 // Enumerate all ports in all clients. | 344 return devices.Pass(); |
| 345 } |
| 346 |
| 347 void MidiManagerAlsa::EnumeratePorts() { |
| 348 ScopedVector<MidiDevice> devices = AllMidiDevices(); |
| 349 |
| 350 snd_seq_port_subscribe_t* subs; |
| 351 snd_seq_port_subscribe_alloca(&subs); |
| 352 |
| 353 int in_client_id = snd_seq_client_id(in_client_); |
| 354 |
| 355 // Enumerate all clients. |
174 snd_seq_client_info_t* client_info; | 356 snd_seq_client_info_t* client_info; |
175 snd_seq_client_info_alloca(&client_info); | 357 snd_seq_client_info_alloca(&client_info); |
176 snd_seq_port_info_t* port_info; | 358 snd_seq_port_info_t* port_info; |
177 snd_seq_port_info_alloca(&port_info); | 359 snd_seq_port_info_alloca(&port_info); |
178 | 360 |
| 361 // Enumerate clients. |
179 snd_seq_client_info_set_client(client_info, -1); | 362 snd_seq_client_info_set_client(client_info, -1); |
180 // Enumerate clients. | |
181 uint32 current_input = 0; | 363 uint32 current_input = 0; |
182 unsigned int current_card = 0; | 364 unsigned int current_device = 0; |
183 while (!snd_seq_query_next_client(in_client_, client_info)) { | 365 while (!snd_seq_query_next_client(in_client_, client_info)) { |
184 int client_id = snd_seq_client_info_get_client(client_info); | 366 int client_id = snd_seq_client_info_get_client(client_info); |
185 if ((client_id == in_client_id) || (client_id == out_client_id_)) { | 367 if ((client_id == in_client_id) || (client_id == out_client_id_)) { |
186 // Skip our own clients. | 368 // Skip our own clients. |
187 continue; | 369 continue; |
188 } | 370 } |
| 371 |
| 372 // Get client metadata. |
189 const std::string client_name = snd_seq_client_info_get_name(client_info); | 373 const std::string client_name = snd_seq_client_info_get_name(client_info); |
190 snd_seq_port_info_set_client(port_info, client_id); | 374 snd_seq_port_info_set_client(port_info, client_id); |
191 snd_seq_port_info_set_port(port_info, -1); | 375 snd_seq_port_info_set_port(port_info, -1); |
192 | 376 |
193 std::string manufacturer; | 377 std::string manufacturer; |
194 std::string driver; | 378 std::string driver; |
195 // In the current Alsa kernel implementation, hardware clients match the | 379 std::string udev_id_path; |
196 // cards in the same order. | 380 std::string udev_id_id; |
| 381 |
| 382 // Join kernel clients against the list of MidiDevices. |
| 383 // In the current ALSA kernel implementation, kernel clients match the |
| 384 // kernel devices in the same order, for devices with client_id over |
| 385 // kMinimumClientIdForCards. |
197 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && | 386 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && |
198 (current_card < cards.size())) { | 387 (current_device < devices.size()) && |
199 const CardInfo* info = cards[current_card]; | 388 (client_id >= kMinimumClientIdForCards)) { |
200 if (info->alsa_name() == client_name) { | 389 const MidiDevice* device = devices[current_device]; |
201 manufacturer = info->manufacturer(); | 390 manufacturer = device->manufacturer(); |
202 driver = info->alsa_driver(); | 391 driver = device->alsa_driver(); |
203 current_card++; | 392 udev_id_path = device->udev_id_path(); |
204 } | 393 udev_id_id = device->udev_id_id(); |
| 394 current_device++; |
205 } | 395 } |
206 // Enumerate ports. | 396 // Enumerate ports. |
207 while (!snd_seq_query_next_port(in_client_, port_info)) { | 397 while (!snd_seq_query_next_port(in_client_, port_info)) { |
208 unsigned int port_type = snd_seq_port_info_get_type(port_info); | 398 unsigned int port_type = snd_seq_port_info_get_type(port_info); |
209 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { | 399 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { |
210 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info); | 400 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info); |
211 const std::string name = snd_seq_port_info_get_name(port_info); | 401 const std::string name = snd_seq_port_info_get_name(port_info); |
212 const std::string id = base::StringPrintf("%d:%d %s", | 402 const std::string id_suffix = |
213 addr->client, | 403 base::StringPrintf("%d:%d/%s/%s", addr->client, addr->port, |
214 addr->port, | 404 udev_id_path.c_str(), udev_id_id.c_str()); |
215 name.c_str()); | |
216 std::string version; | 405 std::string version; |
217 if (!driver.empty()) { | 406 if (!driver.empty()) { |
218 version = driver + " / "; | 407 version = driver + " / "; |
219 } | 408 } |
220 version += base::StringPrintf("ALSA library version %d.%d.%d", | 409 version += base::StringPrintf("ALSA library version %d.%d.%d", |
221 SND_LIB_MAJOR, | 410 SND_LIB_MAJOR, |
222 SND_LIB_MINOR, | 411 SND_LIB_MINOR, |
223 SND_LIB_SUBMINOR); | 412 SND_LIB_SUBMINOR); |
224 unsigned int caps = snd_seq_port_info_get_capability(port_info); | 413 unsigned int caps = snd_seq_port_info_get_capability(port_info); |
225 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) { | 414 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) { |
226 // Subscribe to this port. | 415 // Subscribe to this port. |
227 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info); | 416 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info); |
228 snd_seq_addr_t dest; | 417 snd_seq_addr_t dest; |
229 dest.client = snd_seq_client_id(in_client_); | 418 dest.client = snd_seq_client_id(in_client_); |
230 dest.port = in_port_; | 419 dest.port = in_port_; |
231 snd_seq_port_subscribe_set_sender(subs, sender); | 420 snd_seq_port_subscribe_set_sender(subs, sender); |
232 snd_seq_port_subscribe_set_dest(subs, &dest); | 421 snd_seq_port_subscribe_set_dest(subs, &dest); |
233 err = snd_seq_subscribe_port(in_client_, subs); | 422 int err = snd_seq_subscribe_port(in_client_, subs); |
234 if (err != 0) { | 423 if (err != 0) { |
235 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); | 424 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); |
236 } else { | 425 } else { |
237 source_map_[AddrToInt(sender)] = current_input++; | 426 source_map_[AddrToInt(sender)] = current_input++; |
238 AddInputPort(MidiPortInfo( | 427 AddInputPort( |
239 id, manufacturer, name, version, MIDI_PORT_OPENED)); | 428 MidiPortInfo(base::StringPrintf("input/%s", id_suffix.c_str()), |
| 429 manufacturer, name, version, MIDI_PORT_OPENED)); |
240 } | 430 } |
241 } | 431 } |
242 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) { | 432 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) { |
243 // Create a port for us to send on. | 433 // Create a port for us to send on. |
244 int out_port = | 434 int out_port = |
245 snd_seq_create_simple_port(out_client_, NULL, | 435 snd_seq_create_simple_port(out_client_, NULL, |
246 SND_SEQ_PORT_CAP_READ | | 436 SND_SEQ_PORT_CAP_READ | |
247 SND_SEQ_PORT_CAP_NO_EXPORT, | 437 SND_SEQ_PORT_CAP_NO_EXPORT, |
248 SND_SEQ_PORT_TYPE_MIDI_GENERIC | | 438 SND_SEQ_PORT_TYPE_MIDI_GENERIC | |
249 SND_SEQ_PORT_TYPE_APPLICATION); | 439 SND_SEQ_PORT_TYPE_APPLICATION); |
250 if (out_port < 0) { | 440 if (out_port < 0) { |
251 VLOG(1) << "snd_seq_create_simple_port fails: " | 441 VLOG(1) << "snd_seq_create_simple_port fails: " |
252 << snd_strerror(out_port); | 442 << snd_strerror(out_port); |
253 // Skip this output port for now. | 443 // Skip this output port for now. |
254 continue; | 444 continue; |
255 } | 445 } |
256 | 446 |
257 // Activate port subscription. | 447 // Activate port subscription. |
258 snd_seq_addr_t sender; | 448 snd_seq_addr_t sender; |
259 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info); | 449 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info); |
260 sender.client = snd_seq_client_id(out_client_); | 450 sender.client = snd_seq_client_id(out_client_); |
261 sender.port = out_port; | 451 sender.port = out_port; |
262 snd_seq_port_subscribe_set_sender(subs, &sender); | 452 snd_seq_port_subscribe_set_sender(subs, &sender); |
263 snd_seq_port_subscribe_set_dest(subs, dest); | 453 snd_seq_port_subscribe_set_dest(subs, dest); |
264 err = snd_seq_subscribe_port(out_client_, subs); | 454 int err = snd_seq_subscribe_port(out_client_, subs); |
265 if (err != 0) { | 455 if (err != 0) { |
266 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); | 456 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); |
267 snd_seq_delete_simple_port(out_client_, out_port); | 457 snd_seq_delete_simple_port(out_client_, out_port); |
268 } else { | 458 } else { |
269 snd_midi_event_t* encoder; | 459 snd_midi_event_t* encoder; |
270 snd_midi_event_new(kSendBufferSize, &encoder); | 460 snd_midi_event_new(kSendBufferSize, &encoder); |
271 encoders_.push_back(encoder); | 461 encoders_.push_back(encoder); |
272 out_ports_.push_back(out_port); | 462 out_ports_.push_back(out_port); |
273 AddOutputPort(MidiPortInfo( | 463 AddOutputPort( |
274 id, manufacturer, name, version, MIDI_PORT_OPENED)); | 464 MidiPortInfo(base::StringPrintf("output/%s", id_suffix.c_str()), |
| 465 manufacturer, name, version, MIDI_PORT_OPENED)); |
275 } | 466 } |
276 } | 467 } |
277 } | 468 } |
278 } | 469 } |
279 } | 470 } |
280 | |
281 event_thread_.Start(); | |
282 event_thread_.message_loop()->PostTask( | |
283 FROM_HERE, | |
284 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this))); | |
285 | |
286 CompleteInitialization(MIDI_OK); | |
287 } | |
288 | |
289 MidiManagerAlsa::~MidiManagerAlsa() { | |
290 // Tell the event thread it will soon be time to shut down. This gives | |
291 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT | |
292 // message is lost. | |
293 { | |
294 base::AutoLock lock(shutdown_lock_); | |
295 event_thread_shutdown_ = true; | |
296 } | |
297 | |
298 // Stop the send thread. | |
299 send_thread_.Stop(); | |
300 | |
301 // Close the out client. This will trigger the event thread to stop, | |
302 // because of SND_SEQ_EVENT_CLIENT_EXIT. | |
303 if (out_client_) | |
304 snd_seq_close(out_client_); | |
305 | |
306 // Wait for the event thread to stop. | |
307 event_thread_.Stop(); | |
308 | |
309 // Close the in client. | |
310 if (in_client_) | |
311 snd_seq_close(in_client_); | |
312 | |
313 // Free the decoder. | |
314 snd_midi_event_free(decoder_); | |
315 | |
316 // Free the encoders. | |
317 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i) | |
318 snd_midi_event_free(*i); | |
319 } | |
320 | |
321 MidiManagerAlsa::CardInfo::CardInfo( | |
322 const MidiManagerAlsa* outer, | |
323 const std::string& alsa_name, const std::string& alsa_longname, | |
324 const std::string& alsa_driver, int card_index) | |
325 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) { | |
326 // Get udev properties if available. | |
327 std::string udev_id_vendor; | |
328 std::string udev_id_vendor_id; | |
329 std::string udev_id_vendor_from_database; | |
330 | |
331 #if defined(USE_UDEV) | |
332 const std::string sysname = base::StringPrintf("card%i", card_index); | |
333 device::ScopedUdevDevicePtr udev_device( | |
334 device::udev_device_new_from_subsystem_sysname( | |
335 outer->udev_.get(), "sound", sysname.c_str())); | |
336 udev_id_vendor = device::UdevDecodeString(device::UdevDeviceGetPropertyValue( | |
337 udev_device.get(), "ID_VENDOR_ENC")); | |
338 udev_id_vendor_id = device::UdevDeviceGetPropertyValue( | |
339 udev_device.get(), "ID_VENDOR_ID"); | |
340 udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue( | |
341 udev_device.get(), "ID_VENDOR_FROM_DATABASE"); | |
342 | |
343 udev_id_path_ = device::UdevDeviceGetPropertyValue( | |
344 udev_device.get(), "ID_PATH"); | |
345 udev_id_id_ = device::UdevDeviceGetPropertyValue( | |
346 udev_device.get(), "ID_ID"); | |
347 #endif // defined(USE_UDEV) | |
348 | |
349 manufacturer_ = ExtractManufacturerString( | |
350 udev_id_vendor, udev_id_vendor_id, udev_id_vendor_from_database, | |
351 alsa_name, alsa_longname); | |
352 } | |
353 | |
354 MidiManagerAlsa::CardInfo::~CardInfo() { | |
355 } | |
356 | |
357 const std::string MidiManagerAlsa::CardInfo::alsa_name() const { | |
358 return alsa_name_; | |
359 } | |
360 | |
361 const std::string MidiManagerAlsa::CardInfo::manufacturer() const { | |
362 return manufacturer_; | |
363 } | |
364 | |
365 const std::string MidiManagerAlsa::CardInfo::alsa_driver() const { | |
366 return alsa_driver_; | |
367 } | |
368 | |
369 const std::string MidiManagerAlsa::CardInfo::udev_id_path() const { | |
370 return udev_id_path_; | |
371 } | |
372 | |
373 const std::string MidiManagerAlsa::CardInfo::udev_id_id() const { | |
374 return udev_id_id_; | |
375 } | 471 } |
376 | 472 |
377 void MidiManagerAlsa::SendMidiData(uint32 port_index, | 473 void MidiManagerAlsa::SendMidiData(uint32 port_index, |
378 const std::vector<uint8>& data) { | 474 const std::vector<uint8>& data) { |
379 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 475 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
380 | 476 |
381 snd_midi_event_t* encoder = encoders_[port_index]; | 477 snd_midi_event_t* encoder = encoders_[port_index]; |
382 for (unsigned int i = 0; i < data.size(); i++) { | 478 for (unsigned int i = 0; i < data.size(); i++) { |
383 snd_seq_event_t event; | 479 snd_seq_event_t event; |
384 int result = snd_midi_event_encode_byte(encoder, data[i], &event); | 480 int result = snd_midi_event_encode_byte(encoder, data[i], &event); |
385 if (result == 1) { | 481 if (result == 1) { |
386 // Full event, send it. | 482 // Full event, send it. |
387 snd_seq_ev_set_source(&event, out_ports_[port_index]); | 483 snd_seq_ev_set_source(&event, out_ports_[port_index]); |
388 snd_seq_ev_set_subs(&event); | 484 snd_seq_ev_set_subs(&event); |
389 snd_seq_ev_set_direct(&event); | 485 snd_seq_ev_set_direct(&event); |
390 snd_seq_event_output_direct(out_client_, &event); | 486 snd_seq_event_output_direct(out_client_, &event); |
391 } | 487 } |
392 } | 488 } |
393 } | 489 } |
394 | 490 |
395 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, | 491 void MidiManagerAlsa::ScheduleEventLoop() { |
396 uint32 port_index, | |
397 const std::vector<uint8>& data, | |
398 double timestamp) { | |
399 if (out_ports_.size() <= port_index) | |
400 return; | |
401 | |
402 // Not correct right now. http://crbug.com/374341. | |
403 if (!send_thread_.IsRunning()) | |
404 send_thread_.Start(); | |
405 | |
406 base::TimeDelta delay; | |
407 if (timestamp != 0.0) { | |
408 base::TimeTicks time_to_send = | |
409 base::TimeTicks() + base::TimeDelta::FromMicroseconds( | |
410 timestamp * base::Time::kMicrosecondsPerSecond); | |
411 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta()); | |
412 } | |
413 | |
414 send_thread_.message_loop()->PostDelayedTask( | |
415 FROM_HERE, | |
416 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this), | |
417 port_index, data), delay); | |
418 | |
419 // Acknowledge send. | |
420 send_thread_.message_loop()->PostTask( | |
421 FROM_HERE, | |
422 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, | |
423 base::Unretained(client), data.size())); | |
424 } | |
425 | |
426 void MidiManagerAlsa::EventReset() { | |
427 event_thread_.message_loop()->PostTask( | 492 event_thread_.message_loop()->PostTask( |
428 FROM_HERE, | 493 FROM_HERE, |
429 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); | 494 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); |
430 } | 495 } |
431 | 496 |
432 void MidiManagerAlsa::EventLoop() { | 497 void MidiManagerAlsa::EventLoop() { |
433 // Read available incoming MIDI data. | 498 // Read available incoming MIDI data. |
434 snd_seq_event_t* event; | 499 snd_seq_event_t* event; |
435 int err = snd_seq_event_input(in_client_, &event); | 500 int err = snd_seq_event_input(in_client_, &event); |
436 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); | 501 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); |
| 502 |
| 503 // Handle errors. |
437 if (err == -ENOSPC) { | 504 if (err == -ENOSPC) { |
438 VLOG(1) << "snd_seq_event_input detected buffer overrun"; | 505 VLOG(1) << "snd_seq_event_input detected buffer overrun"; |
| 506 // We've lost events: check another way to see if we need to shut down. |
| 507 base::AutoLock lock(shutdown_lock_); |
| 508 if (!event_thread_shutdown_) |
| 509 ScheduleEventLoop(); |
| 510 return; |
| 511 } else if (err < 0) { |
| 512 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err); |
| 513 // TODO(agoode): Use RecordAction() or similar to log this. |
| 514 return; |
| 515 } |
439 | 516 |
440 // We've lost events: check another way to see if we need to shut down. | 517 // Handle announce events. |
441 base::AutoLock lock(shutdown_lock_); | 518 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && |
442 if (event_thread_shutdown_) { | 519 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) { |
443 return; | 520 switch (event->type) { |
444 } | 521 case SND_SEQ_EVENT_CLIENT_START: |
445 } else if (err < 0) { | 522 // TODO(agoode): rescan hardware devices. |
446 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err); | 523 break; |
447 return; | 524 |
448 } else { | 525 case SND_SEQ_EVENT_CLIENT_EXIT: |
449 // Check for disconnection of out client. This means "shut down". | 526 // Check for disconnection of our "out" client. This means "shut down". |
450 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && | 527 if (event->data.addr.client == out_client_id_) |
451 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE && | 528 return; |
452 event->type == SND_SEQ_EVENT_CLIENT_EXIT && | 529 |
453 event->data.addr.client == out_client_id_) { | 530 // TODO(agoode): remove all ports for a client. |
454 return; | 531 break; |
| 532 |
| 533 case SND_SEQ_EVENT_PORT_START: |
| 534 // TODO(agoode): add port. |
| 535 break; |
| 536 |
| 537 case SND_SEQ_EVENT_PORT_EXIT: |
| 538 // TODO(agoode): remove port. |
| 539 break; |
455 } | 540 } |
| 541 } |
456 | 542 |
457 std::map<int, uint32>::iterator source_it = | 543 ProcessSingleEvent(event, timestamp); |
458 source_map_.find(AddrToInt(&event->source)); | 544 |
459 if (source_it != source_map_.end()) { | 545 // Do again. |
460 uint32 source = source_it->second; | 546 ScheduleEventLoop(); |
461 if (event->type == SND_SEQ_EVENT_SYSEX) { | 547 } |
462 // Special! Variable-length sysex. | 548 |
463 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), | 549 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event, |
464 event->data.ext.len, | 550 double timestamp) { |
465 timestamp); | 551 std::map<int, uint32>::iterator source_it = |
| 552 source_map_.find(AddrToInt(&event->source)); |
| 553 if (source_it != source_map_.end()) { |
| 554 uint32 source = source_it->second; |
| 555 if (event->type == SND_SEQ_EVENT_SYSEX) { |
| 556 // Special! Variable-length sysex. |
| 557 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), |
| 558 event->data.ext.len, timestamp); |
| 559 } else { |
| 560 // Otherwise, decode this and send that on. |
| 561 unsigned char buf[12]; |
| 562 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event); |
| 563 if (count <= 0) { |
| 564 if (count != -ENOENT) { |
| 565 // ENOENT means that it's not a MIDI message, which is not an |
| 566 // error, but other negative values are errors for us. |
| 567 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count); |
| 568 // TODO(agoode): Record this failure. |
| 569 } |
466 } else { | 570 } else { |
467 // Otherwise, decode this and send that on. | 571 ReceiveMidiData(source, buf, count, timestamp); |
468 unsigned char buf[12]; | |
469 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event); | |
470 if (count <= 0) { | |
471 if (count != -ENOENT) { | |
472 // ENOENT means that it's not a MIDI message, which is not an | |
473 // error, but other negative values are errors for us. | |
474 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count); | |
475 } | |
476 } else { | |
477 ReceiveMidiData(source, buf, count, timestamp); | |
478 } | |
479 } | 572 } |
480 } | 573 } |
481 } | 574 } |
482 | |
483 // Do again. | |
484 event_thread_.message_loop()->PostTask( | |
485 FROM_HERE, | |
486 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); | |
487 } | |
488 | |
489 // static | |
490 std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString( | |
491 const std::string& udev_id_vendor, | |
492 const std::string& udev_id_vendor_id, | |
493 const std::string& udev_id_vendor_from_database, | |
494 const std::string& alsa_name, | |
495 const std::string& alsa_longname) { | |
496 // Let's try to determine the manufacturer. Here is the ordered preference | |
497 // in extraction: | |
498 // 1. Vendor name from the USB device iManufacturer string, from | |
499 // the udev property ID_VENDOR_ENC. | |
500 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). | |
501 // 3. Heuristic from ALSA. | |
502 | |
503 // Is the vendor string not just the USB vendor hex id? | |
504 if (udev_id_vendor != udev_id_vendor_id) { | |
505 return udev_id_vendor; | |
506 } | |
507 | |
508 // Is there a vendor string in the hardware database? | |
509 if (!udev_id_vendor_from_database.empty()) { | |
510 return udev_id_vendor_from_database; | |
511 } | |
512 | |
513 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic. | |
514 // We assume that card longname is in the format of | |
515 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect | |
516 // a manufacturer name here. | |
517 size_t at_index = alsa_longname.rfind(" at "); | |
518 if (std::string::npos != at_index) { | |
519 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1); | |
520 if (std::string::npos != name_index) | |
521 return alsa_longname.substr(0, name_index - 1); | |
522 } | |
523 | |
524 // Failure. | |
525 return ""; | |
526 } | 575 } |
527 | 576 |
528 MidiManager* MidiManager::Create() { | 577 MidiManager* MidiManager::Create() { |
529 return new MidiManagerAlsa(); | 578 return new MidiManagerAlsa(); |
530 } | 579 } |
531 | 580 |
532 } // namespace media | 581 } // namespace media |
OLD | NEW |