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