Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: chromecast/media/cma/backend/alsa/volume_control.cc

Issue 2738873002: [Chromecast] Implement new volume control API (Closed)
Patch Set: no need for ALSA volume control Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chromecast/public/volume_control.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <map>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/lazy_instance.h"
20 #include "base/location.h"
21 #include "base/logging.h"
22 #include "base/macros.h"
23 #include "base/memory/ptr_util.h"
24 #include "base/synchronization/lock.h"
25 #include "base/threading/thread.h"
26 #include "base/values.h"
27 #include "chromecast/base/init_command_line_shlib.h"
28 #include "chromecast/base/serializers.h"
29 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h"
30
31 namespace chromecast {
32 namespace media {
33
34 namespace {
35
36 const float kDefaultMediaDbFS = -25.0f;
37 const float kDefaultAlarmDbFS = -20.0f;
38 const float kDefaultCommunicationDbFS = -25.0f;
39
40 const float kMinDbFS = -120.0f;
41
42 const char kKeyMediaDbFS[] = "dbfs.media";
43 const char kKeyAlarmDbFS[] = "dbfs.alarm";
44 const char kKeyCommunicationDbFS[] = "dbfs.communication";
45
46 struct LevelToDb {
47 float level;
48 float db;
49 };
50
51 const LevelToDb kVolumeMap[] = {{0.0f, kMinDbFS},
52 {0.01f, -58.0f},
53 {0.090909f, -48.0f},
54 {0.818182f, -8.0f},
55 {1.0f, 0.0f}};
56
57 float DbFsToScale(float db) {
58 if (db <= kMinDbFS) {
59 return 0.0f;
60 }
61 return std::pow(10, db / 20);
62 }
63
64 std::string ContentTypeToDbFSKey(AudioContentType type) {
65 switch (type) {
66 case AudioContentType::kAlarm:
67 return kKeyAlarmDbFS;
68 case AudioContentType::kCommunication:
69 return kKeyCommunicationDbFS;
70 default:
71 return kKeyMediaDbFS;
72 }
73 }
74
75 class VolumeControlInternal {
76 public:
77 VolumeControlInternal() : thread_("VolumeControl") {
78 stored_values_.SetDouble(kKeyMediaDbFS, kDefaultMediaDbFS);
79 stored_values_.SetDouble(kKeyAlarmDbFS, kDefaultAlarmDbFS);
80 stored_values_.SetDouble(kKeyCommunicationDbFS, kDefaultCommunicationDbFS);
81
82 auto types = {AudioContentType::kMedia, AudioContentType::kAlarm,
83 AudioContentType::kCommunication};
84 double volume;
85
86 storage_path_ = base::GetHomeDir().Append("saved_volumes");
87 auto old_stored_data = DeserializeJsonFromFile(storage_path_);
88 base::DictionaryValue* old_stored_dict;
89 if (old_stored_data && old_stored_data->GetAsDictionary(&old_stored_dict)) {
90 for (auto type : types) {
91 if (old_stored_dict->GetDouble(ContentTypeToDbFSKey(type), &volume)) {
92 stored_values_.SetDouble(ContentTypeToDbFSKey(type), volume);
93 }
94 }
95 }
96
97 for (auto type : types) {
98 CHECK(stored_values_.GetDouble(ContentTypeToDbFSKey(type), &volume));
99 volumes_[type] = VolumeControl::DbFSToVolume(volume);
100 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(volume));
101
102 // Note that mute state is not persisted across reboots.
103 muted_[type] = false;
104 }
105
106 thread_.Start();
107 }
108
109 void AddVolumeObserver(VolumeObserver* observer) {
110 base::AutoLock lock(observer_lock_);
111 volume_observers_.push_back(observer);
112 }
113
114 void RemoveVolumeObserver(VolumeObserver* observer) {
115 base::AutoLock lock(observer_lock_);
116 volume_observers_.erase(std::remove(volume_observers_.begin(),
117 volume_observers_.end(), observer),
118 volume_observers_.end());
119 }
120
121 float GetVolume(AudioContentType type) {
122 base::AutoLock lock(volume_lock_);
123 return volumes_[type];
124 }
125
126 void SetVolume(AudioContentType type, float level) {
127 level = std::max(0.0f, std::min(level, 1.0f));
128 thread_.task_runner()->PostTask(
129 FROM_HERE, base::Bind(&VolumeControlInternal::SetVolumeOnThread,
130 base::Unretained(this), type, level));
131 }
132
133 bool IsMuted(AudioContentType type) {
134 base::AutoLock lock(volume_lock_);
135 return muted_[type];
136 }
137
138 void SetMuted(AudioContentType type, bool muted) {
139 thread_.task_runner()->PostTask(
140 FROM_HERE, base::Bind(&VolumeControlInternal::SetMutedOnThread,
141 base::Unretained(this), type, muted));
142 }
143
144 void SetOutputLimit(AudioContentType type, float limit) {
145 limit = std::max(0.0f, std::min(limit, 1.0f));
146 StreamMixerAlsa::Get()->SetOutputLimit(
147 type, DbFsToScale(VolumeControl::VolumeToDbFS(limit)));
148 }
149
150 private:
151 void SetVolumeOnThread(AudioContentType type, float level) {
152 DCHECK(thread_.task_runner()->BelongsToCurrentThread());
153 {
154 base::AutoLock lock(volume_lock_);
155 if (level == volumes_[type]) {
156 return;
157 }
158 volumes_[type] = level;
159 }
160
161 float dbfs = VolumeControl::VolumeToDbFS(level);
162 StreamMixerAlsa::Get()->SetVolume(type, DbFsToScale(dbfs));
163
164 {
165 base::AutoLock lock(observer_lock_);
166 for (VolumeObserver* observer : volume_observers_) {
167 observer->OnVolumeChange(type, level);
168 }
169 }
170
171 stored_values_.SetDouble(ContentTypeToDbFSKey(type), dbfs);
172 SerializeJsonToFile(storage_path_, stored_values_);
173 }
174
175 void SetMutedOnThread(AudioContentType type, bool muted) {
176 {
177 base::AutoLock lock(volume_lock_);
178 if (muted == muted_[type]) {
179 return;
180 }
181 muted_[type] = muted;
182 }
183
184 StreamMixerAlsa::Get()->SetMuted(type, muted);
185
186 {
187 base::AutoLock lock(observer_lock_);
188 for (VolumeObserver* observer : volume_observers_) {
189 observer->OnMuteChange(type, muted);
190 }
191 }
192 }
193
194 base::FilePath storage_path_;
195 base::DictionaryValue stored_values_;
196
197 base::Lock volume_lock_;
198 std::map<AudioContentType, float> volumes_;
199 std::map<AudioContentType, bool> muted_;
200
201 base::Lock observer_lock_;
202 std::vector<VolumeObserver*> volume_observers_;
203
204 base::Thread thread_;
205
206 DISALLOW_COPY_AND_ASSIGN(VolumeControlInternal);
207 };
208
209 base::LazyInstance<VolumeControlInternal> g_volume_control =
210 LAZY_INSTANCE_INITIALIZER;
211
212 } // namespace
213
214 // static
215 void VolumeControl::Initialize(const std::vector<std::string>& argv) {
216 chromecast::InitCommandLineShlib(argv);
217 }
218
219 // static
220 void VolumeControl::Finalize() {
221 // Nothing to do.
222 }
223
224 // static
225 void VolumeControl::AddVolumeObserver(VolumeObserver* observer) {
226 g_volume_control.Get().AddVolumeObserver(observer);
227 }
228
229 // static
230 void VolumeControl::RemoveVolumeObserver(VolumeObserver* observer) {
231 g_volume_control.Get().RemoveVolumeObserver(observer);
232 }
233
234 // static
235 float VolumeControl::GetVolume(AudioContentType type) {
236 return g_volume_control.Get().GetVolume(type);
237 }
238
239 // static
240 void VolumeControl::SetVolume(AudioContentType type, float level) {
241 g_volume_control.Get().SetVolume(type, level);
242 }
243
244 // static
245 bool VolumeControl::IsMuted(AudioContentType type) {
246 return g_volume_control.Get().IsMuted(type);
247 }
248
249 // static
250 void VolumeControl::SetMuted(AudioContentType type, bool muted) {
251 g_volume_control.Get().SetMuted(type, muted);
252 }
253
254 // static
255 void VolumeControl::SetOutputLimit(AudioContentType type, float limit) {
256 g_volume_control.Get().SetOutputLimit(type, limit);
257 }
258
259 // static
260 float VolumeControl::VolumeToDbFS(float volume) {
261 if (volume <= kVolumeMap[0].level) {
262 return kVolumeMap[0].db;
263 }
264 for (size_t i = 1; i < arraysize(kVolumeMap); ++i) {
265 if (volume < kVolumeMap[i].level) {
266 const float x_diff = kVolumeMap[i].level - kVolumeMap[i - 1].level;
267 const float y_diff = kVolumeMap[i].db - kVolumeMap[i - 1].db;
268
269 return kVolumeMap[i - 1].db +
270 (volume - kVolumeMap[i - 1].level) * y_diff / x_diff;
271 }
272 }
273 return kVolumeMap[arraysize(kVolumeMap) - 1].db;
274 }
275
276 // static
277 float VolumeControl::DbFSToVolume(float db) {
278 if (db <= kVolumeMap[0].db) {
279 return kVolumeMap[0].level;
280 }
281 for (size_t i = 1; i < arraysize(kVolumeMap); ++i) {
282 if (db < kVolumeMap[i].db) {
283 const float x_diff = kVolumeMap[i].db - kVolumeMap[i - 1].db;
284 const float y_diff = kVolumeMap[i].level - kVolumeMap[i - 1].level;
285
286 return kVolumeMap[i - 1].level +
287 (db - kVolumeMap[i - 1].db) * y_diff / x_diff;
288 }
289 }
290 return kVolumeMap[arraysize(kVolumeMap) - 1].level;
291 }
292
293 } // namespace media
294 } // namespace chromecast
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698