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 |