summaryrefslogtreecommitdiffstats
path: root/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
blob: 2bbd043751c13b4325e71471ec73a2118e1b6a9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cmath>

#include "common/core/math.h"
#include "common/core/types.h"
#include "dsp/core/basic.h"
#include "dsp/core/interpolation.h"
#include "dsp/core/dynamic_range_compression.h"

//#define LOG_NDEBUG 0
#include <cutils/log.h>


namespace le_fx {

// Definitions for static const class members declared in
// dynamic_range_compression.h.
const float AdaptiveDynamicRangeCompression::kMinAbsValue = 0.000001f;
const float AdaptiveDynamicRangeCompression::kMinLogAbsValue =
    0.032766999999999997517097227728299912996590137481689453125f;
const float AdaptiveDynamicRangeCompression::kFixedPointLimit = 32767.0f;
const float AdaptiveDynamicRangeCompression::kInverseFixedPointLimit =
    1.0f / AdaptiveDynamicRangeCompression::kFixedPointLimit;
const float AdaptiveDynamicRangeCompression::kDefaultKneeThresholdInDecibel =
    -8.0f;
const float AdaptiveDynamicRangeCompression::kCompressionRatio = 7.0f;
const float AdaptiveDynamicRangeCompression::kTauAttack = 0.001f;
const float AdaptiveDynamicRangeCompression::kTauRelease = 0.015f;

AdaptiveDynamicRangeCompression::AdaptiveDynamicRangeCompression() {
  static const float kTargetGain[] = {
      1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
  static const float kKneeThreshold[] = {
      -8.0f, -8.0f, -8.5f, -9.0f, -10.0f };
  target_gain_to_knee_threshold_.Initialize(
      &kTargetGain[0], &kKneeThreshold[0],
      sizeof(kTargetGain) / sizeof(kTargetGain[0]));
}

bool AdaptiveDynamicRangeCompression::Initialize(
        float target_gain, float sampling_rate) {
  set_knee_threshold_via_target_gain(target_gain);
  sampling_rate_ = sampling_rate;
  state_ = 0.0f;
  compressor_gain_ = 1.0f;
  if (kTauAttack > 0.0f) {
    const float taufs = kTauAttack * sampling_rate_;
    alpha_attack_ = std::exp(-1.0f / taufs);
  } else {
    alpha_attack_ = 0.0f;
  }
  if (kTauRelease > 0.0f) {
    const float taufs = kTauRelease * sampling_rate_;
    alpha_release_ = std::exp(-1.0f / taufs);
  } else {
    alpha_release_ = 0.0f;
  }
  // Feed-forward topology
  slope_ = 1.0f / kCompressionRatio - 1.0f;
  return true;
}

float AdaptiveDynamicRangeCompression::Compress(float x) {
  const float max_abs_x = std::max(std::fabs(x), kMinLogAbsValue);
  const float max_abs_x_dB = math::fast_log(max_abs_x);
  // Subtract Threshold from log-encoded input to get the amount of overshoot
  const float overshoot = max_abs_x_dB - knee_threshold_;
  // Hard half-wave rectifier
  const float rect = std::max(overshoot, 0.0f);
  // Multiply rectified overshoot with slope
  const float cv = rect * slope_;
  const float prev_state = state_;
  if (cv <= state_) {
    state_ = alpha_attack_ * state_ + (1.0f - alpha_attack_) * cv;
  } else {
    state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
  }
  compressor_gain_ *=
      math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
  x *= compressor_gain_;
  if (x > kFixedPointLimit) {
    return kFixedPointLimit;
  }
  if (x < -kFixedPointLimit) {
    return -kFixedPointLimit;
  }
  return x;
}

}  // namespace le_fx