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