xref: /aosp_15_r20/external/webrtc/modules/audio_processing/agc2/limiter.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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/limiter.h"
12 
13 #include <algorithm>
14 #include <array>
15 #include <cmath>
16 
17 #include "absl/strings/string_view.h"
18 #include "api/array_view.h"
19 #include "modules/audio_processing/agc2/agc2_common.h"
20 #include "modules/audio_processing/logging/apm_data_dumper.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/numerics/safe_conversions.h"
23 #include "rtc_base/numerics/safe_minmax.h"
24 
25 namespace webrtc {
26 namespace {
27 
28 // This constant affects the way scaling factors are interpolated for the first
29 // sub-frame of a frame. Only in the case in which the first sub-frame has an
30 // estimated level which is greater than the that of the previous analyzed
31 // sub-frame, linear interpolation is replaced with a power function which
32 // reduces the chances of over-shooting (and hence saturation), however reducing
33 // the fixed gain effectiveness.
34 constexpr float kAttackFirstSubframeInterpolationPower = 8.0f;
35 
InterpolateFirstSubframe(float last_factor,float current_factor,rtc::ArrayView<float> subframe)36 void InterpolateFirstSubframe(float last_factor,
37                               float current_factor,
38                               rtc::ArrayView<float> subframe) {
39   const int n = rtc::dchecked_cast<int>(subframe.size());
40   constexpr float p = kAttackFirstSubframeInterpolationPower;
41   for (int i = 0; i < n; ++i) {
42     subframe[i] = std::pow(1.f - i / n, p) * (last_factor - current_factor) +
43                   current_factor;
44   }
45 }
46 
ComputePerSampleSubframeFactors(const std::array<float,kSubFramesInFrame+1> & scaling_factors,int samples_per_channel,rtc::ArrayView<float> per_sample_scaling_factors)47 void ComputePerSampleSubframeFactors(
48     const std::array<float, kSubFramesInFrame + 1>& scaling_factors,
49     int samples_per_channel,
50     rtc::ArrayView<float> per_sample_scaling_factors) {
51   const int num_subframes = scaling_factors.size() - 1;
52   const int subframe_size =
53       rtc::CheckedDivExact(samples_per_channel, num_subframes);
54 
55   // Handle first sub-frame differently in case of attack.
56   const bool is_attack = scaling_factors[0] > scaling_factors[1];
57   if (is_attack) {
58     InterpolateFirstSubframe(
59         scaling_factors[0], scaling_factors[1],
60         rtc::ArrayView<float>(
61             per_sample_scaling_factors.subview(0, subframe_size)));
62   }
63 
64   for (int i = is_attack ? 1 : 0; i < num_subframes; ++i) {
65     const int subframe_start = i * subframe_size;
66     const float scaling_start = scaling_factors[i];
67     const float scaling_end = scaling_factors[i + 1];
68     const float scaling_diff = (scaling_end - scaling_start) / subframe_size;
69     for (int j = 0; j < subframe_size; ++j) {
70       per_sample_scaling_factors[subframe_start + j] =
71           scaling_start + scaling_diff * j;
72     }
73   }
74 }
75 
ScaleSamples(rtc::ArrayView<const float> per_sample_scaling_factors,AudioFrameView<float> signal)76 void ScaleSamples(rtc::ArrayView<const float> per_sample_scaling_factors,
77                   AudioFrameView<float> signal) {
78   const int samples_per_channel = signal.samples_per_channel();
79   RTC_DCHECK_EQ(samples_per_channel, per_sample_scaling_factors.size());
80   for (int i = 0; i < signal.num_channels(); ++i) {
81     rtc::ArrayView<float> channel = signal.channel(i);
82     for (int j = 0; j < samples_per_channel; ++j) {
83       channel[j] = rtc::SafeClamp(channel[j] * per_sample_scaling_factors[j],
84                                   kMinFloatS16Value, kMaxFloatS16Value);
85     }
86   }
87 }
88 
CheckLimiterSampleRate(int sample_rate_hz)89 void CheckLimiterSampleRate(int sample_rate_hz) {
90   // Check that per_sample_scaling_factors_ is large enough.
91   RTC_DCHECK_LE(sample_rate_hz,
92                 kMaximalNumberOfSamplesPerChannel * 1000 / kFrameDurationMs);
93 }
94 
95 }  // namespace
96 
Limiter(int sample_rate_hz,ApmDataDumper * apm_data_dumper,absl::string_view histogram_name)97 Limiter::Limiter(int sample_rate_hz,
98                  ApmDataDumper* apm_data_dumper,
99                  absl::string_view histogram_name)
100     : interp_gain_curve_(apm_data_dumper, histogram_name),
101       level_estimator_(sample_rate_hz, apm_data_dumper),
102       apm_data_dumper_(apm_data_dumper) {
103   CheckLimiterSampleRate(sample_rate_hz);
104 }
105 
106 Limiter::~Limiter() = default;
107 
Process(AudioFrameView<float> signal)108 void Limiter::Process(AudioFrameView<float> signal) {
109   const std::array<float, kSubFramesInFrame> level_estimate =
110       level_estimator_.ComputeLevel(signal);
111 
112   RTC_DCHECK_EQ(level_estimate.size() + 1, scaling_factors_.size());
113   scaling_factors_[0] = last_scaling_factor_;
114   std::transform(level_estimate.begin(), level_estimate.end(),
115                  scaling_factors_.begin() + 1, [this](float x) {
116                    return interp_gain_curve_.LookUpGainToApply(x);
117                  });
118 
119   const int samples_per_channel = signal.samples_per_channel();
120   RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel);
121 
122   auto per_sample_scaling_factors = rtc::ArrayView<float>(
123       &per_sample_scaling_factors_[0], samples_per_channel);
124   ComputePerSampleSubframeFactors(scaling_factors_, samples_per_channel,
125                                   per_sample_scaling_factors);
126   ScaleSamples(per_sample_scaling_factors, signal);
127 
128   last_scaling_factor_ = scaling_factors_.back();
129 
130   // Dump data for debug.
131   apm_data_dumper_->DumpRaw("agc2_limiter_last_scaling_factor",
132                             last_scaling_factor_);
133   apm_data_dumper_->DumpRaw(
134       "agc2_limiter_region",
135       static_cast<int>(interp_gain_curve_.get_stats().region));
136 }
137 
GetGainCurveStats() const138 InterpolatedGainCurve::Stats Limiter::GetGainCurveStats() const {
139   return interp_gain_curve_.get_stats();
140 }
141 
SetSampleRate(int sample_rate_hz)142 void Limiter::SetSampleRate(int sample_rate_hz) {
143   CheckLimiterSampleRate(sample_rate_hz);
144   level_estimator_.SetSampleRate(sample_rate_hz);
145 }
146 
Reset()147 void Limiter::Reset() {
148   level_estimator_.Reset();
149 }
150 
LastAudioLevel() const151 float Limiter::LastAudioLevel() const {
152   return level_estimator_.LastAudioLevel();
153 }
154 
155 }  // namespace webrtc
156