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 |