blob: 9124baf004f5ecb22284370ccded7773c0500ce1 [file] [log] [blame]
// Copyright (c) 2012 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/audio_handler.h"
#include <algorithm>
#include <cmath>
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "chrome/browser/browser_process.h"
#if defined(USE_CRAS)
#include "chrome/browser/chromeos/audio/audio_mixer_cras.h"
#else
#include "chrome/browser/chromeos/audio/audio_mixer_alsa.h"
#endif
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
using std::max;
using std::min;
namespace chromeos {
namespace {
// Default value for the volume pref, as a percent in the range [0.0, 100.0].
const double kDefaultVolumePercent = 75.0;
// Values used for muted preference.
const int kPrefMuteOff = 0;
const int kPrefMuteOn = 1;
static AudioHandler* g_audio_handler = NULL;
} // namespace
// static
void AudioHandler::Initialize() {
CHECK(!g_audio_handler);
#if defined(USE_CRAS)
g_audio_handler = new AudioHandler(new AudioMixerCras());
#else
g_audio_handler = new AudioHandler(new AudioMixerAlsa());
#endif
}
// static
void AudioHandler::Shutdown() {
// We may call Shutdown without calling Initialize, e.g. if we exit early.
if (g_audio_handler) {
delete g_audio_handler;
g_audio_handler = NULL;
}
}
// static
void AudioHandler::InitializeForTesting(AudioMixer* mixer) {
CHECK(!g_audio_handler);
g_audio_handler = new AudioHandler(mixer);
}
// static
AudioHandler* AudioHandler::GetInstance() {
VLOG_IF(1, !g_audio_handler)
<< "AudioHandler::GetInstance() called with NULL global instance.";
return g_audio_handler;
}
// static
void AudioHandler::RegisterPrefs(PrefService* local_state) {
if (!local_state->FindPreference(prefs::kAudioVolumePercent))
local_state->RegisterDoublePref(prefs::kAudioVolumePercent,
kDefaultVolumePercent,
PrefService::UNSYNCABLE_PREF);
if (!local_state->FindPreference(prefs::kAudioMute))
local_state->RegisterIntegerPref(prefs::kAudioMute,
kPrefMuteOff,
PrefService::UNSYNCABLE_PREF);
// Register the prefs backing the audio muting policies.
local_state->RegisterBooleanPref(prefs::kAudioOutputAllowed,
true,
PrefService::UNSYNCABLE_PREF);
local_state->RegisterBooleanPref(prefs::kAudioCaptureAllowed,
true,
PrefService::UNSYNCABLE_PREF);
// Register the old decibel-based pref so we can clear it.
// TODO(derat): Remove this after R20: http://crbug.com/112039
if (!local_state->FindPreference(prefs::kAudioVolumeDb))
local_state->RegisterDoublePref(prefs::kAudioVolumeDb,
0,
PrefService::UNSYNCABLE_PREF);
local_state->ClearPref(prefs::kAudioVolumeDb);
local_state->UnregisterPreference(prefs::kAudioVolumeDb);
}
double AudioHandler::GetVolumePercent() {
return mixer_->GetVolumePercent();
}
void AudioHandler::SetVolumePercent(double volume_percent) {
volume_percent = min(max(volume_percent, 0.0), 100.0);
if (IsMuted() && volume_percent > 0.0)
SetMuted(false);
mixer_->SetVolumePercent(volume_percent);
local_state_->SetDouble(prefs::kAudioVolumePercent, volume_percent);
FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnVolumeChanged());
}
void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) {
SetVolumePercent(mixer_->GetVolumePercent() + adjust_by_percent);
}
bool AudioHandler::IsMuted() {
return mixer_->IsMuted();
}
void AudioHandler::SetMuted(bool mute) {
if (!mixer_->IsMuteLocked()) {
mixer_->SetMuted(mute);
local_state_->SetInteger(prefs::kAudioMute,
mute ? kPrefMuteOn : kPrefMuteOff);
FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnMuteToggled());
}
}
bool AudioHandler::IsCaptureMuted() {
return mixer_->IsCaptureMuted();
}
void AudioHandler::SetCaptureMuted(bool mute) {
if (!mixer_->IsCaptureMuteLocked())
mixer_->SetCaptureMuted(mute);
}
void AudioHandler::AddVolumeObserver(VolumeObserver* observer) {
volume_observers_.AddObserver(observer);
}
void AudioHandler::RemoveVolumeObserver(VolumeObserver* observer) {
volume_observers_.RemoveObserver(observer);
}
void AudioHandler::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type == chrome::NOTIFICATION_PREF_CHANGED) {
std::string* pref_name = content::Details<std::string>(details).ptr();
if (*pref_name == prefs::kAudioOutputAllowed ||
*pref_name == prefs::kAudioCaptureAllowed) {
ApplyAudioPolicy();
}
} else {
NOTREACHED() << "Unexpected notification type : " << type;
}
}
AudioHandler::AudioHandler(AudioMixer* mixer)
: mixer_(mixer),
local_state_(g_browser_process->local_state()) {
InitializePrefObservers();
ApplyAudioPolicy();
mixer_->SetVolumePercent(local_state_->GetDouble(prefs::kAudioVolumePercent));
mixer_->Init();
}
AudioHandler::~AudioHandler() {
mixer_.reset();
};
void AudioHandler::InitializePrefObservers() {
pref_change_registrar_.Init(local_state_);
pref_change_registrar_.Add(prefs::kAudioOutputAllowed, this);
pref_change_registrar_.Add(prefs::kAudioCaptureAllowed, this);
}
void AudioHandler::ApplyAudioPolicy() {
mixer_->SetMuteLocked(false);
if (local_state_->GetBoolean(prefs::kAudioOutputAllowed)) {
mixer_->SetMuted(
local_state_->GetInteger(prefs::kAudioMute) == kPrefMuteOn);
} else {
mixer_->SetMuted(true);
mixer_->SetMuteLocked(true);
}
FOR_EACH_OBSERVER(VolumeObserver, volume_observers_, OnMuteToggled());
mixer_->SetCaptureMuteLocked(false);
if (local_state_->GetBoolean(prefs::kAudioCaptureAllowed)) {
mixer_->SetCaptureMuted(false);
} else {
mixer_->SetCaptureMuted(true);
mixer_->SetCaptureMuteLocked(true);
}
}
} // namespace chromeos