| 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/message_loop.h" | 12 #include "base/message_loop.h" |
| 11 #include "base/task.h" | 13 #include "base/task.h" |
| 12 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
| 13 #include "chrome/browser/browser_process.h" | 15 #include "chrome/browser/browser_process.h" |
| 14 #include "chrome/browser/browser_thread.h" | 16 #include "chrome/browser/browser_thread.h" |
| 15 #include "chrome/browser/prefs/pref_service.h" | 17 #include "chrome/browser/prefs/pref_service.h" |
| 16 #include "chrome/common/pref_names.h" | 18 #include "chrome/common/pref_names.h" |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 118 *vol_min = min_volume_; | 120 *vol_min = min_volume_; |
| 119 if (vol_max) | 121 if (vol_max) |
| 120 *vol_max = max_volume_; | 122 *vol_max = max_volume_; |
| 121 return true; | 123 return true; |
| 122 } | 124 } |
| 123 | 125 |
| 124 void AudioMixerAlsa::SetVolumeDb(double vol_db) { | 126 void AudioMixerAlsa::SetVolumeDb(double vol_db) { |
| 125 base::AutoLock lock(mixer_state_lock_); | 127 base::AutoLock lock(mixer_state_lock_); |
| 126 if (mixer_state_ != READY) | 128 if (mixer_state_ != READY) |
| 127 return; | 129 return; |
| 128 if (vol_db < kSilenceDb) | 130 |
| 131 if (vol_db < kSilenceDb || isnan(vol_db)) { |
| 132 if (isnan(vol_db)) |
| 133 LOG(WARNING) << "Got request to set volume to NaN"; |
| 129 vol_db = kSilenceDb; | 134 vol_db = kSilenceDb; |
| 135 } |
| 136 |
| 130 DoSetVolumeDb_Locked(vol_db); | 137 DoSetVolumeDb_Locked(vol_db); |
| 131 prefs_->SetDouble(prefs::kAudioVolume, vol_db); | 138 prefs_->SetDouble(prefs::kAudioVolume, vol_db); |
| 132 } | 139 } |
| 133 | 140 |
| 134 bool AudioMixerAlsa::IsMute() const { | 141 bool AudioMixerAlsa::IsMute() const { |
| 135 base::AutoLock lock(mixer_state_lock_); | 142 base::AutoLock lock(mixer_state_lock_); |
| 136 if (mixer_state_ != READY) | 143 if (mixer_state_ != READY) |
| 137 return false; | 144 return false; |
| 138 return GetElementMuted_Locked(elem_master_); | 145 return GetElementMuted_Locked(elem_master_); |
| 139 } | 146 } |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 263 snd_mixer_close(handle); | 270 snd_mixer_close(handle); |
| 264 return false; | 271 return false; |
| 265 } | 272 } |
| 266 | 273 |
| 267 VLOG(1) << "Opened ALSA mixer " << card << " OK"; | 274 VLOG(1) << "Opened ALSA mixer " << card << " OK"; |
| 268 | 275 |
| 269 elem_master_ = FindElementWithName_Locked(handle, kMasterVolume); | 276 elem_master_ = FindElementWithName_Locked(handle, kMasterVolume); |
| 270 if (elem_master_) { | 277 if (elem_master_) { |
| 271 alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); | 278 alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); |
| 272 alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); | 279 alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); |
| 273 snd_mixer_selem_get_playback_dB_range(elem_master_, &long_lo, &long_hi); | 280 err = snd_mixer_selem_get_playback_dB_range( |
| 281 elem_master_, &long_lo, &long_hi); |
| 282 if (err != 0) { |
| 283 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed " |
| 284 << "for master: " << snd_strerror(err); |
| 285 snd_mixer_close(handle); |
| 286 return false; |
| 287 } |
| 274 min_volume_ = static_cast<double>(long_lo) / 100.0; | 288 min_volume_ = static_cast<double>(long_lo) / 100.0; |
| 275 max_volume_ = static_cast<double>(long_hi) / 100.0; | 289 max_volume_ = static_cast<double>(long_hi) / 100.0; |
| 276 } else { | 290 } else { |
| 277 LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card; | 291 LOG(ERROR) << "Cannot find 'Master' ALSA mixer element on " << card; |
| 278 snd_mixer_close(handle); | 292 snd_mixer_close(handle); |
| 279 return false; | 293 return false; |
| 280 } | 294 } |
| 281 | 295 |
| 282 elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume); | 296 elem_pcm_ = FindElementWithName_Locked(handle, kPCMVolume); |
| 283 if (elem_pcm_) { | 297 if (elem_pcm_) { |
| 284 alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); | 298 alsa_long_t long_lo = static_cast<alsa_long_t>(kDefaultMinVolume * 100); |
| 285 alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); | 299 alsa_long_t long_hi = static_cast<alsa_long_t>(kDefaultMaxVolume * 100); |
| 286 snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi); | 300 err = snd_mixer_selem_get_playback_dB_range(elem_pcm_, &long_lo, &long_hi); |
| 301 if (err != 0) { |
| 302 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed for PCM: " |
| 303 << snd_strerror(err); |
| 304 snd_mixer_close(handle); |
| 305 return false; |
| 306 } |
| 287 min_volume_ += static_cast<double>(long_lo) / 100.0; | 307 min_volume_ += static_cast<double>(long_lo) / 100.0; |
| 288 max_volume_ += static_cast<double>(long_hi) / 100.0; | 308 max_volume_ += static_cast<double>(long_hi) / 100.0; |
| 289 } | 309 } |
| 290 | 310 |
| 291 VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to " | 311 VLOG(1) << "ALSA volume range is " << min_volume_ << " dB to " |
| 292 << max_volume_ << " dB"; | 312 << max_volume_ << " dB"; |
| 293 | 313 |
| 294 alsa_mixer_ = handle; | 314 alsa_mixer_ = handle; |
| 295 mixer_state_ = READY; | 315 mixer_state_ = READY; |
| 296 return true; | 316 return true; |
| 297 } | 317 } |
| 298 | 318 |
| 299 void AudioMixerAlsa::FreeAlsaMixer() { | 319 void AudioMixerAlsa::FreeAlsaMixer() { |
| 300 if (alsa_mixer_) { | 320 if (alsa_mixer_) { |
| 301 snd_mixer_close(alsa_mixer_); | 321 snd_mixer_close(alsa_mixer_); |
| 302 alsa_mixer_ = NULL; | 322 alsa_mixer_ = NULL; |
| 303 } | 323 } |
| 304 done_event_.Signal(); | 324 done_event_.Signal(); |
| 305 } | 325 } |
| 306 | 326 |
| 307 void AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { | 327 void AudioMixerAlsa::DoSetVolumeMute(double pref_volume, int pref_mute) { |
| 308 base::AutoLock lock(mixer_state_lock_); | 328 base::AutoLock lock(mixer_state_lock_); |
| 309 if (mixer_state_ != READY) | 329 if (mixer_state_ != READY) |
| 310 return; | 330 return; |
| 311 | 331 |
| 312 // If volume or mute are invalid, set them now to the current actual values. | 332 // If volume or mute are invalid, set them now to the current actual values. |
| 313 if (!PrefVolumeValid(pref_volume)) | 333 if (!PrefVolumeValid(pref_volume)) |
| 314 pref_volume = DoGetVolumeDb_Locked(); | 334 pref_volume = DoGetVolumeDb_Locked(); |
| 315 bool mute; | 335 bool mute = false; |
| 316 if (pref_mute == kPrefMuteInvalid) | 336 if (pref_mute == kPrefMuteInvalid) |
| 317 mute = GetElementMuted_Locked(elem_master_); | 337 mute = GetElementMuted_Locked(elem_master_); |
| 318 else | 338 else |
| 319 mute = (pref_mute == kPrefMuteOn) ? true : false; | 339 mute = (pref_mute == kPrefMuteOn) ? true : false; |
| 320 | 340 |
| 321 VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; | 341 VLOG(1) << "Setting volume to " << pref_volume << " and mute to " << mute; |
| 322 | 342 |
| 323 if (mute) { | 343 if (mute) { |
| 324 save_volume_ = pref_volume; | 344 save_volume_ = pref_volume; |
| 325 DoSetVolumeDb_Locked(min_volume_); | 345 DoSetVolumeDb_Locked(min_volume_); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 341 base::AutoLock lock(mixer_state_lock_); | 361 base::AutoLock lock(mixer_state_lock_); |
| 342 if (mixer_state_ == SHUTTING_DOWN) | 362 if (mixer_state_ == SHUTTING_DOWN) |
| 343 return; | 363 return; |
| 344 thread_->message_loop()->PostTask(FROM_HERE, | 364 thread_->message_loop()->PostTask(FROM_HERE, |
| 345 NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, vol, mute)); | 365 NewRunnableMethod(this, &AudioMixerAlsa::DoSetVolumeMute, vol, mute)); |
| 346 } | 366 } |
| 347 } | 367 } |
| 348 | 368 |
| 349 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { | 369 double AudioMixerAlsa::DoGetVolumeDb_Locked() const { |
| 350 double vol_total = 0.0; | 370 double vol_total = 0.0; |
| 351 GetElementVolume_Locked(elem_master_, &vol_total); | 371 if (!GetElementVolume_Locked(elem_master_, &vol_total)) |
| 372 return 0.0; |
| 352 | 373 |
| 353 double vol_pcm = 0.0; | 374 double vol_pcm = 0.0; |
| 354 if (elem_pcm_ && (GetElementVolume_Locked(elem_pcm_, &vol_pcm))) | 375 if (elem_pcm_ && GetElementVolume_Locked(elem_pcm_, &vol_pcm)) |
| 355 vol_total += vol_pcm; | 376 vol_total += vol_pcm; |
| 356 | 377 |
| 357 return vol_total; | 378 return vol_total; |
| 358 } | 379 } |
| 359 | 380 |
| 360 void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { | 381 void AudioMixerAlsa::DoSetVolumeDb_Locked(double vol_db) { |
| 361 double actual_vol = 0.0; | 382 double actual_vol = 0.0; |
| 362 | 383 |
| 363 // If a PCM volume slider exists, then first set the Master volume to the | 384 // If a PCM volume slider exists, then first set the Master volume to the |
| 364 // nearest volume >= requested volume, then adjust PCM volume down to get | 385 // nearest volume >= requested volume, then adjust PCM volume down to get |
| (...skipping 24 matching lines...) Expand all Loading... |
| 389 << snd_mixer_selem_id_get_name(sid); | 410 << snd_mixer_selem_id_get_name(sid); |
| 390 } | 411 } |
| 391 | 412 |
| 392 snd_mixer_selem_id_free(sid); | 413 snd_mixer_selem_id_free(sid); |
| 393 return elem; | 414 return elem; |
| 394 } | 415 } |
| 395 | 416 |
| 396 bool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem, | 417 bool AudioMixerAlsa::GetElementVolume_Locked(snd_mixer_elem_t* elem, |
| 397 double* current_vol) const { | 418 double* current_vol) const { |
| 398 alsa_long_t long_vol = 0; | 419 alsa_long_t long_vol = 0; |
| 399 snd_mixer_selem_get_playback_dB(elem, | 420 int alsa_result = snd_mixer_selem_get_playback_dB( |
| 400 static_cast<snd_mixer_selem_channel_id_t>(0), | 421 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &long_vol); |
| 401 &long_vol); | 422 if (alsa_result != 0) { |
| 423 LOG(WARNING) << "snd_mixer_selem_get_playback_dB() failed: " |
| 424 << snd_strerror(alsa_result); |
| 425 return false; |
| 426 } |
| 427 |
| 402 *current_vol = static_cast<double>(long_vol) / 100.0; | 428 *current_vol = static_cast<double>(long_vol) / 100.0; |
| 403 | |
| 404 return true; | 429 return true; |
| 405 } | 430 } |
| 406 | 431 |
| 407 bool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem, | 432 bool AudioMixerAlsa::SetElementVolume_Locked(snd_mixer_elem_t* elem, |
| 408 double new_vol, | 433 double new_vol, |
| 409 double* actual_vol, | 434 double* actual_vol, |
| 410 double rounding_bias) { | 435 double rounding_bias) { |
| 411 alsa_long_t vol_lo = 0; | 436 alsa_long_t vol_lo = 0; |
| 412 alsa_long_t vol_hi = 0; | 437 alsa_long_t vol_hi = 0; |
| 413 snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi); | 438 int alsa_result = |
| 439 snd_mixer_selem_get_playback_volume_range(elem, &vol_lo, &vol_hi); |
| 440 if (alsa_result != 0) { |
| 441 LOG(WARNING) << "snd_mixer_selem_get_playback_volume_range() failed: " |
| 442 << snd_strerror(alsa_result); |
| 443 return false; |
| 444 } |
| 414 alsa_long_t vol_range = vol_hi - vol_lo; | 445 alsa_long_t vol_range = vol_hi - vol_lo; |
| 415 if (vol_range <= 0) | 446 if (vol_range <= 0) |
| 416 return false; | 447 return false; |
| 417 | 448 |
| 418 alsa_long_t db_lo_int = 0; | 449 alsa_long_t db_lo_int = 0; |
| 419 alsa_long_t db_hi_int = 0; | 450 alsa_long_t db_hi_int = 0; |
| 420 snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int); | 451 alsa_result = |
| 452 snd_mixer_selem_get_playback_dB_range(elem, &db_lo_int, &db_hi_int); |
| 453 if (alsa_result != 0) { |
| 454 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed: " |
| 455 << snd_strerror(alsa_result); |
| 456 return false; |
| 457 } |
| 458 |
| 421 double db_lo = static_cast<double>(db_lo_int) / 100.0; | 459 double db_lo = static_cast<double>(db_lo_int) / 100.0; |
| 422 double db_hi = static_cast<double>(db_hi_int) / 100.0; | 460 double db_hi = static_cast<double>(db_hi_int) / 100.0; |
| 423 double db_step = static_cast<double>(db_hi - db_lo) / vol_range; | 461 double db_step = static_cast<double>(db_hi - db_lo) / vol_range; |
| 424 if (db_step <= 0.0) | 462 if (db_step <= 0.0) |
| 425 return false; | 463 return false; |
| 426 | 464 |
| 427 if (new_vol < db_lo) | 465 if (new_vol < db_lo) |
| 428 new_vol = db_lo; | 466 new_vol = db_lo; |
| 429 | 467 |
| 430 alsa_long_t value = static_cast<alsa_long_t>(rounding_bias + | 468 alsa_long_t value = static_cast<alsa_long_t>(rounding_bias + |
| 431 (new_vol - db_lo) / db_step) + vol_lo; | 469 (new_vol - db_lo) / db_step) + vol_lo; |
| 432 snd_mixer_selem_set_playback_volume_all(elem, value); | 470 alsa_result = snd_mixer_selem_set_playback_volume_all(elem, value); |
| 471 if (alsa_result != 0) { |
| 472 LOG(WARNING) << "snd_mixer_selem_set_playback_volume_all() failed: " |
| 473 << snd_strerror(alsa_result); |
| 474 return false; |
| 475 } |
| 433 | 476 |
| 434 VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem) | 477 VLOG(1) << "Set volume " << snd_mixer_selem_get_name(elem) |
| 435 << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo | 478 << " to " << new_vol << " ==> " << (value - vol_lo) * db_step + db_lo |
| 436 << " dB"; | 479 << " dB"; |
| 437 | 480 |
| 438 if (actual_vol) { | 481 if (actual_vol) { |
| 439 alsa_long_t volume = vol_lo; | 482 alsa_long_t volume = vol_lo; |
| 440 snd_mixer_selem_get_playback_volume( | 483 alsa_result = snd_mixer_selem_get_playback_volume( |
| 441 elem, | 484 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &volume); |
| 442 static_cast<snd_mixer_selem_channel_id_t>(0), | 485 if (alsa_result != 0) { |
| 443 &volume); | 486 LOG(WARNING) << "snd_mixer_selem_get_playback_volume() failed: " |
| 487 << snd_strerror(alsa_result); |
| 488 return false; |
| 489 } |
| 444 *actual_vol = db_lo + (volume - vol_lo) * db_step; | 490 *actual_vol = db_lo + (volume - vol_lo) * db_step; |
| 445 | 491 |
| 446 VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem) | 492 VLOG(1) << "Actual volume " << snd_mixer_selem_get_name(elem) |
| 447 << " now " << *actual_vol << " dB"; | 493 << " now " << *actual_vol << " dB"; |
| 448 } | 494 } |
| 449 return true; | 495 return true; |
| 450 } | 496 } |
| 451 | 497 |
| 452 bool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const { | 498 bool AudioMixerAlsa::GetElementMuted_Locked(snd_mixer_elem_t* elem) const { |
| 453 int enabled = 0; | 499 int enabled = 0; |
| 454 snd_mixer_selem_get_playback_switch( | 500 int alsa_result = snd_mixer_selem_get_playback_switch( |
| 455 elem, | 501 elem, static_cast<snd_mixer_selem_channel_id_t>(0), &enabled); |
| 456 static_cast<snd_mixer_selem_channel_id_t>(0), | 502 if (alsa_result != 0) { |
| 457 &enabled); | 503 LOG(WARNING) << "snd_mixer_selem_get_playback_switch() failed: " |
| 504 << snd_strerror(alsa_result); |
| 505 return false; |
| 506 } |
| 458 return (enabled) ? false : true; | 507 return (enabled) ? false : true; |
| 459 } | 508 } |
| 460 | 509 |
| 461 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { | 510 void AudioMixerAlsa::SetElementMuted_Locked(snd_mixer_elem_t* elem, bool mute) { |
| 462 int enabled = mute ? 0 : 1; | 511 int enabled = mute ? 0 : 1; |
| 463 snd_mixer_selem_set_playback_switch_all(elem, enabled); | 512 int alsa_result = snd_mixer_selem_set_playback_switch_all(elem, enabled); |
| 464 | 513 if (alsa_result != 0) { |
| 465 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) | 514 LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: " |
| 466 << " to " << enabled; | 515 << snd_strerror(alsa_result); |
| 516 } else { |
| 517 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(elem) |
| 518 << " to " << enabled; |
| 519 } |
| 467 } | 520 } |
| 468 | 521 |
| 469 } // namespace chromeos | 522 } // namespace chromeos |
| OLD | NEW |