Line data Source code
1 : /*
2 : * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 : *
4 : * Use of this source code is governed by a BSD-style license
5 : * that can be found in the LICENSE file in the root of the source
6 : * tree. An additional intellectual property rights grant can be found
7 : * in the file PATENTS. All contributing project authors may
8 : * be found in the AUTHORS file in the root of the source tree.
9 : */
10 :
11 : #include "webrtc/modules/audio_processing/level_controller/gain_applier.h"
12 :
13 : #include <algorithm>
14 :
15 : #include "webrtc/base/array_view.h"
16 : #include "webrtc/base/checks.h"
17 :
18 : #include "webrtc/modules/audio_processing/audio_buffer.h"
19 : #include "webrtc/modules/audio_processing/logging/apm_data_dumper.h"
20 :
21 : namespace webrtc {
22 : namespace {
23 :
24 : const float kMaxSampleValue = 32767.f;
25 : const float kMinSampleValue = -32767.f;
26 :
27 0 : int CountSaturations(rtc::ArrayView<const float> in) {
28 0 : return std::count_if(in.begin(), in.end(), [](const float& v) {
29 0 : return v >= kMaxSampleValue || v <= kMinSampleValue;
30 0 : });
31 : }
32 :
33 0 : int CountSaturations(const AudioBuffer& audio) {
34 0 : int num_saturations = 0;
35 0 : for (size_t k = 0; k < audio.num_channels(); ++k) {
36 0 : num_saturations += CountSaturations(rtc::ArrayView<const float>(
37 0 : audio.channels_const_f()[k], audio.num_frames()));
38 : }
39 0 : return num_saturations;
40 : }
41 :
42 0 : void LimitToAllowedRange(rtc::ArrayView<float> x) {
43 0 : for (auto& v : x) {
44 0 : v = std::max(kMinSampleValue, v);
45 0 : v = std::min(kMaxSampleValue, v);
46 : }
47 0 : }
48 :
49 0 : void LimitToAllowedRange(AudioBuffer* audio) {
50 0 : for (size_t k = 0; k < audio->num_channels(); ++k) {
51 0 : LimitToAllowedRange(
52 0 : rtc::ArrayView<float>(audio->channels_f()[k], audio->num_frames()));
53 : }
54 0 : }
55 :
56 0 : float ApplyIncreasingGain(float new_gain,
57 : float old_gain,
58 : float step_size,
59 : rtc::ArrayView<float> x) {
60 0 : RTC_DCHECK_LT(0.f, step_size);
61 0 : float gain = old_gain;
62 0 : for (auto& v : x) {
63 0 : gain = std::min(new_gain, gain + step_size);
64 0 : v *= gain;
65 : }
66 0 : return gain;
67 : }
68 :
69 0 : float ApplyDecreasingGain(float new_gain,
70 : float old_gain,
71 : float step_size,
72 : rtc::ArrayView<float> x) {
73 0 : RTC_DCHECK_GT(0.f, step_size);
74 0 : float gain = old_gain;
75 0 : for (auto& v : x) {
76 0 : gain = std::max(new_gain, gain + step_size);
77 0 : v *= gain;
78 : }
79 0 : return gain;
80 : }
81 :
82 0 : float ApplyConstantGain(float gain, rtc::ArrayView<float> x) {
83 0 : for (auto& v : x) {
84 0 : v *= gain;
85 : }
86 :
87 0 : return gain;
88 : }
89 :
90 0 : float ApplyGain(float new_gain,
91 : float old_gain,
92 : float increase_step_size,
93 : float decrease_step_size,
94 : rtc::ArrayView<float> x) {
95 0 : RTC_DCHECK_LT(0.f, increase_step_size);
96 0 : RTC_DCHECK_GT(0.f, decrease_step_size);
97 0 : if (new_gain == old_gain) {
98 0 : return ApplyConstantGain(new_gain, x);
99 0 : } else if (new_gain > old_gain) {
100 0 : return ApplyIncreasingGain(new_gain, old_gain, increase_step_size, x);
101 : } else {
102 0 : return ApplyDecreasingGain(new_gain, old_gain, decrease_step_size, x);
103 : }
104 : }
105 :
106 : } // namespace
107 :
108 0 : GainApplier::GainApplier(ApmDataDumper* data_dumper)
109 0 : : data_dumper_(data_dumper) {}
110 :
111 0 : void GainApplier::Initialize(int sample_rate_hz) {
112 0 : RTC_DCHECK(sample_rate_hz == AudioProcessing::kSampleRate8kHz ||
113 : sample_rate_hz == AudioProcessing::kSampleRate16kHz ||
114 : sample_rate_hz == AudioProcessing::kSampleRate32kHz ||
115 0 : sample_rate_hz == AudioProcessing::kSampleRate48kHz);
116 0 : const float kGainIncreaseStepSize48kHz = 0.0001f;
117 0 : const float kGainDecreaseStepSize48kHz = -0.01f;
118 0 : const float kGainSaturatedDecreaseStepSize48kHz = -0.05f;
119 :
120 0 : last_frame_was_saturated_ = false;
121 0 : old_gain_ = 1.f;
122 0 : gain_increase_step_size_ =
123 0 : kGainIncreaseStepSize48kHz *
124 0 : (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
125 0 : gain_normal_decrease_step_size_ =
126 0 : kGainDecreaseStepSize48kHz *
127 0 : (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
128 0 : gain_saturated_decrease_step_size_ =
129 0 : kGainSaturatedDecreaseStepSize48kHz *
130 0 : (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
131 0 : }
132 :
133 0 : int GainApplier::Process(float new_gain, AudioBuffer* audio) {
134 0 : RTC_CHECK_NE(0.f, gain_increase_step_size_);
135 0 : RTC_CHECK_NE(0.f, gain_normal_decrease_step_size_);
136 0 : RTC_CHECK_NE(0.f, gain_saturated_decrease_step_size_);
137 0 : int num_saturations = 0;
138 0 : if (new_gain != 1.f) {
139 0 : float last_applied_gain = 1.f;
140 0 : float gain_decrease_step_size = last_frame_was_saturated_
141 0 : ? gain_saturated_decrease_step_size_
142 0 : : gain_normal_decrease_step_size_;
143 0 : for (size_t k = 0; k < audio->num_channels(); ++k) {
144 0 : last_applied_gain = ApplyGain(
145 : new_gain, old_gain_, gain_increase_step_size_,
146 : gain_decrease_step_size,
147 0 : rtc::ArrayView<float>(audio->channels_f()[k], audio->num_frames()));
148 : }
149 :
150 0 : num_saturations = CountSaturations(*audio);
151 0 : LimitToAllowedRange(audio);
152 0 : old_gain_ = last_applied_gain;
153 : }
154 :
155 0 : data_dumper_->DumpRaw("lc_last_applied_gain", 1, &old_gain_);
156 :
157 0 : return num_saturations;
158 : }
159 :
160 : } // namespace webrtc
|