blob: 018f809e013b1b30fab7d6060293ce2c228cbb43 [file] [log] [blame]
peahca4cac72016-06-29 15:26:12 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_processing/level_controller/gain_applier.h"
peahca4cac72016-06-29 15:26:12 -070012
13#include <algorithm>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "api/array_view.h"
16#include "rtc_base/checks.h"
peahca4cac72016-06-29 15:26:12 -070017
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/audio_processing/audio_buffer.h"
19#include "modules/audio_processing/logging/apm_data_dumper.h"
peahca4cac72016-06-29 15:26:12 -070020
21namespace webrtc {
22namespace {
23
24const float kMaxSampleValue = 32767.f;
25const float kMinSampleValue = -32767.f;
26
27int CountSaturations(rtc::ArrayView<const float> in) {
28 return std::count_if(in.begin(), in.end(), [](const float& v) {
29 return v >= kMaxSampleValue || v <= kMinSampleValue;
30 });
31}
32
33int CountSaturations(const AudioBuffer& audio) {
34 int num_saturations = 0;
35 for (size_t k = 0; k < audio.num_channels(); ++k) {
36 num_saturations += CountSaturations(rtc::ArrayView<const float>(
37 audio.channels_const_f()[k], audio.num_frames()));
38 }
39 return num_saturations;
40}
41
42void LimitToAllowedRange(rtc::ArrayView<float> x) {
43 for (auto& v : x) {
44 v = std::max(kMinSampleValue, v);
45 v = std::min(kMaxSampleValue, v);
46 }
47}
48
49void LimitToAllowedRange(AudioBuffer* audio) {
50 for (size_t k = 0; k < audio->num_channels(); ++k) {
51 LimitToAllowedRange(
52 rtc::ArrayView<float>(audio->channels_f()[k], audio->num_frames()));
53 }
54}
55
56float ApplyIncreasingGain(float new_gain,
57 float old_gain,
58 float step_size,
59 rtc::ArrayView<float> x) {
60 RTC_DCHECK_LT(0.f, step_size);
61 float gain = old_gain;
62 for (auto& v : x) {
63 gain = std::min(new_gain, gain + step_size);
64 v *= gain;
65 }
66 return gain;
67}
68
69float ApplyDecreasingGain(float new_gain,
70 float old_gain,
71 float step_size,
72 rtc::ArrayView<float> x) {
peahb59ff892016-06-30 09:19:32 -070073 RTC_DCHECK_GT(0.f, step_size);
peahca4cac72016-06-29 15:26:12 -070074 float gain = old_gain;
75 for (auto& v : x) {
peahb59ff892016-06-30 09:19:32 -070076 gain = std::max(new_gain, gain + step_size);
peahca4cac72016-06-29 15:26:12 -070077 v *= gain;
78 }
79 return gain;
80}
81
82float ApplyConstantGain(float gain, rtc::ArrayView<float> x) {
83 for (auto& v : x) {
84 v *= gain;
85 }
86
87 return gain;
88}
89
90float ApplyGain(float new_gain,
91 float old_gain,
peahb59ff892016-06-30 09:19:32 -070092 float increase_step_size,
93 float decrease_step_size,
peahca4cac72016-06-29 15:26:12 -070094 rtc::ArrayView<float> x) {
peahb59ff892016-06-30 09:19:32 -070095 RTC_DCHECK_LT(0.f, increase_step_size);
96 RTC_DCHECK_GT(0.f, decrease_step_size);
peahca4cac72016-06-29 15:26:12 -070097 if (new_gain == old_gain) {
98 return ApplyConstantGain(new_gain, x);
99 } else if (new_gain > old_gain) {
peahb59ff892016-06-30 09:19:32 -0700100 return ApplyIncreasingGain(new_gain, old_gain, increase_step_size, x);
peahca4cac72016-06-29 15:26:12 -0700101 } else {
peahb59ff892016-06-30 09:19:32 -0700102 return ApplyDecreasingGain(new_gain, old_gain, decrease_step_size, x);
peahca4cac72016-06-29 15:26:12 -0700103 }
104}
105
106} // namespace
107
108GainApplier::GainApplier(ApmDataDumper* data_dumper)
109 : data_dumper_(data_dumper) {}
110
111void GainApplier::Initialize(int sample_rate_hz) {
112 RTC_DCHECK(sample_rate_hz == AudioProcessing::kSampleRate8kHz ||
113 sample_rate_hz == AudioProcessing::kSampleRate16kHz ||
114 sample_rate_hz == AudioProcessing::kSampleRate32kHz ||
115 sample_rate_hz == AudioProcessing::kSampleRate48kHz);
peahb59ff892016-06-30 09:19:32 -0700116 const float kGainIncreaseStepSize48kHz = 0.0001f;
117 const float kGainDecreaseStepSize48kHz = -0.01f;
118 const float kGainSaturatedDecreaseStepSize48kHz = -0.05f;
119
120 last_frame_was_saturated_ = false;
peahca4cac72016-06-29 15:26:12 -0700121 old_gain_ = 1.f;
peahb59ff892016-06-30 09:19:32 -0700122 gain_increase_step_size_ =
123 kGainIncreaseStepSize48kHz *
124 (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
125 gain_normal_decrease_step_size_ =
126 kGainDecreaseStepSize48kHz *
127 (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
128 gain_saturated_decrease_step_size_ =
129 kGainSaturatedDecreaseStepSize48kHz *
peahca4cac72016-06-29 15:26:12 -0700130 (static_cast<float>(AudioProcessing::kSampleRate48kHz) / sample_rate_hz);
131}
132
133int GainApplier::Process(float new_gain, AudioBuffer* audio) {
peahb59ff892016-06-30 09:19:32 -0700134 RTC_CHECK_NE(0.f, gain_increase_step_size_);
135 RTC_CHECK_NE(0.f, gain_normal_decrease_step_size_);
136 RTC_CHECK_NE(0.f, gain_saturated_decrease_step_size_);
peahca4cac72016-06-29 15:26:12 -0700137 int num_saturations = 0;
138 if (new_gain != 1.f) {
139 float last_applied_gain = 1.f;
peahb59ff892016-06-30 09:19:32 -0700140 float gain_decrease_step_size = last_frame_was_saturated_
141 ? gain_saturated_decrease_step_size_
142 : gain_normal_decrease_step_size_;
peahca4cac72016-06-29 15:26:12 -0700143 for (size_t k = 0; k < audio->num_channels(); ++k) {
peahca4cac72016-06-29 15:26:12 -0700144 last_applied_gain = ApplyGain(
peahb59ff892016-06-30 09:19:32 -0700145 new_gain, old_gain_, gain_increase_step_size_,
146 gain_decrease_step_size,
peahca4cac72016-06-29 15:26:12 -0700147 rtc::ArrayView<float>(audio->channels_f()[k], audio->num_frames()));
148 }
peahb59ff892016-06-30 09:19:32 -0700149
peahca4cac72016-06-29 15:26:12 -0700150 num_saturations = CountSaturations(*audio);
151 LimitToAllowedRange(audio);
152 old_gain_ = last_applied_gain;
153 }
154
155 data_dumper_->DumpRaw("lc_last_applied_gain", 1, &old_gain_);
156
157 return num_saturations;
158}
159
160} // namespace webrtc