Alex Loiko | a05ee82 | 2018-02-20 15:58:36 +0100 | [diff] [blame^] | 1 | /* |
| 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/interpolated_gain_curve.h" |
| 12 | |
| 13 | #include "modules/audio_processing/agc2/agc2_common.h" |
| 14 | #include "modules/audio_processing/logging/apm_data_dumper.h" |
| 15 | #include "rtc_base/checks.h" |
| 16 | #include "rtc_base/logging.h" |
| 17 | |
| 18 | namespace webrtc { |
| 19 | |
| 20 | constexpr std::array<float, kInterpolatedGainCurveTotalPoints> |
| 21 | InterpolatedGainCurve::approximation_params_x_; |
| 22 | |
| 23 | constexpr std::array<float, kInterpolatedGainCurveTotalPoints> |
| 24 | InterpolatedGainCurve::approximation_params_m_; |
| 25 | |
| 26 | constexpr std::array<float, kInterpolatedGainCurveTotalPoints> |
| 27 | InterpolatedGainCurve::approximation_params_q_; |
| 28 | |
| 29 | InterpolatedGainCurve::InterpolatedGainCurve(ApmDataDumper* apm_data_dumper) |
| 30 | : apm_data_dumper_(apm_data_dumper) {} |
| 31 | |
| 32 | InterpolatedGainCurve::~InterpolatedGainCurve() { |
| 33 | if (stats_.available) { |
| 34 | // TODO(alessiob): We might want to add these stats as RTC metrics. |
| 35 | RTC_DCHECK(apm_data_dumper_); |
| 36 | apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_identity", |
| 37 | stats_.look_ups_identity_region); |
| 38 | apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_knee", |
| 39 | stats_.look_ups_knee_region); |
| 40 | apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_limiter", |
| 41 | stats_.look_ups_limiter_region); |
| 42 | apm_data_dumper_->DumpRaw("agc2_interp_gain_curve_lookups_saturation", |
| 43 | stats_.look_ups_saturation_region); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | void InterpolatedGainCurve::UpdateStats(float input_level) const { |
| 48 | stats_.available = true; |
| 49 | |
| 50 | if (input_level < approximation_params_x_[0]) { |
| 51 | stats_.look_ups_identity_region++; |
| 52 | } else if (input_level < |
| 53 | approximation_params_x_[kInterpolatedGainCurveKneePoints - 1]) { |
| 54 | stats_.look_ups_knee_region++; |
| 55 | } else if (input_level < kMaxInputLevelLinear) { |
| 56 | stats_.look_ups_limiter_region++; |
| 57 | } else { |
| 58 | stats_.look_ups_saturation_region++; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // Looks up a gain to apply given a non-negative input level. |
| 63 | // The cost of this operation depends on the region in which |input_level| |
| 64 | // falls. |
| 65 | // For the identity and the saturation regions the cost is O(1). |
| 66 | // For the other regions, namely knee and limiter, the cost is |
| 67 | // O(2 + log2(|LightkInterpolatedGainCurveTotalPoints|), plus O(1) for the |
| 68 | // linear interpolation (one product and one sum). |
| 69 | float InterpolatedGainCurve::LookUpGainToApply(float input_level) const { |
| 70 | UpdateStats(input_level); |
| 71 | |
| 72 | if (input_level <= approximation_params_x_[0]) { |
| 73 | // Identity region. |
| 74 | return 1.0f; |
| 75 | } |
| 76 | |
| 77 | if (input_level >= kMaxInputLevelLinear) { |
| 78 | // Saturating lower bound. The saturing samples exactly hit the clipping |
| 79 | // level. This method achieves has the lowest harmonic distorsion, but it |
| 80 | // may reduce the amplitude of the non-saturating samples too much. |
| 81 | return 32768.f / input_level; |
| 82 | } |
| 83 | |
| 84 | // Knee and limiter regions; find the linear piece index. Spelling |
| 85 | // out the complete type was the only way to silence both the clang |
| 86 | // plugin and the windows compilers. |
| 87 | std::array<float, kInterpolatedGainCurveTotalPoints>::const_iterator it = |
| 88 | std::lower_bound(approximation_params_x_.begin(), |
| 89 | approximation_params_x_.end(), input_level); |
| 90 | const size_t index = std::distance(approximation_params_x_.begin(), it) - 1; |
| 91 | RTC_DCHECK_LE(0, index); |
| 92 | RTC_DCHECK_LT(index, approximation_params_m_.size()); |
| 93 | RTC_DCHECK_LE(approximation_params_x_[index], input_level); |
| 94 | if (index < approximation_params_m_.size() - 1) { |
| 95 | RTC_DCHECK_LE(input_level, approximation_params_x_[index + 1]); |
| 96 | } |
| 97 | |
| 98 | // Piece-wise linear interploation. |
| 99 | const float gain = approximation_params_m_[index] * input_level + |
| 100 | approximation_params_q_[index]; |
| 101 | RTC_DCHECK_LE(0.f, gain); |
| 102 | return gain; |
| 103 | } |
| 104 | |
| 105 | } // namespace webrtc |