| 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 |