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 14 matching lines...) Expand all Loading... |
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/post_processing_pipeline_parser.h" | 33 #include "chromecast/media/cma/backend/alsa/post_processing_pipeline_parser.h" |
34 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" | 34 #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" |
| 35 #include "chromecast/media/cma/backend/alsa/volume_map.h" |
35 | 36 |
36 namespace chromecast { | 37 namespace chromecast { |
37 namespace media { | 38 namespace media { |
38 | 39 |
39 namespace { | 40 namespace { |
40 | 41 |
41 constexpr float kDefaultMediaDbFS = -25.0f; | 42 constexpr float kDefaultMediaDbFS = -25.0f; |
42 constexpr float kDefaultAlarmDbFS = -20.0f; | 43 constexpr float kDefaultAlarmDbFS = -20.0f; |
43 constexpr float kDefaultCommunicationDbFS = -25.0f; | 44 constexpr float kDefaultCommunicationDbFS = -25.0f; |
44 | 45 |
45 constexpr float kMinDbFS = -120.0f; | 46 constexpr float kMinDbFS = -120.0f; |
46 | 47 |
47 constexpr char kKeyMediaDbFS[] = "dbfs.media"; | 48 constexpr char kKeyMediaDbFS[] = "dbfs.media"; |
48 constexpr char kKeyAlarmDbFS[] = "dbfs.alarm"; | 49 constexpr char kKeyAlarmDbFS[] = "dbfs.alarm"; |
49 constexpr char kKeyCommunicationDbFS[] = "dbfs.communication"; | 50 constexpr char kKeyCommunicationDbFS[] = "dbfs.communication"; |
50 constexpr char kKeyVolumeMap[] = "volume_map"; | |
51 constexpr char kKeyLevel[] = "level"; | |
52 constexpr char kKeyDb[] = "db"; | |
53 | |
54 struct LevelToDb { | |
55 float level; | |
56 float db; | |
57 }; | |
58 | |
59 const LevelToDb kDefaultVolumeMap[] = {{0.0f, kMinDbFS}, | |
60 {0.01f, -58.0f}, | |
61 {0.090909f, -48.0f}, | |
62 {0.818182f, -8.0f}, | |
63 {1.0f, 0.0f}}; | |
64 | 51 |
65 float DbFsToScale(float db) { | 52 float DbFsToScale(float db) { |
66 if (db <= kMinDbFS) { | 53 if (db <= kMinDbFS) { |
67 return 0.0f; | 54 return 0.0f; |
68 } | 55 } |
69 return std::pow(10, db / 20); | 56 return std::pow(10, db / 20); |
70 } | 57 } |
71 | 58 |
72 std::string ContentTypeToDbFSKey(AudioContentType type) { | 59 std::string ContentTypeToDbFSKey(AudioContentType type) { |
73 switch (type) { | 60 switch (type) { |
74 case AudioContentType::kAlarm: | 61 case AudioContentType::kAlarm: |
75 return kKeyAlarmDbFS; | 62 return kKeyAlarmDbFS; |
76 case AudioContentType::kCommunication: | 63 case AudioContentType::kCommunication: |
77 return kKeyCommunicationDbFS; | 64 return kKeyCommunicationDbFS; |
78 default: | 65 default: |
79 return kKeyMediaDbFS; | 66 return kKeyMediaDbFS; |
80 } | 67 } |
81 } | 68 } |
82 | 69 |
83 class VolumeMap { | |
84 public: | |
85 VolumeMap() { | |
86 auto cast_audio_config = DeserializeJsonFromFile( | |
87 base::FilePath(PostProcessingPipelineParser::GetFilePath())); | |
88 const base::DictionaryValue* cast_audio_dict; | |
89 if (!cast_audio_config || | |
90 !cast_audio_config->GetAsDictionary(&cast_audio_dict)) { | |
91 LOG(WARNING) << "No cast audio config found; using default volume map."; | |
92 volume_map_.insert(volume_map_.end(), kDefaultVolumeMap, | |
93 kDefaultVolumeMap + arraysize(kDefaultVolumeMap)); | |
94 return; | |
95 } | |
96 | |
97 const base::ListValue* volume_map_list; | |
98 if (!cast_audio_dict->GetList(kKeyVolumeMap, &volume_map_list)) { | |
99 LOG(WARNING) << "No volume map found; using default volume map."; | |
100 volume_map_.insert(volume_map_.end(), kDefaultVolumeMap, | |
101 kDefaultVolumeMap + arraysize(kDefaultVolumeMap)); | |
102 return; | |
103 } | |
104 | |
105 double prev_level = -1.0; | |
106 for (size_t i = 0; i < volume_map_list->GetSize(); ++i) { | |
107 const base::DictionaryValue* volume_map_entry; | |
108 CHECK(volume_map_list->GetDictionary(i, &volume_map_entry)); | |
109 | |
110 double level; | |
111 CHECK(volume_map_entry->GetDouble(kKeyLevel, &level)); | |
112 CHECK_GE(level, 0.0); | |
113 CHECK_LE(level, 1.0); | |
114 CHECK_GT(level, prev_level); | |
115 prev_level = level; | |
116 | |
117 double db; | |
118 CHECK(volume_map_entry->GetDouble(kKeyDb, &db)); | |
119 CHECK_LE(db, 0.0); | |
120 if (level == 1.0) { | |
121 CHECK_EQ(db, 0.0); | |
122 } | |
123 | |
124 volume_map_.push_back({level, db}); | |
125 } | |
126 | |
127 if (volume_map_.empty()) { | |
128 LOG(FATAL) << "No entries in volume map."; | |
129 return; | |
130 } | |
131 | |
132 if (volume_map_[0].level > 0.0) { | |
133 volume_map_.insert(volume_map_.begin(), {0.0, kMinDbFS}); | |
134 } | |
135 | |
136 if (volume_map_.rbegin()->level < 1.0) { | |
137 volume_map_.push_back({1.0, 0.0}); | |
138 } | |
139 } | |
140 | |
141 float VolumeToDbFS(float volume) { | |
142 if (volume <= volume_map_[0].level) { | |
143 return volume_map_[0].db; | |
144 } | |
145 for (size_t i = 1; i < volume_map_.size(); ++i) { | |
146 if (volume < volume_map_[i].level) { | |
147 const float x_range = volume_map_[i].level - volume_map_[i - 1].level; | |
148 const float y_range = volume_map_[i].db - volume_map_[i - 1].db; | |
149 const float x_pos = volume - volume_map_[i - 1].level; | |
150 | |
151 return volume_map_[i - 1].db + x_pos * y_range / x_range; | |
152 } | |
153 } | |
154 return volume_map_[volume_map_.size() - 1].db; | |
155 } | |
156 | |
157 // static | |
158 float DbFSToVolume(float db) { | |
159 if (db <= volume_map_[0].db) { | |
160 return volume_map_[0].level; | |
161 } | |
162 for (size_t i = 1; i < volume_map_.size(); ++i) { | |
163 if (db < volume_map_[i].db) { | |
164 const float x_range = volume_map_[i].db - volume_map_[i - 1].db; | |
165 const float y_range = volume_map_[i].level - volume_map_[i - 1].level; | |
166 const float x_pos = db - volume_map_[i - 1].db; | |
167 | |
168 return volume_map_[i - 1].level + x_pos * y_range / x_range; | |
169 } | |
170 } | |
171 return volume_map_[volume_map_.size() - 1].level; | |
172 } | |
173 | |
174 private: | |
175 std::vector<LevelToDb> volume_map_; | |
176 | |
177 DISALLOW_COPY_AND_ASSIGN(VolumeMap); | |
178 }; | |
179 | |
180 base::LazyInstance<VolumeMap>::Leaky g_volume_map = LAZY_INSTANCE_INITIALIZER; | 70 base::LazyInstance<VolumeMap>::Leaky g_volume_map = LAZY_INSTANCE_INITIALIZER; |
181 | 71 |
182 class VolumeControlInternal : public AlsaVolumeControl::Delegate { | 72 class VolumeControlInternal : public AlsaVolumeControl::Delegate { |
183 public: | 73 public: |
184 VolumeControlInternal() | 74 VolumeControlInternal() |
185 : thread_("VolumeControl"), | 75 : thread_("VolumeControl"), |
186 initialize_complete_event_( | 76 initialize_complete_event_( |
187 base::WaitableEvent::ResetPolicy::MANUAL, | 77 base::WaitableEvent::ResetPolicy::MANUAL, |
188 base::WaitableEvent::InitialState::NOT_SIGNALED) { | 78 base::WaitableEvent::InitialState::NOT_SIGNALED) { |
189 // Load volume map to check that the config file is correct. | 79 // Load volume map to check that the config file is correct. |
(...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 return g_volume_map.Get().VolumeToDbFS(volume); | 335 return g_volume_map.Get().VolumeToDbFS(volume); |
446 } | 336 } |
447 | 337 |
448 // static | 338 // static |
449 float VolumeControl::DbFSToVolume(float db) { | 339 float VolumeControl::DbFSToVolume(float db) { |
450 return g_volume_map.Get().DbFSToVolume(db); | 340 return g_volume_map.Get().DbFSToVolume(db); |
451 } | 341 } |
452 | 342 |
453 } // namespace media | 343 } // namespace media |
454 } // namespace chromecast | 344 } // namespace chromecast |
OLD | NEW |