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_mixer_alsa.h" | 5 #include "chrome/browser/chromeos/audio_mixer_alsa.h" |
| 6 | 6 |
| 7 #include <alsa/asoundlib.h> | 7 #include <alsa/asoundlib.h> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/task.h" | 10 #include "base/task.h" |
| 11 #include "chrome/browser/browser_process.h" | |
| 12 #include "chrome/browser/browser_thread.h" | |
| 13 #include "chrome/browser/prefs/pref_service.h" | |
| 14 #include "chrome/common/pref_names.h" | |
| 11 | 15 |
| 12 namespace chromeos { | 16 namespace chromeos { |
| 13 | 17 |
| 14 // Connect to the ALSA mixer using their simple element API. Init is performed | 18 // Connect to the ALSA mixer using their simple element API. Init is performed |
| 15 // asynchronously on the worker thread. | 19 // asynchronously on the worker thread. |
| 16 // | 20 // |
| 17 // To get a wider range and finer control over volume levels, first the Master | 21 // To get a wider range and finer control over volume levels, first the Master |
| 18 // level is set, then if the PCM element exists, the total level is refined by | 22 // level is set, then if the PCM element exists, the total level is refined by |
| 19 // adjusting that as well. If the PCM element has more volume steps, it allows | 23 // adjusting that as well. If the PCM element has more volume steps, it allows |
| 20 // for finer granularity in the total volume. | 24 // for finer granularity in the total volume. |
| 21 | 25 |
| 22 // TODO(davej): Serialize volume/mute to preserve settings when restarting. | |
| 23 | |
| 24 typedef long alsa_long_t; // 'long' is required for ALSA API calls. | 26 typedef long alsa_long_t; // 'long' is required for ALSA API calls. |
| 25 | 27 |
| 26 namespace { | 28 namespace { |
| 27 | 29 |
| 28 const char* kMasterVolume = "Master"; | 30 const char* kMasterVolume = "Master"; |
| 29 const char* kPCMVolume = "PCM"; | 31 const char* kPCMVolume = "PCM"; |
| 30 const double kDefaultMinVolume = -90.0; | 32 const double kDefaultMinVolume = -90.0; |
| 31 const double kDefaultMaxVolume = 0.0; | 33 const double kDefaultMaxVolume = 0.0; |
| 34 const double kPrefVolumeNotSet = -999.0; | |
| 32 | 35 |
| 33 } // namespace | 36 } // namespace |
| 34 | 37 |
| 35 AudioMixerAlsa::AudioMixerAlsa() | 38 AudioMixerAlsa::AudioMixerAlsa() |
| 36 : min_volume_(kDefaultMinVolume), | 39 : min_volume_(kDefaultMinVolume), |
| 37 max_volume_(kDefaultMaxVolume), | 40 max_volume_(kDefaultMaxVolume), |
| 38 save_volume_(0), | 41 save_volume_(0), |
| 39 mixer_state_(UNINITIALIZED), | 42 mixer_state_(UNINITIALIZED), |
| 40 alsa_mixer_(NULL), | 43 alsa_mixer_(NULL), |
| 41 elem_master_(NULL), | 44 elem_master_(NULL), |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 83 if (mixer_state_ != READY) | 86 if (mixer_state_ != READY) |
| 84 return false; | 87 return false; |
| 85 if (vol_min) | 88 if (vol_min) |
| 86 *vol_min = min_volume_; | 89 *vol_min = min_volume_; |
| 87 if (vol_max) | 90 if (vol_max) |
| 88 *vol_max = max_volume_; | 91 *vol_max = max_volume_; |
| 89 return true; | 92 return true; |
| 90 } | 93 } |
| 91 | 94 |
| 92 void AudioMixerAlsa::SetVolumeDb(double vol_db) { | 95 void AudioMixerAlsa::SetVolumeDb(double vol_db) { |
| 93 AutoLock lock(mixer_state_lock_); | 96 { |
| 94 if (mixer_state_ != READY) | 97 AutoLock lock(mixer_state_lock_); |
| 95 return; | 98 if (mixer_state_ != READY) |
| 96 DoSetVolumeDb_Locked(vol_db); | 99 return; |
| 100 DoSetVolumeDb_Locked(vol_db); | |
| 101 } | |
| 102 SaveVolume(vol_db); | |
| 97 } | 103 } |
| 98 | 104 |
| 99 bool AudioMixerAlsa::IsMute() const { | 105 bool AudioMixerAlsa::IsMute() const { |
| 100 AutoLock lock(mixer_state_lock_); | 106 AutoLock lock(mixer_state_lock_); |
| 101 if (mixer_state_ != READY) | 107 if (mixer_state_ != READY) |
| 102 return false; | 108 return false; |
| 103 return GetElementMuted_Locked(elem_master_); | 109 return GetElementMuted_Locked(elem_master_); |
| 104 } | 110 } |
| 105 | 111 |
| 106 void AudioMixerAlsa::SetMute(bool mute) { | 112 void AudioMixerAlsa::SetMute(bool mute) { |
| 107 AutoLock lock(mixer_state_lock_); | 113 { |
| 108 if (mixer_state_ != READY) | 114 AutoLock lock(mixer_state_lock_); |
| 109 return; | |
| 110 | 115 |
| 111 // Set volume to minimum on mute, since switching the element off does not | 116 if (mixer_state_ != READY) |
| 112 // always mute as it should. | 117 return; |
| 113 | 118 |
| 114 // TODO(davej): Setting volume to minimum can be removed once switching the | 119 // Set volume to minimum on mute, since switching the element off does not |
| 115 // element off can be guaranteed to work. | 120 // always mute as it should. |
| 116 | 121 |
| 117 bool old_value = GetElementMuted_Locked(elem_master_); | 122 // TODO(davej): Remove save_volume_ and setting volume to minimum if |
| 123 // switching the element off can be guaranteed to mute it. | |
| 118 | 124 |
| 119 if (old_value != mute) { | 125 bool old_value = GetElementMuted_Locked(elem_master_); |
| 120 if (mute) { | 126 |
| 121 save_volume_ = DoGetVolumeDb_Locked(); | 127 if (old_value != mute) { |
| 122 DoSetVolumeDb_Locked(min_volume_); | 128 if (mute) { |
| 123 } else { | 129 save_volume_ = DoGetVolumeDb_Locked(); |
| 124 DoSetVolumeDb_Locked(save_volume_); | 130 DoSetVolumeDb_Locked(min_volume_); |
| 131 } else { | |
| 132 DoSetVolumeDb_Locked(save_volume_); | |
| 133 } | |
| 125 } | 134 } |
| 135 | |
| 136 SetElementMuted_Locked(elem_master_, mute); | |
| 137 if (elem_pcm_) | |
| 138 SetElementMuted_Locked(elem_pcm_, mute); | |
| 126 } | 139 } |
| 127 | 140 SaveMute(mute); |
| 128 SetElementMuted_Locked(elem_master_, mute); | |
| 129 if (elem_pcm_) | |
| 130 SetElementMuted_Locked(elem_pcm_, mute); | |
| 131 } | 141 } |
| 132 | 142 |
| 133 AudioMixer::State AudioMixerAlsa::GetState() const { | 143 AudioMixer::State AudioMixerAlsa::GetState() const { |
| 134 AutoLock lock(mixer_state_lock_); | 144 AutoLock lock(mixer_state_lock_); |
| 135 // If we think it's ready, verify it is actually so. | 145 // If we think it's ready, verify it is actually so. |
| 136 if ((mixer_state_ == READY) && (alsa_mixer_ == NULL)) | 146 if ((mixer_state_ == READY) && (alsa_mixer_ == NULL)) |
| 137 mixer_state_ = IN_ERROR; | 147 mixer_state_ = IN_ERROR; |
| 138 return mixer_state_; | 148 return mixer_state_; |
| 139 } | 149 } |
| 140 | 150 |
| 141 //////////////////////////////////////////////////////////////////////////////// | 151 //////////////////////////////////////////////////////////////////////////////// |
| 142 // Private functions follow | 152 // Private functions follow |
| 143 | 153 |
| 144 void AudioMixerAlsa::DoInit(InitDoneCallback* callback) { | 154 void AudioMixerAlsa::DoInit(InitDoneCallback* callback) { |
| 145 bool success = InitializeAlsaMixer(); | 155 bool success = InitializeAlsaMixer(); |
| 146 | 156 |
| 157 if (success) { | |
| 158 BrowserThread::PostTask( | |
| 159 BrowserThread::UI, FROM_HERE, | |
| 160 NewRunnableMethod(this, &AudioMixerAlsa::RestoreVolumeMute)); | |
| 161 } | |
| 162 | |
| 147 if (callback) { | 163 if (callback) { |
| 148 callback->Run(success); | 164 callback->Run(success); |
| 149 delete callback; | 165 delete callback; |
| 150 } | 166 } |
| 151 } | 167 } |
| 152 | 168 |
| 153 bool AudioMixerAlsa::InitThread() { | 169 bool AudioMixerAlsa::InitThread() { |
| 154 AutoLock lock(mixer_state_lock_); | 170 AutoLock lock(mixer_state_lock_); |
| 155 | 171 |
| 156 if (mixer_state_ != UNINITIALIZED) | 172 if (mixer_state_ != UNINITIALIZED) |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 234 | 250 |
| 235 void AudioMixerAlsa::FreeAlsaMixer() { | 251 void AudioMixerAlsa::FreeAlsaMixer() { |
| 236 AutoLock lock(mixer_state_lock_); | 252 AutoLock lock(mixer_state_lock_); |
| 237 mixer_state_ = SHUTTING_DOWN; | 253 mixer_state_ = SHUTTING_DOWN; |
| 238 if (alsa_mixer_) { | 254 if (alsa_mixer_) { |
| 239 snd_mixer_close(alsa_mixer_); | 255 snd_mixer_close(alsa_mixer_); |
| 240 alsa_mixer_ = NULL; | 256 alsa_mixer_ = NULL; |
| 241 } | 257 } |
| 242 } | 258 } |
| 243 | 259 |
| 260 void AudioMixerAlsa::DoSetVolumeMute(double volume, bool mute) { | |
| 261 AutoLock lock(mixer_state_lock_); | |
| 262 if (mixer_state_ != READY) | |
| 263 return; | |
| 264 | |
| 265 VLOG(1) << "Setting volume to " << volume << "and mute to " << mute; | |
| 266 if (mute) { | |
| 267 save_volume_ = volume; | |
| 268 DoSetVolumeDb_Locked(min_volume_); | |
| 269 } else { | |
| 270 DoSetVolumeDb_Locked(volume); | |
| 271 } | |
| 272 | |
| 273 SetElementMuted_Locked(elem_master_, mute); | |
| 274 if (elem_pcm_) | |
| 275 SetElementMuted_Locked(elem_pcm_, mute); | |
| 276 } | |
| 277 | |
| 244 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { | 278 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { |
| 245 double vol_total = 0.0; | 279 double vol_total = 0.0; |
| 246 GetElementVolume_Locked(elem_master_, &vol_total); | 280 GetElementVolume_Locked(elem_master_, &vol_total); |
| 247 | 281 |
| 248 double vol_pcm = 0.0; | 282 double vol_pcm = 0.0; |
| 249 if (elem_pcm_ && (GetElementVolume_Locked(elem_pcm_, &vol_pcm))) | 283 if (elem_pcm_ && (GetElementVolume_Locked(elem_pcm_, &vol_pcm))) |
| 250 vol_total += vol_pcm; | 284 vol_total += vol_pcm; |
| 251 | 285 |
| 252 return vol_total; | 286 return vol_total; |
| 253 } | 287 } |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 } | 389 } |
| 356 | 390 |
| 357 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { | 391 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { |
| 358 int enabled = mute ? 0 : 1; | 392 int enabled = mute ? 0 : 1; |
| 359 snd_mixer_selem_set_playback_switch_all(elem, enabled); | 393 snd_mixer_selem_set_playback_switch_all(elem, enabled); |
| 360 | 394 |
| 361 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) | 395 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) |
| 362 << " to " << enabled; | 396 << " to " << enabled; |
| 363 } | 397 } |
| 364 | 398 |
| 399 void AudioMixerAlsa::RestoreVolumeMute() { | |
| 400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 401 if (!g_browser_process) | |
| 402 return; | |
| 403 | |
| 404 PrefService* local_state = g_browser_process->local_state(); | |
|
scherkus (not reviewing)
2011/01/13 00:54:52
take a look at PrefMember which wraps up all these
davejcool
2011/01/13 21:31:14
Thanks for the PrefMember pointer! This greatly s
| |
| 405 if (!local_state) { | |
| 406 LOG(ERROR) << "Cannot get local_state for prefs, not initializing volume"; | |
| 407 return; | |
| 408 } | |
| 409 | |
| 410 if (!local_state->FindPreference(prefs::kAudioVolume)) { | |
| 411 local_state->RegisterRealPref(prefs::kAudioVolume, kPrefVolumeNotSet); | |
| 412 local_state->RegisterBooleanPref(prefs::kAudioMute, false); | |
| 413 } | |
| 414 | |
| 415 DCHECK(local_state->FindPreference(prefs::kAudioVolume)); | |
| 416 double pref_volume = local_state->GetReal(prefs::kAudioVolume); | |
| 417 | |
| 418 if (pref_volume != kPrefVolumeNotSet) { | |
| 419 DCHECK(local_state->FindPreference(prefs::kAudioMute)); | |
| 420 bool pref_mute = local_state->GetBoolean(prefs::kAudioMute); | |
| 421 | |
| 422 thread_->message_loop()->PostTask(FROM_HERE, | |
| 423 NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, | |
| 424 pref_volume, pref_mute)); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void AudioMixerAlsa::SaveVolume(double volume) { | |
| 429 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 430 if (!g_browser_process) | |
| 431 return; | |
| 432 | |
| 433 PrefService* local_state = g_browser_process->local_state(); | |
| 434 if (!local_state) { | |
| 435 LOG(ERROR) << "Cannot get local_state to store volume in prefs"; | |
| 436 return; | |
| 437 } | |
| 438 DCHECK(local_state->FindPreference(prefs::kAudioVolume)); | |
| 439 local_state->SetReal(prefs::kAudioVolume, volume); | |
| 440 } | |
| 441 | |
| 442 void AudioMixerAlsa::SaveMute(bool mute) { | |
| 443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 444 if (!g_browser_process) | |
| 445 return; | |
| 446 | |
| 447 PrefService* local_state = g_browser_process->local_state(); | |
| 448 if (!local_state) { | |
|
zhurunz
2011/01/13 00:19:25
Could we add a util function to get local_state so
davejcool
2011/01/13 21:31:14
By using the PrefMember class, I was able to encap
| |
| 449 LOG(ERROR) << "Cannot get local_state to store mute in prefs"; | |
| 450 return; | |
| 451 } | |
| 452 DCHECK(local_state->FindPreference(prefs::kAudioMute)); | |
| 453 local_state->SetBoolean(prefs::kAudioMute, mute); | |
| 454 } | |
| 455 | |
| 365 } // namespace chromeos | 456 } // namespace chromeos |
| 366 | 457 |
| OLD | NEW |