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 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData, | 236 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData, |
237 base::Unretained(this), port_index, data), | 237 base::Unretained(this), port_index, data), |
238 delay); | 238 delay); |
239 | 239 |
240 // Acknowledge send. | 240 // Acknowledge send. |
241 send_thread_.message_loop()->PostTask( | 241 send_thread_.message_loop()->PostTask( |
242 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, | 242 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent, |
243 base::Unretained(client), data.size())); | 243 base::Unretained(client), data.size())); |
244 } | 244 } |
245 | 245 |
246 MidiManagerAlsa::AlsaRawmidi::AlsaRawmidi(const MidiManagerAlsa* outer, | 246 MidiManagerAlsa::AlsaCard::AlsaCard(const MidiManagerAlsa* outer, |
247 const std::string& alsa_name, | 247 const std::string& alsa_name, |
248 const std::string& alsa_longname, | 248 const std::string& alsa_longname, |
249 const std::string& alsa_driver, | 249 const std::string& alsa_driver, |
250 int card_index) | 250 int card_index, |
| 251 int midi_count) |
251 : alsa_name_(alsa_name), | 252 : alsa_name_(alsa_name), |
252 alsa_longname_(alsa_longname), | 253 alsa_longname_(alsa_longname), |
253 alsa_driver_(alsa_driver) { | 254 alsa_driver_(alsa_driver), |
| 255 midi_count_(midi_count) { |
254 // Get udev properties if available. | 256 // Get udev properties if available. |
255 std::string vendor; | 257 std::string vendor; |
256 std::string vendor_from_database; | 258 std::string vendor_from_database; |
257 | 259 |
258 #if defined(USE_UDEV) | 260 #if defined(USE_UDEV) |
259 const std::string sysname = base::StringPrintf("card%i", card_index); | 261 const std::string sysname = base::StringPrintf("card%i", card_index); |
260 device::ScopedUdevDevicePtr udev_device( | 262 device::ScopedUdevDevicePtr udev_device( |
261 device::udev_device_new_from_subsystem_sysname( | 263 device::udev_device_new_from_subsystem_sysname( |
262 outer->udev_.get(), kSoundClass, sysname.c_str())); | 264 outer->udev_.get(), kSoundClass, sysname.c_str())); |
263 | 265 |
(...skipping 26 matching lines...) Expand all Loading... |
290 | 292 |
291 // Get the usb interface number. | 293 // Get the usb interface number. |
292 usb_interface_num_ = | 294 usb_interface_num_ = |
293 device::UdevDeviceGetPropertyValue(udev_device.get(), kUsbInterfaceNum); | 295 device::UdevDeviceGetPropertyValue(udev_device.get(), kUsbInterfaceNum); |
294 #endif // defined(USE_UDEV) | 296 #endif // defined(USE_UDEV) |
295 | 297 |
296 manufacturer_ = ExtractManufacturerString( | 298 manufacturer_ = ExtractManufacturerString( |
297 vendor, vendor_id_, vendor_from_database, alsa_name, alsa_longname); | 299 vendor, vendor_id_, vendor_from_database, alsa_name, alsa_longname); |
298 } | 300 } |
299 | 301 |
300 MidiManagerAlsa::AlsaRawmidi::~AlsaRawmidi() { | 302 MidiManagerAlsa::AlsaCard::~AlsaCard() { |
301 } | 303 } |
302 | 304 |
303 const std::string MidiManagerAlsa::AlsaRawmidi::alsa_name() const { | 305 const std::string MidiManagerAlsa::AlsaCard::alsa_name() const { |
304 return alsa_name_; | 306 return alsa_name_; |
305 } | 307 } |
306 | 308 |
307 const std::string MidiManagerAlsa::AlsaRawmidi::alsa_longname() const { | 309 const std::string MidiManagerAlsa::AlsaCard::alsa_longname() const { |
308 return alsa_longname_; | 310 return alsa_longname_; |
309 } | 311 } |
310 | 312 |
311 const std::string MidiManagerAlsa::AlsaRawmidi::manufacturer() const { | 313 const std::string MidiManagerAlsa::AlsaCard::manufacturer() const { |
312 return manufacturer_; | 314 return manufacturer_; |
313 } | 315 } |
314 | 316 |
315 const std::string MidiManagerAlsa::AlsaRawmidi::alsa_driver() const { | 317 const std::string MidiManagerAlsa::AlsaCard::alsa_driver() const { |
316 return alsa_driver_; | 318 return alsa_driver_; |
317 } | 319 } |
318 | 320 |
319 const std::string MidiManagerAlsa::AlsaRawmidi::path() const { | 321 const std::string MidiManagerAlsa::AlsaCard::path() const { |
320 return path_; | 322 return path_; |
321 } | 323 } |
322 | 324 |
323 const std::string MidiManagerAlsa::AlsaRawmidi::bus() const { | 325 const std::string MidiManagerAlsa::AlsaCard::bus() const { |
324 return bus_; | 326 return bus_; |
325 } | 327 } |
326 | 328 |
327 const std::string MidiManagerAlsa::AlsaRawmidi::vendor_id() const { | 329 const std::string MidiManagerAlsa::AlsaCard::vendor_id() const { |
328 return vendor_id_; | 330 return vendor_id_; |
329 } | 331 } |
330 | 332 |
331 const std::string MidiManagerAlsa::AlsaRawmidi::id() const { | 333 const std::string MidiManagerAlsa::AlsaCard::id() const { |
332 std::string id = vendor_id_; | 334 std::string id = vendor_id_; |
333 if (!model_id_.empty()) | 335 if (!model_id_.empty()) |
334 id += ":" + model_id_; | 336 id += ":" + model_id_; |
335 if (!usb_interface_num_.empty()) | 337 if (!usb_interface_num_.empty()) |
336 id += ":" + usb_interface_num_; | 338 id += ":" + usb_interface_num_; |
337 return id; | 339 return id; |
338 } | 340 } |
339 | 341 |
| 342 const int MidiManagerAlsa::AlsaCard::midi_count() const { |
| 343 return midi_count_; |
| 344 } |
| 345 |
340 // static | 346 // static |
341 std::string MidiManagerAlsa::AlsaRawmidi::ExtractManufacturerString( | 347 std::string MidiManagerAlsa::AlsaCard::ExtractManufacturerString( |
342 const std::string& udev_id_vendor, | 348 const std::string& udev_id_vendor, |
343 const std::string& udev_id_vendor_id, | 349 const std::string& udev_id_vendor_id, |
344 const std::string& udev_id_vendor_from_database, | 350 const std::string& udev_id_vendor_from_database, |
345 const std::string& alsa_name, | 351 const std::string& alsa_name, |
346 const std::string& alsa_longname) { | 352 const std::string& alsa_longname) { |
347 // Let's try to determine the manufacturer. Here is the ordered preference | 353 // Let's try to determine the manufacturer. Here is the ordered preference |
348 // in extraction: | 354 // in extraction: |
349 // 1. Vendor name from the hardware device string, from udev properties | 355 // 1. Vendor name from the hardware device string, from udev properties |
350 // or sysattrs. | 356 // or sysattrs. |
351 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). | 357 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE). |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 // mapping and just use a UUID or other random string. | 444 // mapping and just use a UUID or other random string. |
439 // http://crbug.com/465320 | 445 // http://crbug.com/465320 |
440 std::string MidiManagerAlsa::AlsaPortMetadata::OpaqueKey() const { | 446 std::string MidiManagerAlsa::AlsaPortMetadata::OpaqueKey() const { |
441 uint8 hash[crypto::kSHA256Length]; | 447 uint8 hash[crypto::kSHA256Length]; |
442 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash)); | 448 crypto::SHA256HashString(JSONValue(), &hash, sizeof(hash)); |
443 return base::HexEncode(&hash, sizeof(hash)); | 449 return base::HexEncode(&hash, sizeof(hash)); |
444 } | 450 } |
445 | 451 |
446 // TODO(agoode): Add a client->card/rawmidi mapping to the kernel to avoid | 452 // TODO(agoode): Add a client->card/rawmidi mapping to the kernel to avoid |
447 // needing to probe in this way. | 453 // needing to probe in this way. |
448 ScopedVector<MidiManagerAlsa::AlsaRawmidi> MidiManagerAlsa::AllAlsaRawmidis() { | 454 ScopedVector<MidiManagerAlsa::AlsaCard> MidiManagerAlsa::AllMidiCards() { |
449 ScopedVector<AlsaRawmidi> devices; | 455 ScopedVector<AlsaCard> devices; |
450 snd_ctl_card_info_t* card; | 456 snd_ctl_card_info_t* card; |
451 snd_hwdep_info_t* hwdep; | 457 snd_hwdep_info_t* hwdep; |
452 snd_ctl_card_info_alloca(&card); | 458 snd_ctl_card_info_alloca(&card); |
453 snd_hwdep_info_alloca(&hwdep); | 459 snd_hwdep_info_alloca(&hwdep); |
454 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) { | 460 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) { |
| 461 int midi_count = 0; |
455 const std::string id = base::StringPrintf("hw:CARD=%i", card_index); | 462 const std::string id = base::StringPrintf("hw:CARD=%i", card_index); |
456 snd_ctl_t* handle; | 463 snd_ctl_t* handle; |
457 int err = snd_ctl_open(&handle, id.c_str(), 0); | 464 int err = snd_ctl_open(&handle, id.c_str(), 0); |
458 if (err != 0) { | 465 if (err != 0) { |
459 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); | 466 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); |
460 continue; | 467 continue; |
461 } | 468 } |
462 err = snd_ctl_card_info(handle, card); | 469 err = snd_ctl_card_info(handle, card); |
463 if (err != 0) { | 470 if (err != 0) { |
464 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); | 471 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); |
465 snd_ctl_close(handle); | 472 snd_ctl_close(handle); |
466 continue; | 473 continue; |
467 } | 474 } |
468 std::string name = snd_ctl_card_info_get_name(card); | 475 std::string name = snd_ctl_card_info_get_name(card); |
469 std::string longname = snd_ctl_card_info_get_longname(card); | 476 std::string longname = snd_ctl_card_info_get_longname(card); |
470 std::string driver = snd_ctl_card_info_get_driver(card); | 477 std::string driver = snd_ctl_card_info_get_driver(card); |
471 | 478 |
472 // Count rawmidi devices (not subdevices). | 479 // Count rawmidi devices (not subdevices). |
473 for (int device = -1; | 480 for (int device = -1; |
474 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) { | 481 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) |
475 devices.push_back( | 482 ++midi_count; |
476 new AlsaRawmidi(this, name, longname, driver, card_index)); | |
477 } | |
478 | 483 |
479 // Count any hwdep synths that become MIDI devices. | 484 // Count any hwdep synths that become MIDI devices outside of rawmidi. |
480 // | 485 // |
481 // Explanation: | 486 // Explanation: |
482 // Any kernel driver can create an ALSA client (visible to us). | 487 // Any kernel driver can create an ALSA client (visible to us). |
483 // With modern hardware, only rawmidi devices do this. Kernel | 488 // With modern hardware, only rawmidi devices do this. Kernel |
484 // drivers create rawmidi devices and the rawmidi subsystem makes | 489 // drivers create rawmidi devices and the rawmidi subsystem makes |
485 // the seq clients. But the OPL3 driver is special, it does not | 490 // the seq clients. But the OPL3 driver is special, it does not |
486 // make a rawmidi device but a seq client directly. (This is the | 491 // make a rawmidi device but a seq client directly. (This is the |
487 // only one to worry about in the kernel code, as of 2015-03-23.) | 492 // only one to worry about in the kernel code, as of 2015-03-23.) |
488 // | 493 // |
489 // OPL3 is very old (but still possible to get in new | 494 // OPL3 is very old (but still possible to get in new |
490 // hardware). It is unlikely that new drivers would not use | 495 // hardware). It is unlikely that new drivers would not use |
491 // rawmidi and defeat our heuristic. | 496 // rawmidi and defeat our heuristic. |
492 // | 497 // |
493 // Longer term, support should be added in the kernel to expose a | 498 // Longer term, support should be added in the kernel to expose a |
494 // direct link from card->client (or client->card) so that all | 499 // direct link from card->client (or client->card) so that all |
495 // these heuristics will be obsolete. Once that is there, we can | 500 // these heuristics will be obsolete. Once that is there, we can |
496 // assume our old heuristics will work on old kernels and the new | 501 // assume our old heuristics will work on old kernels and the new |
497 // robust code will be used on new. Then we will not need to worry | 502 // robust code will be used on new. Then we will not need to worry |
498 // about changes to kernel internals breaking our code. | 503 // about changes to kernel internals breaking our code. |
499 // See the TODO above at kMinimumClientIdForCards. | 504 // See the TODO above at kMinimumClientIdForCards. |
500 for (int device = -1; | 505 for (int device = -1; |
501 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) { | 506 !snd_ctl_hwdep_next_device(handle, &device) && device >= 0;) { |
502 snd_ctl_hwdep_info(handle, hwdep); | 507 snd_ctl_hwdep_info(handle, hwdep); |
503 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep); | 508 snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(hwdep); |
504 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 || | 509 if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3 || |
505 iface == SND_HWDEP_IFACE_OPL4) | 510 iface == SND_HWDEP_IFACE_OPL4) |
506 devices.push_back( | 511 ++midi_count; |
507 new AlsaRawmidi(this, name, longname, driver, card_index)); | |
508 } | 512 } |
| 513 |
| 514 if (midi_count > 0) |
| 515 devices.push_back( |
| 516 new AlsaCard(this, name, longname, driver, card_index, midi_count)); |
509 snd_ctl_close(handle); | 517 snd_ctl_close(handle); |
510 } | 518 } |
511 | 519 |
512 return devices.Pass(); | 520 return devices.Pass(); |
513 } | 521 } |
514 | 522 |
515 void MidiManagerAlsa::EnumeratePorts() { | 523 void MidiManagerAlsa::EnumeratePorts() { |
516 ScopedVector<AlsaRawmidi> devices = AllAlsaRawmidis(); | 524 ScopedVector<AlsaCard> cards = AllMidiCards(); |
| 525 std::vector<const AlsaCard*> devices; |
| 526 for (const auto* card : cards) { |
| 527 // Insert 1 AlsaCard per number of MIDI devices. |
| 528 for (int n = 0; n < card->midi_count(); ++n) |
| 529 devices.push_back(card); |
| 530 } |
517 | 531 |
518 snd_seq_port_subscribe_t* subs; | 532 snd_seq_port_subscribe_t* subs; |
519 snd_seq_port_subscribe_alloca(&subs); | 533 snd_seq_port_subscribe_alloca(&subs); |
520 | 534 |
521 int in_client_id = snd_seq_client_id(in_client_); | 535 int in_client_id = snd_seq_client_id(in_client_); |
522 | 536 |
523 // Enumerate all clients. | 537 // Enumerate all clients. |
524 snd_seq_client_info_t* client_info; | 538 snd_seq_client_info_t* client_info; |
525 snd_seq_client_info_alloca(&client_info); | 539 snd_seq_client_info_alloca(&client_info); |
526 snd_seq_port_info_t* port_info; | 540 snd_seq_port_info_t* port_info; |
(...skipping 17 matching lines...) Expand all Loading... |
544 | 558 |
545 std::string manufacturer; | 559 std::string manufacturer; |
546 std::string driver; | 560 std::string driver; |
547 std::string path; | 561 std::string path; |
548 std::string bus; | 562 std::string bus; |
549 std::string vendor_id; | 563 std::string vendor_id; |
550 std::string id; | 564 std::string id; |
551 std::string card_name; | 565 std::string card_name; |
552 std::string card_longname; | 566 std::string card_longname; |
553 | 567 |
554 // Join kernel clients against the list of AlsaRawmidis. | 568 // Join kernel clients against the list of AlsaCards. |
555 // In the current ALSA kernel implementation, kernel clients match the | 569 // In the current ALSA kernel implementation, kernel clients match the |
556 // kernel devices in the same order, for devices with client_id over | 570 // kernel devices in the same order, for clients with client_id over |
557 // kMinimumClientIdForCards. | 571 // kMinimumClientIdForCards. |
558 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && | 572 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && |
559 (current_device < devices.size()) && | 573 (current_device < devices.size()) && |
560 (client_id >= kMinimumClientIdForCards)) { | 574 (client_id >= kMinimumClientIdForCards)) { |
561 const AlsaRawmidi* device = devices[current_device]; | 575 const AlsaCard* device = devices[current_device]; |
562 manufacturer = device->manufacturer(); | 576 manufacturer = device->manufacturer(); |
563 driver = device->alsa_driver(); | 577 driver = device->alsa_driver(); |
564 path = device->path(); | 578 path = device->path(); |
565 bus = device->bus(); | 579 bus = device->bus(); |
566 vendor_id = device->vendor_id(); | 580 vendor_id = device->vendor_id(); |
567 id = device->id(); | 581 id = device->id(); |
568 card_name = device->alsa_name(); | 582 card_name = device->alsa_name(); |
569 card_longname = device->alsa_longname(); | 583 card_longname = device->alsa_longname(); |
570 current_device++; | 584 current_device++; |
571 } | 585 } |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
693 } | 707 } |
694 | 708 |
695 // Handle announce events. | 709 // Handle announce events. |
696 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && | 710 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && |
697 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) { | 711 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) { |
698 switch (event->type) { | 712 switch (event->type) { |
699 case SND_SEQ_EVENT_CLIENT_START: | 713 case SND_SEQ_EVENT_CLIENT_START: |
700 // TODO(agoode): rescan hardware devices. | 714 // TODO(agoode): rescan hardware devices. |
701 break; | 715 break; |
702 | 716 |
| 717 case SND_SEQ_EVENT_PORT_START: |
| 718 // TODO(agoode): add port. |
| 719 break; |
| 720 |
703 case SND_SEQ_EVENT_CLIENT_EXIT: | 721 case SND_SEQ_EVENT_CLIENT_EXIT: |
704 // Check for disconnection of our "out" client. This means "shut down". | 722 // Check for disconnection of our "out" client. This means "shut down". |
705 if (event->data.addr.client == out_client_id_) | 723 if (event->data.addr.client == out_client_id_) |
706 return; | 724 return; |
707 | 725 |
708 // TODO(agoode): remove all ports for a client. | 726 // TODO(agoode): remove all ports for a client. |
709 break; | 727 break; |
710 | 728 |
711 case SND_SEQ_EVENT_PORT_START: | |
712 // TODO(agoode): add port. | |
713 break; | |
714 | |
715 case SND_SEQ_EVENT_PORT_EXIT: | 729 case SND_SEQ_EVENT_PORT_EXIT: |
716 // TODO(agoode): remove port. | 730 // TODO(agoode): remove port. |
717 break; | 731 break; |
718 } | 732 } |
| 733 } else { |
| 734 ProcessSingleEvent(event, timestamp); |
719 } | 735 } |
720 | 736 |
721 ProcessSingleEvent(event, timestamp); | |
722 | |
723 // Do again. | 737 // Do again. |
724 ScheduleEventLoop(); | 738 ScheduleEventLoop(); |
725 } | 739 } |
726 | 740 |
727 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event, | 741 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event, |
728 double timestamp) { | 742 double timestamp) { |
729 std::map<int, uint32>::iterator source_it = | 743 auto source_it = source_map_.find(AddrToInt(&event->source)); |
730 source_map_.find(AddrToInt(&event->source)); | |
731 if (source_it != source_map_.end()) { | 744 if (source_it != source_map_.end()) { |
732 uint32 source = source_it->second; | 745 uint32 source = source_it->second; |
733 if (event->type == SND_SEQ_EVENT_SYSEX) { | 746 if (event->type == SND_SEQ_EVENT_SYSEX) { |
734 // Special! Variable-length sysex. | 747 // Special! Variable-length sysex. |
735 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), | 748 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), |
736 event->data.ext.len, timestamp); | 749 event->data.ext.len, timestamp); |
737 } else { | 750 } else { |
738 // Otherwise, decode this and send that on. | 751 // Otherwise, decode this and send that on. |
739 unsigned char buf[12]; | 752 unsigned char buf[12]; |
740 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event); | 753 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event); |
741 if (count <= 0) { | 754 if (count <= 0) { |
742 if (count != -ENOENT) { | 755 if (count != -ENOENT) { |
743 // ENOENT means that it's not a MIDI message, which is not an | 756 // ENOENT means that it's not a MIDI message, which is not an |
744 // error, but other negative values are errors for us. | 757 // error, but other negative values are errors for us. |
745 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count); | 758 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count); |
746 // TODO(agoode): Record this failure. | 759 // TODO(agoode): Record this failure. |
747 } | 760 } |
748 } else { | 761 } else { |
749 ReceiveMidiData(source, buf, count, timestamp); | 762 ReceiveMidiData(source, buf, count, timestamp); |
750 } | 763 } |
751 } | 764 } |
752 } | 765 } |
753 } | 766 } |
754 | 767 |
755 MidiManager* MidiManager::Create() { | 768 MidiManager* MidiManager::Create() { |
756 return new MidiManagerAlsa(); | 769 return new MidiManagerAlsa(); |
757 } | 770 } |
758 | 771 |
759 } // namespace media | 772 } // namespace media |
OLD | NEW |