• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/audio_processing/agc2/interpolated_gain_curve.h"
12 
13 #include <algorithm>
14 #include <iterator>
15 
16 #include "modules/audio_processing/agc2/agc2_common.h"
17 #include "modules/audio_processing/logging/apm_data_dumper.h"
18 #include "rtc_base/checks.h"
19 
20 namespace webrtc {
21 
22 constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
23     InterpolatedGainCurve::approximation_params_x_;
24 
25 constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
26     InterpolatedGainCurve::approximation_params_m_;
27 
28 constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
29     InterpolatedGainCurve::approximation_params_q_;
30 
InterpolatedGainCurve(ApmDataDumper * apm_data_dumper,std::string histogram_name_prefix)31 InterpolatedGainCurve::InterpolatedGainCurve(ApmDataDumper* apm_data_dumper,
32                                              std::string histogram_name_prefix)
33     : region_logger_("WebRTC.Audio." + histogram_name_prefix +
34                          ".FixedDigitalGainCurveRegion.Identity",
35                      "WebRTC.Audio." + histogram_name_prefix +
36                          ".FixedDigitalGainCurveRegion.Knee",
37                      "WebRTC.Audio." + histogram_name_prefix +
38                          ".FixedDigitalGainCurveRegion.Limiter",
39                      "WebRTC.Audio." + histogram_name_prefix +
40                          ".FixedDigitalGainCurveRegion.Saturation"),
41       apm_data_dumper_(apm_data_dumper) {}
42 
~InterpolatedGainCurve()43 InterpolatedGainCurve::~InterpolatedGainCurve() {
44   if (stats_.available) {
45     RTC_DCHECK(apm_data_dumper_);
46     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity",
47                               stats_.look_ups_identity_region);
48     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_knee",
49                               stats_.look_ups_knee_region);
50     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_limiter",
51                               stats_.look_ups_limiter_region);
52     apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation",
53                               stats_.look_ups_saturation_region);
54     region_logger_.LogRegionStats(stats_);
55   }
56 }
57 
RegionLogger(std::string identity_histogram_name,std::string knee_histogram_name,std::string limiter_histogram_name,std::string saturation_histogram_name)58 InterpolatedGainCurve::RegionLogger::RegionLogger(
59     std::string identity_histogram_name,
60     std::string knee_histogram_name,
61     std::string limiter_histogram_name,
62     std::string saturation_histogram_name)
63     : identity_histogram(
64           metrics::HistogramFactoryGetCounts(identity_histogram_name,
65                                              1,
66                                              10000,
67                                              50)),
68       knee_histogram(metrics::HistogramFactoryGetCounts(knee_histogram_name,
69                                                         1,
70                                                         10000,
71                                                         50)),
72       limiter_histogram(
73           metrics::HistogramFactoryGetCounts(limiter_histogram_name,
74                                              1,
75                                              10000,
76                                              50)),
77       saturation_histogram(
78           metrics::HistogramFactoryGetCounts(saturation_histogram_name,
79                                              1,
80                                              10000,
81                                              50)) {}
82 
83 InterpolatedGainCurve::RegionLogger::~RegionLogger() = default;
84 
LogRegionStats(const InterpolatedGainCurve::Stats & stats) const85 void InterpolatedGainCurve::RegionLogger::LogRegionStats(
86     const InterpolatedGainCurve::Stats& stats) const {
87   using Region = InterpolatedGainCurve::GainCurveRegion;
88   const int duration_s =
89       stats.region_duration_frames / (1000 / kFrameDurationMs);
90 
91   switch (stats.region) {
92     case Region::kIdentity: {
93       if (identity_histogram) {
94         metrics::HistogramAdd(identity_histogram, duration_s);
95       }
96       break;
97     }
98     case Region::kKnee: {
99       if (knee_histogram) {
100         metrics::HistogramAdd(knee_histogram, duration_s);
101       }
102       break;
103     }
104     case Region::kLimiter: {
105       if (limiter_histogram) {
106         metrics::HistogramAdd(limiter_histogram, duration_s);
107       }
108       break;
109     }
110     case Region::kSaturation: {
111       if (saturation_histogram) {
112         metrics::HistogramAdd(saturation_histogram, duration_s);
113       }
114       break;
115     }
116     default: {
117       RTC_NOTREACHED();
118     }
119   }
120 }
121 
UpdateStats(float input_level) const122 void InterpolatedGainCurve::UpdateStats(float input_level) const {
123   stats_.available = true;
124 
125   GainCurveRegion region;
126 
127   if (input_level < approximation_params_x_[0]) {
128     stats_.look_ups_identity_region++;
129     region = GainCurveRegion::kIdentity;
130   } else if (input_level <
131              approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) {
132     stats_.look_ups_knee_region++;
133     region = GainCurveRegion::kKnee;
134   } else if (input_level < kMaxInputLevelLinear) {
135     stats_.look_ups_limiter_region++;
136     region = GainCurveRegion::kLimiter;
137   } else {
138     stats_.look_ups_saturation_region++;
139     region = GainCurveRegion::kSaturation;
140   }
141 
142   if (region == stats_.region) {
143     ++stats_.region_duration_frames;
144   } else {
145     region_logger_.LogRegionStats(stats_);
146 
147     stats_.region_duration_frames = 0;
148     stats_.region = region;
149   }
150 }
151 
152 // Looks up a gain to apply given a non-negative input level.
153 // The cost of this operation depends on the region in which |input_level|
154 // falls.
155 // For the identity and the saturation regions the cost is O(1).
156 // For the other regions, namely knee and limiter, the cost is
157 // O(2 + log2(|LightkInterpolatedGainCurveTotalPoints|), plus O(1) for the
158 // linear interpolation (one product and one sum).
LookUpGainToApply(float input_level) const159 float InterpolatedGainCurve::LookUpGainToApply(float input_level) const {
160   UpdateStats(input_level);
161 
162   if (input_level <= approximation_params_x_[0]) {
163     // Identity region.
164     return 1.0f;
165   }
166 
167   if (input_level >= kMaxInputLevelLinear) {
168     // Saturating lower bound. The saturing samples exactly hit the clipping
169     // level. This method achieves has the lowest harmonic distorsion, but it
170     // may reduce the amplitude of the non-saturating samples too much.
171     return 32768.f / input_level;
172   }
173 
174   // Knee and limiter regions; find the linear piece index. Spelling
175   // out the complete type was the only way to silence both the clang
176   // plugin and the windows compilers.
177   std::array<float, kInterpolatedGainCurveTotalPoints>::const_iterator it =
178       std::lower_bound(approximation_params_x_.begin(),
179                        approximation_params_x_.end(), input_level);
180   const size_t index = std::distance(approximation_params_x_.begin(), it) - 1;
181   RTC_DCHECK_LE(0, index);
182   RTC_DCHECK_LT(index, approximation_params_m_.size());
183   RTC_DCHECK_LE(approximation_params_x_[index], input_level);
184   if (index < approximation_params_m_.size() - 1) {
185     RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]);
186   }
187 
188   // Piece-wise linear interploation.
189   const float gain = approximation_params_m_[index] * input_level +
190                      approximation_params_q_[index];
191   RTC_DCHECK_LE(0.f, gain);
192   return gain;
193 }
194 
195 }  // namespace webrtc
196