diff options
author | davej@chromium.org <davej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-19 00:52:12 +0000 |
---|---|---|
committer | davej@chromium.org <davej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-19 00:52:12 +0000 |
commit | 5fbc3638abc89b0668dfe598d57b6347343ea0c8 (patch) | |
tree | 8f1925f8e078b216dea19385eceae22671bf993f | |
parent | ee284a1c31d2dec5ae7cfb3bb79b070c54fc06b4 (diff) | |
download | chromium_src-5fbc3638abc89b0668dfe598d57b6347343ea0c8.zip chromium_src-5fbc3638abc89b0668dfe598d57b6347343ea0c8.tar.gz chromium_src-5fbc3638abc89b0668dfe598d57b6347343ea0c8.tar.bz2 |
Save/Restore ALSA volume/mute
When the ALSA volume mixer is used (if PulseAudio is disabled), the current volume and mute settings will be stored in the browser prefs so they can be restored when starting a new session.
BUG=chrome-os:10470
TEST=Manual, volume and mute setting via keyboard should persist after rebooting.
Review URL: http://codereview.chromium.org/6118006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71734 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/chromeos/audio_mixer_alsa.cc | 89 | ||||
-rw-r--r-- | chrome/browser/chromeos/audio_mixer_alsa.h | 14 | ||||
-rw-r--r-- | chrome/browser/prefs/browser_prefs.cc | 2 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 6 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 2 |
5 files changed, 105 insertions, 8 deletions
diff --git a/chrome/browser/chromeos/audio_mixer_alsa.cc b/chrome/browser/chromeos/audio_mixer_alsa.cc index 7f96fa4..1dcc964 100644 --- a/chrome/browser/chromeos/audio_mixer_alsa.cc +++ b/chrome/browser/chromeos/audio_mixer_alsa.cc @@ -8,6 +8,10 @@ #include "base/logging.h" #include "base/task.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/pref_names.h" namespace chromeos { @@ -19,8 +23,6 @@ namespace chromeos { // adjusting that as well. If the PCM element has more volume steps, it allows // for finer granularity in the total volume. -// TODO(davej): Serialize volume/mute to preserve settings when restarting. - typedef long alsa_long_t; // 'long' is required for ALSA API calls. namespace { @@ -29,6 +31,10 @@ const char* kMasterVolume = "Master"; const char* kPCMVolume = "PCM"; const double kDefaultMinVolume = -90.0; const double kDefaultMaxVolume = 0.0; +const double kPrefVolumeInvalid = -999.0; +const int kPrefMuteOff = 0; +const int kPrefMuteOn = 1; +const int kPrefMuteInvalid = 2; } // namespace @@ -57,8 +63,9 @@ void AudioMixerAlsa::Init(InitDoneCallback* callback) { delete callback; return; } + InitPrefs(); - // Post the task of starting up, which can block for 200-500ms, + // Post the task of starting up, which may block on the order of ms, // so best not to do it on the caller's thread. thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &AudioMixerAlsa::DoInit, callback)); @@ -67,6 +74,7 @@ void AudioMixerAlsa::Init(InitDoneCallback* callback) { bool AudioMixerAlsa::InitSync() { if (!InitThread()) return false; + InitPrefs(); return InitializeAlsaMixer(); } @@ -93,7 +101,10 @@ void AudioMixerAlsa::SetVolumeDb(double vol_db) { AutoLock lock(mixer_state_lock_); if (mixer_state_ != READY) return; + if (vol_db < kSilenceDb) + vol_db = kSilenceDb; DoSetVolumeDb_Locked(vol_db); + volume_pref_.SetValue(vol_db); } bool AudioMixerAlsa::IsMute() const { @@ -103,6 +114,12 @@ bool AudioMixerAlsa::IsMute() const { return GetElementMuted_Locked(elem_master_); } +// To indicate the volume is not valid yet, a very low volume value is stored. +// We compare against a slightly higher value in case of rounding errors. +static bool PrefVolumeValid(double volume) { + return (volume > kPrefVolumeInvalid + 0.1); +} + void AudioMixerAlsa::SetMute(bool mute) { AutoLock lock(mixer_state_lock_); if (mixer_state_ != READY) @@ -111,8 +128,9 @@ void AudioMixerAlsa::SetMute(bool mute) { // Set volume to minimum on mute, since switching the element off does not // always mute as it should. - // TODO(davej): Setting volume to minimum can be removed once switching the - // element off can be guaranteed to work. + // TODO(davej): Remove save_volume_ and setting volume to minimum if + // switching the element off can be guaranteed to mute it. Currently mute + // is done by setting the volume to min_volume_. bool old_value = GetElementMuted_Locked(elem_master_); @@ -128,6 +146,7 @@ void AudioMixerAlsa::SetMute(bool mute) { SetElementMuted_Locked(elem_master_, mute); if (elem_pcm_) SetElementMuted_Locked(elem_pcm_, mute); + mute_pref_.SetValue(mute ? kPrefMuteOn : kPrefMuteOff); } AudioMixer::State AudioMixerAlsa::GetState() const { @@ -138,12 +157,26 @@ AudioMixer::State AudioMixerAlsa::GetState() const { return mixer_state_; } +// static +void AudioMixerAlsa::RegisterPrefs(PrefService* local_state) { + if (!local_state->FindPreference(prefs::kAudioVolume)) + local_state->RegisterRealPref(prefs::kAudioVolume, kPrefVolumeInvalid); + if (!local_state->FindPreference(prefs::kAudioMute)) + local_state->RegisterIntegerPref(prefs::kAudioMute, kPrefMuteInvalid); +} + //////////////////////////////////////////////////////////////////////////////// // Private functions follow void AudioMixerAlsa::DoInit(InitDoneCallback* callback) { bool success = InitializeAlsaMixer(); + if (success) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &AudioMixerAlsa::RestoreVolumeMuteOnUIThread)); + } + if (callback) { callback->Run(success); delete callback; @@ -168,6 +201,13 @@ bool AudioMixerAlsa::InitThread() { return true; } +void AudioMixerAlsa::InitPrefs() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + PrefService* prefs = g_browser_process->local_state(); + volume_pref_.Init(prefs::kAudioVolume, prefs, NULL); + mute_pref_.Init(prefs::kAudioMute, prefs, NULL); +} + bool AudioMixerAlsa::InitializeAlsaMixer() { AutoLock lock(mixer_state_lock_); if (mixer_state_ != INITIALIZING) @@ -241,6 +281,42 @@ void AudioMixerAlsa::FreeAlsaMixer() { } } +void AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { + AutoLock lock(mixer_state_lock_); + if (mixer_state_ != READY) + return; + + // If volume or mute are invalid, set them now to the current actual values. + if (!PrefVolumeValid(pref_volume)) + pref_volume = DoGetVolumeDb_Locked(); + bool mute; + if (pref_mute == kPrefMuteInvalid) + mute = GetElementMuted_Locked(elem_master_); + else + mute = (pref_mute == kPrefMuteOn) ? true : false; + + VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; + + if (mute) { + save_volume_ = pref_volume; + DoSetVolumeDb_Locked(min_volume_); + } else if (pref_mute == kPrefMuteOff) { + DoSetVolumeDb_Locked(pref_volume); + } + + SetElementMuted_Locked(elem_master_, mute); + if (elem_pcm_) + SetElementMuted_Locked(elem_pcm_, mute); +} + +void AudioMixerAlsa::RestoreVolumeMuteOnUIThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // This happens during init, so set the volume off the UI thread. + thread_->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, + volume_pref_.GetValue(), mute_pref_.GetValue())); +} + double AudioMixerAlsa::DoGetVolumeDb_Locked() const { double vol_total = 0.0; GetElementVolume_Locked(elem_master_, &vol_total); @@ -258,7 +334,6 @@ void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { // If a PCM volume slider exists, then first set the Master volume to the // nearest volume >= requested volume, then adjust PCM volume down to get // closer to the requested volume. - if (elem_pcm_) { SetElementVolume_Locked(elem_master_, vol_db, &actual_vol, 0.9999f); SetElementVolume_Locked(elem_pcm_, vol_db - actual_vol, NULL, 0.5f); @@ -273,7 +348,7 @@ snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName_Locked( snd_mixer_selem_id_t* sid; // Using id_malloc/id_free API instead of id_alloca since the latter gives the - // warning: the address of 'sid' will always evaluate as 'true' + // warning: the address of 'sid' will always evaluate as 'true'. if (snd_mixer_selem_id_malloc(&sid)) return NULL; diff --git a/chrome/browser/chromeos/audio_mixer_alsa.h b/chrome/browser/chromeos/audio_mixer_alsa.h index d365a22..2cd8489 100644 --- a/chrome/browser/chromeos/audio_mixer_alsa.h +++ b/chrome/browser/chromeos/audio_mixer_alsa.h @@ -12,6 +12,7 @@ #include "base/scoped_ptr.h" #include "base/threading/thread.h" #include "chrome/browser/chromeos/audio_mixer.h" +#include "chrome/browser/prefs/pref_member.h" struct _snd_mixer_elem; struct _snd_mixer; @@ -33,17 +34,25 @@ class AudioMixerAlsa : public AudioMixer { virtual void SetMute(bool mute); virtual State GetState() const; + // Registers volume and mute in preferences + static void RegisterPrefs(PrefService* local_state); + private: // Called to do initialization in background from worker thread. void DoInit(InitDoneCallback* callback); - // Helper function to just get our message loop thread going. + // Helper functions to get our message loop thread and prefs initialized. bool InitThread(); + void InitPrefs(); // Try to connect to the ALSA mixer through their simple controls interface, // and cache mixer handle and mixer elements we'll be using. bool InitializeAlsaMixer(); void FreeAlsaMixer(); + void DoSetVolumeMute(double pref_volume, int pref_mute); + + // Access to PrefMember variables must be done on UI thread. + void RestoreVolumeMuteOnUIThread(); // All these internal volume commands must be called with the lock held. double DoGetVolumeDb_Locked() const; @@ -87,6 +96,9 @@ class AudioMixerAlsa : public AudioMixer { _snd_mixer_elem* elem_master_; _snd_mixer_elem* elem_pcm_; + IntegerPrefMember mute_pref_; + RealPrefMember volume_pref_; + scoped_ptr<base::Thread> thread_; DISALLOW_COPY_AND_ASSIGN(AudioMixerAlsa); diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index a90aed0..88e4b13 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -65,6 +65,7 @@ #endif #if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/audio_mixer_alsa.h" #include "chrome/browser/chromeos/login/apply_services_customization.h" #include "chrome/browser/chromeos/login/signed_settings_temp_storage.h" #include "chrome/browser/chromeos/login/user_manager.h" @@ -103,6 +104,7 @@ void RegisterLocalState(PrefService* local_state) { BackgroundPageTracker::RegisterPrefs(local_state); NotificationUIManager::RegisterPrefs(local_state); #if defined(OS_CHROMEOS) + chromeos::AudioMixerAlsa::RegisterPrefs(local_state); chromeos::UserManager::RegisterPrefs(local_state); chromeos::UserCrosSettingsProvider::RegisterPrefs(local_state); WizardController::RegisterPrefs(local_state); diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 67d8b0a..b5c04be 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -267,6 +267,12 @@ const char kTLS1Enabled[] = "ssl.tls1.enabled"; #endif #if defined(OS_CHROMEOS) +// An integer pref to initially mute volume if 1. +const char kAudioMute[] = "settings.audio.mute"; + +// A double pref to set initial volume. +const char kAudioVolume[] = "settings.audio.volume"; + // A boolean pref set to true if TapToClick is being done in browser. const char kTapToClickEnabled[] = "settings.touchpad.enable_tap_to_click"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 8cc39ce..930e69a 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -98,6 +98,8 @@ extern const char kSSL3Enabled[]; extern const char kTLS1Enabled[]; #endif #if defined(OS_CHROMEOS) +extern const char kAudioMute[]; +extern const char kAudioVolume[]; extern const char kTapToClickEnabled[]; extern const char kTouchpadSensitivity[]; extern const char kLanguageCurrentInputMethod[]; |