| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/chromeos/audio_handler.h" |
| |
| #include <math.h> |
| |
| #include "base/logging.h" |
| #include "chrome/browser/chromeos/pulse_audio_mixer.h" |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| const double kSilenceDb = -200.0; |
| const double kMinVolumeDb = -90.0; |
| // Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some |
| // in case sounds or their setup is too quiet for them. |
| const double kMaxVolumeDb = 6.0; |
| // A value of less than one adjusts quieter volumes in larger steps (giving |
| // finer resolution in the higher volumes). |
| const double kVolumeBias = 0.5; |
| // If a connection is lost, we try again this many times |
| const int kMaxReconnectTries = 4; |
| |
| } // namespace |
| |
| // This class will set volume using PulseAudio to adjust volume and mute, and |
| // handles the volume level logic. |
| |
| // TODO(davej): Serialize volume/mute for next startup? |
| |
| double AudioHandler::GetVolumePercent() { |
| if (!VerifyMixerConnection()) |
| return 0; |
| |
| return VolumeDbToPercent(mixer_->GetVolumeDb()); |
| } |
| |
| // Set volume using our internal 0-100% range. Notice 0% is a special case of |
| // silence, so we set the mixer volume to kSilenceDb instead of kMinVolumeDb. |
| void AudioHandler::SetVolumePercent(double volume_percent) { |
| if (!VerifyMixerConnection()) |
| return; |
| DCHECK(volume_percent >= 0.0); |
| |
| double vol_db; |
| if (volume_percent <= 0) |
| vol_db = kSilenceDb; |
| else |
| vol_db = PercentToVolumeDb(volume_percent); |
| |
| mixer_->SetVolumeDb(vol_db); |
| } |
| |
| void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { |
| if (!VerifyMixerConnection()) |
| return; |
| |
| DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; |
| |
| double volume = mixer_->GetVolumeDb(); |
| double pct = VolumeDbToPercent(volume); |
| |
| if (pct < 0) |
| pct = 0; |
| pct = pct + adjust_by_percent; |
| if (pct > 100.0) |
| pct = 100.0; |
| |
| double new_volume; |
| if (pct <= 0.1) |
| new_volume = kSilenceDb; |
| else |
| new_volume = PercentToVolumeDb(pct); |
| |
| if (new_volume != volume) |
| mixer_->SetVolumeDb(new_volume); |
| } |
| |
| bool AudioHandler::IsMute() { |
| if (!VerifyMixerConnection()) |
| return false; |
| |
| return mixer_->IsMute(); |
| } |
| |
| void AudioHandler::SetMute(bool do_mute) { |
| if (!VerifyMixerConnection()) |
| return; |
| |
| DVLOG(1) << "Setting Mute to " << do_mute; |
| |
| mixer_->SetMute(do_mute); |
| } |
| |
| void AudioHandler::OnMixerInitialized(bool success) { |
| connected_ = success; |
| DVLOG(1) << "OnMixerInitialized, success = " << success; |
| } |
| |
| AudioHandler::AudioHandler() |
| : connected_(false), |
| reconnect_tries_(0) { |
| mixer_.reset(new PulseAudioMixer()); |
| if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) { |
| LOG(ERROR) << "Unable to connect to PulseAudio"; |
| } |
| } |
| |
| AudioHandler::~AudioHandler() { |
| mixer_.reset(); |
| }; |
| |
| bool AudioHandler::VerifyMixerConnection() { |
| PulseAudioMixer::State mixer_state = mixer_->CheckState(); |
| if (mixer_state == PulseAudioMixer::READY) |
| return true; |
| if (connected_) { |
| // Something happened and the mixer is no longer valid after having been |
| // initialized earlier. |
| connected_ = false; |
| LOG(ERROR) << "Lost connection to PulseAudio"; |
| } else { |
| LOG(ERROR) << "Mixer not valid"; |
| } |
| |
| if ((mixer_state == PulseAudioMixer::INITIALIZING) || |
| (mixer_state == PulseAudioMixer::SHUTTING_DOWN)) |
| return false; |
| |
| if (reconnect_tries_ < kMaxReconnectTries) { |
| reconnect_tries_++; |
| VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/" |
| << kMaxReconnectTries; |
| mixer_.reset(new PulseAudioMixer()); |
| connected_ = mixer_->InitSync(); |
| if (connected_) { |
| reconnect_tries_ = 0; |
| return true; |
| } |
| LOG(ERROR) << "Unable to re-connect to PulseAudio"; |
| } |
| return false; |
| } |
| |
| // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us |
| // complete control over how the 0 to 100% range is mapped to actual loudness. |
| // Volume range is from kMinVolumeDb at just above 0% to kMaxVolumeDb at 100% |
| // with a special case at 0% which maps to kSilenceDb. |
| // |
| // The mapping is confined to these two functions to make it easy to adjust and |
| // have everything else just work. The range is biased to give finer resolution |
| // in the higher volumes if kVolumeBias is less than 1.0. |
| |
| // static |
| double AudioHandler::VolumeDbToPercent(double volume_db) { |
| if (volume_db < kMinVolumeDb) |
| return 0; |
| return 100.0 * pow((volume_db - kMinVolumeDb) / |
| (kMaxVolumeDb - kMinVolumeDb), 1/kVolumeBias); |
| } |
| |
| // static |
| double AudioHandler::PercentToVolumeDb(double volume_percent) { |
| return pow(volume_percent / 100.0, kVolumeBias) * |
| (kMaxVolumeDb - kMinVolumeDb) + kMinVolumeDb; |
| } |
| |
| } // namespace chromeos |