1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cmath>
18
19#include "common/core/math.h"
20#include "common/core/types.h"
21#include "dsp/core/basic.h"
22#include "dsp/core/interpolation.h"
23#include "dsp/core/dynamic_range_compression.h"
24
25//#define LOG_NDEBUG 0
26#include <cutils/log.h>
27
28
29namespace le_fx {
30
31// Definitions for static const class members declared in
32// dynamic_range_compression.h.
33const float AdaptiveDynamicRangeCompression::kMinAbsValue = 0.000001f;
34const float AdaptiveDynamicRangeCompression::kMinLogAbsValue =
35    0.032766999999999997517097227728299912996590137481689453125f;
36const float AdaptiveDynamicRangeCompression::kFixedPointLimit = 32767.0f;
37const float AdaptiveDynamicRangeCompression::kInverseFixedPointLimit =
38    1.0f / AdaptiveDynamicRangeCompression::kFixedPointLimit;
39const float AdaptiveDynamicRangeCompression::kDefaultKneeThresholdInDecibel =
40    -8.0f;
41const float AdaptiveDynamicRangeCompression::kCompressionRatio = 7.0f;
42const float AdaptiveDynamicRangeCompression::kTauAttack = 0.001f;
43const float AdaptiveDynamicRangeCompression::kTauRelease = 0.015f;
44
45AdaptiveDynamicRangeCompression::AdaptiveDynamicRangeCompression() {
46  static const float kTargetGain[] = {
47      1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
48  static const float kKneeThreshold[] = {
49      -8.0f, -8.0f, -8.5f, -9.0f, -10.0f };
50  target_gain_to_knee_threshold_.Initialize(
51      &kTargetGain[0], &kKneeThreshold[0],
52      sizeof(kTargetGain) / sizeof(kTargetGain[0]));
53}
54
55bool AdaptiveDynamicRangeCompression::Initialize(
56        float target_gain, float sampling_rate) {
57  set_knee_threshold_via_target_gain(target_gain);
58  sampling_rate_ = sampling_rate;
59  state_ = 0.0f;
60  compressor_gain_ = 1.0f;
61  if (kTauAttack > 0.0f) {
62    const float taufs = kTauAttack * sampling_rate_;
63    alpha_attack_ = std::exp(-1.0f / taufs);
64  } else {
65    alpha_attack_ = 0.0f;
66  }
67  if (kTauRelease > 0.0f) {
68    const float taufs = kTauRelease * sampling_rate_;
69    alpha_release_ = std::exp(-1.0f / taufs);
70  } else {
71    alpha_release_ = 0.0f;
72  }
73  // Feed-forward topology
74  slope_ = 1.0f / kCompressionRatio - 1.0f;
75  return true;
76}
77
78float AdaptiveDynamicRangeCompression::Compress(float x) {
79  const float max_abs_x = std::max(std::fabs(x), kMinLogAbsValue);
80  const float max_abs_x_dB = math::fast_log(max_abs_x);
81  // Subtract Threshold from log-encoded input to get the amount of overshoot
82  const float overshoot = max_abs_x_dB - knee_threshold_;
83  // Hard half-wave rectifier
84  const float rect = std::max(overshoot, 0.0f);
85  // Multiply rectified overshoot with slope
86  const float cv = rect * slope_;
87  const float prev_state = state_;
88  if (cv <= state_) {
89    state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
90  } else {
91    state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
92  }
93  compressor_gain_ *=
94      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
95  x *= compressor_gain_;
96  if (x > kFixedPointLimit) {
97    return kFixedPointLimit;
98  }
99  if (x < -kFixedPointLimit) {
100    return -kFixedPointLimit;
101  }
102  return x;
103}
104
105void AdaptiveDynamicRangeCompression::Compress(float *x1, float *x2) {
106  // Taking the maximum amplitude of both channels
107  const float max_abs_x = std::max(std::fabs(*x1),
108    std::max(std::fabs(*x2), kMinLogAbsValue));
109  const float max_abs_x_dB = math::fast_log(max_abs_x);
110  // Subtract Threshold from log-encoded input to get the amount of overshoot
111  const float overshoot = max_abs_x_dB - knee_threshold_;
112  // Hard half-wave rectifier
113  const float rect = std::max(overshoot, 0.0f);
114  // Multiply rectified overshoot with slope
115  const float cv = rect * slope_;
116  const float prev_state = state_;
117  if (cv <= state_) {
118    state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
119  } else {
120    state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
121  }
122  compressor_gain_ *=
123      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
124  *x1 *= compressor_gain_;
125  if (*x1 > kFixedPointLimit) {
126    *x1 = kFixedPointLimit;
127  }
128  if (*x1 < -kFixedPointLimit) {
129    *x1 = -kFixedPointLimit;
130  }
131  *x2 *= compressor_gain_;
132  if (*x2 > kFixedPointLimit) {
133    *x2 = kFixedPointLimit;
134  }
135  if (*x2 < -kFixedPointLimit) {
136    *x2 = -kFixedPointLimit;
137  }
138}
139
140}  // namespace le_fx
141
142