blob: 3dd501096a3650e7b71bee2e90ed3038d4cc58eb [file] [log] [blame]
Alex Loikoa05ee822018-02-20 15:58:36 +01001/*
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
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <algorithm>
14#include <iterator>
15
Alex Loikoa05ee822018-02-20 15:58:36 +010016#include "modules/audio_processing/agc2/agc2_common.h"
17#include "modules/audio_processing/logging/apm_data_dumper.h"
18#include "rtc_base/checks.h"
Alex Loikoa05ee822018-02-20 15:58:36 +010019
20namespace webrtc {
21
22constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
23 InterpolatedGainCurve::approximation_params_x_;
24
25constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
26 InterpolatedGainCurve::approximation_params_m_;
27
28constexpr std::array<float, kInterpolatedGainCurveTotalPoints>
29 InterpolatedGainCurve::approximation_params_q_;
30
Alessio Bazzica76714a62021-01-13 18:08:40 +010031InterpolatedGainCurve::InterpolatedGainCurve(
32 ApmDataDumper* apm_data_dumper,
33 const std::string& histogram_name_prefix)
Alex Loiko03ad9b82018-08-13 17:40:43 +020034 : region_logger_("WebRTC.Audio." + histogram_name_prefix +
35 ".FixedDigitalGainCurveRegion.Identity",
36 "WebRTC.Audio." + histogram_name_prefix +
37 ".FixedDigitalGainCurveRegion.Knee",
38 "WebRTC.Audio." + histogram_name_prefix +
39 ".FixedDigitalGainCurveRegion.Limiter",
40 "WebRTC.Audio." + histogram_name_prefix +
41 ".FixedDigitalGainCurveRegion.Saturation"),
42 apm_data_dumper_(apm_data_dumper) {}
Alex Loikoa05ee822018-02-20 15:58:36 +010043
44InterpolatedGainCurve::~InterpolatedGainCurve() {
45 if (stats_.available) {
Alex Loikoa05ee822018-02-20 15:58:36 +010046 RTC_DCHECK(apm_data_dumper_);
47 apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity",
48 stats_.look_ups_identity_region);
49 apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_knee",
50 stats_.look_ups_knee_region);
51 apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_limiter",
52 stats_.look_ups_limiter_region);
53 apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation",
54 stats_.look_ups_saturation_region);
Alex Loiko03ad9b82018-08-13 17:40:43 +020055 region_logger_.LogRegionStats(stats_);
56 }
57}
58
59InterpolatedGainCurve::RegionLogger::RegionLogger(
Alessio Bazzica76714a62021-01-13 18:08:40 +010060 const std::string& identity_histogram_name,
61 const std::string& knee_histogram_name,
62 const std::string& limiter_histogram_name,
63 const std::string& saturation_histogram_name)
Alex Loiko03ad9b82018-08-13 17:40:43 +020064 : identity_histogram(
65 metrics::HistogramFactoryGetCounts(identity_histogram_name,
66 1,
67 10000,
68 50)),
69 knee_histogram(metrics::HistogramFactoryGetCounts(knee_histogram_name,
70 1,
71 10000,
72 50)),
73 limiter_histogram(
74 metrics::HistogramFactoryGetCounts(limiter_histogram_name,
75 1,
76 10000,
77 50)),
78 saturation_histogram(
79 metrics::HistogramFactoryGetCounts(saturation_histogram_name,
80 1,
81 10000,
82 50)) {}
83
84InterpolatedGainCurve::RegionLogger::~RegionLogger() = default;
85
86void InterpolatedGainCurve::RegionLogger::LogRegionStats(
87 const InterpolatedGainCurve::Stats& stats) const {
88 using Region = InterpolatedGainCurve::GainCurveRegion;
89 const int duration_s =
90 stats.region_duration_frames / (1000 / kFrameDurationMs);
91
92 switch (stats.region) {
93 case Region::kIdentity: {
94 if (identity_histogram) {
95 metrics::HistogramAdd(identity_histogram, duration_s);
96 }
97 break;
98 }
99 case Region::kKnee: {
100 if (knee_histogram) {
101 metrics::HistogramAdd(knee_histogram, duration_s);
102 }
103 break;
104 }
105 case Region::kLimiter: {
106 if (limiter_histogram) {
107 metrics::HistogramAdd(limiter_histogram, duration_s);
108 }
109 break;
110 }
111 case Region::kSaturation: {
112 if (saturation_histogram) {
113 metrics::HistogramAdd(saturation_histogram, duration_s);
114 }
115 break;
116 }
Jonas Olssona4d87372019-07-05 19:08:33 +0200117 default: {
118 RTC_NOTREACHED();
119 }
Alex Loikoa05ee822018-02-20 15:58:36 +0100120 }
121}
122
123void InterpolatedGainCurve::UpdateStats(float input_level) const {
124 stats_.available = true;
125
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100126 GainCurveRegion region;
127
Alex Loikoa05ee822018-02-20 15:58:36 +0100128 if (input_level < approximation_params_x_[0]) {
129 stats_.look_ups_identity_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100130 region = GainCurveRegion::kIdentity;
Alex Loikoa05ee822018-02-20 15:58:36 +0100131 } else if (input_level <
132 approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) {
133 stats_.look_ups_knee_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100134 region = GainCurveRegion::kKnee;
Alex Loikoa05ee822018-02-20 15:58:36 +0100135 } else if (input_level < kMaxInputLevelLinear) {
136 stats_.look_ups_limiter_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100137 region = GainCurveRegion::kLimiter;
Alex Loikoa05ee822018-02-20 15:58:36 +0100138 } else {
139 stats_.look_ups_saturation_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100140 region = GainCurveRegion::kSaturation;
141 }
142
143 if (region == stats_.region) {
144 ++stats_.region_duration_frames;
145 } else {
Alex Loiko03ad9b82018-08-13 17:40:43 +0200146 region_logger_.LogRegionStats(stats_);
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100147
148 stats_.region_duration_frames = 0;
149 stats_.region = region;
Alex Loikoa05ee822018-02-20 15:58:36 +0100150 }
151}
152
153// Looks up a gain to apply given a non-negative input level.
154// The cost of this operation depends on the region in which |input_level|
155// falls.
156// For the identity and the saturation regions the cost is O(1).
157// For the other regions, namely knee and limiter, the cost is
158// O(2 + log2(|LightkInterpolatedGainCurveTotalPoints|), plus O(1) for the
159// linear interpolation (one product and one sum).
160float InterpolatedGainCurve::LookUpGainToApply(float input_level) const {
161 UpdateStats(input_level);
162
163 if (input_level <= approximation_params_x_[0]) {
164 // Identity region.
165 return 1.0f;
166 }
167
168 if (input_level >= kMaxInputLevelLinear) {
169 // Saturating lower bound. The saturing samples exactly hit the clipping
170 // level. This method achieves has the lowest harmonic distorsion, but it
171 // may reduce the amplitude of the non-saturating samples too much.
172 return 32768.f / input_level;
173 }
174
175 // Knee and limiter regions; find the linear piece index. Spelling
176 // out the complete type was the only way to silence both the clang
177 // plugin and the windows compilers.
178 std::array<float, kInterpolatedGainCurveTotalPoints>::const_iterator it =
179 std::lower_bound(approximation_params_x_.begin(),
180 approximation_params_x_.end(), input_level);
181 const size_t index = std::distance(approximation_params_x_.begin(), it) - 1;
182 RTC_DCHECK_LE(0, index);
183 RTC_DCHECK_LT(index, approximation_params_m_.size());
184 RTC_DCHECK_LE(approximation_params_x_[index], input_level);
185 if (index < approximation_params_m_.size() - 1) {
186 RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]);
187 }
188
189 // Piece-wise linear interploation.
190 const float gain = approximation_params_m_[index] * input_level +
191 approximation_params_q_[index];
192 RTC_DCHECK_LE(0.f, gain);
193 return gain;
194}
195
196} // namespace webrtc