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//#define LOG_NDEBUG 0
17
18#include <cmath>
19
20#include "common/core/math.h"
21#include "common/core/types.h"
22#include "dsp/core/basic.h"
23#include "dsp/core/interpolation.h"
24#include "dsp/core/dynamic_range_compression.h"
25
26#include <android/log.h>
27
28namespace le_fx {
29
30// Definitions for static const class members declared in
31// dynamic_range_compression.h.
32const float AdaptiveDynamicRangeCompression::kMinAbsValue = 0.000001f;
33const float AdaptiveDynamicRangeCompression::kMinLogAbsValue =
34    0.032766999999999997517097227728299912996590137481689453125f;
35const float AdaptiveDynamicRangeCompression::kFixedPointLimit = 32767.0f;
36const float AdaptiveDynamicRangeCompression::kInverseFixedPointLimit =
37    1.0f / AdaptiveDynamicRangeCompression::kFixedPointLimit;
38const float AdaptiveDynamicRangeCompression::kDefaultKneeThresholdInDecibel =
39    -8.0f;
40const float AdaptiveDynamicRangeCompression::kCompressionRatio = 7.0f;
41const float AdaptiveDynamicRangeCompression::kTauAttack = 0.001f;
42const float AdaptiveDynamicRangeCompression::kTauRelease = 0.015f;
43
44AdaptiveDynamicRangeCompression::AdaptiveDynamicRangeCompression() {
45  static const float kTargetGain[] = {
46      1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
47  static const float kKneeThreshold[] = {
48      -8.0f, -8.0f, -8.5f, -9.0f, -10.0f };
49  target_gain_to_knee_threshold_.Initialize(
50      &kTargetGain[0], &kKneeThreshold[0],
51      sizeof(kTargetGain) / sizeof(kTargetGain[0]));
52}
53
54bool AdaptiveDynamicRangeCompression::Initialize(
55        float target_gain, float sampling_rate) {
56  set_knee_threshold_via_target_gain(target_gain);
57  sampling_rate_ = sampling_rate;
58  state_ = 0.0f;
59  compressor_gain_ = 1.0f;
60  if (kTauAttack > 0.0f) {
61    const float taufs = kTauAttack * sampling_rate_;
62    alpha_attack_ = std::exp(-1.0f / taufs);
63  } else {
64    alpha_attack_ = 0.0f;
65  }
66  if (kTauRelease > 0.0f) {
67    const float taufs = kTauRelease * sampling_rate_;
68    alpha_release_ = std::exp(-1.0f / taufs);
69  } else {
70    alpha_release_ = 0.0f;
71  }
72  // Feed-forward topology
73  slope_ = 1.0f / kCompressionRatio - 1.0f;
74  return true;
75}
76
77float AdaptiveDynamicRangeCompression::Compress(float x) {
78  const float max_abs_x = std::max(std::fabs(x), kMinLogAbsValue);
79  const float max_abs_x_dB = math::fast_log(max_abs_x);
80  // Subtract Threshold from log-encoded input to get the amount of overshoot
81  const float overshoot = max_abs_x_dB - knee_threshold_;
82  // Hard half-wave rectifier
83  const float rect = std::max(overshoot, 0.0f);
84  // Multiply rectified overshoot with slope
85  const float cv = rect * slope_;
86  const float prev_state = state_;
87  if (cv <= state_) {
88    state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
89  } else {
90    state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
91  }
92  compressor_gain_ *=
93      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
94  x *= compressor_gain_;
95  if (x > kFixedPointLimit) {
96    return kFixedPointLimit;
97  }
98  if (x < -kFixedPointLimit) {
99    return -kFixedPointLimit;
100  }
101  return x;
102}
103
104void AdaptiveDynamicRangeCompression::Compress(float *x1, float *x2) {
105  // Taking the maximum amplitude of both channels
106  const float max_abs_x = std::max(std::fabs(*x1),
107    std::max(std::fabs(*x2), kMinLogAbsValue));
108  const float max_abs_x_dB = math::fast_log(max_abs_x);
109  // Subtract Threshold from log-encoded input to get the amount of overshoot
110  const float overshoot = max_abs_x_dB - knee_threshold_;
111  // Hard half-wave rectifier
112  const float rect = std::max(overshoot, 0.0f);
113  // Multiply rectified overshoot with slope
114  const float cv = rect * slope_;
115  const float prev_state = state_;
116  if (cv <= state_) {
117    state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
118  } else {
119    state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
120  }
121  compressor_gain_ *=
122      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
123  *x1 *= compressor_gain_;
124  if (*x1 > kFixedPointLimit) {
125    *x1 = kFixedPointLimit;
126  }
127  if (*x1 < -kFixedPointLimit) {
128    *x1 = -kFixedPointLimit;
129  }
130  *x2 *= compressor_gain_;
131  if (*x2 > kFixedPointLimit) {
132    *x2 = kFixedPointLimit;
133  }
134  if (*x2 < -kFixedPointLimit) {
135    *x2 = -kFixedPointLimit;
136  }
137}
138
139}  // namespace le_fx
140
141