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_handler.h" | 5 #include "chrome/browser/chromeos/audio_handler.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/singleton.h" | 10 #include "base/singleton.h" |
11 #include "chrome/browser/chromeos/pulse_audio_mixer.h" | 11 #include "chrome/browser/browser_thread.h" |
12 #include "chrome/browser/chromeos/audio_mixer_alsa.h" | |
13 #include "chrome/browser/chromeos/audio_mixer_pulse.h" | |
12 | 14 |
13 namespace chromeos { | 15 namespace chromeos { |
14 | 16 |
15 namespace { | 17 namespace { |
16 | 18 |
17 const double kSilenceDb = -200.0; | |
18 const double kMinVolumeDb = -90.0; | 19 const double kMinVolumeDb = -90.0; |
19 // Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some | 20 // Choosing 6.0dB here instead of 0dB to give user chance to amplify audio some |
20 // in case sounds or their setup is too quiet for them. | 21 // in case sounds or their setup is too quiet for them. |
21 const double kMaxVolumeDb = 6.0; | 22 const double kMaxVolumeDb = 6.0; |
22 // A value of less than one adjusts quieter volumes in larger steps (giving | 23 // A value of less than one adjusts quieter volumes in larger steps (giving |
23 // finer resolution in the higher volumes). | 24 // finer resolution in the higher volumes). |
24 const double kVolumeBias = 0.5; | 25 const double kVolumeBias = 0.5; |
25 // If a connection is lost, we try again this many times | 26 // If a connection is lost, we try again this many times |
26 const int kMaxReconnectTries = 4; | 27 const int kMaxReconnectTries = 4; |
27 | 28 |
28 } // namespace | 29 } // namespace |
29 | 30 |
30 // This class will set volume using PulseAudio to adjust volume and mute, and | 31 // This class will set volume using PulseAudio or ALSA to adjust volume and |
31 // handles the volume level logic. | 32 // mute, and handles the volume level logic. |
32 | |
33 // TODO(davej): Serialize volume/mute for next startup? | |
34 | 33 |
35 double AudioHandler::GetVolumePercent() { | 34 double AudioHandler::GetVolumePercent() { |
36 if (!VerifyMixerConnection()) | 35 if (!VerifyMixerConnection()) |
37 return 0; | 36 return 0; |
38 | 37 |
39 return VolumeDbToPercent(mixer_->GetVolumeDb()); | 38 return VolumeDbToPercent(mixer_->GetVolumeDb()); |
40 } | 39 } |
41 | 40 |
42 // Set volume using our internal 0-100% range. Notice 0% is a special case of | 41 // Set volume using our internal 0-100% range. Notice 0% is a special case of |
43 // silence, so we set the mixer volume to kSilenceDb instead of kMinVolumeDb. | 42 // silence, so we set the mixer volume to kSilenceDb instead of min_volume_db_. |
44 void AudioHandler::SetVolumePercent(double volume_percent) { | 43 void AudioHandler::SetVolumePercent(double volume_percent) { |
45 if (!VerifyMixerConnection()) | 44 if (!VerifyMixerConnection()) |
46 return; | 45 return; |
47 DCHECK(volume_percent >= 0.0); | 46 DCHECK(volume_percent >= 0.0); |
48 | 47 |
49 double vol_db; | 48 double vol_db; |
50 if (volume_percent <= 0) | 49 if (volume_percent <= 0) |
51 vol_db = kSilenceDb; | 50 vol_db = AudioMixer::kSilenceDb; |
52 else | 51 else |
53 vol_db = PercentToVolumeDb(volume_percent); | 52 vol_db = PercentToVolumeDb(volume_percent); |
54 | 53 |
55 mixer_->SetVolumeDb(vol_db); | 54 mixer_->SetVolumeDb(vol_db); |
56 } | 55 } |
57 | 56 |
58 void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { | 57 void AudioHandler::AdjustVolumeByPercent(double adjust_by_percent) { |
59 if (!VerifyMixerConnection()) | 58 if (!VerifyMixerConnection()) |
60 return; | 59 return; |
61 | 60 |
62 DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; | 61 DVLOG(1) << "Adjusting Volume by " << adjust_by_percent << " percent"; |
63 | 62 |
64 double volume = mixer_->GetVolumeDb(); | 63 double volume = mixer_->GetVolumeDb(); |
65 double pct = VolumeDbToPercent(volume); | 64 double pct = VolumeDbToPercent(volume); |
66 | 65 |
67 if (pct < 0) | 66 if (pct < 0) |
68 pct = 0; | 67 pct = 0; |
69 pct = pct + adjust_by_percent; | 68 pct = pct + adjust_by_percent; |
70 if (pct > 100.0) | 69 if (pct > 100.0) |
71 pct = 100.0; | 70 pct = 100.0; |
72 | 71 |
73 double new_volume; | 72 double new_volume; |
74 if (pct <= 0.1) | 73 if (pct <= 0.1) |
75 new_volume = kSilenceDb; | 74 new_volume = AudioMixer::kSilenceDb; |
76 else | 75 else |
77 new_volume = PercentToVolumeDb(pct); | 76 new_volume = PercentToVolumeDb(pct); |
78 | 77 |
79 if (new_volume != volume) | 78 if (new_volume != volume) |
80 mixer_->SetVolumeDb(new_volume); | 79 mixer_->SetVolumeDb(new_volume); |
81 } | 80 } |
82 | 81 |
83 bool AudioHandler::IsMute() { | 82 bool AudioHandler::IsMute() { |
84 if (!VerifyMixerConnection()) | 83 if (!VerifyMixerConnection()) |
85 return false; | 84 return false; |
86 | 85 |
87 return mixer_->IsMute(); | 86 return mixer_->IsMute(); |
88 } | 87 } |
89 | 88 |
90 void AudioHandler::SetMute(bool do_mute) { | 89 void AudioHandler::SetMute(bool do_mute) { |
91 if (!VerifyMixerConnection()) | 90 if (!VerifyMixerConnection()) |
92 return; | 91 return; |
92 DVLOG(1) << "Setting Mute to " << do_mute; | |
93 mixer_->SetMute(do_mute); | |
94 } | |
93 | 95 |
94 DVLOG(1) << "Setting Mute to " << do_mute; | 96 bool AudioHandler::TryToConnect(bool async) { |
97 if (mixer_type_ == MIXER_TYPE_PULSEAUDIO) { | |
98 VLOG(1) << "Trying to connect to PulseAudio"; | |
99 mixer_.reset(new AudioMixerPulse()); | |
100 } else if (mixer_type_ == MIXER_TYPE_ALSA) { | |
101 VLOG(1) << "Trying to connect to ALSA"; | |
102 mixer_.reset(new AudioMixerAlsa()); | |
103 } else { | |
104 VLOG(1) << "Cannot find valid volume mixer"; | |
105 mixer_.reset(); | |
106 return false; | |
107 } | |
95 | 108 |
96 mixer_->SetMute(do_mute); | 109 if (async) { |
110 mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized)); | |
111 } else { | |
112 if (!mixer_->InitSync()) { | |
113 VLOG(1) << "Unable to reconnect to Mixer"; | |
114 return false; | |
115 } | |
116 } | |
117 return true; | |
118 } | |
119 | |
120 void AudioHandler::UseNextMixer() { | |
121 if (mixer_type_ == MIXER_TYPE_PULSEAUDIO) | |
122 mixer_type_ = MIXER_TYPE_ALSA; | |
123 else | |
124 mixer_type_ = MIXER_TYPE_NONE; | |
125 } | |
126 | |
127 static void ClipVolume(double* min_volume, double* max_volume) { | |
128 if (*min_volume < kMinVolumeDb) | |
129 *min_volume = kMinVolumeDb; | |
130 if (*max_volume > kMaxVolumeDb) | |
131 *max_volume = kMaxVolumeDb; | |
97 } | 132 } |
98 | 133 |
99 void AudioHandler::OnMixerInitialized(bool success) { | 134 void AudioHandler::OnMixerInitialized(bool success) { |
100 connected_ = success; | 135 connected_ = success; |
101 DVLOG(1) << "OnMixerInitialized, success = " << success; | 136 DVLOG(1) << "OnMixerInitialized, success = " << success; |
137 | |
138 if (connected_) { | |
139 if (mixer_->GetVolumeLimits(&min_volume_db_, &max_volume_db_)) { | |
140 ClipVolume(&min_volume_db_, &max_volume_db_); | |
141 } | |
142 return; | |
143 } | |
144 | |
145 VLOG(1) << "Unable to connect to mixer, trying next"; | |
146 UseNextMixer(); | |
147 | |
148 BrowserThread::PostTask( | |
149 BrowserThread::UI, FROM_HERE, | |
150 NewRunnableMethod(this, &AudioHandler::TryToConnect, true)); | |
102 } | 151 } |
103 | 152 |
104 AudioHandler::AudioHandler() | 153 AudioHandler::AudioHandler() |
105 : connected_(false), | 154 : connected_(false), |
106 reconnect_tries_(0) { | 155 reconnect_tries_(0), |
107 mixer_.reset(new PulseAudioMixer()); | 156 max_volume_db_(kMaxVolumeDb), |
108 if (!mixer_->Init(NewCallback(this, &AudioHandler::OnMixerInitialized))) { | 157 min_volume_db_(kMinVolumeDb), |
109 LOG(ERROR) << "Unable to connect to PulseAudio"; | 158 mixer_type_(MIXER_TYPE_PULSEAUDIO) { |
110 } | 159 |
160 // Start trying to connect to mixers asychronously, starting with the current | |
161 // mixer_type_. If the connection fails, another TryToConnect for the next | |
scherkus (not reviewing)
2011/01/11 20:10:39
nit: add () to function names and get rid of blank
| |
162 // mixer will be posted at that time. | |
163 | |
164 TryToConnect(true); | |
111 } | 165 } |
112 | 166 |
113 AudioHandler::~AudioHandler() { | 167 AudioHandler::~AudioHandler() { |
114 mixer_.reset(); | 168 mixer_.reset(); |
115 }; | 169 }; |
116 | 170 |
117 bool AudioHandler::VerifyMixerConnection() { | 171 bool AudioHandler::VerifyMixerConnection() { |
118 PulseAudioMixer::State mixer_state = mixer_->CheckState(); | 172 if (mixer_ == NULL) |
119 if (mixer_state == PulseAudioMixer::READY) | 173 return false; |
174 | |
175 AudioMixer::State mixer_state = mixer_->GetState(); | |
176 if (mixer_state == AudioMixer::READY) | |
120 return true; | 177 return true; |
121 if (connected_) { | 178 if (connected_) { |
122 // Something happened and the mixer is no longer valid after having been | 179 // Something happened and the mixer is no longer valid after having been |
123 // initialized earlier. | 180 // initialized earlier. |
124 connected_ = false; | 181 connected_ = false; |
125 LOG(ERROR) << "Lost connection to PulseAudio"; | 182 LOG(ERROR) << "Lost connection to mixer"; |
126 } else { | 183 } else { |
127 LOG(ERROR) << "Mixer not valid"; | 184 LOG(ERROR) << "Mixer not valid"; |
128 } | 185 } |
129 | 186 |
130 if ((mixer_state == PulseAudioMixer::INITIALIZING) || | 187 if ((mixer_state == AudioMixer::INITIALIZING) || |
131 (mixer_state == PulseAudioMixer::SHUTTING_DOWN)) | 188 (mixer_state == AudioMixer::SHUTTING_DOWN)) |
132 return false; | 189 return false; |
133 | 190 |
134 if (reconnect_tries_ < kMaxReconnectTries) { | 191 if (reconnect_tries_ < kMaxReconnectTries) { |
135 reconnect_tries_++; | 192 reconnect_tries_++; |
136 VLOG(1) << "Re-connecting to PulseAudio attempt " << reconnect_tries_ << "/" | 193 VLOG(1) << "Re-connecting to mixer attempt " << reconnect_tries_ << "/" |
137 << kMaxReconnectTries; | 194 << kMaxReconnectTries; |
138 mixer_.reset(new PulseAudioMixer()); | 195 |
139 connected_ = mixer_->InitSync(); | 196 connected_ = TryToConnect(false); |
197 | |
140 if (connected_) { | 198 if (connected_) { |
141 reconnect_tries_ = 0; | 199 reconnect_tries_ = 0; |
142 return true; | 200 return true; |
143 } | 201 } |
144 LOG(ERROR) << "Unable to re-connect to PulseAudio"; | 202 LOG(ERROR) << "Unable to re-connect to mixer"; |
145 } | 203 } |
146 return false; | 204 return false; |
147 } | 205 } |
148 | 206 |
149 // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us | 207 // VolumeDbToPercent() and PercentToVolumeDb() conversion functions allow us |
150 // complete control over how the 0 to 100% range is mapped to actual loudness. | 208 // complete control over how the 0 to 100% range is mapped to actual loudness. |
151 // Volume range is from kMinVolumeDb at just above 0% to kMaxVolumeDb at 100% | 209 // Volume range is from min_volume_db_ at just above 0% to max_volume_db_ |
152 // with a special case at 0% which maps to kSilenceDb. | 210 // at 100% with a special case at 0% which maps to kSilenceDb. |
153 // | 211 // |
154 // The mapping is confined to these two functions to make it easy to adjust and | 212 // The mapping is confined to these two functions to make it easy to adjust and |
155 // have everything else just work. The range is biased to give finer resolution | 213 // have everything else just work. The range is biased to give finer resolution |
156 // in the higher volumes if kVolumeBias is less than 1.0. | 214 // in the higher volumes if kVolumeBias is less than 1.0. |
157 | 215 |
158 // static | 216 // static |
159 double AudioHandler::VolumeDbToPercent(double volume_db) { | 217 double AudioHandler::VolumeDbToPercent(double volume_db) const { |
160 if (volume_db < kMinVolumeDb) | 218 if (volume_db < min_volume_db_) |
161 return 0; | 219 return 0; |
162 return 100.0 * pow((volume_db - kMinVolumeDb) / | 220 return 100.0 * pow((volume_db - min_volume_db_) / |
163 (kMaxVolumeDb - kMinVolumeDb), 1/kVolumeBias); | 221 (max_volume_db_ - min_volume_db_), 1/kVolumeBias); |
164 } | 222 } |
165 | 223 |
166 // static | 224 // static |
167 double AudioHandler::PercentToVolumeDb(double volume_percent) { | 225 double AudioHandler::PercentToVolumeDb(double volume_percent) const { |
168 return pow(volume_percent / 100.0, kVolumeBias) * | 226 return pow(volume_percent / 100.0, kVolumeBias) * |
169 (kMaxVolumeDb - kMinVolumeDb) + kMinVolumeDb; | 227 (max_volume_db_ - min_volume_db_) + min_volume_db_; |
170 } | 228 } |
171 | 229 |
172 // static | 230 // static |
173 AudioHandler* AudioHandler::GetInstance() { | 231 AudioHandler* AudioHandler::GetInstance() { |
174 return Singleton<AudioHandler>::get(); | 232 return Singleton<AudioHandler>::get(); |
175 } | 233 } |
176 | 234 |
177 } // namespace chromeos | 235 } // namespace chromeos |
OLD | NEW |