blob: b52e0d7f57955aee95ff691b0f24cd1126f80130 [file] [log] [blame]
Alex Loikoa05ee822018-02-20 15:58:36 +01001/*
2 * Copyright (c) 2018 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 "modules/audio_processing/agc2/gain_curve_applier.h"
12
13#include <algorithm>
14#include <array>
15#include <cmath>
16
17#include "api/array_view.h"
Alex Loiko507e8d12018-02-27 13:51:47 +010018#include "modules/audio_processing/logging/apm_data_dumper.h"
Alex Loikoa05ee822018-02-20 15:58:36 +010019#include "rtc_base/checks.h"
20
21namespace webrtc {
22namespace {
23
24// This constant affects the way scaling factors are interpolated for the first
25// sub-frame of a frame. Only in the case in which the first sub-frame has an
26// estimated level which is greater than the that of the previous analyzed
27// sub-frame, linear interpolation is replaced with a power function which
28// reduces the chances of over-shooting (and hence saturation), however reducing
29// the fixed gain effectiveness.
30constexpr float kAttackFirstSubframeInterpolationPower = 8.f;
31
32void InterpolateFirstSubframe(float last_factor,
33 float current_factor,
34 rtc::ArrayView<float> subframe) {
35 const auto n = subframe.size();
36 constexpr auto p = kAttackFirstSubframeInterpolationPower;
37 for (size_t i = 0; i < n; ++i) {
38 subframe[i] = std::pow(1.f - i / n, p) * (last_factor - current_factor) +
39 current_factor;
40 }
41}
42
43void ComputePerSampleSubframeFactors(
44 const std::array<float, kSubFramesInFrame + 1>& scaling_factors,
45 size_t samples_per_channel,
46 rtc::ArrayView<float> per_sample_scaling_factors) {
47 const size_t num_subframes = scaling_factors.size() - 1;
48 const size_t subframe_size =
49 rtc::CheckedDivExact(samples_per_channel, num_subframes);
50
51 // Handle first sub-frame differently in case of attack.
52 const bool is_attack = scaling_factors[0] > scaling_factors[1];
53 if (is_attack) {
54 InterpolateFirstSubframe(
55 scaling_factors[0], scaling_factors[1],
56 rtc::ArrayView<float>(
57 per_sample_scaling_factors.subview(0, subframe_size)));
58 }
59
60 for (size_t i = is_attack ? 1 : 0; i < num_subframes; ++i) {
61 const size_t subframe_start = i * subframe_size;
62 const float scaling_start = scaling_factors[i];
63 const float scaling_end = scaling_factors[i + 1];
64 const float scaling_diff = (scaling_end - scaling_start) / subframe_size;
65 for (size_t j = 0; j < subframe_size; ++j) {
66 per_sample_scaling_factors[subframe_start + j] =
67 scaling_start + scaling_diff * j;
68 }
69 }
70}
71
72void ScaleSamples(rtc::ArrayView<const float> per_sample_scaling_factors,
73 AudioFrameView<float> signal) {
74 const size_t samples_per_channel = signal.samples_per_channel();
75 RTC_DCHECK_EQ(samples_per_channel, per_sample_scaling_factors.size());
76 for (size_t i = 0; i < signal.num_channels(); ++i) {
77 auto channel = signal.channel(i);
78 for (size_t j = 0; j < samples_per_channel; ++j) {
79 channel[j] *= per_sample_scaling_factors[j];
80 }
81 }
82}
83
84} // namespace
85
86GainCurveApplier::GainCurveApplier(size_t sample_rate_hz,
Alex Loiko03ad9b82018-08-13 17:40:43 +020087 ApmDataDumper* apm_data_dumper,
88 std::string histogram_name)
89 : interp_gain_curve_(apm_data_dumper, histogram_name),
Alex Loikoa05ee822018-02-20 15:58:36 +010090 level_estimator_(sample_rate_hz, apm_data_dumper),
91 apm_data_dumper_(apm_data_dumper) {}
92
93GainCurveApplier::~GainCurveApplier() = default;
94
95void GainCurveApplier::Process(AudioFrameView<float> signal) {
96 const auto level_estimate = level_estimator_.ComputeLevel(signal);
97
98 RTC_DCHECK_EQ(level_estimate.size() + 1, scaling_factors_.size());
99 scaling_factors_[0] = last_scaling_factor_;
100 std::transform(level_estimate.begin(), level_estimate.end(),
101 scaling_factors_.begin() + 1, [this](float x) {
102 return interp_gain_curve_.LookUpGainToApply(x);
103 });
104
105 const size_t samples_per_channel = signal.samples_per_channel();
106 RTC_DCHECK_LE(samples_per_channel, kMaximalNumberOfSamplesPerChannel);
107
108 auto per_sample_scaling_factors = rtc::ArrayView<float>(
109 &per_sample_scaling_factors_[0], samples_per_channel);
110 ComputePerSampleSubframeFactors(scaling_factors_, samples_per_channel,
111 per_sample_scaling_factors);
112 ScaleSamples(per_sample_scaling_factors, signal);
113
114 last_scaling_factor_ = scaling_factors_.back();
115
116 // Dump data for debug.
117 apm_data_dumper_->DumpRaw("agc2_gain_curve_applier_scaling_factors",
118 samples_per_channel,
119 per_sample_scaling_factors_.data());
120}
121
122InterpolatedGainCurve::Stats GainCurveApplier::GetGainCurveStats() const {
123 return interp_gain_curve_.get_stats();
124}
125
126void GainCurveApplier::SetSampleRate(size_t sample_rate_hz) {
127 level_estimator_.SetSampleRate(sample_rate_hz);
128 // Check that per_sample_scaling_factors_ is large enough.
129 RTC_DCHECK_LE(sample_rate_hz,
130 kMaximalNumberOfSamplesPerChannel * 1000 / kFrameDurationMs);
131}
132
Alessio Bazzica82ec0fa2018-08-27 14:24:16 +0200133void GainCurveApplier::Reset() {
134 level_estimator_.Reset();
135}
136
Alex Loikoa05ee822018-02-20 15:58:36 +0100137} // namespace webrtc