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 12 matching lines...) Expand all Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |