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
28 namespace le_fx {
29
30 // Definitions for static const class members declared in
31 // dynamic_range_compression.h.
32 const float AdaptiveDynamicRangeCompression::kMinAbsValue = 0.000001f;
33 const float AdaptiveDynamicRangeCompression::kMinLogAbsValue =
34 0.032766999999999997517097227728299912996590137481689453125f;
35 const float AdaptiveDynamicRangeCompression::kFixedPointLimit = 32767.0f;
36 const float AdaptiveDynamicRangeCompression::kInverseFixedPointLimit =
37 1.0f / AdaptiveDynamicRangeCompression::kFixedPointLimit;
38 const float AdaptiveDynamicRangeCompression::kDefaultKneeThresholdInDecibel =
39 -8.0f;
40 const float AdaptiveDynamicRangeCompression::kCompressionRatio = 7.0f;
41 const float AdaptiveDynamicRangeCompression::kTauAttack = 0.001f;
42 const float AdaptiveDynamicRangeCompression::kTauRelease = 0.015f;
43
AdaptiveDynamicRangeCompression()44 AdaptiveDynamicRangeCompression::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
Initialize(float target_gain,float sampling_rate)54 bool 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
Compress(float x)77 float 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
Compress(float * x1,float * x2)104 void 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