Home | History | Annotate | Download | only in core
      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