blob: c954d2ef3b258ac96ac00c0dd9207c54bffb59da [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.
#ifndef CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_
#define CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_
#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/browser/chromeos/audio/audio_mixer.h"
struct _snd_mixer_elem;
struct _snd_mixer;
namespace chromeos {
// Simple wrapper around ALSA's mixer functions.
// Interaction with ALSA takes place on a background thread.
class AudioMixerAlsa : public AudioMixer {
public:
AudioMixerAlsa();
virtual ~AudioMixerAlsa();
// AudioMixer implementation.
virtual void Init() OVERRIDE;
virtual double GetVolumePercent() OVERRIDE;
virtual void SetVolumePercent(double percent) OVERRIDE;
virtual bool IsMuted() OVERRIDE;
virtual void SetMuted(bool mute) OVERRIDE;
virtual bool IsMuteLocked() OVERRIDE;
virtual void SetMuteLocked(bool locked) OVERRIDE;
virtual bool IsCaptureMuted() OVERRIDE;
virtual void SetCaptureMuted(bool mute) OVERRIDE;
virtual bool IsCaptureMuteLocked() OVERRIDE;
virtual void SetCaptureMuteLocked(bool locked) OVERRIDE;
private:
// Tries to connect to ALSA. On failure, posts a delayed Connect() task to
// try again.
void Connect();
// Helper method called by Connect(). On success, initializes
// |min_volume_db_|, |max_volume_db_|, |alsa_mixer_|, and |element_*| and
// returns true.
bool ConnectInternal();
// Disconnects from ALSA if currently connected and signals
// |disconnected_event_|.
void Disconnect();
// Updates |alsa_mixer_| for current values of |volume_db_| and |is_muted_|.
// No-op if not connected.
void ApplyState();
// Finds the element named |element_name|. Returns NULL on failure.
_snd_mixer_elem* FindElementWithName(_snd_mixer* handle,
const std::string& element_name) const;
// Queries |element|'s current volume, copying it to |new_volume_db|.
// Returns true on success.
bool GetElementVolume(_snd_mixer_elem* element, double* current_volume_db);
// Sets |element|'s volume. Since volume is done in steps, we may not get the
// exact volume asked for. |rounding_bias| is added in before rounding to the
// nearest volume step (use 0.5 to round to nearest).
bool SetElementVolume(_snd_mixer_elem* element,
double new_volume_db,
double rounding_bias);
// Mutes or unmutes |element|.
void SetElementMuted(_snd_mixer_elem* element, bool mute);
// Converts between percentages (in the range [0.0, 100.0]) and decibels.
// |lock_| must be held.
double DbToPercent(double db) const;
double PercentToDb(double percent) const;
// Calls ApplyState on the proper thread only if no other call is currently
// pending.
void ApplyStateIfNeeded();
// Guards |min_volume_db_|, |max_volume_db_|, |volume_db_|, |is_muted_|,
// |is_mute_locked_|, |is_capture_muted_|, |is_capture_mute_locked_|,
// |apply_is_pending_|, and |initial_volume_percent_|.
base::Lock lock_;
// Volume range limits are computed once in ConnectInternal().
double min_volume_db_;
double max_volume_db_;
// Most recently-requested volume, in decibels. This variable is updated
// immediately by SetVolumePercent() (post-initialization); the actual mixer
// volume is updated later on |thread_| by ApplyState().
double volume_db_;
// Most recently-requested muting state.
bool is_muted_;
bool is_capture_muted_;
// If set to true the mute state of the respective channel is locked and can
// not be changed until it is unlocked. We don't have control over ALSA so
// other sound clients might unmute channels but we can do best effort to
// mimic CRAS's locking functionality.
bool is_mute_locked_;
bool is_capture_mute_locked_;
// Is there already a pending call to ApplyState() scheduled on |thread_|?
bool apply_is_pending_;
// Before |min_volume_db_| and |max_volume_db_| are fetched from ALSA, we
// can't convert percentages to decibels. The default initial volume and any
// subsequent requests we get early on are stored here and then applied during
// initialization. This variable is unused after initialization.
double initial_volume_percent_;
// Connection to ALSA. NULL if not connected.
_snd_mixer* alsa_mixer_;
// Master mixer.
_snd_mixer_elem* master_element_;
// PCM mixer. May be NULL if the driver doesn't expose one.
_snd_mixer_elem* pcm_element_;
// Mic mixers. Used to mute capture. Both might be NULL on some drivers.
_snd_mixer_elem* mic_element_;
_snd_mixer_elem* front_mic_element_;
// Signalled after Disconnect() finishes (which is itself invoked by the
// d'tor).
base::WaitableEvent disconnected_event_;
// Background thread used for interacting with ALSA.
scoped_ptr<base::Thread> thread_;
// Number of times that we've attempted to connect to ALSA. Just used to keep
// us from spamming the logs.
int num_connection_attempts_;
DISALLOW_COPY_AND_ASSIGN(AudioMixerAlsa);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_AUDIO_AUDIO_MIXER_ALSA_H_