| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <unistd.h> | 7 #include <unistd.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <cmath> | 10 #include <cmath> |
| 11 | 11 |
| 12 #include <alsa/asoundlib.h> | 12 #include <alsa/asoundlib.h> |
| 13 | 13 |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/message_loop.h" | 15 #include "base/message_loop.h" |
| 16 #include "base/task.h" | 16 #include "base/task.h" |
| 17 #include "base/threading/thread.h" | 17 #include "base/threading/thread.h" |
| 18 #include "base/threading/thread_restrictions.h" | 18 #include "base/threading/thread_restrictions.h" |
| 19 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
| 20 #include "chrome/browser/prefs/pref_service.h" | 20 #include "chrome/browser/prefs/pref_service.h" |
| 21 #include "chrome/common/pref_names.h" | 21 #include "chrome/common/pref_names.h" |
| 22 #include "content/browser/browser_thread.h" | 22 #include "content/browser/browser_thread.h" |
| 23 | 23 |
| 24 typedef long alsa_long_t; // 'long' is required for ALSA API calls. | 24 typedef long alsa_long_t; // 'long' is required for ALSA API calls. |
| 25 | 25 |
| 26 using std::max; | 26 using std::max; |
| 27 using std::min; | 27 using std::min; |
| 28 using std::string; | 28 using std::string; |
| 29 using std::vector; | |
| 30 | 29 |
| 31 namespace chromeos { | 30 namespace chromeos { |
| 32 | 31 |
| 33 namespace { | 32 namespace { |
| 34 | 33 |
| 35 // Name of the ALSA card to which we connect. | 34 // Name of the ALSA card to which we connect. |
| 36 const char kCardName[] = "default"; | 35 const char kCardName[] = "default"; |
| 37 | 36 |
| 38 // Mixer element names. We try to connect to the preferred master element | 37 // Mixer element names. We'll use the first master element from the list that |
| 39 // first; if it doesn't exist, we'll control any of the alternates that exist. | 38 // exists. |
| 40 const char kPreferredMasterElementName[] = "Master"; | 39 const char* const kMasterElementNames[] = { |
| 41 const char* const kAlternateMasterElementNames[] = { | 40 "Master", // x86 |
| 42 "Headphone", | 41 "Digital", // ARM |
| 43 "Speaker", | |
| 44 }; | 42 }; |
| 45 const char kPCMElementName[] = "PCM"; | 43 const char kPCMElementName[] = "PCM"; |
| 46 | 44 |
| 47 // Default minimum and maximum volume (before we've loaded the actual range from | 45 // Default minimum and maximum volume (before we've loaded the actual range from |
| 48 // ALSA), in decibels. | 46 // ALSA), in decibels. |
| 49 const double kDefaultMinVolumeDb = -90.0; | 47 const double kDefaultMinVolumeDb = -90.0; |
| 50 const double kDefaultMaxVolumeDb = 0.0; | 48 const double kDefaultMaxVolumeDb = 0.0; |
| 51 | 49 |
| 52 // Default value assigned to the pref when it's first created, in decibels. | 50 // Default value assigned to the pref when it's first created, in decibels. |
| 53 const double kDefaultVolumeDb = -10.0; | 51 const double kDefaultVolumeDb = -10.0; |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 << snd_strerror(err); | 231 << snd_strerror(err); |
| 234 snd_mixer_close(handle); | 232 snd_mixer_close(handle); |
| 235 return false; | 233 return false; |
| 236 } | 234 } |
| 237 | 235 |
| 238 VLOG(1) << "Opened mixer " << kCardName << " successfully"; | 236 VLOG(1) << "Opened mixer " << kCardName << " successfully"; |
| 239 | 237 |
| 240 double min_volume_db = kDefaultMinVolumeDb; | 238 double min_volume_db = kDefaultMinVolumeDb; |
| 241 double max_volume_db = kDefaultMaxVolumeDb; | 239 double max_volume_db = kDefaultMaxVolumeDb; |
| 242 | 240 |
| 243 vector<snd_mixer_elem_t*> master_elements; | 241 snd_mixer_elem_t* master_element = NULL; |
| 244 snd_mixer_elem_t* element = | 242 for (size_t i = 0; i < arraysize(kMasterElementNames); ++i) { |
| 245 FindElementWithName(handle, kPreferredMasterElementName); | 243 master_element = FindElementWithName(handle, kMasterElementNames[i]); |
| 246 if (element) { | 244 if (master_element) |
| 247 master_elements.push_back(element); | 245 break; |
| 248 } else { | |
| 249 for (size_t i = 0; i < arraysize(kAlternateMasterElementNames); ++i) { | |
| 250 element = FindElementWithName(handle, kAlternateMasterElementNames[i]); | |
| 251 if (element) | |
| 252 master_elements.push_back(element); | |
| 253 } | |
| 254 } | 246 } |
| 255 | 247 |
| 256 if (master_elements.empty()) { | 248 if (!master_element) { |
| 257 if (num_connection_attempts_ == kConnectionAttemptToLogFailure) | 249 if (num_connection_attempts_ == kConnectionAttemptToLogFailure) |
| 258 LOG(WARNING) << "Unable to find any mixer elements on " << kCardName; | 250 LOG(WARNING) << "Unable to find a master element on " << kCardName; |
| 259 snd_mixer_close(handle); | 251 snd_mixer_close(handle); |
| 260 return false; | 252 return false; |
| 261 } | 253 } |
| 262 | 254 |
| 263 alsa_long_t long_low = static_cast<alsa_long_t>(kDefaultMinVolumeDb * 100); | 255 alsa_long_t long_low = static_cast<alsa_long_t>(kDefaultMinVolumeDb * 100); |
| 264 alsa_long_t long_high = static_cast<alsa_long_t>(kDefaultMaxVolumeDb * 100); | 256 alsa_long_t long_high = static_cast<alsa_long_t>(kDefaultMaxVolumeDb * 100); |
| 265 err = snd_mixer_selem_get_playback_dB_range( | 257 err = snd_mixer_selem_get_playback_dB_range( |
| 266 master_elements.at(0), &long_low, &long_high); | 258 master_element, &long_low, &long_high); |
| 267 if (err != 0) { | 259 if (err != 0) { |
| 268 if (num_connection_attempts_ == kConnectionAttemptToLogFailure) | 260 if (num_connection_attempts_ == kConnectionAttemptToLogFailure) |
| 269 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed:" | 261 LOG(WARNING) << "snd_mixer_selem_get_playback_dB_range() failed:" |
| 270 << snd_strerror(err); | 262 << snd_strerror(err); |
| 271 snd_mixer_close(handle); | 263 snd_mixer_close(handle); |
| 272 return false; | 264 return false; |
| 273 } | 265 } |
| 274 min_volume_db = static_cast<double>(long_low) / 100.0; | 266 min_volume_db = static_cast<double>(long_low) / 100.0; |
| 275 max_volume_db = static_cast<double>(long_high) / 100.0; | 267 max_volume_db = static_cast<double>(long_high) / 100.0; |
| 276 | 268 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 289 } | 281 } |
| 290 min_volume_db += static_cast<double>(long_low) / 100.0; | 282 min_volume_db += static_cast<double>(long_low) / 100.0; |
| 291 max_volume_db += static_cast<double>(long_high) / 100.0; | 283 max_volume_db += static_cast<double>(long_high) / 100.0; |
| 292 } | 284 } |
| 293 | 285 |
| 294 VLOG(1) << "Volume range is " << min_volume_db << " dB to " | 286 VLOG(1) << "Volume range is " << min_volume_db << " dB to " |
| 295 << max_volume_db << " dB"; | 287 << max_volume_db << " dB"; |
| 296 { | 288 { |
| 297 base::AutoLock lock(lock_); | 289 base::AutoLock lock(lock_); |
| 298 alsa_mixer_ = handle; | 290 alsa_mixer_ = handle; |
| 299 master_elements_.swap(master_elements); | 291 master_element_ = master_element; |
| 300 pcm_element_ = pcm_element; | 292 pcm_element_ = pcm_element; |
| 301 min_volume_db_ = min_volume_db; | 293 min_volume_db_ = min_volume_db; |
| 302 max_volume_db_ = max_volume_db; | 294 max_volume_db_ = max_volume_db; |
| 303 volume_db_ = min(max(volume_db_, min_volume_db_), max_volume_db_); | 295 volume_db_ = min(max(volume_db_, min_volume_db_), max_volume_db_); |
| 304 } | 296 } |
| 305 | 297 |
| 306 ApplyState(); | 298 ApplyState(); |
| 307 return true; | 299 return true; |
| 308 } | 300 } |
| 309 | 301 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 327 base::AutoLock lock(lock_); | 319 base::AutoLock lock(lock_); |
| 328 should_mute = is_muted_; | 320 should_mute = is_muted_; |
| 329 new_volume_db = should_mute ? min_volume_db_ : volume_db_; | 321 new_volume_db = should_mute ? min_volume_db_ : volume_db_; |
| 330 apply_is_pending_ = false; | 322 apply_is_pending_ = false; |
| 331 } | 323 } |
| 332 | 324 |
| 333 if (pcm_element_) { | 325 if (pcm_element_) { |
| 334 // If a PCM volume slider exists, then first set the Master volume to the | 326 // If a PCM volume slider exists, then first set the Master volume to the |
| 335 // nearest volume >= requested volume, then adjust PCM volume down to get | 327 // nearest volume >= requested volume, then adjust PCM volume down to get |
| 336 // closer to the requested volume. | 328 // closer to the requested volume. |
| 337 for (vector<snd_mixer_elem_t*>::iterator it = master_elements_.begin(); | 329 SetElementVolume(master_element_, new_volume_db, 0.9999f); |
| 338 it != master_elements_.end(); ++it) | |
| 339 SetElementVolume(*it, new_volume_db, 0.9999f); | |
| 340 | 330 |
| 341 double pcm_volume_db = 0.0; | 331 double pcm_volume_db = 0.0; |
| 342 double master_volume_db = 0.0; | 332 double master_volume_db = 0.0; |
| 343 if (GetElementVolume(master_elements_.at(0), &master_volume_db)) | 333 if (GetElementVolume(master_element_, &master_volume_db)) |
| 344 pcm_volume_db = new_volume_db - master_volume_db; | 334 pcm_volume_db = new_volume_db - master_volume_db; |
| 345 SetElementVolume(pcm_element_, pcm_volume_db, 0.5f); | 335 SetElementVolume(pcm_element_, pcm_volume_db, 0.5f); |
| 346 } else { | 336 } else { |
| 347 for (vector<snd_mixer_elem_t*>::iterator it = master_elements_.begin(); | 337 SetElementVolume(master_element_, new_volume_db, 0.5f); |
| 348 it != master_elements_.end(); ++it) | |
| 349 SetElementVolume(*it, new_volume_db, 0.5f); | |
| 350 } | 338 } |
| 351 | 339 |
| 352 for (vector<snd_mixer_elem_t*>::iterator it = master_elements_.begin(); | 340 SetElementMuted(master_element_, should_mute); |
| 353 it != master_elements_.end(); ++it) | |
| 354 SetElementMuted(*it, should_mute); | |
| 355 } | 341 } |
| 356 | 342 |
| 357 snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName( | 343 snd_mixer_elem_t* AudioMixerAlsa::FindElementWithName( |
| 358 snd_mixer_t* handle, const string& element_name) const { | 344 snd_mixer_t* handle, const string& element_name) const { |
| 359 DCHECK(MessageLoop::current() == thread_->message_loop()); | 345 DCHECK(MessageLoop::current() == thread_->message_loop()); |
| 360 snd_mixer_selem_id_t* sid = NULL; | 346 snd_mixer_selem_id_t* sid = NULL; |
| 361 | 347 |
| 362 // Using id_malloc/id_free API instead of id_alloca since the latter gives the | 348 // Using id_malloc/id_free API instead of id_alloca since the latter gives the |
| 363 // warning: the address of 'sid' will always evaluate as 'true'. | 349 // warning: the address of 'sid' will always evaluate as 'true'. |
| 364 if (snd_mixer_selem_id_malloc(&sid)) | 350 if (snd_mixer_selem_id_malloc(&sid)) |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 447 if (alsa_result != 0) { | 433 if (alsa_result != 0) { |
| 448 LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: " | 434 LOG(WARNING) << "snd_mixer_selem_set_playback_switch_all() failed: " |
| 449 << snd_strerror(alsa_result); | 435 << snd_strerror(alsa_result); |
| 450 } else { | 436 } else { |
| 451 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(element) | 437 VLOG(1) << "Set playback switch " << snd_mixer_selem_get_name(element) |
| 452 << " to " << mute; | 438 << " to " << mute; |
| 453 } | 439 } |
| 454 } | 440 } |
| 455 | 441 |
| 456 } // namespace chromeos | 442 } // namespace chromeos |
| OLD | NEW |