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 |