Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 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 "chrome/browser/chromeos/audio_handler.h" | 5 #include "chrome/browser/chromeos/audio_handler.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/singleton.h" | 10 #include "base/singleton.h" |
| 11 #include "chrome/browser/chromeos/pulse_audio_mixer.h" | 11 #include "chrome/browser/chromeos/audio_mixer_alsa.h" |
| 12 #include "chrome/browser/chromeos/audio_mixer_pulse.h" | |
| 13 #include "chrome/browser/browser_thread.h" | |
| 12 | 14 |
| 13 namespace chromeos { | 15 namespace chromeos { |
| 14 | 16 |
| 15 namespace { | 17 namespace { |
| 16 | 18 |
| 17 const double kSilenceDb = -200.0; | |
| 18 const double kMinVolumeDb = -90.0; | 19 const double kMinVolumeDb = -90.0; |
| 19 // Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some | 20 // 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. | 21 // in case sounds or their setup is too quiet for them. |
| 21 const double kMaxVolumeDb = 6.0; | 22 const double kMaxVolumeDb = 6.0; |
| 22 // A value of less than one adjusts quieter volumes in larger steps (giving | 23 // A value of less than one adjusts quieter volumes in larger steps (giving |
| 23 // finer resolution in the higher volumes). | 24 // finer resolution in the higher volumes). |
| 24 const double kVolumeBias = 0.5; | 25 const double kVolumeBias = 0.5; |
| 25 // If a connection is lost, we try again this many times | 26 // If a connection is lost, we try again this many times |
| 26 const int kMaxReconnectTries = 4; | 27 const int kMaxReconnectTries = 4; |
| 27 | 28 |
| 28 } // namespace | 29 } // namespace |
| 29 | 30 |
| 30 // This class will set volume using PulseAudio to adjust volume and mute, and | 31 // This class will set volume using PulseAudio or ALSA to adjust volume and |
| 31 // handles the volume level logic. | 32 // mute, and handles the volume level logic. |
| 32 | |
| 33 // TODO(davej): Serialize volume/mute for next startup? | |
| 34 | 33 |
| 35 double AudioHandler::GetVolumePercent() { | 34 double AudioHandler::GetVolumePercent() { |
| 36 if (!VerifyMixerConnection()) | 35 if (!VerifyMixerConnection()) |
| 37 return 0; | 36 return 0; |
| 38 | 37 |
| 39 return VolumeDbToPercent(mixer_->GetVolumeDb()); | 38 return VolumeDbToPercent(mixer_->GetVolumeDb()); |
| 40 } | 39 } |
| 41 | 40 |
| 42 // Set volume using our internal 0-100% range. Notice 0% is a special case of | 41 // 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. | 42 // silence, so we set the mixer volume to kSilenceDb instead of min_volume_db_. |
| 44 void AudioHandler::SetVolumePercent(double volume_percent) { | 43 void AudioHandler::SetVolumePercent(double volume_percent) { |
| 45 if (!VerifyMixerConnection()) | 44 if (!VerifyMixerConnection()) |
| 46 return; | 45 return; |
| 47 DCHECK(volume_percent >= 0.0); | 46 DCHECK(volume_percent >= 0.0); |
| 48 | 47 |
| 49 double vol_db; | 48 double vol_db; |
| 50 if (volume_percent <= 0) | 49 if (volume_percent <= 0) |
| 51 vol_db = kSilenceDb; | 50 vol_db = AudioMixer::kSilenceDb; |
| 52 else | 51 else |
| 53 vol_db = PercentToVolumeDb(volume_percent); | 52 vol_db = PercentToVolumeDb(volume_percent); |
| 54 | 53 |
| 55 mixer_->SetVolumeDb(vol_db); | 54 mixer_->SetVolumeDb(vol_db); |
| 56 } | 55 } |
| 57 | 56 |
| 58 void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { | 57 void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { |
| 59 if (!VerifyMixerConnection()) | 58 if (!VerifyMixerConnection()) |
| 60 return; | 59 return; |
| 61 | 60 |
| 62 DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; | 61 DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; |
| 63 | 62 |
| 64 double volume = mixer_->GetVolumeDb(); | 63 double volume = mixer_->GetVolumeDb(); |
| 65 double pct = VolumeDbToPercent(volume); | 64 double pct = VolumeDbToPercent(volume); |
| 66 | 65 |
| 67 if (pct < 0) | 66 if (pct < 0) |
| 68 pct = 0; | 67 pct = 0; |
| 69 pct = pct + adjust_by_percent; | 68 pct = pct + adjust_by_percent; |
| 70 if (pct > 100.0) | 69 if (pct > 100.0) |
| 71 pct = 100.0; | 70 pct = 100.0; |
| 72 | 71 |
| 73 double new_volume; | 72 double new_volume; |
| 74 if (pct <= 0.1) | 73 if (pct <= 0.1) |
| 75 new_volume = kSilenceDb; | 74 new_volume = AudioMixer::kSilenceDb; |
| 76 else | 75 else |
| 77 new_volume = PercentToVolumeDb(pct); | 76 new_volume = PercentToVolumeDb(pct); |
| 78 | 77 |
| 79 if (new_volume != volume) | 78 if (new_volume != volume) |
| 80 mixer_->SetVolumeDb(new_volume); | 79 mixer_->SetVolumeDb(new_volume); |
| 81 } | 80 } |
| 82 | 81 |
| 83 bool AudioHandler::IsMute() { | 82 bool AudioHandler::IsMute() { |
| 84 if (!VerifyMixerConnection()) | 83 if (!VerifyMixerConnection()) |
| 85 return false; | 84 return false; |
| 86 | 85 |
| 87 return mixer_->IsMute(); | 86 return mixer_->IsMute(); |
| 88 } | 87 } |
| 89 | 88 |
| 90 void AudioHandler::SetMute(bool do_mute) { | 89 void AudioHandler::SetMute(bool do_mute) { |
| 91 if (!VerifyMixerConnection()) | 90 if (!VerifyMixerConnection()) |
| 92 return; | 91 return; |
| 93 | 92 |
| 94 DVLOG(1) << "Setting Mute to " << do_mute; | 93 DVLOG(1) << "Setting Mute to " << do_mute; |
| 95 | 94 |
| 96 mixer_->SetMute(do_mute); | 95 mixer_->SetMute(do_mute); |
| 97 } | 96 } |
| 98 | 97 |
| 98 bool AudioHandler::TryToConnect(bool async) { | |
| 99 if (using_mixer_ == MIXER_TYPE_PULSEAUDIO) { | |
| 100 VLOG(1) << "Trying to connect to PulseAudio"; | |
| 101 mixer_.reset(new AudioMixerPulse()); | |
| 102 } else if (using_mixer_ == MIXER_TYPE_ALSA) { | |
| 103 VLOG(1) << "Trying to connect to ALSA"; | |
| 104 mixer_.reset(new AudioMixerAlsa()); | |
| 105 } else { | |
| 106 VLOG(1) << "Cannot find valid volume mixer"; | |
| 107 mixer_.reset(); | |
| 108 return false; | |
| 109 } | |
| 110 | |
| 111 if (async) { | |
| 112 if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) | |
| 113 return false; | |
| 114 } else { | |
| 115 if (!mixer_->InitSync()) { | |
| 116 VLOG(1) << "Unable to reconnect to Mixer"; | |
| 117 return false; | |
| 118 } | |
| 119 } | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 void AudioHandler::UseNextMixer() { | |
| 124 if (using_mixer_ == MIXER_TYPE_PULSEAUDIO) | |
| 125 using_mixer_ = MIXER_TYPE_ALSA; | |
| 126 else | |
| 127 using_mixer_ = MIXER_TYPE_NONE; | |
| 128 } | |
| 129 | |
| 130 static void ClipVolume(double* min_volume, double* max_volume) { | |
| 131 if (*min_volume < kMinVolumeDb) | |
| 132 *min_volume = kMinVolumeDb; | |
| 133 if (*max_volume > kMaxVolumeDb) | |
| 134 *max_volume = kMaxVolumeDb; | |
| 135 } | |
| 136 | |
| 99 void AudioHandler::OnMixerInitialized(bool success) { | 137 void AudioHandler::OnMixerInitialized(bool success) { |
| 100 connected_ = success; | 138 connected_ = success; |
| 101 DVLOG(1) << "OnMixerInitialized, success = " << success; | 139 DVLOG(1) << "OnMixerInitialized, success = " << success; |
| 140 | |
| 141 if (connected_) { | |
| 142 if (mixer_->GetVolumeLimits(&min_volume_db_, &max_volume_db_)) { | |
| 143 ClipVolume(&min_volume_db_, &max_volume_db_); | |
| 144 } | |
| 145 } else { | |
| 146 UseNextMixer(); | |
| 147 VLOG(1) << "Unable to connect to mixer, trying next"; | |
| 148 | |
| 149 BrowserThread::PostTask( | |
| 150 BrowserThread::UI, FROM_HERE, | |
| 151 NewRunnableMethod(this, &AudioHandler::TryToConnect, true)); | |
| 152 } | |
| 102 } | 153 } |
| 103 | 154 |
| 104 AudioHandler::AudioHandler() | 155 AudioHandler::AudioHandler() |
| 105 : connected_(false), | 156 : connected_(false), |
| 106 reconnect_tries_(0) { | 157 reconnect_tries_(0), |
| 107 mixer_.reset(new PulseAudioMixer()); | 158 max_volume_db_(0), |
| 108 if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) { | 159 min_volume_db_(-90), |
| 109 LOG(ERROR) << "Unable to connect to PulseAudio"; | 160 using_mixer_(MIXER_TYPE_PULSEAUDIO) { |
| 161 while (using_mixer_ != MIXER_TYPE_NONE) { | |
| 162 if (TryToConnect(true)) { | |
|
Daniel Erat
2011/01/05 01:06:53
I think that Chrome style is to omit curly braces
| |
| 163 break; | |
| 164 } | |
| 165 UseNextMixer(); | |
| 110 } | 166 } |
| 111 } | 167 } |
| 112 | 168 |
| 113 AudioHandler::~AudioHandler() { | 169 AudioHandler::~AudioHandler() { |
| 114 mixer_.reset(); | 170 mixer_.reset(); |
| 115 }; | 171 }; |
| 116 | 172 |
| 117 bool AudioHandler::VerifyMixerConnection() { | 173 bool AudioHandler::VerifyMixerConnection() { |
| 118 PulseAudioMixer::State mixer_state = mixer_->CheckState(); | 174 if (mixer_ == NULL) |
| 119 if (mixer_state == PulseAudioMixer::READY) | 175 return false; |
| 176 | |
| 177 AudioMixer::State mixer_state = mixer_->CheckState(); | |
| 178 if (mixer_state == AudioMixer::READY) | |
| 120 return true; | 179 return true; |
| 121 if (connected_) { | 180 if (connected_) { |
| 122 // Something happened and the mixer is no longer valid after having been | 181 // Something happened and the mixer is no longer valid after having been |
| 123 // initialized earlier. | 182 // initialized earlier. |
| 124 connected_ = false; | 183 connected_ = false; |
| 125 LOG(ERROR) << "Lost connection to PulseAudio"; | 184 LOG(ERROR) << "Lost connection to mixer"; |
| 126 } else { | 185 } else { |
| 127 LOG(ERROR) << "Mixer not valid"; | 186 LOG(ERROR) << "Mixer not valid"; |
| 128 } | 187 } |
| 129 | 188 |
| 130 if ((mixer_state == PulseAudioMixer::INITIALIZING) || | 189 if ((mixer_state == AudioMixer::INITIALIZING) || |
| 131 (mixer_state == PulseAudioMixer::SHUTTING_DOWN)) | 190 (mixer_state == AudioMixer::SHUTTING_DOWN)) |
| 132 return false; | 191 return false; |
| 133 | 192 |
| 134 if (reconnect_tries_ < kMaxReconnectTries) { | 193 if (reconnect_tries_ < kMaxReconnectTries) { |
| 135 reconnect_tries_++; | 194 reconnect_tries_++; |
| 136 VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/" | 195 VLOG(1) << "Re-connecting to mixer attempt " << reconnect_tries_ << "/" |
| 137 << kMaxReconnectTries; | 196 << kMaxReconnectTries; |
| 138 mixer_.reset(new PulseAudioMixer()); | 197 |
| 139 connected_ = mixer_->InitSync(); | 198 connected_ = TryToConnect(false); |
| 199 | |
| 140 if (connected_) { | 200 if (connected_) { |
| 141 reconnect_tries_ = 0; | 201 reconnect_tries_ = 0; |
| 142 return true; | 202 return true; |
| 143 } | 203 } |
| 144 LOG(ERROR) << "Unable to re-connect to PulseAudio"; | 204 LOG(ERROR) << "Unable to re-connect to mixer"; |
| 145 } | 205 } |
| 146 return false; | 206 return false; |
| 147 } | 207 } |
| 148 | 208 |
| 149 // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us | 209 // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us |
| 150 // complete control over how the 0 to 100% range is mapped to actual loudness. | 210 // 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% | 211 // Volume range is from min_volume_db_ at just above 0% to max_volume_db_ |
| 152 // with a special case at 0% which maps to kSilenceDb. | 212 // at 100% with a special case at 0% which maps to kSilenceDb. |
| 153 // | 213 // |
| 154 // The mapping is confined to these two functions to make it easy to adjust and | 214 // 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 | 215 // 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. | 216 // in the higher volumes if kVolumeBias is less than 1.0. |
| 157 | 217 |
| 158 // static | 218 // static |
| 159 double AudioHandler::VolumeDbToPercent(double volume_db) { | 219 double AudioHandler::VolumeDbToPercent(double volume_db) const { |
| 160 if (volume_db < kMinVolumeDb) | 220 if (volume_db < min_volume_db_) |
| 161 return 0; | 221 return 0; |
| 162 return 100.0 * pow((volume_db - kMinVolumeDb) / | 222 return 100.0 * pow((volume_db - min_volume_db_) / |
| 163 (kMaxVolumeDb - kMinVolumeDb), 1/kVolumeBias); | 223 (max_volume_db_ - min_volume_db_), 1/kVolumeBias); |
| 164 } | 224 } |
| 165 | 225 |
| 166 // static | 226 // static |
| 167 double AudioHandler::PercentToVolumeDb(double volume_percent) { | 227 double AudioHandler::PercentToVolumeDb(double volume_percent) const { |
| 168 return pow(volume_percent / 100.0, kVolumeBias) * | 228 return pow(volume_percent / 100.0, kVolumeBias) * |
| 169 (kMaxVolumeDb - kMinVolumeDb) + kMinVolumeDb; | 229 (max_volume_db_ - min_volume_db_) + min_volume_db_; |
| 170 } | 230 } |
| 171 | 231 |
| 172 // static | 232 // static |
| 173 AudioHandler* AudioHandler::GetInstance() { | 233 AudioHandler* AudioHandler::GetInstance() { |
| 174 return Singleton<AudioHandler>::get(); | 234 return Singleton<AudioHandler>::get(); |
| 175 } | 235 } |
| 176 | 236 |
| 177 } // namespace chromeos | 237 } // namespace chromeos |
| OLD | NEW |