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

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

Issue 2958393002: Add a simple, safe gain PostProcessor. (Closed)
Patch Set: Unrelated cleanup Created 3 years, 5 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
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 12 matching lines...) Expand all
23 #include "base/memory/ptr_util.h" 23 #include "base/memory/ptr_util.h"
24 #include "base/message_loop/message_loop.h" 24 #include "base/message_loop/message_loop.h"
25 #include "base/synchronization/lock.h" 25 #include "base/synchronization/lock.h"
26 #include "base/synchronization/waitable_event.h" 26 #include "base/synchronization/waitable_event.h"
27 #include "base/threading/thread.h" 27 #include "base/threading/thread.h"
28 #include "base/values.h" 28 #include "base/values.h"
29 #include "chromecast/base/init_command_line_shlib.h" 29 #include "chromecast/base/init_command_line_shlib.h"
30 #include "chromecast/base/serializers.h" 30 #include "chromecast/base/serializers.h"
31 #include "chromecast/media/cma/backend/alsa/alsa_features.h" 31 #include "chromecast/media/cma/backend/alsa/alsa_features.h"
32 #include "chromecast/media/cma/backend/alsa/alsa_volume_control.h" 32 #include "chromecast/media/cma/backend/alsa/alsa_volume_control.h"
33 #include "chromecast/media/cma/backend/alsa/cast_audio_json.h"
33 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h" 34 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h"
34 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" 35 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h"
36 #include "chromecast/media/cma/backend/alsa/volume_map.h"
35 37
36 namespace chromecast { 38 namespace chromecast {
37 namespace media { 39 namespace media {
38 40
39 namespace { 41 namespace {
40 42
41 constexpr float kDefaultMediaDbFS = -25.0f; 43 constexpr float kDefaultMediaDbFS = -25.0f;
42 constexpr float kDefaultAlarmDbFS = -20.0f; 44 constexpr float kDefaultAlarmDbFS = -20.0f;
43 constexpr float kDefaultCommunicationDbFS = -25.0f; 45 constexpr float kDefaultCommunicationDbFS = -25.0f;
44 46
45 constexpr float kMinDbFS = -120.0f; 47 constexpr float kMinDbFS = -120.0f;
46 48
47 constexpr char kKeyMediaDbFS[] = "dbfs.media"; 49 constexpr char kKeyMediaDbFS[] = "dbfs.media";
48 constexpr char kKeyAlarmDbFS[] = "dbfs.alarm"; 50 constexpr char kKeyAlarmDbFS[] = "dbfs.alarm";
49 constexpr char kKeyCommunicationDbFS[] = "dbfs.communication"; 51 constexpr char kKeyCommunicationDbFS[] = "dbfs.communication";
50 constexpr char kKeyVolumeMap[] = "volume_map";
51 constexpr char kKeyLevel[] = "level";
52 constexpr char kKeyDb[] = "db";
53 constexpr char kKeyDefaultVolume[] = "default_volume"; 52 constexpr char kKeyDefaultVolume[] = "default_volume";
54 53
55 struct LevelToDb {
56 float level;
57 float db;
58 };
59
60 const LevelToDb kDefaultVolumeMap[] = {{0.0f, kMinDbFS},
61 {0.01f, -58.0f},
62 {0.090909f, -48.0f},
63 {0.818182f, -8.0f},
64 {1.0f, 0.0f}};
65
66 float DbFsToScale(float db) { 54 float DbFsToScale(float db) {
67 if (db <= kMinDbFS) { 55 if (db <= kMinDbFS) {
68 return 0.0f; 56 return 0.0f;
69 } 57 }
70 return std::pow(10, db / 20); 58 return std::pow(10, db / 20);
71 } 59 }
72 60
73 std::string ContentTypeToDbFSKey(AudioContentType type) { 61 std::string ContentTypeToDbFSKey(AudioContentType type) {
74 switch (type) { 62 switch (type) {
75 case AudioContentType::kAlarm: 63 case AudioContentType::kAlarm:
76 return kKeyAlarmDbFS; 64 return kKeyAlarmDbFS;
77 case AudioContentType::kCommunication: 65 case AudioContentType::kCommunication:
78 return kKeyCommunicationDbFS; 66 return kKeyCommunicationDbFS;
79 default: 67 default:
80 return kKeyMediaDbFS; 68 return kKeyMediaDbFS;
81 } 69 }
82 } 70 }
83 71
84 class VolumeMap {
85 public:
86 VolumeMap() {
87 auto cast_audio_config = DeserializeJsonFromFile(
88 base::FilePath(PostProcessingPipelineParser::GetFilePath()));
89 const base::DictionaryValue* cast_audio_dict;
90 if (!cast_audio_config ||
91 !cast_audio_config->GetAsDictionary(&cast_audio_dict)) {
92 LOG(WARNING) << "No cast audio config found; using default volume map.";
93 volume_map_.insert(volume_map_.end(), kDefaultVolumeMap,
94 kDefaultVolumeMap + arraysize(kDefaultVolumeMap));
95 return;
96 }
97
98 const base::ListValue* volume_map_list;
99 if (!cast_audio_dict->GetList(kKeyVolumeMap, &volume_map_list)) {
100 LOG(WARNING) << "No volume map found; using default volume map.";
101 volume_map_.insert(volume_map_.end(), kDefaultVolumeMap,
102 kDefaultVolumeMap + arraysize(kDefaultVolumeMap));
103 return;
104 }
105
106 double prev_level = -1.0;
107 for (size_t i = 0; i < volume_map_list->GetSize(); ++i) {
108 const base::DictionaryValue* volume_map_entry;
109 CHECK(volume_map_list->GetDictionary(i, &volume_map_entry));
110
111 double level;
112 CHECK(volume_map_entry->GetDouble(kKeyLevel, &level));
113 CHECK_GE(level, 0.0);
114 CHECK_LE(level, 1.0);
115 CHECK_GT(level, prev_level);
116 prev_level = level;
117
118 double db;
119 CHECK(volume_map_entry->GetDouble(kKeyDb, &db));
120 CHECK_LE(db, 0.0);
121 if (level == 1.0) {
122 CHECK_EQ(db, 0.0);
123 }
124
125 volume_map_.push_back({level, db});
126 }
127
128 if (volume_map_.empty()) {
129 LOG(FATAL) << "No entries in volume map.";
130 return;
131 }
132
133 if (volume_map_[0].level > 0.0) {
134 volume_map_.insert(volume_map_.begin(), {0.0, kMinDbFS});
135 }
136
137 if (volume_map_.rbegin()->level < 1.0) {
138 volume_map_.push_back({1.0, 0.0});
139 }
140 }
141
142 float VolumeToDbFS(float volume) {
143 if (volume <= volume_map_[0].level) {
144 return volume_map_[0].db;
145 }
146 for (size_t i = 1; i < volume_map_.size(); ++i) {
147 if (volume < volume_map_[i].level) {
148 const float x_range = volume_map_[i].level - volume_map_[i - 1].level;
149 const float y_range = volume_map_[i].db - volume_map_[i - 1].db;
150 const float x_pos = volume - volume_map_[i - 1].level;
151
152 return volume_map_[i - 1].db + x_pos * y_range / x_range;
153 }
154 }
155 return volume_map_[volume_map_.size() - 1].db;
156 }
157
158 // static
159 float DbFSToVolume(float db) {
160 if (db <= volume_map_[0].db) {
161 return volume_map_[0].level;
162 }
163 for (size_t i = 1; i < volume_map_.size(); ++i) {
164 if (db < volume_map_[i].db) {
165 const float x_range = volume_map_[i].db - volume_map_[i - 1].db;
166 const float y_range = volume_map_[i].level - volume_map_[i - 1].level;
167 const float x_pos = db - volume_map_[i - 1].db;
168
169 return volume_map_[i - 1].level + x_pos * y_range / x_range;
170 }
171 }
172 return volume_map_[volume_map_.size() - 1].level;
173 }
174
175 private:
176 std::vector<LevelToDb> volume_map_;
177
178 DISALLOW_COPY_AND_ASSIGN(VolumeMap);
179 };
180
181 base::LazyInstance<VolumeMap>::Leaky g_volume_map = LAZY_INSTANCE_INITIALIZER; 72 base::LazyInstance<VolumeMap>::Leaky g_volume_map = LAZY_INSTANCE_INITIALIZER;
182 73
183 class VolumeControlInternal : public AlsaVolumeControl::Delegate { 74 class VolumeControlInternal : public AlsaVolumeControl::Delegate {
184 public: 75 public:
185 VolumeControlInternal() 76 VolumeControlInternal()
186 : thread_("VolumeControl"), 77 : thread_("VolumeControl"),
187 initialize_complete_event_( 78 initialize_complete_event_(
188 base::WaitableEvent::ResetPolicy::MANUAL, 79 base::WaitableEvent::ResetPolicy::MANUAL,
189 base::WaitableEvent::InitialState::NOT_SIGNALED) { 80 base::WaitableEvent::InitialState::NOT_SIGNALED) {
190 // Load volume map to check that the config file is correct. 81 // Load volume map to check that the config file is correct.
(...skipping 11 matching lines...) Expand all
202 auto old_stored_data = DeserializeJsonFromFile(storage_path_); 93 auto old_stored_data = DeserializeJsonFromFile(storage_path_);
203 base::DictionaryValue* old_stored_dict; 94 base::DictionaryValue* old_stored_dict;
204 if (old_stored_data && old_stored_data->GetAsDictionary(&old_stored_dict)) { 95 if (old_stored_data && old_stored_data->GetAsDictionary(&old_stored_dict)) {
205 for (auto type : types) { 96 for (auto type : types) {
206 if (old_stored_dict->GetDouble(ContentTypeToDbFSKey(type), &volume)) { 97 if (old_stored_dict->GetDouble(ContentTypeToDbFSKey(type), &volume)) {
207 stored_values_.SetDouble(ContentTypeToDbFSKey(type), volume); 98 stored_values_.SetDouble(ContentTypeToDbFSKey(type), volume);
208 } 99 }
209 } 100 }
210 } else { 101 } else {
211 // If saved_volumes does not exist, use per device default if it exists. 102 // If saved_volumes does not exist, use per device default if it exists.
212 auto cast_audio_config = DeserializeJsonFromFile( 103 auto cast_audio_config =
213 base::FilePath(PostProcessingPipelineParser::GetFilePath())); 104 DeserializeJsonFromFile(base::FilePath(kCastAudioJsonFilePath));
214 const base::DictionaryValue* cast_audio_dict; 105 const base::DictionaryValue* cast_audio_dict;
215 if (cast_audio_config && 106 if (cast_audio_config &&
216 cast_audio_config->GetAsDictionary(&cast_audio_dict)) { 107 cast_audio_config->GetAsDictionary(&cast_audio_dict)) {
217 const base::DictionaryValue* default_volume_dict; 108 const base::DictionaryValue* default_volume_dict;
218 if (cast_audio_dict && cast_audio_dict->GetDictionary( 109 if (cast_audio_dict && cast_audio_dict->GetDictionary(
219 kKeyDefaultVolume, &default_volume_dict)) { 110 kKeyDefaultVolume, &default_volume_dict)) {
220 for (auto type : types) { 111 for (auto type : types) {
221 if (default_volume_dict->GetDouble(ContentTypeToDbFSKey(type), 112 if (default_volume_dict->GetDouble(ContentTypeToDbFSKey(type),
222 &volume)) { 113 &volume)) {
223 stored_values_.SetDouble(ContentTypeToDbFSKey(type), volume); 114 stored_values_.SetDouble(ContentTypeToDbFSKey(type), volume);
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 return g_volume_map.Get().VolumeToDbFS(volume); 357 return g_volume_map.Get().VolumeToDbFS(volume);
467 } 358 }
468 359
469 // static 360 // static
470 float VolumeControl::DbFSToVolume(float db) { 361 float VolumeControl::DbFSToVolume(float db) {
471 return g_volume_map.Get().DbFSToVolume(db); 362 return g_volume_map.Get().DbFSToVolume(db);
472 } 363 }
473 364
474 } // namespace media 365 } // namespace media
475 } // namespace chromecast 366 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc ('k') | chromecast/media/cma/backend/alsa/volume_map.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698