LoudnessEnhancer compatible with stereo imaging

Use a single compressor for both channels.
Envelope of signal is determined by looking at both channels.

Bug 8413913

Change-Id: Ia9b6f34923d2977c60a3352500b858dfa1fab33c
diff --git a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
index dfc25db..91ed677 100644
--- a/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
+++ b/media/libeffects/loudness/EffectLoudnessEnhancer.cpp
@@ -56,8 +56,7 @@
     int32_t mTargetGainmB;// target gain in mB
     // in this implementation, there is no coupling between the compression on the left and right
     // channels
-    le_fx::AdaptiveDynamicRangeCompression* mCompressorL;
-    le_fx::AdaptiveDynamicRangeCompression* mCompressorR;
+    le_fx::AdaptiveDynamicRangeCompression* mCompressor;
 };
 
 //
@@ -68,11 +67,10 @@
 {
     ALOGV("  > LE_reset(%p)", pContext);
 
-    if ((pContext->mCompressorL != NULL) && (pContext->mCompressorR != NULL)) {
+    if (pContext->mCompressor != NULL) {
         float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
         ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
-        pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
-        pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
+        pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
     } else {
         ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext);
     }
@@ -176,13 +174,9 @@
     float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
     ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
 
-    if (pContext->mCompressorL == NULL) {
-        pContext->mCompressorL = new le_fx::AdaptiveDynamicRangeCompression();
-        pContext->mCompressorL->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
-    }
-    if (pContext->mCompressorR == NULL) {
-        pContext->mCompressorR = new le_fx::AdaptiveDynamicRangeCompression();
-        pContext->mCompressorR->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
+    if (pContext->mCompressor == NULL) {
+        pContext->mCompressor = new le_fx::AdaptiveDynamicRangeCompression();
+        pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
     }
 
     LE_setConfig(pContext, &pContext->mConfig);
@@ -215,8 +209,7 @@
     pContext->mItfe = &gLEInterface;
     pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
 
-    pContext->mCompressorL = NULL;
-    pContext->mCompressorR = NULL;
+    pContext->mCompressor = NULL;
     ret = LE_init(pContext);
     if (ret < 0) {
         ALOGW("LELib_Create() init failed");
@@ -242,13 +235,9 @@
         return -EINVAL;
     }
     pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
-    if (pContext->mCompressorL != NULL) {
-        delete pContext->mCompressorL;
-        pContext->mCompressorL = NULL;
-    }
-    if (pContext->mCompressorR != NULL) {
-        delete pContext->mCompressorR;
-        pContext->mCompressorR = NULL;
+    if (pContext->mCompressor != NULL) {
+        delete pContext->mCompressor;
+        pContext->mCompressor = NULL;
     }
     delete pContext;
 
@@ -293,11 +282,14 @@
     //ALOGV("LE about to process %d samples", inBuffer->frameCount);
     uint16_t inIdx;
     float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f);
+    float leftSample, rightSample;
     for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
-        inBuffer->s16[2*inIdx] = pContext->mCompressorL->Compress(
-                inputAmp * (float)inBuffer->s16[2*inIdx]);
-        inBuffer->s16[2*inIdx +1] = pContext->mCompressorR->Compress(
-                inputAmp * (float)inBuffer->s16[2*inIdx +1]);
+        // makeup gain is applied on the input of the compressor
+        leftSample  = inputAmp * (float)inBuffer->s16[2*inIdx];
+        rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1];
+        pContext->mCompressor->Compress(&leftSample, &rightSample);
+        inBuffer->s16[2*inIdx]    = (int16_t) leftSample;
+        inBuffer->s16[2*inIdx +1] = (int16_t) rightSample;
     }
 
     if (inBuffer->raw != outBuffer->raw) {
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
index fed8c2a..da75ceb 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression-inl.h
@@ -35,7 +35,7 @@
     float target_gain) {
   const float decibel = target_gain_to_knee_threshold_.Interpolate(
         target_gain);
-  ALOGE("set_knee_threshold_via_target_gain: decibel =%.3f", decibel);
+  ALOGV("set_knee_threshold_via_target_gain: decibel =%.3fdB", decibel);
   set_knee_threshold(decibel);
 }
 
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
index 2bbd043..7bd068e 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
@@ -102,5 +102,40 @@
   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
 
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
index 4c015df..2821a78 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.h
@@ -55,6 +55,9 @@
   // log(.) and exp(.).
   float Compress(float x);
 
+  // Stereo channel version of the compressor
+  void Compress(float *x1, float *x2);
+
   // This version is slower than Compress(.) but faster than CompressSlow(.)
   float CompressNormalSpeed(float x);