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