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 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 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 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 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