xref: /aosp_15_r20/external/webrtc/modules/audio_processing/agc2/fixed_digital_level_estimator.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/fixed_digital_level_estimator.h"
12 
13 #include <algorithm>
14 #include <cmath>
15 
16 #include "api/array_view.h"
17 #include "modules/audio_processing/logging/apm_data_dumper.h"
18 #include "rtc_base/checks.h"
19 
20 namespace webrtc {
21 namespace {
22 
23 constexpr float kInitialFilterStateLevel = 0.0f;
24 
25 // Instant attack.
26 constexpr float kAttackFilterConstant = 0.0f;
27 
28 // Limiter decay constant.
29 // Computed as `10 ** (-1/20 * subframe_duration / kDecayMs)` where:
30 // - `subframe_duration` is `kFrameDurationMs / kSubFramesInFrame`;
31 // - `kDecayMs` is defined in agc2_testing_common.h.
32 constexpr float kDecayFilterConstant = 0.9971259f;
33 
34 }  // namespace
35 
FixedDigitalLevelEstimator(int sample_rate_hz,ApmDataDumper * apm_data_dumper)36 FixedDigitalLevelEstimator::FixedDigitalLevelEstimator(
37     int sample_rate_hz,
38     ApmDataDumper* apm_data_dumper)
39     : apm_data_dumper_(apm_data_dumper),
40       filter_state_level_(kInitialFilterStateLevel) {
41   SetSampleRate(sample_rate_hz);
42   CheckParameterCombination();
43   RTC_DCHECK(apm_data_dumper_);
44   apm_data_dumper_->DumpRaw("agc2_level_estimator_samplerate", sample_rate_hz);
45 }
46 
CheckParameterCombination()47 void FixedDigitalLevelEstimator::CheckParameterCombination() {
48   RTC_DCHECK_GT(samples_in_frame_, 0);
49   RTC_DCHECK_LE(kSubFramesInFrame, samples_in_frame_);
50   RTC_DCHECK_EQ(samples_in_frame_ % kSubFramesInFrame, 0);
51   RTC_DCHECK_GT(samples_in_sub_frame_, 1);
52 }
53 
ComputeLevel(const AudioFrameView<const float> & float_frame)54 std::array<float, kSubFramesInFrame> FixedDigitalLevelEstimator::ComputeLevel(
55     const AudioFrameView<const float>& float_frame) {
56   RTC_DCHECK_GT(float_frame.num_channels(), 0);
57   RTC_DCHECK_EQ(float_frame.samples_per_channel(), samples_in_frame_);
58 
59   // Compute max envelope without smoothing.
60   std::array<float, kSubFramesInFrame> envelope{};
61   for (int channel_idx = 0; channel_idx < float_frame.num_channels();
62        ++channel_idx) {
63     const auto channel = float_frame.channel(channel_idx);
64     for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) {
65       for (int sample_in_sub_frame = 0;
66            sample_in_sub_frame < samples_in_sub_frame_; ++sample_in_sub_frame) {
67         envelope[sub_frame] =
68             std::max(envelope[sub_frame],
69                      std::abs(channel[sub_frame * samples_in_sub_frame_ +
70                                       sample_in_sub_frame]));
71       }
72     }
73   }
74 
75   // Make sure envelope increases happen one step earlier so that the
76   // corresponding *gain decrease* doesn't miss a sudden signal
77   // increase due to interpolation.
78   for (int sub_frame = 0; sub_frame < kSubFramesInFrame - 1; ++sub_frame) {
79     if (envelope[sub_frame] < envelope[sub_frame + 1]) {
80       envelope[sub_frame] = envelope[sub_frame + 1];
81     }
82   }
83 
84   // Add attack / decay smoothing.
85   for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) {
86     const float envelope_value = envelope[sub_frame];
87     if (envelope_value > filter_state_level_) {
88       envelope[sub_frame] = envelope_value * (1 - kAttackFilterConstant) +
89                             filter_state_level_ * kAttackFilterConstant;
90     } else {
91       envelope[sub_frame] = envelope_value * (1 - kDecayFilterConstant) +
92                             filter_state_level_ * kDecayFilterConstant;
93     }
94     filter_state_level_ = envelope[sub_frame];
95 
96     // Dump data for debug.
97     RTC_DCHECK(apm_data_dumper_);
98     const auto channel = float_frame.channel(0);
99     apm_data_dumper_->DumpRaw("agc2_level_estimator_samples",
100                               samples_in_sub_frame_,
101                               &channel[sub_frame * samples_in_sub_frame_]);
102     apm_data_dumper_->DumpRaw("agc2_level_estimator_level",
103                               envelope[sub_frame]);
104   }
105 
106   return envelope;
107 }
108 
SetSampleRate(int sample_rate_hz)109 void FixedDigitalLevelEstimator::SetSampleRate(int sample_rate_hz) {
110   samples_in_frame_ =
111       rtc::CheckedDivExact(sample_rate_hz * kFrameDurationMs, 1000);
112   samples_in_sub_frame_ =
113       rtc::CheckedDivExact(samples_in_frame_, kSubFramesInFrame);
114   CheckParameterCombination();
115 }
116 
Reset()117 void FixedDigitalLevelEstimator::Reset() {
118   filter_state_level_ = kInitialFilterStateLevel;
119 }
120 
121 }  // namespace webrtc
122