xref: /aosp_15_r20/external/webrtc/modules/audio_processing/agc2/speech_probability_buffer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2022 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/speech_probability_buffer.h"
12 
13 #include <algorithm>
14 
15 #include "rtc_base/checks.h"
16 
17 namespace webrtc {
18 namespace {
19 
20 constexpr float kActivityThreshold = 0.9f;
21 constexpr int kNumAnalysisFrames = 100;
22 // We use 12 in AGC2 adaptive digital, but with a slightly different logic.
23 constexpr int kTransientWidthThreshold = 7;
24 
25 }  // namespace
26 
SpeechProbabilityBuffer(float low_probability_threshold)27 SpeechProbabilityBuffer::SpeechProbabilityBuffer(
28     float low_probability_threshold)
29     : low_probability_threshold_(low_probability_threshold),
30       probabilities_(kNumAnalysisFrames) {
31   RTC_DCHECK_GE(low_probability_threshold, 0.0f);
32   RTC_DCHECK_LE(low_probability_threshold, 1.0f);
33   RTC_DCHECK(!probabilities_.empty());
34 }
35 
Update(float probability)36 void SpeechProbabilityBuffer::Update(float probability) {
37   // Remove the oldest entry if the circular buffer is full.
38   if (buffer_is_full_) {
39     const float oldest_probability = probabilities_[buffer_index_];
40     sum_probabilities_ -= oldest_probability;
41   }
42 
43   // Check for transients.
44   if (probability <= low_probability_threshold_) {
45     // Set a probability lower than the threshold to zero.
46     probability = 0.0f;
47 
48     // Check if this has been a transient.
49     if (num_high_probability_observations_ <= kTransientWidthThreshold) {
50       RemoveTransient();
51     }
52     num_high_probability_observations_ = 0;
53   } else if (num_high_probability_observations_ <= kTransientWidthThreshold) {
54     ++num_high_probability_observations_;
55   }
56 
57   // Update the circular buffer and the current sum.
58   probabilities_[buffer_index_] = probability;
59   sum_probabilities_ += probability;
60 
61   // Increment the buffer index and check for wrap-around.
62   if (++buffer_index_ >= kNumAnalysisFrames) {
63     buffer_index_ = 0;
64     buffer_is_full_ = true;
65   }
66 }
67 
RemoveTransient()68 void SpeechProbabilityBuffer::RemoveTransient() {
69   // Don't expect to be here if high-activity region is longer than
70   // `kTransientWidthThreshold` or there has not been any transient.
71   RTC_DCHECK_LE(num_high_probability_observations_, kTransientWidthThreshold);
72 
73   // Replace previously added probabilities with zero.
74   int index =
75       (buffer_index_ > 0) ? (buffer_index_ - 1) : (kNumAnalysisFrames - 1);
76 
77   while (num_high_probability_observations_-- > 0) {
78     sum_probabilities_ -= probabilities_[index];
79     probabilities_[index] = 0.0f;
80 
81     // Update the circular buffer index.
82     index = (index > 0) ? (index - 1) : (kNumAnalysisFrames - 1);
83   }
84 }
85 
IsActiveSegment() const86 bool SpeechProbabilityBuffer::IsActiveSegment() const {
87   if (!buffer_is_full_) {
88     return false;
89   }
90   if (sum_probabilities_ < kActivityThreshold * kNumAnalysisFrames) {
91     return false;
92   }
93   return true;
94 }
95 
Reset()96 void SpeechProbabilityBuffer::Reset() {
97   sum_probabilities_ = 0.0f;
98 
99   // Empty the circular buffer.
100   buffer_index_ = 0;
101   buffer_is_full_ = false;
102   num_high_probability_observations_ = 0;
103 }
104 
105 }  // namespace webrtc
106