/* * 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/fixed_digital_level_estimator.h" #include #include #include "api/array_view.h" #include "modules/audio_processing/logging/apm_data_dumper.h" #include "rtc_base/checks.h" namespace webrtc { namespace { constexpr float kInitialFilterStateLevel = 0.0f; // Instant attack. constexpr float kAttackFilterConstant = 0.0f; // Limiter decay constant. // Computed as `10 ** (-1/20 * subframe_duration / kDecayMs)` where: // - `subframe_duration` is `kFrameDurationMs / kSubFramesInFrame`; // - `kDecayMs` is defined in agc2_testing_common.h. constexpr float kDecayFilterConstant = 0.9971259f; } // namespace FixedDigitalLevelEstimator::FixedDigitalLevelEstimator( int sample_rate_hz, ApmDataDumper* apm_data_dumper) : apm_data_dumper_(apm_data_dumper), filter_state_level_(kInitialFilterStateLevel) { SetSampleRate(sample_rate_hz); CheckParameterCombination(); RTC_DCHECK(apm_data_dumper_); apm_data_dumper_->DumpRaw("agc2_level_estimator_samplerate", sample_rate_hz); } void FixedDigitalLevelEstimator::CheckParameterCombination() { RTC_DCHECK_GT(samples_in_frame_, 0); RTC_DCHECK_LE(kSubFramesInFrame, samples_in_frame_); RTC_DCHECK_EQ(samples_in_frame_ % kSubFramesInFrame, 0); RTC_DCHECK_GT(samples_in_sub_frame_, 1); } std::array FixedDigitalLevelEstimator::ComputeLevel( const AudioFrameView& float_frame) { RTC_DCHECK_GT(float_frame.num_channels(), 0); RTC_DCHECK_EQ(float_frame.samples_per_channel(), samples_in_frame_); // Compute max envelope without smoothing. std::array envelope{}; for (int channel_idx = 0; channel_idx < float_frame.num_channels(); ++channel_idx) { const auto channel = float_frame.channel(channel_idx); for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { for (int sample_in_sub_frame = 0; sample_in_sub_frame < samples_in_sub_frame_; ++sample_in_sub_frame) { envelope[sub_frame] = std::max(envelope[sub_frame], std::abs(channel[sub_frame * samples_in_sub_frame_ + sample_in_sub_frame])); } } } // Make sure envelope increases happen one step earlier so that the // corresponding *gain decrease* doesn't miss a sudden signal // increase due to interpolation. for (int sub_frame = 0; sub_frame < kSubFramesInFrame - 1; ++sub_frame) { if (envelope[sub_frame] < envelope[sub_frame + 1]) { envelope[sub_frame] = envelope[sub_frame + 1]; } } // Add attack / decay smoothing. for (int sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { const float envelope_value = envelope[sub_frame]; if (envelope_value > filter_state_level_) { envelope[sub_frame] = envelope_value * (1 - kAttackFilterConstant) + filter_state_level_ * kAttackFilterConstant; } else { envelope[sub_frame] = envelope_value * (1 - kDecayFilterConstant) + filter_state_level_ * kDecayFilterConstant; } filter_state_level_ = envelope[sub_frame]; // Dump data for debug. RTC_DCHECK(apm_data_dumper_); const auto channel = float_frame.channel(0); apm_data_dumper_->DumpRaw("agc2_level_estimator_samples", samples_in_sub_frame_, &channel[sub_frame * samples_in_sub_frame_]); apm_data_dumper_->DumpRaw("agc2_level_estimator_level", envelope[sub_frame]); } return envelope; } void FixedDigitalLevelEstimator::SetSampleRate(int sample_rate_hz) { samples_in_frame_ = rtc::CheckedDivExact(sample_rate_hz * kFrameDurationMs, 1000); samples_in_sub_frame_ = rtc::CheckedDivExact(samples_in_frame_, kSubFramesInFrame); CheckParameterCombination(); } void FixedDigitalLevelEstimator::Reset() { filter_state_level_ = kInitialFilterStateLevel; } } // namespace webrtc