| /* |
| * 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 "api/array_view.h" |
| #include "modules/audio_processing/agc2/agc2_common.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(AudioFrameView<float> signal) { |
| for (int k = 0; k < signal.num_channels(); ++k) { |
| rtc::ArrayView<float> channel_view = signal.channel(k); |
| for (auto& sample : channel_view) { |
| sample = rtc::SafeClamp(sample, kMinFloatS16Value, kMaxFloatS16Value); |
| } |
| } |
| } |
| |
| void ApplyGainWithRamping(float last_gain_linear, |
| float gain_at_end_of_frame_linear, |
| float inverse_samples_per_channel, |
| AudioFrameView<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 (int k = 0; k < float_frame.num_channels(); ++k) { |
| rtc::ArrayView<float> channel_view = float_frame.channel(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; |
| float gain = last_gain_linear; |
| for (int i = 0; i < float_frame.samples_per_channel(); ++i) { |
| for (int ch = 0; ch < float_frame.num_channels(); ++ch) { |
| float_frame.channel(ch)[i] *= 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(AudioFrameView<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 |