| /* |
| * 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 <algorithm> |
| #include <cmath> |
| |
| #include "modules/audio_processing/logging/apm_data_dumper.h" |
| #include "rtc_base/checks.h" |
| |
| namespace webrtc { |
| |
| FixedDigitalLevelEstimator::FixedDigitalLevelEstimator( |
| size_t sample_rate_hz, |
| ApmDataDumper* apm_data_dumper) |
| : apm_data_dumper_(apm_data_dumper) { |
| 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<float, kSubFramesInFrame> FixedDigitalLevelEstimator::ComputeLevel( |
| const AudioFrameView<const float>& 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<float, kSubFramesInFrame> envelope{}; |
| for (size_t channel_idx = 0; channel_idx < float_frame.num_channels(); |
| ++channel_idx) { |
| const auto channel = float_frame.channel(channel_idx); |
| for (size_t sub_frame = 0; sub_frame < kSubFramesInFrame; ++sub_frame) { |
| for (size_t 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 (size_t 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 (size_t 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(size_t sample_rate_hz) { |
| samples_in_frame_ = rtc::CheckedDivExact(sample_rate_hz * kFrameDurationMs, |
| static_cast<size_t>(1000)); |
| samples_in_sub_frame_ = |
| rtc::CheckedDivExact(samples_in_frame_, kSubFramesInFrame); |
| CheckParameterCombination(); |
| } |
| } // namespace webrtc |