1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// found in the LICENSE file. 472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/chromeos/audio_mixer_alsa.h" 672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include <cmath> 8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <unistd.h> 9dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <alsa/asoundlib.h> 1172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 1272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/logging.h" 1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/message_loop.h" 1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/task.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/threading/thread_restrictions.h" 1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/browser_process.h" 1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/prefs/pref_service.h" 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/pref_names.h" 19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace chromeos { 2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Connect to the ALSA mixer using their simple element API. Init is performed 2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// asynchronously on the worker thread. 2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// 2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// To get a wider range and finer control over volume levels, first the Master 2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// level is set, then if the PCM element exists, the total level is refined by 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// adjusting that as well. If the PCM element has more volume steps, it allows 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// for finer granularity in the total volume. 3072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsentypedef long alsa_long_t; // 'long' is required for ALSA API calls. 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace { 3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst char kMasterVolume[] = "Master"; 3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst char kPCMVolume[] = "PCM"; 3772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst double kDefaultMinVolume = -90.0; 3872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst double kDefaultMaxVolume = 0.0; 3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst double kPrefVolumeInvalid = -999.0; 4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int kPrefMuteOff = 0; 4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int kPrefMuteOn = 1; 4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int kPrefMuteInvalid = 2; 4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Maximum number of times that we'll attempt to initialize the mixer. 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// We'll fail until the ALSA modules have been loaded; see 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// http://crosbug.com/13162. 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kMaxInitAttempts = 20; 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Number of seconds that we'll sleep between each initialization attempt. 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenconst int kInitRetrySleepSec = 1; 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 5272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} // namespace 5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenAudioMixerAlsa::AudioMixerAlsa() 5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen : min_volume_(kDefaultMinVolume), 5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen max_volume_(kDefaultMaxVolume), 5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen save_volume_(0), 5872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mixer_state_(UNINITIALIZED), 5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_mixer_(NULL), 6072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elem_master_(NULL), 6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elem_pcm_(NULL), 6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefs_(NULL), 6372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen done_event_(true, false) { 6472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenAudioMixerAlsa::~AudioMixerAlsa() { 6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (thread_ != NULL) { 6872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen { 6972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mixer_state_ = SHUTTING_DOWN; 7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_->message_loop()->PostTask(FROM_HERE, 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, &AudioMixerAlsa::FreeAlsaMixer)); 7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen done_event_.Wait(); 7572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // A ScopedAllowIO object is required to join the thread when calling Stop. 7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // The worker thread should be idle at this time. 7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // See http://crosbug.com/11110 for discussion. 8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join; 8172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_->message_loop()->AssertIdle(); 8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_->Stop(); 8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_.reset(); 8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 8772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::Init(InitDoneCallback* callback) { 8972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(callback); 9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!InitThread()) { 9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen callback->Run(false); 9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen delete callback; 9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen InitPrefs(); 9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen { 9872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ == SHUTTING_DOWN) 10072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 10172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Post the task of starting up, which may block on the order of ms, 10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // so best not to do it on the caller's thread. 10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_->message_loop()->PostTask(FROM_HERE, 10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, &AudioMixerAlsa::DoInit, callback)); 10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::InitSync() { 11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!InitThread()) 11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 11272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen InitPrefs(); 11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return InitializeAlsaMixer(); 11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsendouble AudioMixerAlsa::GetVolumeDb() const { 11772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 11972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return kSilenceDb; 12072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return DoGetVolumeDb_Locked(); 12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 12472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::GetVolumeLimits(double* vol_min, double* vol_max) { 12572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (vol_min) 12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen *vol_min = min_volume_; 13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (vol_max) 13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen *vol_max = max_volume_; 13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::SetVolumeDb(double vol_db) { 13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (vol_db < kSilenceDb || isnan(vol_db)) { 141dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (isnan(vol_db)) 142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "Got request to set volume to NaN"; 14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen vol_db = kSilenceDb; 144dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 145dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 14672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DoSetVolumeDb_Locked(vol_db); 14772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefs_->SetDouble(prefs::kAudioVolume, vol_db); 14872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 14972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 15072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::IsMute() const { 15172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 15272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 15372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return GetElementMuted_Locked(elem_master_); 15572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 15672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// To indicate the volume is not valid yet, a very low volume value is stored. 15872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// We compare against a slightly higher value in case of rounding errors. 15972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstatic bool PrefVolumeValid(double volume) { 16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return (volume > kPrefVolumeInvalid + 0.1); 16172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 16272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 16372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::SetMute(bool mute) { 16472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 16572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 16872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Set volume to minimum on mute, since switching the element off does not 16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // always mute as it should. 17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 17172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // TODO(davej): Remove save_volume_ and setting volume to minimum if 17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // switching the element off can be guaranteed to mute it. Currently mute 17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // is done by setting the volume to min_volume_. 17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool old_value = GetElementMuted_Locked(elem_master_); 17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (old_value != mute) { 17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mute) { 17972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen save_volume_ = DoGetVolumeDb_Locked(); 18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DoSetVolumeDb_Locked(min_volume_); 18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DoSetVolumeDb_Locked(save_volume_); 18372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 18472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 18572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SetElementMuted_Locked(elem_master_, mute); 18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefs_->SetInteger(prefs::kAudioMute, mute ? kPrefMuteOn : kPrefMuteOff); 18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 18972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenAudioMixer::State AudioMixerAlsa::GetState() const { 19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If we think it's ready, verify it is actually so. 19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((mixer_state_ == READY) && (alsa_mixer_ == NULL)) 19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mixer_state_ = IN_ERROR; 19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return mixer_state_; 19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static 19972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::RegisterPrefs(PrefService* local_state) { 20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!local_state->FindPreference(prefs::kAudioVolume)) 20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen local_state->RegisterDoublePref(prefs::kAudioVolume, kPrefVolumeInvalid); 20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!local_state->FindPreference(prefs::kAudioMute)) 20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen local_state->RegisterIntegerPref(prefs::kAudioMute, kPrefMuteInvalid); 20472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 20572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen//////////////////////////////////////////////////////////////////////////////// 20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Private functions follow 20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::DoInit(InitDoneCallback* callback) { 210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool success = false; 211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (int num_attempts = 0; num_attempts < kMaxInitAttempts; ++num_attempts) { 212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen success = InitializeAlsaMixer(); 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (success) { 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen break; 215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } else { 216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If the destructor has reset the state, give up. 217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::AutoLock lock(mixer_state_lock_); 219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (mixer_state_ != INITIALIZING) 220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen break; 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sleep(kInitRetrySleepSec); 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 22672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (success) { 22772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen BrowserThread::PostTask( 22872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen BrowserThread::UI, FROM_HERE, 22972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, &AudioMixerAlsa::RestoreVolumeMuteOnUIThread)); 23072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 23172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 23272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (callback) { 23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen callback->Run(success); 23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen delete callback; 23572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 23672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 23772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 23872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::InitThread() { 23972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 24172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != UNINITIALIZED) 24272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 24372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 24472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (thread_ == NULL) { 24572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 24672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_.reset(new base::Thread("AudioMixerAlsa")); 24772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!thread_->Start()) { 24872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_.reset(); 24972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 25072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 25172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 25272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 25372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mixer_state_ = INITIALIZING; 25472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 25572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 25672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 25772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::InitPrefs() { 25872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen prefs_ = g_browser_process->local_state(); 25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 26072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 26172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::InitializeAlsaMixer() { 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We can block; make sure that we're not on the UI thread. 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 26572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 26672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != INITIALIZING) 26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int err; 27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_t* handle = NULL; 27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const char* card = "default"; 27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((err = snd_mixer_open(&handle, 0)) < 0) { 27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "ALSA mixer " << card << " open error: " << snd_strerror(err); 27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((err = snd_mixer_attach(handle, card)) < 0) { 27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "ALSA Attach to card " << card << " failed: " 28072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << snd_strerror(err); 28172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_close(handle); 28272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 28372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 28472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 285dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // Verify PCM can be opened, which also instantiates the PCM mixer element 286dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // which is needed for finer volume control and for muting by setting to zero. 287dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen // If it fails, we can still try to use the mixer as best we can. 288dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_pcm_t* pcm_out_handle; 289dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if ((err = snd_pcm_open(&pcm_out_handle, 290dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen card, 291dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen SND_PCM_STREAM_PLAYBACK, 292dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 0)) >= 0) { 293dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_pcm_close(pcm_out_handle); 294dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } else { 295dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "ALSA PCM open: " << snd_strerror(err); 296dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 297dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { 29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "ALSA mixer register error: " << snd_strerror(err); 30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_close(handle); 30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if ((err = snd_mixer_load(handle)) < 0) { 30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "ALSA mixer " << card << " load error: %s" 30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << snd_strerror(err); 30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_close(handle); 30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen VLOG(1) << "Opened ALSA mixer " << card << " OK"; 31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 31372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elem_master_ = FindElementWithName_Locked(handle, kMasterVolume); 31472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (elem_master_) { 31572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); 31672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); 317dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen err = snd_mixer_selem_get_playback_dB_range( 318dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen elem_master_, &long_lo, &long_hi); 319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (err != 0) { 320dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed " 321dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << "for master: " << snd_strerror(err); 322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_mixer_close(handle); 323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 324dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen min_volume_ = static_cast<double>(long_lo) / 100.0; 32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen max_volume_ = static_cast<double>(long_hi) / 100.0; 32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card; 32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_close(handle); 33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 33272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume); 33472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (elem_pcm_) { 33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); 33672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); 337dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen err = snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi); 338dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (err != 0) { 339dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed for PCM: " 340dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(err); 341dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_mixer_close(handle); 342dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 343dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen min_volume_ += static_cast<double>(long_lo) / 100.0; 34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen max_volume_ += static_cast<double>(long_hi) / 100.0; 34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to " 34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << max_volume_ << " dB"; 35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_mixer_ = handle; 35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mixer_state_ = READY; 35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::FreeAlsaMixer() { 35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (alsa_mixer_) { 35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_close(alsa_mixer_); 35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_mixer_ = NULL; 36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen done_event_.Signal(); 36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { 36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ != READY) 36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If volume or mute are invalid, set them now to the current actual values. 37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!PrefVolumeValid(pref_volume)) 37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen pref_volume = DoGetVolumeDb_Locked(); 372dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen bool mute = false; 37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (pref_mute == kPrefMuteInvalid) 37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mute = GetElementMuted_Locked(elem_master_); 37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen else 37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen mute = (pref_mute == kPrefMuteOn) ? true : false; 37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; 37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mute) { 38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen save_volume_ = pref_volume; 38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DoSetVolumeDb_Locked(min_volume_); 38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 38472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DoSetVolumeDb_Locked(pref_volume); 38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SetElementMuted_Locked(elem_master_, mute); 38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::RestoreVolumeMuteOnUIThread() { 39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 39272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // This happens during init, so set the volume off the UI thread. 39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int mute = prefs_->GetInteger(prefs::kAudioMute); 39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double vol = prefs_->GetDouble(prefs::kAudioVolume); 39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen { 39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(mixer_state_lock_); 39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (mixer_state_ == SHUTTING_DOWN) 39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return; 39972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen thread_->message_loop()->PostTask(FROM_HERE, 40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, vol, mute)); 40172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 40272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 40372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 40472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsendouble AudioMixerAlsa::DoGetVolumeDb_Locked() const { 40572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double vol_total = 0.0; 406dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (!GetElementVolume_Locked(elem_master_, &vol_total)) 407dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return 0.0; 40872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double vol_pcm = 0.0; 410dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (elem_pcm_ && GetElementVolume_Locked(elem_pcm_, &vol_pcm)) 41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen vol_total += vol_pcm; 41272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return vol_total; 41472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 41672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { 41772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double actual_vol = 0.0; 41872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 41972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If a PCM volume slider exists, then first set the Master volume to the 42072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // nearest volume >= requested volume, then adjust PCM volume down to get 42172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // closer to the requested volume. 42272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (elem_pcm_) { 42372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SetElementVolume_Locked(elem_master_, vol_db, &actual_vol, 0.9999f); 42472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SetElementVolume_Locked(elem_pcm_, vol_db - actual_vol, NULL, 0.5f); 42572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 42672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen SetElementVolume_Locked(elem_master_, vol_db, NULL, 0.5f); 42772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 42872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 42972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 43072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsensnd_mixer_elem_t* AudioMixerAlsa::FindElementWithName_Locked( 43172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_t* handle, 43272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const char* element_name) const { 43372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_selem_id_t* sid; 43472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 43572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Using id_malloc/id_free API instead of id_alloca since the latter gives the 43672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // warning: the address of 'sid' will always evaluate as 'true'. 43772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (snd_mixer_selem_id_malloc(&sid)) 43872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return NULL; 43972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 44072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_selem_id_set_index(sid, 0); 44172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_selem_id_set_name(sid, element_name); 44272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); 44372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!elem) { 44472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "ALSA unable to find simple control " 44572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << snd_mixer_selem_id_get_name(sid); 44672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 44772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 44872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen snd_mixer_selem_id_free(sid); 44972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return elem; 45072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 45172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 45272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem, 45372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double* current_vol) const { 45472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t long_vol = 0; 455dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int alsa_result = snd_mixer_selem_get_playback_dB( 456dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen elem, static_cast<snd_mixer_selem_channel_id_t>(0), &long_vol); 457dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 458dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_dB() failed: " 459dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 460dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 461dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 46272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 463dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen *current_vol = static_cast<double>(long_vol) / 100.0; 46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem, 46872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double new_vol, 46972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double* actual_vol, 47072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double rounding_bias) { 47172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t vol_lo = 0; 47272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t vol_hi = 0; 473dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int alsa_result = 474dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi); 475dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 476dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_volume_range() failed: " 477dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 478dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 479dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 48072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t vol_range = vol_hi - vol_lo; 48172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (vol_range <= 0) 48272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 48372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 48472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t db_lo_int = 0; 48572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t db_hi_int = 0; 486dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen alsa_result = 487dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int); 488dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 489dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed: " 490dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 491dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 492dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 493dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 49472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double db_lo = static_cast<double>(db_lo_int) / 100.0; 49572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double db_hi = static_cast<double>(db_hi_int) / 100.0; 49672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double db_step = static_cast<double>(db_hi - db_lo) / vol_range; 49772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (db_step <= 0.0) 49872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return false; 49972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 50072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (new_vol < db_lo) 50172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen new_vol = db_lo; 50272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 50372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t value = static_cast<alsa_long_t>(rounding_bias + 50472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen (new_vol - db_lo) / db_step) + vol_lo; 505dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen alsa_result = snd_mixer_selem_set_playback_volume_all(elem, value); 506dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 507dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_set_playback_volume_all() failed: " 508dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 509dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 510dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 51172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 51272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem) 51372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo 51472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << " dB"; 51572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 51672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (actual_vol) { 51772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen alsa_long_t volume = vol_lo; 518dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen alsa_result = snd_mixer_selem_get_playback_volume( 519dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen elem, static_cast<snd_mixer_selem_channel_id_t>(0), &volume); 520dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 521dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_volume() failed: " 522dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 523dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 524dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 52572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen *actual_vol = db_lo + (volume - vol_lo) * db_step; 52672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 52772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem) 52872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen << " now " << *actual_vol << " dB"; 52972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 53072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 53172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 53272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 53372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const { 53472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int enabled = 0; 535dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int alsa_result = snd_mixer_selem_get_playback_switch( 536dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen elem, static_cast<snd_mixer_selem_channel_id_t>(0), &enabled); 537dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 538dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_get_playback_switch() failed: " 539dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 540dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen return false; 541dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 54272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return (enabled) ? false : true; 54372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 54472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 54572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { 54672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int enabled = mute ? 0 : 1; 547dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen int alsa_result = snd_mixer_selem_set_playback_switch_all(elem, enabled); 548dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (alsa_result != 0) { 549dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: " 550dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << snd_strerror(alsa_result); 551dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } else { 552dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) 553dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen << " to " << enabled; 554dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 55572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 55672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 55772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} // namespace chromeos 558