| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/audio_mixer_alsa.h" | 5 #include "chrome/browser/chromeos/audio_mixer_alsa.h" |
| 6 | 6 |
| 7 #include <cmath> |
| 8 |
| 7 #include <alsa/asoundlib.h> | 9 #include <alsa/asoundlib.h> |
| 8 | 10 |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 10 #include "base/task.h" | 12 #include "base/task.h" |
| 11 #include "base/threading/thread_restrictions.h" | 13 #include "base/threading/thread_restrictions.h" |
| 12 #include "chrome/browser/browser_process.h" | 14 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/browser_thread.h" | 15 #include "chrome/browser/browser_thread.h" |
| 14 #include "chrome/browser/prefs/pref_service.h" | 16 #include "chrome/browser/prefs/pref_service.h" |
| 15 #include "chrome/common/pref_names.h" | 17 #include "chrome/common/pref_names.h" |
| 16 | 18 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 101 *vol_min = min_volume_; | 103 *vol_min = min_volume_; |
| 102 if (vol_max) | 104 if (vol_max) |
| 103 *vol_max = max_volume_; | 105 *vol_max = max_volume_; |
| 104 return true; | 106 return true; |
| 105 } | 107 } |
| 106 | 108 |
| 107 void AudioMixerAlsa::SetVolumeDb(double vol_db) { | 109 void AudioMixerAlsa::SetVolumeDb(double vol_db) { |
| 108 base::AutoLock lock(mixer_state_lock_); | 110 base::AutoLock lock(mixer_state_lock_); |
| 109 if (mixer_state_ != READY) | 111 if (mixer_state_ != READY) |
| 110 return; | 112 return; |
| 111 if (vol_db < kSilenceDb) | 113 |
| 114 if (vol_db < kSilenceDb || isnan(vol_db)) { |
| 115 if (isnan(vol_db)) |
| 116 LOG(WARNING) << "Got request to set volume to NaN"; |
| 112 vol_db = kSilenceDb; | 117 vol_db = kSilenceDb; |
| 118 } |
| 119 |
| 113 DoSetVolumeDb_Locked(vol_db); | 120 DoSetVolumeDb_Locked(vol_db); |
| 114 volume_pref_.SetValue(vol_db); | 121 volume_pref_.SetValue(vol_db); |
| 115 } | 122 } |
| 116 | 123 |
| 117 bool AudioMixerAlsa::IsMute() const { | 124 bool AudioMixerAlsa::IsMute() const { |
| 118 base::AutoLock lock(mixer_state_lock_); | 125 base::AutoLock lock(mixer_state_lock_); |
| 119 if (mixer_state_ != READY) | 126 if (mixer_state_ != READY) |
| 120 return false; | 127 return false; |
| 121 return GetElementMuted_Locked(elem_master_); | 128 return GetElementMuted_Locked(elem_master_); |
| 122 } | 129 } |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 << snd_strerror(err); | 255 << snd_strerror(err); |
| 249 snd_mixer_close(handle); | 256 snd_mixer_close(handle); |
| 250 return false; | 257 return false; |
| 251 } | 258 } |
| 252 | 259 |
| 253 VLOG(1) << "Opened ALSA mixer " << card << " OK"; | 260 VLOG(1) << "Opened ALSA mixer " << card << " OK"; |
| 254 | 261 |
| 255 elem_master_ = FindElementWithName_Locked(handle, kMasterVolume); | 262 elem_master_ = FindElementWithName_Locked(handle, kMasterVolume); |
| 256 if (elem_master_) { | 263 if (elem_master_) { |
| 257 alsa_long_t long_lo, long_hi; | 264 alsa_long_t long_lo, long_hi; |
| 258 snd_mixer_selem_get_playback_dB_range(elem_master_, &long_lo, &long_hi); | 265 err = snd_mixer_selem_get_playback_dB_range( |
| 266 elem_master_, &long_lo, &long_hi); |
| 267 if (err != 0) { |
| 268 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed " |
| 269 << "for master: " << snd_strerror(err); |
| 270 snd_mixer_close(handle); |
| 271 return false; |
| 272 } |
| 259 min_volume_ = static_cast<double>(long_lo) / 100.0; | 273 min_volume_ = static_cast<double>(long_lo) / 100.0; |
| 260 max_volume_ = static_cast<double>(long_hi) / 100.0; | 274 max_volume_ = static_cast<double>(long_hi) / 100.0; |
| 261 } else { | 275 } else { |
| 262 LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card; | 276 LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card; |
| 263 snd_mixer_close(handle); | 277 snd_mixer_close(handle); |
| 264 return false; | 278 return false; |
| 265 } | 279 } |
| 266 | 280 |
| 267 elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume); | 281 elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume); |
| 268 if (elem_pcm_) { | 282 if (elem_pcm_) { |
| 269 alsa_long_t long_lo, long_hi; | 283 alsa_long_t long_lo, long_hi; |
| 270 snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi); | 284 err = snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi); |
| 285 if (err != 0) { |
| 286 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed for PCM: " |
| 287 << snd_strerror(err); |
| 288 snd_mixer_close(handle); |
| 289 return false; |
| 290 } |
| 271 min_volume_ += static_cast<double>(long_lo) / 100.0; | 291 min_volume_ += static_cast<double>(long_lo) / 100.0; |
| 272 max_volume_ += static_cast<double>(long_hi) / 100.0; | 292 max_volume_ += static_cast<double>(long_hi) / 100.0; |
| 273 } | 293 } |
| 274 | 294 |
| 275 VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to " | 295 VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to " |
| 276 << max_volume_ << " dB"; | 296 << max_volume_ << " dB"; |
| 277 | 297 |
| 278 alsa_mixer_ = handle; | 298 alsa_mixer_ = handle; |
| 279 mixer_state_ = READY; | 299 mixer_state_ = READY; |
| 280 return true; | 300 return true; |
| 281 } | 301 } |
| 282 | 302 |
| 283 void AudioMixerAlsa::FreeAlsaMixer() { | 303 void AudioMixerAlsa::FreeAlsaMixer() { |
| 284 base::AutoLock lock(mixer_state_lock_); | 304 base::AutoLock lock(mixer_state_lock_); |
| 285 mixer_state_ = SHUTTING_DOWN; | 305 mixer_state_ = SHUTTING_DOWN; |
| 286 if (alsa_mixer_) { | 306 if (alsa_mixer_) { |
| 287 snd_mixer_close(alsa_mixer_); | 307 snd_mixer_close(alsa_mixer_); |
| 288 alsa_mixer_ = NULL; | 308 alsa_mixer_ = NULL; |
| 289 } | 309 } |
| 290 } | 310 } |
| 291 | 311 |
| 292 void AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { | 312 void AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { |
| 293 base::AutoLock lock(mixer_state_lock_); | 313 base::AutoLock lock(mixer_state_lock_); |
| 294 if (mixer_state_ != READY) | 314 if (mixer_state_ != READY) |
| 295 return; | 315 return; |
| 296 | 316 |
| 297 // If volume or mute are invalid, set them now to the current actual values. | 317 // If volume or mute are invalid, set them now to the current actual values. |
| 298 if (!PrefVolumeValid(pref_volume)) | 318 if (!PrefVolumeValid(pref_volume)) |
| 299 pref_volume = DoGetVolumeDb_Locked(); | 319 pref_volume = DoGetVolumeDb_Locked(); |
| 300 bool mute; | 320 bool mute = false; |
| 301 if (pref_mute == kPrefMuteInvalid) | 321 if (pref_mute == kPrefMuteInvalid) |
| 302 mute = GetElementMuted_Locked(elem_master_); | 322 mute = GetElementMuted_Locked(elem_master_); |
| 303 else | 323 else |
| 304 mute = (pref_mute == kPrefMuteOn) ? true : false; | 324 mute = (pref_mute == kPrefMuteOn) ? true : false; |
| 305 | 325 |
| 306 VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; | 326 VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; |
| 307 | 327 |
| 308 if (mute) { | 328 if (mute) { |
| 309 save_volume_ = pref_volume; | 329 save_volume_ = pref_volume; |
| 310 DoSetVolumeDb_Locked(min_volume_); | 330 DoSetVolumeDb_Locked(min_volume_); |
| 311 } else { | 331 } else { |
| 312 DoSetVolumeDb_Locked(pref_volume); | 332 DoSetVolumeDb_Locked(pref_volume); |
| 313 } | 333 } |
| 314 | 334 |
| 315 SetElementMuted_Locked(elem_master_, mute); | 335 SetElementMuted_Locked(elem_master_, mute); |
| 316 if (elem_pcm_) | 336 if (elem_pcm_) |
| 317 SetElementMuted_Locked(elem_pcm_, mute); | 337 SetElementMuted_Locked(elem_pcm_, mute); |
| 318 } | 338 } |
| 319 | 339 |
| 320 void AudioMixerAlsa::RestoreVolumeMuteOnUIThread() { | 340 void AudioMixerAlsa::RestoreVolumeMuteOnUIThread() { |
| 321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 322 // This happens during init, so set the volume off the UI thread. | 342 // This happens during init, so set the volume off the UI thread. |
| 323 thread_->message_loop()->PostTask(FROM_HERE, | 343 thread_->message_loop()->PostTask(FROM_HERE, |
| 324 NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, | 344 NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, |
| 325 volume_pref_.GetValue(), mute_pref_.GetValue())); | 345 volume_pref_.GetValue(), mute_pref_.GetValue())); |
| 326 } | 346 } |
| 327 | 347 |
| 328 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { | 348 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { |
| 329 double vol_total = 0.0; | 349 double vol_total = 0.0; |
| 330 GetElementVolume_Locked(elem_master_, &vol_total); | 350 if (!GetElementVolume_Locked(elem_master_, &vol_total)) |
| 351 return 0.0; |
| 331 | 352 |
| 332 double vol_pcm = 0.0; | 353 double vol_pcm = 0.0; |
| 333 if (elem_pcm_ && (GetElementVolume_Locked(elem_pcm_, &vol_pcm))) | 354 if (elem_pcm_ && GetElementVolume_Locked(elem_pcm_, &vol_pcm)) |
| 334 vol_total += vol_pcm; | 355 vol_total += vol_pcm; |
| 335 | 356 |
| 336 return vol_total; | 357 return vol_total; |
| 337 } | 358 } |
| 338 | 359 |
| 339 void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { | 360 void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { |
| 340 double actual_vol = 0.0; | 361 double actual_vol = 0.0; |
| 341 | 362 |
| 342 // If a PCM volume slider exists, then first set the Master volume to the | 363 // If a PCM volume slider exists, then first set the Master volume to the |
| 343 // nearest volume >= requested volume, then adjust PCM volume down to get | 364 // nearest volume >= requested volume, then adjust PCM volume down to get |
| (...skipping 24 matching lines...) Expand all Loading... |
| 368 << snd_mixer_selem_id_get_name(sid); | 389 << snd_mixer_selem_id_get_name(sid); |
| 369 } | 390 } |
| 370 | 391 |
| 371 snd_mixer_selem_id_free(sid); | 392 snd_mixer_selem_id_free(sid); |
| 372 return elem; | 393 return elem; |
| 373 } | 394 } |
| 374 | 395 |
| 375 bool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem, | 396 bool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem, |
| 376 double* current_vol) const { | 397 double* current_vol) const { |
| 377 alsa_long_t long_vol = 0; | 398 alsa_long_t long_vol = 0; |
| 378 snd_mixer_selem_get_playback_dB(elem, | 399 int alsa_result = snd_mixer_selem_get_playback_dB( |
| 379 static_cast<snd_mixer_selem_channel_id_t>(0), | 400 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &long_vol); |
| 380 &long_vol); | 401 if (alsa_result != 0) { |
| 402 LOG(WARNING) << "snd_mixer_selem_get_playback_dB() failed: " |
| 403 << snd_strerror(alsa_result); |
| 404 return false; |
| 405 } |
| 406 |
| 381 *current_vol = static_cast<double>(long_vol) / 100.0; | 407 *current_vol = static_cast<double>(long_vol) / 100.0; |
| 382 | |
| 383 return true; | 408 return true; |
| 384 } | 409 } |
| 385 | 410 |
| 386 bool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem, | 411 bool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem, |
| 387 double new_vol, | 412 double new_vol, |
| 388 double* actual_vol, | 413 double* actual_vol, |
| 389 double rounding_bias) { | 414 double rounding_bias) { |
| 390 alsa_long_t vol_lo = 0; | 415 alsa_long_t vol_lo = 0; |
| 391 alsa_long_t vol_hi = 0; | 416 alsa_long_t vol_hi = 0; |
| 392 snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi); | 417 int alsa_result = |
| 418 snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi); |
| 419 if (alsa_result != 0) { |
| 420 LOG(WARNING) << "snd_mixer_selem_get_playback_volume_range() failed: " |
| 421 << snd_strerror(alsa_result); |
| 422 return false; |
| 423 } |
| 393 alsa_long_t vol_range = vol_hi - vol_lo; | 424 alsa_long_t vol_range = vol_hi - vol_lo; |
| 394 if (vol_range <= 0) | 425 if (vol_range <= 0) |
| 395 return false; | 426 return false; |
| 396 | 427 |
| 397 alsa_long_t db_lo_int = 0; | 428 alsa_long_t db_lo_int = 0; |
| 398 alsa_long_t db_hi_int = 0; | 429 alsa_long_t db_hi_int = 0; |
| 399 snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int); | 430 alsa_result = |
| 431 snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int); |
| 432 if (alsa_result != 0) { |
| 433 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed: " |
| 434 << snd_strerror(alsa_result); |
| 435 return false; |
| 436 } |
| 437 |
| 400 double db_lo = static_cast<double>(db_lo_int) / 100.0; | 438 double db_lo = static_cast<double>(db_lo_int) / 100.0; |
| 401 double db_hi = static_cast<double>(db_hi_int) / 100.0; | 439 double db_hi = static_cast<double>(db_hi_int) / 100.0; |
| 402 double db_step = static_cast<double>(db_hi - db_lo) / vol_range; | 440 double db_step = static_cast<double>(db_hi - db_lo) / vol_range; |
| 403 if (db_step <= 0.0) | 441 if (db_step <= 0.0) |
| 404 return false; | 442 return false; |
| 405 | 443 |
| 406 if (new_vol < db_lo) | 444 if (new_vol < db_lo) |
| 407 new_vol = db_lo; | 445 new_vol = db_lo; |
| 408 | 446 |
| 409 alsa_long_t value = static_cast<alsa_long_t>(rounding_bias + | 447 alsa_long_t value = static_cast<alsa_long_t>(rounding_bias + |
| 410 (new_vol - db_lo) / db_step) + vol_lo; | 448 (new_vol - db_lo) / db_step) + vol_lo; |
| 411 snd_mixer_selem_set_playback_volume_all(elem, value); | 449 alsa_result = snd_mixer_selem_set_playback_volume_all(elem, value); |
| 450 if (alsa_result != 0) { |
| 451 LOG(WARNING) << "snd_mixer_selem_set_playback_volume_all() failed: " |
| 452 << snd_strerror(alsa_result); |
| 453 return false; |
| 454 } |
| 412 | 455 |
| 413 VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem) | 456 VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem) |
| 414 << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo | 457 << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo |
| 415 << " dB"; | 458 << " dB"; |
| 416 | 459 |
| 417 if (actual_vol) { | 460 if (actual_vol) { |
| 418 alsa_long_t volume; | 461 alsa_long_t volume; |
| 419 snd_mixer_selem_get_playback_volume( | 462 alsa_result = snd_mixer_selem_get_playback_volume( |
| 420 elem, | 463 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &volume); |
| 421 static_cast<snd_mixer_selem_channel_id_t>(0), | 464 if (alsa_result != 0) { |
| 422 &volume); | 465 LOG(WARNING) << "snd_mixer_selem_get_playback_volume() failed: " |
| 466 << snd_strerror(alsa_result); |
| 467 return false; |
| 468 } |
| 423 *actual_vol = db_lo + (volume - vol_lo) * db_step; | 469 *actual_vol = db_lo + (volume - vol_lo) * db_step; |
| 424 | 470 |
| 425 VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem) | 471 VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem) |
| 426 << " now " << *actual_vol << " dB"; | 472 << " now " << *actual_vol << " dB"; |
| 427 } | 473 } |
| 428 return true; | 474 return true; |
| 429 } | 475 } |
| 430 | 476 |
| 431 bool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const { | 477 bool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const { |
| 432 int enabled; | 478 int enabled; |
| 433 snd_mixer_selem_get_playback_switch( | 479 int alsa_result = snd_mixer_selem_get_playback_switch( |
| 434 elem, | 480 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &enabled); |
| 435 static_cast<snd_mixer_selem_channel_id_t>(0), | 481 if (alsa_result != 0) { |
| 436 &enabled); | 482 LOG(WARNING) << "snd_mixer_selem_get_playback_switch() failed: " |
| 483 << snd_strerror(alsa_result); |
| 484 return false; |
| 485 } |
| 437 return (enabled) ? false : true; | 486 return (enabled) ? false : true; |
| 438 } | 487 } |
| 439 | 488 |
| 440 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { | 489 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { |
| 441 int enabled = mute ? 0 : 1; | 490 int enabled = mute ? 0 : 1; |
| 442 snd_mixer_selem_set_playback_switch_all(elem, enabled); | 491 int alsa_result = snd_mixer_selem_set_playback_switch_all(elem, enabled); |
| 443 | 492 if (alsa_result != 0) { |
| 444 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) | 493 LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: " |
| 445 << " to " << enabled; | 494 << snd_strerror(alsa_result); |
| 495 } else { |
| 496 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) |
| 497 << " to " << enabled; |
| 498 } |
| 446 } | 499 } |
| 447 | 500 |
| 448 } // namespace chromeos | 501 } // namespace chromeos |
| 449 | 502 |
| OLD | NEW |