blob: 7bd068e738a93cfca63bf1a1dc548540f055afda [file] [log] [blame]
/*
* 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;
}
void AdaptiveDynamicRangeCompression::Compress(float *x1, float *x2) {
// Taking the maximum amplitude of both channels
const float max_abs_x = std::max(std::fabs(*x1),
std::max(std::fabs(*x2), 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);
*x1 *= compressor_gain_;
if (*x1 > kFixedPointLimit) {
*x1 = kFixedPointLimit;
}
if (*x1 < -kFixedPointLimit) {
*x1 = -kFixedPointLimit;
}
*x2 *= compressor_gain_;
if (*x2 > kFixedPointLimit) {
*x2 = kFixedPointLimit;
}
if (*x2 < -kFixedPointLimit) {
*x2 = -kFixedPointLimit;
}
}
} // namespace le_fx