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)27SpeechProbabilityBuffer::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)36void 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()68void 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() const86bool 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()96void 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