OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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 "chromecast/public/volume_control.h" | 5 #include "chromecast/public/volume_control.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cmath> | 8 #include <cmath> |
9 #include <map> | 9 #include <map> |
10 #include <memory> | 10 #include <memory> |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 } | 126 } |
127 | 127 |
128 float GetVolume(AudioContentType type) { | 128 float GetVolume(AudioContentType type) { |
129 base::AutoLock lock(volume_lock_); | 129 base::AutoLock lock(volume_lock_); |
130 return volumes_[type]; | 130 return volumes_[type]; |
131 } | 131 } |
132 | 132 |
133 void SetVolume(AudioContentType type, float level) { | 133 void SetVolume(AudioContentType type, float level) { |
134 level = std::max(0.0f, std::min(level, 1.0f)); | 134 level = std::max(0.0f, std::min(level, 1.0f)); |
135 thread_.task_runner()->PostTask( | 135 thread_.task_runner()->PostTask( |
136 FROM_HERE, base::Bind(&VolumeControlInternal::SetVolumeOnThread, | 136 FROM_HERE, |
137 base::Unretained(this), type, level)); | 137 base::Bind(&VolumeControlInternal::SetVolumeOnThread, |
| 138 base::Unretained(this), type, level, false /* from_alsa */)); |
138 } | 139 } |
139 | 140 |
140 bool IsMuted(AudioContentType type) { | 141 bool IsMuted(AudioContentType type) { |
141 base::AutoLock lock(volume_lock_); | 142 base::AutoLock lock(volume_lock_); |
142 return muted_[type]; | 143 return muted_[type]; |
143 } | 144 } |
144 | 145 |
145 void SetMuted(AudioContentType type, bool muted) { | 146 void SetMuted(AudioContentType type, bool muted) { |
146 thread_.task_runner()->PostTask( | 147 thread_.task_runner()->PostTask( |
147 FROM_HERE, base::Bind(&VolumeControlInternal::SetMutedOnThread, | 148 FROM_HERE, |
148 base::Unretained(this), type, muted)); | 149 base::Bind(&VolumeControlInternal::SetMutedOnThread, |
| 150 base::Unretained(this), type, muted, false /* from_alsa */)); |
149 } | 151 } |
150 | 152 |
151 void SetOutputLimit(AudioContentType type, float limit) { | 153 void SetOutputLimit(AudioContentType type, float limit) { |
152 if (BUILDFLAG(ALSA_CONTROLS_VOLUME)) { | 154 if (BUILDFLAG(ALSA_OWNS_VOLUME)) { |
153 return; | 155 return; |
154 } | 156 } |
155 limit = std::max(0.0f, std::min(limit, 1.0f)); | 157 limit = std::max(0.0f, std::min(limit, 1.0f)); |
156 StreamMixerAlsa::Get()->SetOutputLimit( | 158 StreamMixerAlsa::Get()->SetOutputLimit( |
157 type, DbFsToScale(VolumeControl::VolumeToDbFS(limit))); | 159 type, DbFsToScale(VolumeControl::VolumeToDbFS(limit))); |
158 } | 160 } |
159 | 161 |
160 private: | 162 private: |
161 void InitializeOnThread() { | 163 void InitializeOnThread() { |
162 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); | 164 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); |
163 alsa_volume_control_ = base::MakeUnique<AlsaVolumeControl>(this); | 165 alsa_volume_control_ = base::MakeUnique<AlsaVolumeControl>(this); |
164 | 166 |
165 double dbfs; | 167 double dbfs; |
166 for (auto type : {AudioContentType::kMedia, AudioContentType::kAlarm, | 168 for (auto type : {AudioContentType::kMedia, AudioContentType::kAlarm, |
167 AudioContentType::kCommunication}) { | 169 AudioContentType::kCommunication}) { |
168 CHECK(stored_values_.GetDouble(ContentTypeToDbFSKey(type), &dbfs)); | 170 CHECK(stored_values_.GetDouble(ContentTypeToDbFSKey(type), &dbfs)); |
169 volumes_[type] = VolumeControl::DbFSToVolume(dbfs); | 171 volumes_[type] = VolumeControl::DbFSToVolume(dbfs); |
170 if (BUILDFLAG(ALSA_CONTROLS_VOLUME)) { | 172 if (BUILDFLAG(ALSA_OWNS_VOLUME)) { |
171 // If ALSA controls volume, our internal mixer should not apply any | 173 // If ALSA owns volume, our internal mixer should not apply any scaling |
172 // scaling multiplier. | 174 // multiplier. |
173 StreamMixerAlsa::Get()->SetVolume(type, 1.0f); | 175 StreamMixerAlsa::Get()->SetVolume(type, 1.0f); |
174 } else { | 176 } else { |
175 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(dbfs)); | 177 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(dbfs)); |
176 } | 178 } |
177 | 179 |
178 // Note that mute state is not persisted across reboots. | 180 // Note that mute state is not persisted across reboots. |
179 muted_[type] = false; | 181 muted_[type] = false; |
180 } | 182 } |
181 | 183 |
182 if (BUILDFLAG(ALSA_CONTROLS_VOLUME)) { | 184 if (BUILDFLAG(ALSA_OWNS_VOLUME)) { |
183 // If ALSA controls the volume, then read the current volume and mute | 185 // If ALSA owns the volume, then read the current volume and mute state |
184 // state from the ALSA mixer element(s). | 186 // from the ALSA mixer element(s). |
185 volumes_[AudioContentType::kMedia] = alsa_volume_control_->GetVolume(); | 187 volumes_[AudioContentType::kMedia] = alsa_volume_control_->GetVolume(); |
186 muted_[AudioContentType::kMedia] = alsa_volume_control_->IsMuted(); | 188 muted_[AudioContentType::kMedia] = alsa_volume_control_->IsMuted(); |
187 } else { | 189 } else { |
188 // Otherwise, make sure the ALSA mixer element correctly reflects the | 190 // Otherwise, make sure the ALSA mixer element correctly reflects the |
189 // current volume state. | 191 // current volume state. |
190 alsa_volume_control_->SetVolume(volumes_[AudioContentType::kMedia]); | 192 alsa_volume_control_->SetVolume(volumes_[AudioContentType::kMedia]); |
191 alsa_volume_control_->SetMuted(false); | 193 alsa_volume_control_->SetMuted(false); |
192 } | 194 } |
193 | 195 |
194 initialize_complete_event_.Signal(); | 196 initialize_complete_event_.Signal(); |
195 } | 197 } |
196 | 198 |
197 void SetVolumeOnThread(AudioContentType type, float level) { | 199 void SetVolumeOnThread(AudioContentType type, float level, bool from_alsa) { |
198 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); | 200 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); |
| 201 DCHECK(!from_alsa || type == AudioContentType::kMedia); |
199 { | 202 { |
200 base::AutoLock lock(volume_lock_); | 203 base::AutoLock lock(volume_lock_); |
| 204 if (from_alsa && alsa_volume_control_->VolumeThroughAlsa( |
| 205 volumes_[AudioContentType::kMedia]) == level) { |
| 206 return; |
| 207 } |
201 if (level == volumes_[type]) { | 208 if (level == volumes_[type]) { |
202 return; | 209 return; |
203 } | 210 } |
204 volumes_[type] = level; | 211 volumes_[type] = level; |
205 } | 212 } |
206 | 213 |
207 float dbfs = VolumeControl::VolumeToDbFS(level); | 214 float dbfs = VolumeControl::VolumeToDbFS(level); |
208 if (!BUILDFLAG(ALSA_CONTROLS_VOLUME)) { | 215 if (!BUILDFLAG(ALSA_OWNS_VOLUME)) { |
209 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(dbfs)); | 216 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(dbfs)); |
210 } | 217 } |
211 | 218 |
212 if (type == AudioContentType::kMedia) { | 219 if (!from_alsa && type == AudioContentType::kMedia) { |
213 alsa_volume_control_->SetVolume(level); | 220 alsa_volume_control_->SetVolume(level); |
214 } | 221 } |
215 | 222 |
216 { | 223 { |
217 base::AutoLock lock(observer_lock_); | 224 base::AutoLock lock(observer_lock_); |
218 for (VolumeObserver* observer : volume_observers_) { | 225 for (VolumeObserver* observer : volume_observers_) { |
219 observer->OnVolumeChange(type, level); | 226 observer->OnVolumeChange(type, level); |
220 } | 227 } |
221 } | 228 } |
222 | 229 |
223 stored_values_.SetDouble(ContentTypeToDbFSKey(type), dbfs); | 230 stored_values_.SetDouble(ContentTypeToDbFSKey(type), dbfs); |
224 SerializeJsonToFile(storage_path_, stored_values_); | 231 SerializeJsonToFile(storage_path_, stored_values_); |
225 } | 232 } |
226 | 233 |
227 void SetMutedOnThread(AudioContentType type, bool muted) { | 234 void SetMutedOnThread(AudioContentType type, bool muted, bool from_alsa) { |
228 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); | 235 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); |
229 { | 236 { |
230 base::AutoLock lock(volume_lock_); | 237 base::AutoLock lock(volume_lock_); |
231 if (muted == muted_[type]) { | 238 if (muted == muted_[type]) { |
232 return; | 239 return; |
233 } | 240 } |
234 muted_[type] = muted; | 241 muted_[type] = muted; |
235 } | 242 } |
236 | 243 |
237 if (!BUILDFLAG(ALSA_CONTROLS_VOLUME)) { | 244 if (!BUILDFLAG(ALSA_OWNS_VOLUME)) { |
238 StreamMixerAlsa::Get()->SetMuted(type, muted); | 245 StreamMixerAlsa::Get()->SetMuted(type, muted); |
239 } | 246 } |
240 | 247 |
241 if (type == AudioContentType::kMedia) { | 248 if (!from_alsa && type == AudioContentType::kMedia) { |
242 alsa_volume_control_->SetMuted(muted); | 249 alsa_volume_control_->SetMuted(muted); |
243 } | 250 } |
244 | 251 |
245 { | 252 { |
246 base::AutoLock lock(observer_lock_); | 253 base::AutoLock lock(observer_lock_); |
247 for (VolumeObserver* observer : volume_observers_) { | 254 for (VolumeObserver* observer : volume_observers_) { |
248 observer->OnMuteChange(type, muted); | 255 observer->OnMuteChange(type, muted); |
249 } | 256 } |
250 } | 257 } |
251 } | 258 } |
252 | 259 |
253 // AlsaVolumeControl::Delegate implementation: | 260 // AlsaVolumeControl::Delegate implementation: |
254 void OnAlsaVolumeOrMuteChange(float new_volume, bool new_mute) override { | 261 void OnAlsaVolumeOrMuteChange(float new_volume, bool new_mute) override { |
255 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); | 262 DCHECK(thread_.task_runner()->BelongsToCurrentThread()); |
256 bool volume_changed = false; | 263 SetVolumeOnThread(AudioContentType::kMedia, new_volume, |
257 bool mute_changed = false; | 264 true /* from_alsa */); |
258 { | 265 SetMutedOnThread(AudioContentType::kMedia, new_mute, true /* from_alsa */); |
259 base::AutoLock lock(volume_lock_); | |
260 if (alsa_volume_control_->VolumeThroughAlsa( | |
261 volumes_[AudioContentType::kMedia]) != new_volume) { | |
262 volume_changed = true; | |
263 volumes_[AudioContentType::kMedia] = new_volume; | |
264 } | |
265 | |
266 if (muted_[AudioContentType::kMedia] != new_mute) { | |
267 mute_changed = true; | |
268 muted_[AudioContentType::kMedia] = new_mute; | |
269 } | |
270 } | |
271 | |
272 if (!volume_changed && !mute_changed) { | |
273 return; | |
274 } | |
275 | |
276 { | |
277 base::AutoLock lock(observer_lock_); | |
278 if (volume_changed) { | |
279 for (VolumeObserver* observer : volume_observers_) { | |
280 observer->OnVolumeChange(AudioContentType::kMedia, new_volume); | |
281 } | |
282 } | |
283 if (mute_changed) { | |
284 for (VolumeObserver* observer : volume_observers_) { | |
285 observer->OnMuteChange(AudioContentType::kMedia, new_mute); | |
286 } | |
287 } | |
288 } | |
289 | |
290 if (volume_changed) { | |
291 float dbfs = VolumeControl::VolumeToDbFS(new_volume); | |
292 stored_values_.SetDouble(ContentTypeToDbFSKey(AudioContentType::kMedia), | |
293 dbfs); | |
294 SerializeJsonToFile(storage_path_, stored_values_); | |
295 } | |
296 } | 266 } |
297 | 267 |
298 base::FilePath storage_path_; | 268 base::FilePath storage_path_; |
299 base::DictionaryValue stored_values_; | 269 base::DictionaryValue stored_values_; |
300 | 270 |
301 base::Lock volume_lock_; | 271 base::Lock volume_lock_; |
302 std::map<AudioContentType, float> volumes_; | 272 std::map<AudioContentType, float> volumes_; |
303 std::map<AudioContentType, bool> muted_; | 273 std::map<AudioContentType, bool> muted_; |
304 | 274 |
305 base::Lock observer_lock_; | 275 base::Lock observer_lock_; |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 | 362 |
393 return kVolumeMap[i - 1].level + | 363 return kVolumeMap[i - 1].level + |
394 (db - kVolumeMap[i - 1].db) * y_diff / x_diff; | 364 (db - kVolumeMap[i - 1].db) * y_diff / x_diff; |
395 } | 365 } |
396 } | 366 } |
397 return kVolumeMap[arraysize(kVolumeMap) - 1].level; | 367 return kVolumeMap[arraysize(kVolumeMap) - 1].level; |
398 } | 368 } |
399 | 369 |
400 } // namespace media | 370 } // namespace media |
401 } // namespace chromecast | 371 } // namespace chromecast |
OLD | NEW |