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/saturation_protector.h"
12 
13 #include <algorithm>
14 #include <iterator>
15 
16 #include "modules/audio_processing/logging/apm_data_dumper.h"
17 #include "rtc_base/numerics/safe_minmax.h"
18 
19 namespace webrtc {
20 
21 namespace {
ShiftBuffer(std::array<float,kPeakEnveloperBufferSize> * buffer_)22 void ShiftBuffer(std::array<float, kPeakEnveloperBufferSize>* buffer_) {
23   // Move everything one element back.
24   std::copy(buffer_->begin() + 1, buffer_->end(), buffer_->begin());
25 }
26 }  // namespace
27 
28 SaturationProtector::PeakEnveloper::PeakEnveloper() = default;
29 
Process(float frame_peak_dbfs)30 void SaturationProtector::PeakEnveloper::Process(float frame_peak_dbfs) {
31   // Update the delayed buffer and the current superframe peak.
32   current_superframe_peak_dbfs_ =
33       std::max(current_superframe_peak_dbfs_, frame_peak_dbfs);
34   speech_time_in_estimate_ms_ += kFrameDurationMs;
35   if (speech_time_in_estimate_ms_ > kPeakEnveloperSuperFrameLengthMs) {
36     speech_time_in_estimate_ms_ = 0;
37     const bool buffer_full = elements_in_buffer_ == kPeakEnveloperBufferSize;
38     if (buffer_full) {
39       ShiftBuffer(&peak_delay_buffer_);
40       *peak_delay_buffer_.rbegin() = current_superframe_peak_dbfs_;
41     } else {
42       peak_delay_buffer_[elements_in_buffer_] = current_superframe_peak_dbfs_;
43       elements_in_buffer_++;
44     }
45     current_superframe_peak_dbfs_ = -90.f;
46   }
47 }
48 
Query() const49 float SaturationProtector::PeakEnveloper::Query() const {
50   float result;
51   if (elements_in_buffer_ > 0) {
52     result = peak_delay_buffer_[0];
53   } else {
54     result = current_superframe_peak_dbfs_;
55   }
56   return result;
57 }
58 
SaturationProtector(ApmDataDumper * apm_data_dumper)59 SaturationProtector::SaturationProtector(ApmDataDumper* apm_data_dumper)
60     : SaturationProtector(apm_data_dumper, GetExtraSaturationMarginOffsetDb()) {
61 }
62 
SaturationProtector(ApmDataDumper * apm_data_dumper,float extra_saturation_margin_db)63 SaturationProtector::SaturationProtector(ApmDataDumper* apm_data_dumper,
64                                          float extra_saturation_margin_db)
65     : apm_data_dumper_(apm_data_dumper),
66       last_margin_(GetInitialSaturationMarginDb()),
67       extra_saturation_margin_db_(extra_saturation_margin_db) {}
68 
UpdateMargin(const VadWithLevel::LevelAndProbability & vad_data,float last_speech_level_estimate)69 void SaturationProtector::UpdateMargin(
70     const VadWithLevel::LevelAndProbability& vad_data,
71     float last_speech_level_estimate) {
72   peak_enveloper_.Process(vad_data.speech_peak_dbfs);
73   const float delayed_peak_dbfs = peak_enveloper_.Query();
74   const float difference_db = delayed_peak_dbfs - last_speech_level_estimate;
75 
76   if (last_margin_ < difference_db) {
77     last_margin_ = last_margin_ * kSaturationProtectorAttackConstant +
78                    difference_db * (1.f - kSaturationProtectorAttackConstant);
79   } else {
80     last_margin_ = last_margin_ * kSaturationProtectorDecayConstant +
81                    difference_db * (1.f - kSaturationProtectorDecayConstant);
82   }
83 
84   last_margin_ = rtc::SafeClamp<float>(last_margin_, 12.f, 25.f);
85 }
86 
LastMargin() const87 float SaturationProtector::LastMargin() const {
88   return last_margin_ + extra_saturation_margin_db_;
89 }
90 
Reset()91 void SaturationProtector::Reset() {
92   peak_enveloper_ = PeakEnveloper();
93 }
94 
DebugDumpEstimate() const95 void SaturationProtector::DebugDumpEstimate() const {
96   if (apm_data_dumper_) {
97     apm_data_dumper_->DumpRaw(
98         "agc2_adaptive_saturation_protector_delayed_peak_dbfs",
99         peak_enveloper_.Query());
100     apm_data_dumper_->DumpRaw("agc2_adaptive_saturation_margin_db",
101                               last_margin_);
102   }
103 }
104 
105 }  // namespace webrtc
106