audio_handler.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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 "chrome/browser/chromeos/audio_handler.h" 6 7#include <math.h> 8 9#include "base/logging.h" 10#include "base/singleton.h" 11#include "chrome/browser/chromeos/pulse_audio_mixer.h" 12 13namespace chromeos { 14 15namespace { 16 17const double kSilenceDb = -200.0; 18const double kMinVolumeDb = -90.0; 19// Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some 20// in case sounds or their setup is too quiet for them. 21const double kMaxVolumeDb = 6.0; 22// A value of less than one adjusts quieter volumes in larger steps (giving 23// finer resolution in the higher volumes). 24const double kVolumeBias = 0.5; 25// If a connection is lost, we try again this many times 26const int kMaxReconnectTries = 4; 27 28} // namespace 29 30// This class will set volume using PulseAudio to adjust volume and mute, and 31// handles the volume level logic. 32 33// TODO(davej): Serialize volume/mute for next startup? 34 35double AudioHandler::GetVolumePercent() { 36 if (!VerifyMixerConnection()) 37 return 0; 38 39 return VolumeDbToPercent(mixer_->GetVolumeDb()); 40} 41 42// Set volume using our internal 0-100% range. Notice 0% is a special case of 43// silence, so we set the mixer volume to kSilenceDb instead of kMinVolumeDb. 44void AudioHandler::SetVolumePercent(double volume_percent) { 45 if (!VerifyMixerConnection()) 46 return; 47 DCHECK(volume_percent >= 0.0); 48 49 double vol_db; 50 if (volume_percent <= 0) 51 vol_db = kSilenceDb; 52 else 53 vol_db = PercentToVolumeDb(volume_percent); 54 55 mixer_->SetVolumeDb(vol_db); 56} 57 58void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { 59 if (!VerifyMixerConnection()) 60 return; 61 62 DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; 63 64 double volume = mixer_->GetVolumeDb(); 65 double pct = VolumeDbToPercent(volume); 66 67 if (pct < 0) 68 pct = 0; 69 pct = pct + adjust_by_percent; 70 if (pct > 100.0) 71 pct = 100.0; 72 73 double new_volume; 74 if (pct <= 0.1) 75 new_volume = kSilenceDb; 76 else 77 new_volume = PercentToVolumeDb(pct); 78 79 if (new_volume != volume) 80 mixer_->SetVolumeDb(new_volume); 81} 82 83bool AudioHandler::IsMute() { 84 if (!VerifyMixerConnection()) 85 return false; 86 87 return mixer_->IsMute(); 88} 89 90void AudioHandler::SetMute(bool do_mute) { 91 if (!VerifyMixerConnection()) 92 return; 93 94 DVLOG(1) << "Setting Mute to " << do_mute; 95 96 mixer_->SetMute(do_mute); 97} 98 99void AudioHandler::OnMixerInitialized(bool success) { 100 connected_ = success; 101 DVLOG(1) << "OnMixerInitialized, success = " << success; 102} 103 104AudioHandler::AudioHandler() 105 : connected_(false), 106 reconnect_tries_(0) { 107 mixer_.reset(new PulseAudioMixer()); 108 if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) { 109 LOG(ERROR) << "Unable to connect to PulseAudio"; 110 } 111} 112 113AudioHandler::~AudioHandler() { 114 mixer_.reset(); 115}; 116 117bool AudioHandler::VerifyMixerConnection() { 118 PulseAudioMixer::State mixer_state = mixer_->CheckState(); 119 if (mixer_state == PulseAudioMixer::READY) 120 return true; 121 if (connected_) { 122 // Something happened and the mixer is no longer valid after having been 123 // initialized earlier. 124 connected_ = false; 125 LOG(ERROR) << "Lost connection to PulseAudio"; 126 } else { 127 LOG(ERROR) << "Mixer not valid"; 128 } 129 130 if ((mixer_state == PulseAudioMixer::INITIALIZING) || 131 (mixer_state == PulseAudioMixer::SHUTTING_DOWN)) 132 return false; 133 134 if (reconnect_tries_ < kMaxReconnectTries) { 135 reconnect_tries_++; 136 VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/" 137 << kMaxReconnectTries; 138 mixer_.reset(new PulseAudioMixer()); 139 connected_ = mixer_->InitSync(); 140 if (connected_) { 141 reconnect_tries_ = 0; 142 return true; 143 } 144 LOG(ERROR) << "Unable to re-connect to PulseAudio"; 145 } 146 return false; 147} 148 149// VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us 150// complete control over how the 0 to 100% range is mapped to actual loudness. 151// Volume range is from kMinVolumeDb at just above 0% to kMaxVolumeDb at 100% 152// with a special case at 0% which maps to kSilenceDb. 153// 154// The mapping is confined to these two functions to make it easy to adjust and 155// have everything else just work. The range is biased to give finer resolution 156// in the higher volumes if kVolumeBias is less than 1.0. 157 158// static 159double AudioHandler::VolumeDbToPercent(double volume_db) { 160 if (volume_db < kMinVolumeDb) 161 return 0; 162 return 100.0 * pow((volume_db - kMinVolumeDb) / 163 (kMaxVolumeDb - kMinVolumeDb), 1/kVolumeBias); 164} 165 166// static 167double AudioHandler::PercentToVolumeDb(double volume_percent) { 168 return pow(volume_percent / 100.0, kVolumeBias) * 169 (kMaxVolumeDb - kMinVolumeDb) + kMinVolumeDb; 170} 171 172// static 173AudioHandler* AudioHandler::GetInstance() { 174 return Singleton<AudioHandler>::get(); 175} 176 177} // namespace chromeos 178