| /* | |
| * 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/gain_applier.h" | |
| #include <cstddef> | |
| #include "api/audio/audio_view.h" | |
| #include "modules/audio_processing/agc2/agc2_common.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/numerics/safe_minmax.h" | |
| namespace webrtc { | |
| namespace { | |
| // Returns true when the gain factor is so close to 1 that it would | |
| // not affect int16 samples. | |
| bool GainCloseToOne(float gain_factor) { | |
| return 1.f - 1.f / kMaxFloatS16Value <= gain_factor && | |
| gain_factor <= 1.f + 1.f / kMaxFloatS16Value; | |
| } | |
| void ClipSignal(DeinterleavedView<float> signal) { | |
| for (size_t k = 0; k < signal.num_channels(); ++k) { | |
| MonoView<float> channel_view = signal[k]; | |
| for (auto& sample : channel_view) { | |
| sample = SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); | |
| } | |
| } | |
| } | |
| void ApplyGainWithRamping(float last_gain_linear, | |
| float gain_at_end_of_frame_linear, | |
| float inverse_samples_per_channel, | |
| DeinterleavedView<float> float_frame) { | |
| // Do not modify the signal. | |
| if (last_gain_linear == gain_at_end_of_frame_linear && | |
| GainCloseToOne(gain_at_end_of_frame_linear)) { | |
| return; | |
| } | |
| // Gain is constant and different from 1. | |
| if (last_gain_linear == gain_at_end_of_frame_linear) { | |
| for (size_t k = 0; k < float_frame.num_channels(); ++k) { | |
| MonoView<float> channel_view = float_frame[k]; | |
| for (auto& sample : channel_view) { | |
| sample *= gain_at_end_of_frame_linear; | |
| } | |
| } | |
| return; | |
| } | |
| // The gain changes. We have to change slowly to avoid discontinuities. | |
| const float increment = (gain_at_end_of_frame_linear - last_gain_linear) * | |
| inverse_samples_per_channel; | |
| for (size_t ch = 0; ch < float_frame.num_channels(); ++ch) { | |
| float gain = last_gain_linear; | |
| for (float& sample : float_frame[ch]) { | |
| sample *= gain; | |
| gain += increment; | |
| } | |
| } | |
| } | |
| } // namespace | |
| GainApplier::GainApplier(bool hard_clip_samples, float initial_gain_factor) | |
| : hard_clip_samples_(hard_clip_samples), | |
| last_gain_factor_(initial_gain_factor), | |
| current_gain_factor_(initial_gain_factor) {} | |
| void GainApplier::ApplyGain(DeinterleavedView<float> signal) { | |
| if (static_cast<int>(signal.samples_per_channel()) != samples_per_channel_) { | |
| Initialize(signal.samples_per_channel()); | |
| } | |
| ApplyGainWithRamping(last_gain_factor_, current_gain_factor_, | |
| inverse_samples_per_channel_, signal); | |
| last_gain_factor_ = current_gain_factor_; | |
| if (hard_clip_samples_) { | |
| ClipSignal(signal); | |
| } | |
| } | |
| // TODO(bugs.webrtc.org/7494): Remove once switched to gains in dB. | |
| void GainApplier::SetGainFactor(float gain_factor) { | |
| RTC_DCHECK_GT(gain_factor, 0.f); | |
| current_gain_factor_ = gain_factor; | |
| } | |
| void GainApplier::Initialize(int samples_per_channel) { | |
| RTC_DCHECK_GT(samples_per_channel, 0); | |
| samples_per_channel_ = static_cast<int>(samples_per_channel); | |
| inverse_samples_per_channel_ = 1.f / samples_per_channel_; | |
| } | |
| } // namespace webrtc |