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