/* * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "modules/audio_processing/agc2/saturation_protector.h" #include #include #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/numerics/safe_minmax.h" namespace webrtc { namespace { void ShiftBuffer(std::array* buffer_) { // Move everything one element back. std::copy(buffer_->begin() + 1, buffer_->end(), buffer_->begin()); } } // namespace SaturationProtector::PeakEnveloper::PeakEnveloper() = default; void SaturationProtector::PeakEnveloper::Process(float frame_peak_dbfs) { // Update the delayed buffer and the current superframe peak. current_superframe_peak_dbfs_ = std::max(current_superframe_peak_dbfs_, frame_peak_dbfs); speech_time_in_estimate_ms_ += kFrameDurationMs; if (speech_time_in_estimate_ms_ > kPeakEnveloperSuperFrameLengthMs) { speech_time_in_estimate_ms_ = 0; const bool buffer_full = elements_in_buffer_ == kPeakEnveloperBufferSize; if (buffer_full) { ShiftBuffer(&peak_delay_buffer_); *peak_delay_buffer_.rbegin() = current_superframe_peak_dbfs_; } else { peak_delay_buffer_[elements_in_buffer_] = current_superframe_peak_dbfs_; elements_in_buffer_++; } current_superframe_peak_dbfs_ = -90.f; } } float SaturationProtector::PeakEnveloper::Query() const { float result; if (elements_in_buffer_ > 0) { result = peak_delay_buffer_[0]; } else { result = current_superframe_peak_dbfs_; } return result; } SaturationProtector::SaturationProtector(ApmDataDumper* apm_data_dumper) : SaturationProtector(apm_data_dumper, GetExtraSaturationMarginOffsetDb()) { } SaturationProtector::SaturationProtector(ApmDataDumper* apm_data_dumper, float extra_saturation_margin_db) : apm_data_dumper_(apm_data_dumper), last_margin_(GetInitialSaturationMarginDb()), extra_saturation_margin_db_(extra_saturation_margin_db) {} void SaturationProtector::UpdateMargin( const VadWithLevel::LevelAndProbability& vad_data, float last_speech_level_estimate) { peak_enveloper_.Process(vad_data.speech_peak_dbfs); const float delayed_peak_dbfs = peak_enveloper_.Query(); const float difference_db = delayed_peak_dbfs - last_speech_level_estimate; if (last_margin_ < difference_db) { last_margin_ = last_margin_ * kSaturationProtectorAttackConstant + difference_db * (1.f - kSaturationProtectorAttackConstant); } else { last_margin_ = last_margin_ * kSaturationProtectorDecayConstant + difference_db * (1.f - kSaturationProtectorDecayConstant); } last_margin_ = rtc::SafeClamp(last_margin_, 12.f, 25.f); } float SaturationProtector::LastMargin() const { return last_margin_ + extra_saturation_margin_db_; } void SaturationProtector::Reset() { peak_enveloper_ = PeakEnveloper(); } void SaturationProtector::DebugDumpEstimate() const { apm_data_dumper_->DumpRaw( "agc2_adaptive_saturation_protector_delayed_peak_dbfs", peak_enveloper_.Query()); apm_data_dumper_->DumpRaw("agc2_adaptive_saturation_margin_db", last_margin_); } } // namespace webrtc