summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chromeos/audio_handler.cc123
-rw-r--r--chrome/browser/chromeos/audio_handler.h32
-rw-r--r--chrome/browser/chromeos/audio_mixer.h67
-rw-r--r--chrome/browser/chromeos/audio_mixer_alsa.cc366
-rw-r--r--chrome/browser/chromeos/audio_mixer_alsa.h100
-rw-r--r--chrome/browser/chromeos/audio_mixer_pulse.cc (renamed from chrome/browser/chromeos/pulse_audio_mixer.cc)116
-rw-r--r--chrome/browser/chromeos/audio_mixer_pulse.h (renamed from chrome/browser/chromeos/pulse_audio_mixer.h)66
-rw-r--r--chrome/chrome_browser.gypi7
8 files changed, 734 insertions, 143 deletions
diff --git a/chrome/browser/chromeos/audio_handler.cc b/chrome/browser/chromeos/audio_handler.cc
index f8ad726..7e5d4b9 100644
--- a/chrome/browser/chromeos/audio_handler.cc
+++ b/chrome/browser/chromeos/audio_handler.cc
@@ -8,13 +8,14 @@
#include "base/logging.h"
#include "base/singleton.h"
-#include "chrome/browser/chromeos/pulse_audio_mixer.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/chromeos/audio_mixer_alsa.h"
+#include "chrome/browser/chromeos/audio_mixer_pulse.h"
namespace chromeos {
namespace {
-const double kSilenceDb = -200.0;
const double kMinVolumeDb = -90.0;
// Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some
// in case sounds or their setup is too quiet for them.
@@ -27,10 +28,8 @@ const int kMaxReconnectTries = 4;
} // namespace
-// This class will set volume using PulseAudio to adjust volume and mute, and
-// handles the volume level logic.
-
-// TODO(davej): Serialize volume/mute for next startup?
+// This class will set volume using PulseAudio or ALSA to adjust volume and
+// mute, and handles the volume level logic.
double AudioHandler::GetVolumePercent() {
if (!VerifyMixerConnection())
@@ -40,7 +39,7 @@ double AudioHandler::GetVolumePercent() {
}
// Set volume using our internal 0-100% range. Notice 0% is a special case of
-// silence, so we set the mixer volume to kSilenceDb instead of kMinVolumeDb.
+// silence, so we set the mixer volume to kSilenceDb instead of min_volume_db_.
void AudioHandler::SetVolumePercent(double volume_percent) {
if (!VerifyMixerConnection())
return;
@@ -48,7 +47,7 @@ void AudioHandler::SetVolumePercent(double volume_percent) {
double vol_db;
if (volume_percent <= 0)
- vol_db = kSilenceDb;
+ vol_db = AudioMixer::kSilenceDb;
else
vol_db = PercentToVolumeDb(volume_percent);
@@ -72,7 +71,7 @@ void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) {
double new_volume;
if (pct <= 0.1)
- new_volume = kSilenceDb;
+ new_volume = AudioMixer::kSilenceDb;
else
new_volume = PercentToVolumeDb(pct);
@@ -90,24 +89,78 @@ bool AudioHandler::IsMute() {
void AudioHandler::SetMute(bool do_mute) {
if (!VerifyMixerConnection())
return;
-
DVLOG(1) << "Setting Mute to " << do_mute;
-
mixer_->SetMute(do_mute);
}
+bool AudioHandler::TryToConnect(bool async) {
+ if (mixer_type_ == MIXER_TYPE_PULSEAUDIO) {
+ VLOG(1) << "Trying to connect to PulseAudio";
+ mixer_.reset(new AudioMixerPulse());
+ } else if (mixer_type_ == MIXER_TYPE_ALSA) {
+ VLOG(1) << "Trying to connect to ALSA";
+ mixer_.reset(new AudioMixerAlsa());
+ } else {
+ VLOG(1) << "Cannot find valid volume mixer";
+ mixer_.reset();
+ return false;
+ }
+
+ if (async) {
+ mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized));
+ } else {
+ if (!mixer_->InitSync()) {
+ VLOG(1) << "Unable to reconnect to Mixer";
+ return false;
+ }
+ }
+ return true;
+}
+
+void AudioHandler::UseNextMixer() {
+ if (mixer_type_ == MIXER_TYPE_PULSEAUDIO)
+ mixer_type_ = MIXER_TYPE_ALSA;
+ else
+ mixer_type_ = MIXER_TYPE_NONE;
+}
+
+static void ClipVolume(double* min_volume, double* max_volume) {
+ if (*min_volume < kMinVolumeDb)
+ *min_volume = kMinVolumeDb;
+ if (*max_volume > kMaxVolumeDb)
+ *max_volume = kMaxVolumeDb;
+}
+
void AudioHandler::OnMixerInitialized(bool success) {
connected_ = success;
DVLOG(1) << "OnMixerInitialized, success = " << success;
+
+ if (connected_) {
+ if (mixer_->GetVolumeLimits(&min_volume_db_, &max_volume_db_)) {
+ ClipVolume(&min_volume_db_, &max_volume_db_);
+ }
+ return;
+ }
+
+ VLOG(1) << "Unable to connect to mixer, trying next";
+ UseNextMixer();
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this, &AudioHandler::TryToConnect, true));
}
AudioHandler::AudioHandler()
: connected_(false),
- reconnect_tries_(0) {
- mixer_.reset(new PulseAudioMixer());
- if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) {
- LOG(ERROR) << "Unable to connect to PulseAudio";
- }
+ reconnect_tries_(0),
+ max_volume_db_(kMaxVolumeDb),
+ min_volume_db_(kMinVolumeDb),
+ mixer_type_(MIXER_TYPE_PULSEAUDIO) {
+
+ // Start trying to connect to mixers asychronously, starting with the current
+ // mixer_type_. If the connection fails, another TryToConnect() for the next
+ // mixer will be posted at that time.
+ TryToConnect(true);
}
AudioHandler::~AudioHandler() {
@@ -115,58 +168,62 @@ AudioHandler::~AudioHandler() {
};
bool AudioHandler::VerifyMixerConnection() {
- PulseAudioMixer::State mixer_state = mixer_->CheckState();
- if (mixer_state == PulseAudioMixer::READY)
+ if (mixer_ == NULL)
+ return false;
+
+ AudioMixer::State mixer_state = mixer_->GetState();
+ if (mixer_state == AudioMixer::READY)
return true;
if (connected_) {
// Something happened and the mixer is no longer valid after having been
// initialized earlier.
connected_ = false;
- LOG(ERROR) << "Lost connection to PulseAudio";
+ LOG(ERROR) << "Lost connection to mixer";
} else {
LOG(ERROR) << "Mixer not valid";
}
- if ((mixer_state == PulseAudioMixer::INITIALIZING) ||
- (mixer_state == PulseAudioMixer::SHUTTING_DOWN))
+ if ((mixer_state == AudioMixer::INITIALIZING) ||
+ (mixer_state == AudioMixer::SHUTTING_DOWN))
return false;
if (reconnect_tries_ < kMaxReconnectTries) {
reconnect_tries_++;
- VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/"
+ VLOG(1) << "Re-connecting to mixer attempt " << reconnect_tries_ << "/"
<< kMaxReconnectTries;
- mixer_.reset(new PulseAudioMixer());
- connected_ = mixer_->InitSync();
+
+ connected_ = TryToConnect(false);
+
if (connected_) {
reconnect_tries_ = 0;
return true;
}
- LOG(ERROR) << "Unable to re-connect to PulseAudio";
+ LOG(ERROR) << "Unable to re-connect to mixer";
}
return false;
}
// VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us
// complete control over how the 0 to 100% range is mapped to actual loudness.
-// Volume range is from kMinVolumeDb at just above 0% to kMaxVolumeDb at 100%
-// with a special case at 0% which maps to kSilenceDb.
+// Volume range is from min_volume_db_ at just above 0% to max_volume_db_
+// at 100% with a special case at 0% which maps to kSilenceDb.
//
// The mapping is confined to these two functions to make it easy to adjust and
// have everything else just work. The range is biased to give finer resolution
// in the higher volumes if kVolumeBias is less than 1.0.
// static
-double AudioHandler::VolumeDbToPercent(double volume_db) {
- if (volume_db < kMinVolumeDb)
+double AudioHandler::VolumeDbToPercent(double volume_db) const {
+ if (volume_db < min_volume_db_)
return 0;
- return 100.0 * pow((volume_db - kMinVolumeDb) /
- (kMaxVolumeDb - kMinVolumeDb), 1/kVolumeBias);
+ return 100.0 * pow((volume_db - min_volume_db_) /
+ (max_volume_db_ - min_volume_db_), 1/kVolumeBias);
}
// static
-double AudioHandler::PercentToVolumeDb(double volume_percent) {
+double AudioHandler::PercentToVolumeDb(double volume_percent) const {
return pow(volume_percent / 100.0, kVolumeBias) *
- (kMaxVolumeDb - kMinVolumeDb) + kMinVolumeDb;
+ (max_volume_db_ - min_volume_db_) + min_volume_db_;
}
// static
diff --git a/chrome/browser/chromeos/audio_handler.h b/chrome/browser/chromeos/audio_handler.h
index 59e9d70..b835f5a 100644
--- a/chrome/browser/chromeos/audio_handler.h
+++ b/chrome/browser/chromeos/audio_handler.h
@@ -8,12 +8,13 @@
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
+#include "base/threading/thread.h"
template <typename T> struct DefaultSingletonTraits;
namespace chromeos {
-class PulseAudioMixer;
+class AudioMixer;
class AudioHandler {
public:
@@ -30,7 +31,7 @@ class AudioHandler {
// as the percentage gets lower, and then switches to silence at 0%.
void SetVolumePercent(double volume_percent);
- // Adust volume up (positive percentage) or down (negative percentage),
+ // Adjust volume up (positive percentage) or down (negative percentage),
// capping at 100%. GetVolumePercent() will be accurate after this
// blocking call.
void AdjustVolumeByPercent(double adjust_by_percent);
@@ -43,10 +44,20 @@ class AudioHandler {
void SetMute(bool do_mute);
private:
+ enum MixerType {
+ MIXER_TYPE_PULSEAUDIO = 0,
+ MIXER_TYPE_ALSA,
+ MIXER_TYPE_NONE,
+ };
+
// Defines the delete on exit Singleton traits we like. Best to have this
// and constructor/destructor private as recommended for Singletons.
friend struct DefaultSingletonTraits<AudioHandler>;
+ // Connect to the current mixer_type_.
+ bool TryToConnect(bool async);
+ void UseNextMixer();
+
void OnMixerInitialized(bool success);
AudioHandler();
@@ -54,17 +65,28 @@ class AudioHandler {
bool VerifyMixerConnection();
// Conversion between our internal scaling (0-100%) and decibels.
- static double VolumeDbToPercent(double volume_db);
- static double PercentToVolumeDb(double volume_percent);
+ double VolumeDbToPercent(double volume_db) const;
+ double PercentToVolumeDb(double volume_percent) const;
+
+ scoped_ptr<AudioMixer> mixer_;
- scoped_ptr<PulseAudioMixer> mixer_;
bool connected_;
int reconnect_tries_;
+ // The min and max volume in decibels, limited to the maximum range of the
+ // audio system being used.
+ double max_volume_db_;
+ double min_volume_db_;
+
+ // Which mixer is being used, PulseAudio, ALSA or none.
+ MixerType mixer_type_;
+
DISALLOW_COPY_AND_ASSIGN(AudioHandler);
};
} // namespace chromeos
+DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::AudioHandler);
+
#endif // CHROME_BROWSER_CHROMEOS_AUDIO_HANDLER_H_
diff --git a/chrome/browser/chromeos/audio_mixer.h b/chrome/browser/chromeos/audio_mixer.h
new file mode 100644
index 0000000..b77e827
--- /dev/null
+++ b/chrome/browser/chromeos/audio_mixer.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_H_
+#define CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+
+namespace chromeos {
+
+class AudioMixer {
+ public:
+ // Approximation of pure silence expressed in decibels.
+ static const double kSilenceDb = -200.0;
+
+ enum State {
+ UNINITIALIZED = 0,
+ INITIALIZING,
+ READY,
+ SHUTTING_DOWN,
+ IN_ERROR,
+ };
+
+ AudioMixer() {}
+ virtual ~AudioMixer() {}
+
+ // Non-Blocking call. Connect to Mixer, find a default device, then call the
+ // callback when complete with success code.
+ typedef Callback1<bool>::Type InitDoneCallback;
+ virtual void Init(InitDoneCallback* callback) = 0;
+
+ // Call may block. Mixer will be connected before returning, unless error.
+ virtual bool InitSync() = 0;
+
+ // Call may block. Returns a default of kSilenceDb on error.
+ virtual double GetVolumeDb() const = 0;
+
+ // Non-Blocking call. Returns the actual volume limits possible according
+ // to the mixer. Limits are left unchanged on error
+ virtual bool GetVolumeLimits(double* vol_min, double* vol_max) = 0;
+
+ // Non-blocking call.
+ virtual void SetVolumeDb(double vol_db) = 0;
+
+ // Call may block. Gets the mute state of the default device (true == mute).
+ // Returns a default of false on error.
+ virtual bool IsMute() const = 0;
+
+ // Non-Blocking call.
+ virtual void SetMute(bool mute) = 0;
+
+ // Returns READY if we have a valid working connection to the Mixer.
+ // This can return IN_ERROR if we lose the connection, even after an original
+ // successful init. Non-blocking call.
+ virtual State GetState() const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AudioMixer);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_H_
+
diff --git a/chrome/browser/chromeos/audio_mixer_alsa.cc b/chrome/browser/chromeos/audio_mixer_alsa.cc
new file mode 100644
index 0000000..7f96fa4
--- /dev/null
+++ b/chrome/browser/chromeos/audio_mixer_alsa.cc
@@ -0,0 +1,366 @@
+// Copyright (c) 2010 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_mixer_alsa.h"
+
+#include <alsa/asoundlib.h>
+
+#include "base/logging.h"
+#include "base/task.h"
+
+namespace chromeos {
+
+// Connect to the ALSA mixer using their simple element API. Init is performed
+// asynchronously on the worker thread.
+//
+// To get a wider range and finer control over volume levels, first the Master
+// level is set, then if the PCM element exists, the total level is refined by
+// 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 {
+
+const char* kMasterVolume = "Master";
+const char* kPCMVolume = "PCM";
+const double kDefaultMinVolume = -90.0;
+const double kDefaultMaxVolume = 0.0;
+
+} // namespace
+
+AudioMixerAlsa::AudioMixerAlsa()
+ : min_volume_(kDefaultMinVolume),
+ max_volume_(kDefaultMaxVolume),
+ save_volume_(0),
+ mixer_state_(UNINITIALIZED),
+ alsa_mixer_(NULL),
+ elem_master_(NULL),
+ elem_pcm_(NULL) {
+}
+
+AudioMixerAlsa::~AudioMixerAlsa() {
+ FreeAlsaMixer();
+ if (thread_ != NULL) {
+ thread_->Stop();
+ thread_.reset();
+ }
+}
+
+void AudioMixerAlsa::Init(InitDoneCallback* callback) {
+ DCHECK(callback);
+ if (!InitThread()) {
+ callback->Run(false);
+ delete callback;
+ return;
+ }
+
+ // Post the task of starting up, which can block for 200-500ms,
+ // so best not to do it on the caller's thread.
+ thread_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioMixerAlsa::DoInit, callback));
+}
+
+bool AudioMixerAlsa::InitSync() {
+ if (!InitThread())
+ return false;
+ return InitializeAlsaMixer();
+}
+
+double AudioMixerAlsa::GetVolumeDb() const {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != READY)
+ return kSilenceDb;
+
+ return DoGetVolumeDb_Locked();
+}
+
+bool AudioMixerAlsa::GetVolumeLimits(double* vol_min, double* vol_max) {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != READY)
+ return false;
+ if (vol_min)
+ *vol_min = min_volume_;
+ if (vol_max)
+ *vol_max = max_volume_;
+ return true;
+}
+
+void AudioMixerAlsa::SetVolumeDb(double vol_db) {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != READY)
+ return;
+ DoSetVolumeDb_Locked(vol_db);
+}
+
+bool AudioMixerAlsa::IsMute() const {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != READY)
+ return false;
+ return GetElementMuted_Locked(elem_master_);
+}
+
+void AudioMixerAlsa::SetMute(bool mute) {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != READY)
+ return;
+
+ // 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.
+
+ bool old_value = GetElementMuted_Locked(elem_master_);
+
+ if (old_value != mute) {
+ if (mute) {
+ save_volume_ = DoGetVolumeDb_Locked();
+ DoSetVolumeDb_Locked(min_volume_);
+ } else {
+ DoSetVolumeDb_Locked(save_volume_);
+ }
+ }
+
+ SetElementMuted_Locked(elem_master_, mute);
+ if (elem_pcm_)
+ SetElementMuted_Locked(elem_pcm_, mute);
+}
+
+AudioMixer::State AudioMixerAlsa::GetState() const {
+ AutoLock lock(mixer_state_lock_);
+ // If we think it's ready, verify it is actually so.
+ if ((mixer_state_ == READY) && (alsa_mixer_ == NULL))
+ mixer_state_ = IN_ERROR;
+ return mixer_state_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private functions follow
+
+void AudioMixerAlsa::DoInit(InitDoneCallback* callback) {
+ bool success = InitializeAlsaMixer();
+
+ if (callback) {
+ callback->Run(success);
+ delete callback;
+ }
+}
+
+bool AudioMixerAlsa::InitThread() {
+ AutoLock lock(mixer_state_lock_);
+
+ if (mixer_state_ != UNINITIALIZED)
+ return false;
+
+ if (thread_ == NULL) {
+ thread_.reset(new base::Thread("AudioMixerAlsa"));
+ if (!thread_->Start()) {
+ thread_.reset();
+ return false;
+ }
+ }
+
+ mixer_state_ = INITIALIZING;
+ return true;
+}
+
+bool AudioMixerAlsa::InitializeAlsaMixer() {
+ AutoLock lock(mixer_state_lock_);
+ if (mixer_state_ != INITIALIZING)
+ return false;
+
+ int err;
+ snd_mixer_t* handle = NULL;
+ const char* card = "default";
+
+ if ((err = snd_mixer_open(&handle, 0)) < 0) {
+ LOG(ERROR) << "ALSA mixer " << card << " open error: " << snd_strerror(err);
+ return false;
+ }
+
+ if ((err = snd_mixer_attach(handle, card)) < 0) {
+ LOG(ERROR) << "ALSA Attach to card " << card << " failed: "
+ << snd_strerror(err);
+ snd_mixer_close(handle);
+ return false;
+ }
+
+ if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
+ LOG(ERROR) << "ALSA mixer register error: " << snd_strerror(err);
+ snd_mixer_close(handle);
+ return false;
+ }
+
+ if ((err = snd_mixer_load(handle)) < 0) {
+ LOG(ERROR) << "ALSA mixer " << card << " load error: %s"
+ << snd_strerror(err);
+ snd_mixer_close(handle);
+ return false;
+ }
+
+ VLOG(1) << "Opened ALSA mixer " << card << " OK";
+
+ elem_master_ = FindElementWithName_Locked(handle, kMasterVolume);
+ if (elem_master_) {
+ alsa_long_t long_lo, long_hi;
+ snd_mixer_selem_get_playback_dB_range(elem_master_, &long_lo, &long_hi);
+ min_volume_ = static_cast<double>(long_lo) / 100.0;
+ max_volume_ = static_cast<double>(long_hi) / 100.0;
+ } else {
+ LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card;
+ snd_mixer_close(handle);
+ return false;
+ }
+
+ elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume);
+ if (elem_pcm_) {
+ alsa_long_t long_lo, long_hi;
+ snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi);
+ min_volume_ += static_cast<double>(long_lo) / 100.0;
+ max_volume_ += static_cast<double>(long_hi) / 100.0;
+ }
+
+ VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to "
+ << max_volume_ << " dB";
+
+ alsa_mixer_ = handle;
+ mixer_state_ = READY;
+ return true;
+}
+
+void AudioMixerAlsa::FreeAlsaMixer() {
+ AutoLock lock(mixer_state_lock_);
+ mixer_state_ = SHUTTING_DOWN;
+ if (alsa_mixer_) {
+ snd_mixer_close(alsa_mixer_);
+ alsa_mixer_ = NULL;
+ }
+}
+
+double AudioMixerAlsa::DoGetVolumeDb_Locked() const {
+ double vol_total = 0.0;
+ GetElementVolume_Locked(elem_master_, &vol_total);
+
+ double vol_pcm = 0.0;
+ if (elem_pcm_ && (GetElementVolume_Locked(elem_pcm_, &vol_pcm)))
+ vol_total += vol_pcm;
+
+ return vol_total;
+}
+
+void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) {
+ double actual_vol = 0.0;
+
+ // 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);
+ } else {
+ SetElementVolume_Locked(elem_master_, vol_db, NULL, 0.5f);
+ }
+}
+
+snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName_Locked(
+ snd_mixer_t* handle,
+ const char* element_name) const {
+ 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'
+ if (snd_mixer_selem_id_malloc(&sid))
+ return NULL;
+
+ snd_mixer_selem_id_set_index(sid, 0);
+ snd_mixer_selem_id_set_name(sid, element_name);
+ snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
+ if (!elem) {
+ LOG(ERROR) << "ALSA unable to find simple control "
+ << snd_mixer_selem_id_get_name(sid);
+ }
+
+ snd_mixer_selem_id_free(sid);
+ return elem;
+}
+
+bool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem,
+ double* current_vol) const {
+ alsa_long_t long_vol = 0;
+ snd_mixer_selem_get_playback_dB(elem,
+ static_cast<snd_mixer_selem_channel_id_t>(0),
+ &long_vol);
+ *current_vol = static_cast<double>(long_vol) / 100.0;
+
+ return true;
+}
+
+bool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem,
+ double new_vol,
+ double* actual_vol,
+ double rounding_bias) {
+ alsa_long_t vol_lo = 0;
+ alsa_long_t vol_hi = 0;
+ snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi);
+ alsa_long_t vol_range = vol_hi - vol_lo;
+ if (vol_range <= 0)
+ return false;
+
+ alsa_long_t db_lo_int = 0;
+ alsa_long_t db_hi_int = 0;
+ snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int);
+ double db_lo = static_cast<double>(db_lo_int) / 100.0;
+ double db_hi = static_cast<double>(db_hi_int) / 100.0;
+ double db_step = static_cast<double>(db_hi - db_lo) / vol_range;
+ if (db_step <= 0.0)
+ return false;
+
+ if (new_vol < db_lo)
+ new_vol = db_lo;
+
+ alsa_long_t value = static_cast<alsa_long_t>(rounding_bias +
+ (new_vol - db_lo) / db_step) + vol_lo;
+ snd_mixer_selem_set_playback_volume_all(elem, value);
+
+ VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem)
+ << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo
+ << " dB";
+
+ if (actual_vol) {
+ alsa_long_t volume;
+ snd_mixer_selem_get_playback_volume(
+ elem,
+ static_cast<snd_mixer_selem_channel_id_t>(0),
+ &volume);
+ *actual_vol = db_lo + (volume - vol_lo) * db_step;
+
+ VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem)
+ << " now " << *actual_vol << " dB";
+ }
+ return true;
+}
+
+bool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const {
+ int enabled;
+ snd_mixer_selem_get_playback_switch(
+ elem,
+ static_cast<snd_mixer_selem_channel_id_t>(0),
+ &enabled);
+ return (enabled) ? false : true;
+}
+
+void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) {
+ int enabled = mute ? 0 : 1;
+ snd_mixer_selem_set_playback_switch_all(elem, enabled);
+
+ VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem)
+ << " to " << enabled;
+}
+
+} // namespace chromeos
+
diff --git a/chrome/browser/chromeos/audio_mixer_alsa.h b/chrome/browser/chromeos/audio_mixer_alsa.h
new file mode 100644
index 0000000..d365a22
--- /dev/null
+++ b/chrome/browser/chromeos/audio_mixer_alsa.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_ALSA_H_
+#define CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_ALSA_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/chromeos/audio_mixer.h"
+
+struct _snd_mixer_elem;
+struct _snd_mixer;
+
+namespace chromeos {
+
+class AudioMixerAlsa : public AudioMixer {
+ public:
+ AudioMixerAlsa();
+ virtual ~AudioMixerAlsa();
+
+ // Implementation of AudioMixer
+ virtual void Init(InitDoneCallback* callback);
+ virtual bool InitSync();
+ virtual double GetVolumeDb() const;
+ virtual bool GetVolumeLimits(double* vol_min, double* vol_max);
+ virtual void SetVolumeDb(double vol_db);
+ virtual bool IsMute() const;
+ virtual void SetMute(bool mute);
+ virtual State GetState() const;
+
+ private:
+ // Called to do initialization in background from worker thread.
+ void DoInit(InitDoneCallback* callback);
+
+ // Helper function to just get our message loop thread going.
+ bool InitThread();
+
+ // 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();
+
+ // All these internal volume commands must be called with the lock held.
+ double DoGetVolumeDb_Locked() const;
+ void DoSetVolumeDb_Locked(double vol_db);
+
+ _snd_mixer_elem* FindElementWithName_Locked(_snd_mixer* handle,
+ const char* element_name) const;
+
+ bool GetElementVolume_Locked(_snd_mixer_elem* elem,
+ double* current_vol) const;
+
+ // Since volume is done in steps, we may not get the exact volume asked for,
+ // so actual_vol will contain the true volume that was set. This information
+ // can be used to further refine the volume by adjust a different mixer
+ // element. The rounding_bias is added in before rounding to the nearest
+ // volume step (use 0.5 to round to nearest).
+ bool SetElementVolume_Locked(_snd_mixer_elem* elem,
+ double new_vol,
+ double* actual_vol,
+ double rounding_bias);
+
+ // In ALSA, the mixer element's 'switch' is turned off to mute.
+ bool GetElementMuted_Locked(_snd_mixer_elem* elem) const;
+ void SetElementMuted_Locked(_snd_mixer_elem* elem, bool mute);
+
+ // Volume range limits are computed once during InitializeAlsaMixer.
+ double min_volume_;
+ double max_volume_;
+
+ // Muting is done by setting volume to minimum, so we must save the original.
+ // This is the only state information kept in this object. In some cases,
+ // ALSA can report it has a volume switch and we can turn it off, but it has
+ // no effect.
+ double save_volume_;
+
+ mutable Lock mixer_state_lock_;
+ mutable State mixer_state_;
+
+ // Cached contexts for use in ALSA calls.
+ _snd_mixer* alsa_mixer_;
+ _snd_mixer_elem* elem_master_;
+ _snd_mixer_elem* elem_pcm_;
+
+ scoped_ptr<base::Thread> thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioMixerAlsa);
+};
+
+} // namespace chromeos
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::AudioMixerAlsa);
+
+#endif // CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_ALSA_H_
+
diff --git a/chrome/browser/chromeos/pulse_audio_mixer.cc b/chrome/browser/chromeos/audio_mixer_pulse.cc
index a58548e..0001548 100644
--- a/chrome/browser/chromeos/pulse_audio_mixer.cc
+++ b/chrome/browser/chromeos/audio_mixer_pulse.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/chromeos/pulse_audio_mixer.h"
+#include "chrome/browser/chromeos/audio_mixer_pulse.h"
#include <pulse/pulseaudio.h>
@@ -26,9 +26,14 @@ namespace {
const int kInvalidDeviceId = -1;
+const double kMinVolumeDb = -90.0;
+// Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some
+// in case sounds or their setup is too quiet for them.
+const double kMaxVolumeDb = 6.0;
+
// Used for passing custom data to the PulseAudio callbacks.
struct CallbackWrapper {
- PulseAudioMixer* instance;
+ AudioMixerPulse* instance;
bool done;
void* userdata;
};
@@ -37,66 +42,66 @@ struct CallbackWrapper {
// AudioInfo contains all the values we care about when getting info for a
// Sink (output device) used by GetAudioInfo().
-struct PulseAudioMixer::AudioInfo {
+struct AudioMixerPulse::AudioInfo {
pa_cvolume cvolume;
bool muted;
};
-PulseAudioMixer::PulseAudioMixer()
+AudioMixerPulse::AudioMixerPulse()
: device_id_(kInvalidDeviceId),
last_channels_(0),
mainloop_lock_count_(0),
- mixer_state_lock_(),
mixer_state_(UNINITIALIZED),
pa_context_(NULL),
- pa_mainloop_(NULL),
- thread_(NULL) {
+ pa_mainloop_(NULL) {
}
-PulseAudioMixer::~PulseAudioMixer() {
+AudioMixerPulse::~AudioMixerPulse() {
PulseAudioFree();
- thread_->Stop();
- thread_.reset();
+ if (thread_ != NULL) {
+ thread_->Stop();
+ thread_.reset();
+ }
}
-bool PulseAudioMixer::Init(InitDoneCallback* callback) {
- if (!InitThread())
- return false;
+void AudioMixerPulse::Init(InitDoneCallback* callback) {
+ DCHECK(callback);
+ if (!InitThread()) {
+ callback->Run(false);
+ delete callback;
+ return;
+ }
// Post the task of starting up, which can block for 200-500ms,
// so best not to do it on the caller's thread.
thread_->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &PulseAudioMixer::DoInit, callback));
- return true;
+ NewRunnableMethod(this, &AudioMixerPulse::DoInit, callback));
}
-bool PulseAudioMixer::InitSync() {
+bool AudioMixerPulse::InitSync() {
if (!InitThread())
return false;
return PulseAudioInit();
}
-double PulseAudioMixer::GetVolumeDb() const {
+double AudioMixerPulse::GetVolumeDb() const {
if (!MainloopLockIfReady())
- return pa_sw_volume_to_dB(0); // this returns -inf.
+ return AudioMixer::kSilenceDb;
AudioInfo data;
GetAudioInfo(&data);
MainloopUnlock();
return pa_sw_volume_to_dB(data.cvolume.values[0]);
}
-bool PulseAudioMixer::GetVolumeDbAsync(GetVolumeCallback* callback,
- void* user) {
- if (CheckState() != READY)
- return false;
- thread_->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this,
- &PulseAudioMixer::DoGetVolume,
- callback, user));
+bool AudioMixerPulse::GetVolumeLimits(double* vol_min, double* vol_max) {
+ if (vol_min)
+ *vol_min = kMinVolumeDb;
+ if (vol_max)
+ *vol_max = kMaxVolumeDb;
return true;
}
-void PulseAudioMixer::SetVolumeDb(double vol_db) {
+void AudioMixerPulse::SetVolumeDb(double vol_db) {
if (!MainloopLockIfReady())
return;
@@ -115,9 +120,10 @@ void PulseAudioMixer::SetVolumeDb(double vol_db) {
&cvolume, NULL, NULL);
pa_operation_unref(pa_op);
MainloopUnlock();
+ VLOG(1) << "Set volume to " << vol_db << " dB";
}
-bool PulseAudioMixer::IsMute() const {
+bool AudioMixerPulse::IsMute() const {
if (!MainloopLockIfReady())
return false;
AudioInfo data;
@@ -126,7 +132,7 @@ bool PulseAudioMixer::IsMute() const {
return data.muted;
}
-void PulseAudioMixer::SetMute(bool mute) {
+void AudioMixerPulse::SetMute(bool mute) {
if (!MainloopLockIfReady())
return;
pa_operation* pa_op;
@@ -134,9 +140,10 @@ void PulseAudioMixer::SetMute(bool mute) {
mute ? 1 : 0, NULL, NULL);
pa_operation_unref(pa_op);
MainloopUnlock();
+ VLOG(1) << "Set mute to " << mute;
}
-PulseAudioMixer::State PulseAudioMixer::CheckState() const {
+AudioMixer::State AudioMixerPulse::GetState() const {
AutoLock lock(mixer_state_lock_);
// If we think it's ready, verify it is actually so.
if ((mixer_state_ == READY) &&
@@ -148,25 +155,20 @@ PulseAudioMixer::State PulseAudioMixer::CheckState() const {
////////////////////////////////////////////////////////////////////////////////
// Private functions follow
-void PulseAudioMixer::DoInit(InitDoneCallback* callback) {
+void AudioMixerPulse::DoInit(InitDoneCallback* callback) {
bool success = PulseAudioInit();
callback->Run(success);
delete callback;
}
-void PulseAudioMixer::DoGetVolume(GetVolumeCallback* callback,
- void* user) {
- callback->Run(GetVolumeDb(), user);
- delete callback;
-}
-
-bool PulseAudioMixer::InitThread() {
+bool AudioMixerPulse::InitThread() {
AutoLock lock(mixer_state_lock_);
if (mixer_state_ != UNINITIALIZED)
return false;
+
if (thread_ == NULL) {
- thread_.reset(new base::Thread("PulseAudioMixer"));
+ thread_.reset(new base::Thread("AudioMixerPulse"));
if (!thread_->Start()) {
thread_.reset();
return false;
@@ -177,14 +179,14 @@ bool PulseAudioMixer::InitThread() {
}
// static
-void PulseAudioMixer::ConnectToPulseCallbackThunk(
+void AudioMixerPulse::ConnectToPulseCallbackThunk(
pa_context* context, void* userdata) {
CallbackWrapper* data =
static_cast<CallbackWrapper*>(userdata);
data->instance->OnConnectToPulseCallback(context, &data->done);
}
-void PulseAudioMixer::OnConnectToPulseCallback(
+void AudioMixerPulse::OnConnectToPulseCallback(
pa_context* context, bool* connect_done) {
pa_context_state_t state = pa_context_get_state(context);
if (state == PA_CONTEXT_READY ||
@@ -196,7 +198,7 @@ void PulseAudioMixer::OnConnectToPulseCallback(
}
}
-bool PulseAudioMixer::PulseAudioInit() {
+bool AudioMixerPulse::PulseAudioInit() {
pa_context_state_t state = PA_CONTEXT_FAILED;
{
@@ -298,13 +300,14 @@ bool PulseAudioMixer::PulseAudioInit() {
return false;
}
-void PulseAudioMixer::PulseAudioFree() {
+void AudioMixerPulse::PulseAudioFree() {
{
AutoLock lock(mixer_state_lock_);
if (!pa_mainloop_)
mixer_state_ = UNINITIALIZED;
if ((mixer_state_ == UNINITIALIZED) || (mixer_state_ == SHUTTING_DOWN))
return;
+
// If still initializing on another thread, this will cause it to exit.
mixer_state_ = SHUTTING_DOWN;
}
@@ -329,7 +332,7 @@ void PulseAudioMixer::PulseAudioFree() {
}
}
-void PulseAudioMixer::CompleteOperation(pa_operation* pa_op,
+void AudioMixerPulse::CompleteOperation(pa_operation* pa_op,
bool* done) const {
// After starting any operation, this helper checks if it started OK, then
// waits for it to complete by iterating through the mainloop until the
@@ -348,7 +351,7 @@ void PulseAudioMixer::CompleteOperation(pa_operation* pa_op,
}
// Must be called with mainloop lock held
-void PulseAudioMixer::GetDefaultPlaybackDevice() {
+void AudioMixerPulse::GetDefaultPlaybackDevice() {
DCHECK_GT(mainloop_lock_count_, 0);
DCHECK(pa_context_);
DCHECK(pa_context_get_state(pa_context_) == PA_CONTEXT_READY);
@@ -362,7 +365,7 @@ void PulseAudioMixer::GetDefaultPlaybackDevice() {
return;
}
-void PulseAudioMixer::OnEnumerateDevices(const pa_sink_info* sink_info,
+void AudioMixerPulse::OnEnumerateDevices(const pa_sink_info* sink_info,
int eol, bool* done) {
if (device_id_ != kInvalidDeviceId)
return;
@@ -378,7 +381,7 @@ void PulseAudioMixer::OnEnumerateDevices(const pa_sink_info* sink_info,
}
// static
-void PulseAudioMixer::EnumerateDevicesCallback(pa_context* unused,
+void AudioMixerPulse::EnumerateDevicesCallback(pa_context* unused,
const pa_sink_info* sink_info,
int eol,
void* userdata) {
@@ -388,9 +391,9 @@ void PulseAudioMixer::EnumerateDevicesCallback(pa_context* unused,
}
// Must be called with lock held
-void PulseAudioMixer::GetAudioInfo(AudioInfo* info) const {
+void AudioMixerPulse::GetAudioInfo(AudioInfo* info) const {
DCHECK_GT(mainloop_lock_count_, 0);
- CallbackWrapper data = {const_cast<PulseAudioMixer*>(this), false, info};
+ CallbackWrapper data = {const_cast<AudioMixerPulse*>(this), false, info};
pa_operation* pa_op = pa_context_get_sink_info_by_index(pa_context_,
device_id_,
GetAudioInfoCallback,
@@ -399,7 +402,7 @@ void PulseAudioMixer::GetAudioInfo(AudioInfo* info) const {
}
// static
-void PulseAudioMixer::GetAudioInfoCallback(pa_context* unused,
+void AudioMixerPulse::GetAudioInfoCallback(pa_context* unused,
const pa_sink_info* sink_info,
int eol,
void* userdata) {
@@ -415,38 +418,39 @@ void PulseAudioMixer::GetAudioInfoCallback(pa_context* unused,
data->instance->MainloopSignal();
}
-inline void PulseAudioMixer::MainloopLock() const {
+inline void AudioMixerPulse::MainloopLock() const {
pa_threaded_mainloop_lock(pa_mainloop_);
++mainloop_lock_count_;
}
-inline void PulseAudioMixer::MainloopUnlock() const {
+inline void AudioMixerPulse::MainloopUnlock() const {
--mainloop_lock_count_;
pa_threaded_mainloop_unlock(pa_mainloop_);
}
// Must be called with the lock held.
-inline void PulseAudioMixer::MainloopWait() const {
+inline void AudioMixerPulse::MainloopWait() const {
DCHECK_GT(mainloop_lock_count_, 0);
pa_threaded_mainloop_wait(pa_mainloop_);
}
// Must be called with the lock held.
-inline void PulseAudioMixer::MainloopSignal() const {
+inline void AudioMixerPulse::MainloopSignal() const {
DCHECK_GT(mainloop_lock_count_, 0);
pa_threaded_mainloop_signal(pa_mainloop_, 0);
}
-inline bool PulseAudioMixer::MainloopSafeLock() const {
+inline bool AudioMixerPulse::MainloopSafeLock() const {
AutoLock lock(mixer_state_lock_);
if ((mixer_state_ == SHUTTING_DOWN) || (!pa_mainloop_))
return false;
+
pa_threaded_mainloop_lock(pa_mainloop_);
++mainloop_lock_count_;
return true;
}
-inline bool PulseAudioMixer::MainloopLockIfReady() const {
+inline bool AudioMixerPulse::MainloopLockIfReady() const {
AutoLock lock(mixer_state_lock_);
if (mixer_state_ != READY)
return false;
diff --git a/chrome/browser/chromeos/pulse_audio_mixer.h b/chrome/browser/chromeos/audio_mixer_pulse.h
index f01e738..8ce8274 100644
--- a/chrome/browser/chromeos/pulse_audio_mixer.h
+++ b/chrome/browser/chromeos/audio_mixer_pulse.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_CHROMEOS_PULSE_AUDIO_MIXER_H_
-#define CHROME_BROWSER_CHROMEOS_PULSE_AUDIO_MIXER_H_
+#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_PULSE_H_
+#define CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_PULSE_H_
#pragma once
#include "base/basictypes.h"
@@ -11,6 +11,7 @@
#include "base/lock.h"
#include "base/scoped_ptr.h"
#include "base/threading/thread.h"
+#include "chrome/browser/chromeos/audio_mixer.h"
struct pa_context;
struct pa_cvolume;
@@ -20,56 +21,27 @@ struct pa_sink_info;
namespace chromeos {
-class PulseAudioMixer {
+class AudioMixerPulse : public AudioMixer {
public:
- enum State {
- UNINITIALIZED = 0,
- INITIALIZING,
- READY,
- SHUTTING_DOWN,
- IN_ERROR
- };
- PulseAudioMixer();
- ~PulseAudioMixer();
+ AudioMixerPulse();
+ virtual ~AudioMixerPulse();
- // Non-blocking, connect to PulseAudio and find a default device, and call
- // callback when complete with success code.
- typedef Callback1<bool>::Type InitDoneCallback;
- bool Init(InitDoneCallback* callback);
-
- // Blocking init call guarantees PulseAudio is started before returning.
- bool InitSync();
-
- // Blocking call. Returns a default of -inf on error.
- double GetVolumeDb() const;
-
- // Non-blocking, volume sent in as first param to callback. The callback is
- // only called if the function returns true.
- typedef Callback2<double, void*>::Type GetVolumeCallback;
- bool GetVolumeDbAsync(GetVolumeCallback* callback, void* user);
-
- // Non-blocking call.
- void SetVolumeDb(double vol_db);
-
- // Gets the mute state of the default device (true == mute). Blocking call.
- // Returns a default of false on error.
- bool IsMute() const;
-
- // Non-Blocking call.
- void SetMute(bool mute);
-
- // Returns READY if we have a valid working connection to PulseAudio.
- // This can return IN_ERROR if we lose the connection, even after an original
- // successful init. Non-blocking call.
- State CheckState() const;
+ // Implementation of AudioMixer
+ virtual void Init(InitDoneCallback* callback);
+ virtual bool InitSync();
+ virtual double GetVolumeDb() const;
+ virtual bool GetVolumeLimits(double* vol_min, double* vol_max);
+ virtual void SetVolumeDb(double vol_db);
+ virtual bool IsMute() const;
+ virtual void SetMute(bool mute);
+ virtual State GetState() const;
private:
struct AudioInfo;
- // These are the tasks to be run in the background on the worker thread.
+ // The initialization task is run in the background on the worker thread.
void DoInit(InitDoneCallback* callback);
- void DoGetVolume(GetVolumeCallback* callback, void* user);
static void ConnectToPulseCallbackThunk(pa_context* c, void* userdata);
void OnConnectToPulseCallback(pa_context* c, bool* connect_done);
@@ -137,12 +109,12 @@ class PulseAudioMixer {
scoped_ptr<base::Thread> thread_;
- DISALLOW_COPY_AND_ASSIGN(PulseAudioMixer);
+ DISALLOW_COPY_AND_ASSIGN(AudioMixerPulse);
};
} // namespace chromeos
-DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::PulseAudioMixer);
+DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::AudioMixerPulse);
-#endif // CHROME_BROWSER_CHROMEOS_PULSE_AUDIO_MIXER_H_
+#endif // CHROME_BROWSER_CHROMEOS_AUDIO_MIXER_PULSE_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index d708af3..c0c00b7 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -404,6 +404,11 @@
'browser/chromeos/notifications/system_notification_factory.h',
'browser/chromeos/audio_handler.cc',
'browser/chromeos/audio_handler.h',
+ 'browser/chromeos/audio_mixer.h',
+ 'browser/chromeos/audio_mixer_alsa.cc',
+ 'browser/chromeos/audio_mixer_alsa.h',
+ 'browser/chromeos/audio_mixer_pulse.cc',
+ 'browser/chromeos/audio_mixer_pulse.h',
'browser/chromeos/boot_times_loader.cc',
'browser/chromeos/boot_times_loader.h',
'browser/chromeos/brightness_bubble.cc',
@@ -739,8 +744,6 @@
'browser/chromeos/proxy_config_service_impl.h',
'browser/chromeos/proxy_cros_settings_provider.cc',
'browser/chromeos/proxy_cros_settings_provider.h',
- 'browser/chromeos/pulse_audio_mixer.cc',
- 'browser/chromeos/pulse_audio_mixer.h',
'browser/chromeos/setting_level_bubble.cc',
'browser/chromeos/setting_level_bubble.h',
'browser/chromeos/setting_level_bubble_view.cc',