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