blob: f5d6b47169c2226d4d1c2dd125b396d00b792c57 [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
Alex Loiko03ad9b82018-08-13 17:40:43 +020031InterpolatedGainCurve::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) {}
Alex Loikoa05ee822018-02-20 15:58:36 +010042
43InterpolatedGainCurve::~InterpolatedGainCurve() {
44 if (stats_.available) {
Alex Loikoa05ee822018-02-20 15:58:36 +010045 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);
Alex Loiko03ad9b82018-08-13 17:40:43 +020054 region_logger_.LogRegionStats(stats_);
55 }
56}
57
58InterpolatedGainCurve::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
83InterpolatedGainCurve::RegionLogger::~RegionLogger() = default;
84
85void 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: { RTC_NOTREACHED(); }
Alex Loikoa05ee822018-02-20 15:58:36 +0100117 }
118}
119
120void InterpolatedGainCurve::UpdateStats(float input_level) const {
121 stats_.available = true;
122
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100123 GainCurveRegion region;
124
Alex Loikoa05ee822018-02-20 15:58:36 +0100125 if (input_level < approximation_params_x_[0]) {
126 stats_.look_ups_identity_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100127 region = GainCurveRegion::kIdentity;
Alex Loikoa05ee822018-02-20 15:58:36 +0100128 } else if (input_level <
129 approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) {
130 stats_.look_ups_knee_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100131 region = GainCurveRegion::kKnee;
Alex Loikoa05ee822018-02-20 15:58:36 +0100132 } else if (input_level < kMaxInputLevelLinear) {
133 stats_.look_ups_limiter_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100134 region = GainCurveRegion::kLimiter;
Alex Loikoa05ee822018-02-20 15:58:36 +0100135 } else {
136 stats_.look_ups_saturation_region++;
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100137 region = GainCurveRegion::kSaturation;
138 }
139
140 if (region == stats_.region) {
141 ++stats_.region_duration_frames;
142 } else {
Alex Loiko03ad9b82018-08-13 17:40:43 +0200143 region_logger_.LogRegionStats(stats_);
Alex Loiko6f2fcb42018-03-14 12:27:05 +0100144
145 stats_.region_duration_frames = 0;
146 stats_.region = region;
Alex Loikoa05ee822018-02-20 15:58:36 +0100147 }
148}
149
150// Looks up a gain to apply given a non-negative input level.
151// The cost of this operation depends on the region in which |input_level|
152// falls.
153// For the identity and the saturation regions the cost is O(1).
154// For the other regions, namely knee and limiter, the cost is
155// O(2 + log2(|LightkInterpolatedGainCurveTotalPoints|), plus O(1) for the
156// linear interpolation (one product and one sum).
157float InterpolatedGainCurve::LookUpGainToApply(float input_level) const {
158 UpdateStats(input_level);
159
160 if (input_level <= approximation_params_x_[0]) {
161 // Identity region.
162 return 1.0f;
163 }
164
165 if (input_level >= kMaxInputLevelLinear) {
166 // Saturating lower bound. The saturing samples exactly hit the clipping
167 // level. This method achieves has the lowest harmonic distorsion, but it
168 // may reduce the amplitude of the non-saturating samples too much.
169 return 32768.f / input_level;
170 }
171
172 // Knee and limiter regions; find the linear piece index. Spelling
173 // out the complete type was the only way to silence both the clang
174 // plugin and the windows compilers.
175 std::array<float, kInterpolatedGainCurveTotalPoints>::const_iterator it =
176 std::lower_bound(approximation_params_x_.begin(),
177 approximation_params_x_.end(), input_level);
178 const size_t index = std::distance(approximation_params_x_.begin(), it) - 1;
179 RTC_DCHECK_LE(0, index);
180 RTC_DCHECK_LT(index, approximation_params_m_.size());
181 RTC_DCHECK_LE(approximation_params_x_[index], input_level);
182 if (index < approximation_params_m_.size() - 1) {
183 RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]);
184 }
185
186 // Piece-wise linear interploation.
187 const float gain = approximation_params_m_[index] * input_level +
188 approximation_params_q_[index];
189 RTC_DCHECK_LE(0.f, gain);
190 return gain;
191}
192
193} // namespace webrtc