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