Home | History | Annotate | Download | only in audio
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos/audio/cras_audio_handler.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/logging.h"
     13 #include "chromeos/audio/audio_devices_pref_handler.h"
     14 #include "chromeos/audio/audio_devices_pref_handler_stub.h"
     15 #include "chromeos/dbus/dbus_thread_manager.h"
     16 
     17 using std::max;
     18 using std::min;
     19 
     20 namespace chromeos {
     21 
     22 namespace {
     23 
     24 // Default value for unmuting, as a percent in the range [0, 100].
     25 // Used when sound is unmuted, but volume was less than kMuteThresholdPercent.
     26 const int kDefaultUnmuteVolumePercent = 4;
     27 
     28 // Volume value which should be considered as muted in range [0, 100].
     29 const int kMuteThresholdPercent = 1;
     30 
     31 static CrasAudioHandler* g_cras_audio_handler = NULL;
     32 
     33 }  // namespace
     34 
     35 CrasAudioHandler::AudioObserver::AudioObserver() {
     36 }
     37 
     38 CrasAudioHandler::AudioObserver::~AudioObserver() {
     39 }
     40 
     41 void CrasAudioHandler::AudioObserver::OnOutputVolumeChanged() {
     42 }
     43 
     44 void CrasAudioHandler::AudioObserver::OnInputGainChanged() {
     45 }
     46 
     47 void CrasAudioHandler::AudioObserver::OnOutputMuteChanged() {
     48 }
     49 
     50 void CrasAudioHandler::AudioObserver::OnInputMuteChanged() {
     51 }
     52 
     53 void CrasAudioHandler::AudioObserver::OnAudioNodesChanged() {
     54 }
     55 
     56 void CrasAudioHandler::AudioObserver::OnActiveOutputNodeChanged() {
     57 }
     58 
     59 void CrasAudioHandler::AudioObserver::OnActiveInputNodeChanged() {
     60 }
     61 
     62 // static
     63 void CrasAudioHandler::Initialize(
     64     scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler) {
     65   CHECK(!g_cras_audio_handler);
     66   g_cras_audio_handler = new CrasAudioHandler(audio_pref_handler);
     67 }
     68 
     69 // static
     70 void CrasAudioHandler::InitializeForTesting() {
     71   CHECK(!g_cras_audio_handler);
     72   CrasAudioHandler::Initialize(new AudioDevicesPrefHandlerStub());
     73 }
     74 
     75 // static
     76 void CrasAudioHandler::Shutdown() {
     77   CHECK(g_cras_audio_handler);
     78   delete g_cras_audio_handler;
     79   g_cras_audio_handler = NULL;
     80 }
     81 
     82 // static
     83 bool CrasAudioHandler::IsInitialized() {
     84   return g_cras_audio_handler != NULL;
     85 }
     86 
     87 // static
     88 CrasAudioHandler* CrasAudioHandler::Get() {
     89   CHECK(g_cras_audio_handler)
     90       << "CrasAudioHandler::Get() called before Initialize().";
     91   return g_cras_audio_handler;
     92 }
     93 
     94 void CrasAudioHandler::AddAudioObserver(AudioObserver* observer) {
     95   observers_.AddObserver(observer);
     96 }
     97 
     98 void CrasAudioHandler::RemoveAudioObserver(AudioObserver* observer) {
     99   observers_.RemoveObserver(observer);
    100 }
    101 
    102 bool CrasAudioHandler::IsOutputMuted() {
    103   return output_mute_on_;
    104 }
    105 
    106 bool CrasAudioHandler::IsOutputMutedForDevice(uint64 device_id) {
    107   const AudioDevice* device = GetDeviceFromId(device_id);
    108   if (!device)
    109     return false;
    110   return audio_pref_handler_->GetMuteValue(*device);
    111 }
    112 
    113 bool CrasAudioHandler::IsOutputVolumeBelowDefaultMuteLvel() {
    114   return output_volume_ <= kMuteThresholdPercent;
    115 }
    116 
    117 bool CrasAudioHandler::IsInputMuted() {
    118   return input_mute_on_;
    119 }
    120 
    121 bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
    122   const AudioDevice* device = GetDeviceFromId(device_id);
    123   if (!device)
    124     return false;
    125   return audio_pref_handler_->GetMuteValue(*device);
    126 }
    127 
    128 int CrasAudioHandler::GetOutputVolumePercent() {
    129   return output_volume_;
    130 }
    131 
    132 int CrasAudioHandler::GetOutputVolumePercentForDevice(uint64 device_id) {
    133   if (device_id == active_output_node_id_) {
    134     return output_volume_;
    135   } else {
    136     const AudioDevice* device = GetDeviceFromId(device_id);
    137     if (!device)
    138       return kDefaultVolumeGainPercent;
    139     return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device));
    140   }
    141 }
    142 
    143 int CrasAudioHandler::GetInputGainPercent() {
    144   return input_gain_;
    145 }
    146 
    147 int CrasAudioHandler::GetInputGainPercentForDevice(uint64 device_id) {
    148   if (device_id == active_input_node_id_) {
    149     return input_gain_;
    150   } else {
    151     const AudioDevice* device = GetDeviceFromId(device_id);
    152     if (!device)
    153       return kDefaultVolumeGainPercent;
    154     return static_cast<int>(audio_pref_handler_->GetVolumeGainValue(*device));
    155   }
    156 }
    157 
    158 uint64 CrasAudioHandler::GetActiveOutputNode() const {
    159   return active_output_node_id_;
    160 }
    161 
    162 uint64 CrasAudioHandler::GetActiveInputNode() const {
    163   return active_input_node_id_;
    164 }
    165 
    166 void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
    167   device_list->clear();
    168   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
    169        it != audio_devices_.end(); ++it)
    170     device_list->push_back(it->second);
    171 }
    172 
    173 bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const {
    174   const AudioDevice* active_device = GetDeviceFromId(active_output_node_id_);
    175   if (!active_device || !device)
    176     return false;
    177   *device = *active_device;
    178   return true;
    179 }
    180 
    181 bool CrasAudioHandler::has_alternative_input() const {
    182   return has_alternative_input_;
    183 }
    184 
    185 bool CrasAudioHandler::has_alternative_output() const {
    186   return has_alternative_output_;
    187 }
    188 
    189 void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
    190   volume_percent = min(max(volume_percent, 0), 100);
    191   if (volume_percent <= kMuteThresholdPercent)
    192     volume_percent = 0;
    193   output_volume_ = volume_percent;
    194 
    195   if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
    196     audio_pref_handler_->SetVolumeGainValue(*device, output_volume_);
    197 
    198   SetOutputNodeVolume(active_output_node_id_, output_volume_);
    199   FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputVolumeChanged());
    200 }
    201 
    202 // TODO: Rename the 'Percent' to something more meaningful.
    203 void CrasAudioHandler::SetInputGainPercent(int gain_percent) {
    204   // NOTE: We do not sanitize input gain values since the range is completely
    205   // dependent on the device.
    206   input_gain_ = gain_percent;
    207 
    208   if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
    209     audio_pref_handler_->SetVolumeGainValue(*device, input_gain_);
    210 
    211   SetInputNodeGain(active_input_node_id_, input_gain_);
    212   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputGainChanged());
    213 }
    214 
    215 void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
    216   SetOutputVolumePercent(output_volume_ + adjust_by_percent);
    217 }
    218 
    219 void CrasAudioHandler::SetOutputMute(bool mute_on) {
    220   if (!SetOutputMuteInternal(mute_on))
    221     return;
    222 
    223   if (const AudioDevice* device = GetDeviceFromId(active_output_node_id_))
    224     audio_pref_handler_->SetMuteValue(*device, output_mute_on_);
    225 
    226   FOR_EACH_OBSERVER(AudioObserver, observers_, OnOutputMuteChanged());
    227 }
    228 
    229 void CrasAudioHandler::AdjustOutputVolumeToAudibleLevel() {
    230   if (output_volume_ <= kMuteThresholdPercent) {
    231     // Avoid the situation when sound has been unmuted, but the volume
    232     // is set to a very low value, so user still can't hear any sound.
    233     SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
    234   }
    235 }
    236 
    237 void CrasAudioHandler::SetInputMute(bool mute_on) {
    238   if (!SetInputMuteInternal(mute_on))
    239     return;
    240 
    241   AudioDevice device;
    242   if (const AudioDevice* device = GetDeviceFromId(active_input_node_id_))
    243     audio_pref_handler_->SetMuteValue(*device, input_mute_on_);
    244 
    245 
    246   FOR_EACH_OBSERVER(AudioObserver, observers_, OnInputMuteChanged());
    247 }
    248 
    249 void CrasAudioHandler::SetActiveOutputNode(uint64 node_id) {
    250   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    251       SetActiveOutputNode(node_id);
    252   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
    253 }
    254 
    255 void CrasAudioHandler::SetActiveInputNode(uint64 node_id) {
    256   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    257       SetActiveInputNode(node_id);
    258   FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
    259 }
    260 
    261 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64 device_id,
    262                                                      int value) {
    263   if (device_id == active_output_node_id_) {
    264     SetOutputVolumePercent(value);
    265     return;
    266   } else if (device_id == active_input_node_id_) {
    267     SetInputGainPercent(value);
    268     return;
    269   }
    270 
    271   if (const AudioDevice* device = GetDeviceFromId(device_id)) {
    272     if (!device->is_input) {
    273       value = min(max(value, 0), 100);
    274       if (value <= kMuteThresholdPercent)
    275         value = 0;
    276     }
    277     audio_pref_handler_->SetVolumeGainValue(*device, value);
    278   }
    279 }
    280 
    281 void CrasAudioHandler::SetMuteForDevice(uint64 device_id, bool mute_on) {
    282   if (device_id == active_output_node_id_) {
    283     SetOutputMute(mute_on);
    284     return;
    285   } else if (device_id == active_input_node_id_) {
    286     SetInputMute(mute_on);
    287     return;
    288   }
    289 
    290   AudioDevice device;
    291   if (const AudioDevice* device = GetDeviceFromId(device_id))
    292     audio_pref_handler_->SetMuteValue(*device, mute_on);
    293 }
    294 
    295 CrasAudioHandler::CrasAudioHandler(
    296     scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler)
    297     : audio_pref_handler_(audio_pref_handler),
    298       weak_ptr_factory_(this),
    299       output_mute_on_(false),
    300       input_mute_on_(false),
    301       output_volume_(0),
    302       input_gain_(0),
    303       active_output_node_id_(0),
    304       active_input_node_id_(0),
    305       has_alternative_input_(false),
    306       has_alternative_output_(false),
    307       output_mute_locked_(false),
    308       input_mute_locked_(false) {
    309   if (!audio_pref_handler.get())
    310     return;
    311   // If the DBusThreadManager or the CrasAudioClient aren't available, there
    312   // isn't much we can do. This should only happen when running tests.
    313   if (!chromeos::DBusThreadManager::IsInitialized() ||
    314       !chromeos::DBusThreadManager::Get() ||
    315       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
    316     return;
    317   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
    318   audio_pref_handler_->AddAudioPrefObserver(this);
    319   InitializeAudioState();
    320 }
    321 
    322 CrasAudioHandler::~CrasAudioHandler() {
    323   if (!chromeos::DBusThreadManager::IsInitialized() ||
    324       !chromeos::DBusThreadManager::Get() ||
    325       !chromeos::DBusThreadManager::Get()->GetCrasAudioClient())
    326     return;
    327   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    328       RemoveObserver(this);
    329   if (audio_pref_handler_.get())
    330     audio_pref_handler_->RemoveAudioPrefObserver(this);
    331   audio_pref_handler_ = NULL;
    332 }
    333 
    334 void CrasAudioHandler::AudioClientRestarted() {
    335   InitializeAudioState();
    336 }
    337 
    338 void CrasAudioHandler::NodesChanged() {
    339   // Refresh audio nodes data.
    340   GetNodes();
    341 }
    342 
    343 void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
    344   if (active_output_node_id_ == node_id)
    345     return;
    346 
    347   // Active audio output device should always be changed by chrome.
    348   LOG(WARNING) << "Active output node changed unexpectedly by system node_id="
    349       << "0x" << std::hex << node_id;
    350 }
    351 
    352 void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
    353   if (active_input_node_id_ == node_id)
    354     return;
    355 
    356   // Active audio input device should always be changed by chrome.
    357   LOG(WARNING) << "Active input node changed unexpectedly by system node_id="
    358       << "0x" << std::hex << node_id;
    359 }
    360 
    361 void CrasAudioHandler::OnAudioPolicyPrefChanged() {
    362   ApplyAudioPolicy();
    363 }
    364 
    365 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64 device_id) const {
    366   AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
    367   if (it == audio_devices_.end())
    368     return NULL;
    369 
    370   return &(it->second);
    371 }
    372 
    373 void CrasAudioHandler::SetupAudioInputState() {
    374   // Set the initial audio state to the ones read from audio prefs.
    375   const AudioDevice* device = GetDeviceFromId(active_input_node_id_);
    376   if (!device) {
    377     LOG(ERROR) << "Can't set up audio state for unknow input device id ="
    378         << "0x" << std::hex << active_input_node_id_;
    379     return;
    380   }
    381   input_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
    382   input_gain_ = audio_pref_handler_->GetVolumeGainValue(*device);
    383   SetInputMuteInternal(input_mute_on_);
    384   // TODO(rkc,jennyz): Set input gain once we decide on how to store
    385   // the gain values since the range and step are both device specific.
    386 }
    387 
    388 void CrasAudioHandler::SetupAudioOutputState() {
    389   const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
    390   if (!device) {
    391     LOG(ERROR) << "Can't set up audio state for unknow output device id ="
    392                << "0x" << std::hex << active_output_node_id_;
    393     return;
    394   }
    395   output_mute_on_ = audio_pref_handler_->GetMuteValue(*device);
    396   output_volume_ = audio_pref_handler_->GetVolumeGainValue(*device);
    397 
    398   SetOutputMuteInternal(output_mute_on_);
    399   SetOutputNodeVolume(active_output_node_id_, output_volume_);
    400 }
    401 
    402 void CrasAudioHandler::InitializeAudioState() {
    403   ApplyAudioPolicy();
    404   GetNodes();
    405 }
    406 
    407 void CrasAudioHandler::ApplyAudioPolicy() {
    408   output_mute_locked_ = false;
    409   if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
    410     // Mute the device, but do not update the preference.
    411     SetOutputMuteInternal(true);
    412     output_mute_locked_ = true;
    413   } else {
    414     // Restore the mute state.
    415     const AudioDevice* device = GetDeviceFromId(active_output_node_id_);
    416     if (device)
    417       SetOutputMuteInternal(audio_pref_handler_->GetMuteValue(*device));
    418   }
    419 
    420   input_mute_locked_ = false;
    421   if (audio_pref_handler_->GetAudioCaptureAllowedValue()) {
    422     SetInputMute(false);
    423   } else {
    424     SetInputMute(true);
    425     input_mute_locked_ = true;
    426   }
    427 }
    428 
    429 void CrasAudioHandler::SetOutputNodeVolume(uint64 node_id, int volume) {
    430   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    431       SetOutputNodeVolume(node_id, volume);
    432 }
    433 
    434 bool  CrasAudioHandler::SetOutputMuteInternal(bool mute_on) {
    435   if (output_mute_locked_)
    436     return false;
    437 
    438   output_mute_on_ = mute_on;
    439   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    440       SetOutputUserMute(mute_on);
    441   return true;
    442 }
    443 
    444 void CrasAudioHandler::SetInputNodeGain(uint64 node_id, int gain) {
    445   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    446       SetInputNodeGain(node_id, gain);
    447 }
    448 
    449 bool CrasAudioHandler::SetInputMuteInternal(bool mute_on) {
    450   if (input_mute_locked_)
    451     return false;
    452 
    453   input_mute_on_ = mute_on;
    454   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->
    455       SetInputMute(mute_on);
    456   return true;
    457 }
    458 
    459 void CrasAudioHandler::GetNodes() {
    460   chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
    461       base::Bind(&CrasAudioHandler::HandleGetNodes,
    462                  weak_ptr_factory_.GetWeakPtr()));
    463 }
    464 
    465 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device,
    466                                           uint64* current_active_node_id) {
    467   // If the device we want to switch to is already the current active device,
    468   // do nothing.
    469   if (new_active_device.active &&
    470       new_active_device.id == *current_active_node_id) {
    471     return false;
    472   }
    473 
    474   // Reset all other input or output devices' active status. The active audio
    475   // device from the previous user session can be remembered by cras, but not
    476   // in chrome. see crbug.com/273271.
    477   for (AudioDeviceMap::iterator it = audio_devices_.begin();
    478        it != audio_devices_.end(); ++it) {
    479     if (it->second.is_input == new_active_device.is_input &&
    480         it->second.id != new_active_device.id)
    481       it->second.active = false;
    482   }
    483 
    484   // Set the current active input/output device to the new_active_device.
    485   *current_active_node_id = new_active_device.id;
    486   audio_devices_[*current_active_node_id].active = true;
    487   return true;
    488 }
    489 
    490 bool CrasAudioHandler::NonActiveDeviceUnplugged(
    491     size_t old_devices_size,
    492     size_t new_devices_size,
    493     uint64 current_active_node) {
    494   // There could be cases that more than one NodesChanged signals are
    495   // triggered by cras for unplugging or plugging one audio devices, both coming
    496   // with the same node data. After handling the first NodesChanged signal, the
    497   // audio_devices_ can be overwritten by staled node data from handling 2nd
    498   // NodesChanged signal. Therefore, we need to check if the device with
    499   // current_active_node is consistently active or not.
    500   // crbug.com/274641.
    501   return (new_devices_size <= old_devices_size &&
    502           GetDeviceFromId(current_active_node) &&
    503           audio_devices_[current_active_node].active);
    504 }
    505 
    506 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device) {
    507   if (device.is_input) {
    508     if (!ChangeActiveDevice(device, &active_input_node_id_))
    509       return;
    510     SetupAudioInputState();
    511     SetActiveInputNode(active_input_node_id_);
    512   } else {
    513     if (!ChangeActiveDevice(device, &active_output_node_id_))
    514       return;
    515     SetupAudioOutputState();
    516     SetActiveOutputNode(active_output_node_id_);
    517   }
    518 }
    519 
    520 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
    521                                        bool is_input) {
    522   size_t num_old_devices = 0;
    523   size_t num_new_devices = 0;
    524   for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
    525        it != audio_devices_.end(); ++it) {
    526     if (is_input == it->second.is_input)
    527       ++num_old_devices;
    528   }
    529 
    530   for (AudioNodeList::const_iterator it = new_nodes.begin();
    531        it != new_nodes.end(); ++it) {
    532     if (is_input == it->is_input)
    533       ++num_new_devices;
    534   }
    535   return num_old_devices != num_new_devices;
    536 }
    537 
    538 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
    539     const AudioNodeList& nodes) {
    540   size_t old_audio_devices_size = audio_devices_.size();
    541   bool output_devices_changed = HasDeviceChange(nodes, false);
    542   bool input_devices_changed = HasDeviceChange(nodes, true);
    543   audio_devices_.clear();
    544   has_alternative_input_ = false;
    545   has_alternative_output_ = false;
    546 
    547   while (!input_devices_pq_.empty())
    548     input_devices_pq_.pop();
    549   while (!output_devices_pq_.empty())
    550     output_devices_pq_.pop();
    551 
    552   for (size_t i = 0; i < nodes.size(); ++i) {
    553     AudioDevice device(nodes[i]);
    554     audio_devices_[device.id] = device;
    555 
    556     if (!has_alternative_input_ &&
    557         device.is_input &&
    558         device.type != AUDIO_TYPE_INTERNAL_MIC) {
    559       has_alternative_input_ = true;
    560     } else if (!has_alternative_output_ &&
    561                !device.is_input &&
    562                device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
    563       has_alternative_output_ = true;
    564     }
    565 
    566     if (device.is_input)
    567       input_devices_pq_.push(device);
    568     else
    569       output_devices_pq_.push(device);
    570   }
    571 
    572   // If audio nodes change is caused by unplugging some non-active audio
    573   // devices, the previously set active audio device will stay active.
    574   // Otherwise, switch to a new active audio device according to their priority.
    575   if (input_devices_changed &&
    576       !NonActiveDeviceUnplugged(old_audio_devices_size,
    577                                 audio_devices_.size(),
    578                                 active_input_node_id_) &&
    579       !input_devices_pq_.empty())
    580     SwitchToDevice(input_devices_pq_.top());
    581   if (output_devices_changed &&
    582       !NonActiveDeviceUnplugged(old_audio_devices_size,
    583                                 audio_devices_.size(),
    584                                 active_output_node_id_) &&
    585       !output_devices_pq_.empty()) {
    586     SwitchToDevice(output_devices_pq_.top());
    587   }
    588 }
    589 
    590 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
    591                                       bool success) {
    592   if (!success) {
    593     LOG(ERROR) << "Failed to retrieve audio nodes data";
    594     return;
    595   }
    596 
    597   UpdateDevicesAndSwitchActive(node_list);
    598   FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
    599 }
    600 
    601 }  // namespace chromeos
    602