Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad3020b4 authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "LoudnessEnhancer: Add multichannel support" into main

parents 9da0b43f 398c4878
Loading
Loading
Loading
Loading
+10 −14
Original line number Diff line number Diff line
@@ -103,7 +103,9 @@ int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig)
    if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
    if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
    if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
    if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
    if (audio_channel_count_from_out_mask(pConfig->inputCfg.channels) > FCC_LIMIT) {
        return -EINVAL;
    }
    if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
            pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
    if (pConfig->inputCfg.format != kProcessFormat) return -EINVAL;
@@ -278,27 +280,22 @@ int LE_process(
    }

    //ALOGV("LE about to process %d samples", inBuffer->frameCount);
    uint16_t inIdx;
    constexpr float scale = 1 << 15; // power of 2 is lossless conversion to int16_t range
    constexpr float inverseScale = 1.f / scale;
    const float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f) * scale;
    float leftSample, rightSample;
    for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
        // makeup gain is applied on the input of the compressor
        leftSample  = inputAmp * inBuffer->f32[2*inIdx];
        rightSample = inputAmp * inBuffer->f32[2*inIdx +1];
        pContext->mCompressor->Compress(&leftSample, &rightSample);
        inBuffer->f32[2*inIdx]    = leftSample * inverseScale;
        inBuffer->f32[2*inIdx +1] = rightSample * inverseScale;
    }
    const size_t channelCount =
            audio_channel_count_from_out_mask(pContext->mConfig.outputCfg.channels);
    pContext->mCompressor->Compress(
            channelCount, inputAmp, inverseScale, inBuffer->f32, inBuffer->frameCount);

    if (inBuffer->raw != outBuffer->raw) {
        const size_t sampleCount = outBuffer->frameCount * channelCount;
        if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
            for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
            for (size_t i = 0; i < sampleCount; i++) {
                outBuffer->f32[i] += inBuffer->f32[i];
            }
        } else {
            memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(float));
            memcpy(outBuffer->raw, inBuffer->raw, sampleCount * sizeof(float));
        }
    }
    if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
@@ -462,4 +459,3 @@ audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
};

}; // extern "C"
+2 −1
Original line number Diff line number Diff line
@@ -139,7 +139,8 @@ std::shared_ptr<EffectContext> LoudnessEnhancerImpl::createContext(
        LOG(DEBUG) << __func__ << " context already exist";
        return mContext;
    }
    if (2 < getChannelCount(common.input.base.channelMask)) {
    const int channelCount = getChannelCount(common.input.base.channelMask);
    if (FCC_LIMIT < channelCount) {
        LOG(ERROR) << __func__
                   << " channelCount not supported: " << common.input.base.channelMask.toString();
        return nullptr;
+6 −25
Original line number Diff line number Diff line
@@ -69,34 +69,15 @@ IEffect::Status LoudnessEnhancerContext::process(float* in, float* out, int samp
    constexpr float scale = 1 << 15;  // power of 2 is lossless conversion to int16_t range
    constexpr float inverseScale = 1.f / scale;
    const float inputAmp = pow(10, mGain / 2000.0f) * scale;
    float leftSample, rightSample;

    if (mCompressor != nullptr) {
        for (int inIdx = 0; inIdx < samples; inIdx += 2) {
            // makeup gain is applied on the input of the compressor
            leftSample = inputAmp * in[inIdx];
            rightSample = inputAmp * in[inIdx + 1];
            mCompressor->Compress(&leftSample, &rightSample);
            in[inIdx] = leftSample * inverseScale;
            in[inIdx + 1] = rightSample * inverseScale;
        }
    } else {
        for (int inIdx = 0; inIdx < samples; inIdx += 2) {
            leftSample = inputAmp * in[inIdx];
            rightSample = inputAmp * in[inIdx + 1];
            in[inIdx] = leftSample * inverseScale;
            in[inIdx + 1] = rightSample * inverseScale;
        }
        const size_t channelCount = aidl::android::hardware::audio::common::getChannelCount(
                mCommon.input.base.channelMask);
        const size_t frameCount = samples / channelCount;
        mCompressor->Compress(channelCount, inputAmp, inverseScale, in, frameCount);
    }
    bool accumulate = false;
    if (in != out) {
        for (int i = 0; i < samples; i++) {
            if (accumulate) {
                out[i] += in[i];
            } else {
                out[i] = in[i];
            }
        }
        // nit: update Compress() to write to out.
        memcpy(out, in, samples * sizeof(float));
    }
    return {STATUS_OK, samples, samples};
}
+45 −57
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include "dsp/core/basic.h"
#include "dsp/core/interpolation.h"
#include "dsp/core/dynamic_range_compression.h"
#include <system/audio.h>

#include <android/log.h>

@@ -74,64 +75,51 @@ bool AdaptiveDynamicRangeCompression::Initialize(
  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_ *= expf(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_ *= expf(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;
// Instantiate Compress for supported channel counts.
#define INSTANTIATE_COMPRESS(CHANNEL_COUNT) \
case CHANNEL_COUNT: \
    if constexpr (CHANNEL_COUNT <= FCC_LIMIT) { \
        Compress(inputAmp, inverseScale, \
                reinterpret_cast<internal_array_t<float, CHANNEL_COUNT>*>(buffer), frameCount); \
        return; \
    } \
    break;

 void AdaptiveDynamicRangeCompression::Compress(size_t channelCount,
        float inputAmp, float inverseScale, float* buffer, size_t frameCount) {
    using android::audio_utils::intrinsics::internal_array_t;
    switch (channelCount) {
        INSTANTIATE_COMPRESS(1);
        INSTANTIATE_COMPRESS(2);
        INSTANTIATE_COMPRESS(3);
        INSTANTIATE_COMPRESS(4);
        INSTANTIATE_COMPRESS(5);
        INSTANTIATE_COMPRESS(6);
        INSTANTIATE_COMPRESS(7);
        INSTANTIATE_COMPRESS(8);
        INSTANTIATE_COMPRESS(9);
        INSTANTIATE_COMPRESS(10);
        INSTANTIATE_COMPRESS(11);
        INSTANTIATE_COMPRESS(12);
        INSTANTIATE_COMPRESS(13);
        INSTANTIATE_COMPRESS(14);
        INSTANTIATE_COMPRESS(15);
        INSTANTIATE_COMPRESS(16);
        INSTANTIATE_COMPRESS(17);
        INSTANTIATE_COMPRESS(18);
        INSTANTIATE_COMPRESS(19);
        INSTANTIATE_COMPRESS(20);
        INSTANTIATE_COMPRESS(21);
        INSTANTIATE_COMPRESS(22);
        INSTANTIATE_COMPRESS(23);
        INSTANTIATE_COMPRESS(24);
        INSTANTIATE_COMPRESS(25);
        INSTANTIATE_COMPRESS(26);
        INSTANTIATE_COMPRESS(27);
        INSTANTIATE_COMPRESS(28);
    }
    LOG_ALWAYS_FATAL("%s: channelCount: %zu not supported", __func__, channelCount);
}

}  // namespace le_fx
+32 −13
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "dsp/core/basic.h"
#include "dsp/core/interpolation.h"

#include <audio_utils/intrinsic_utils.h>
#include <android/log.h>

namespace le_fx {
@@ -51,19 +52,9 @@ class AdaptiveDynamicRangeCompression {
    // relatively safe choice for many signals.
    bool Initialize(float target_gain, float sampling_rate);

  // A fast version of the algorithm that uses approximate computations for the
  // 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);

  // A slow version of the algorithm that is easier for further developement,
  // tuning and debugging
  float CompressSlow(float x);
  // in-place compression.
  void Compress(size_t channelCount,
          float inputAmp, float inverseScale, float* buffer, size_t frameCount);

  // Sets knee threshold (in decibel).
  void set_knee_threshold(float decibel);
@@ -73,6 +64,34 @@ class AdaptiveDynamicRangeCompression {
  void set_knee_threshold_via_target_gain(float target_gain);

 private:

  // Templated Compress routine.
  template <typename V>
  void Compress(float inputAmp, float inverseScale, V* buffer, size_t frameCount) {
    for (size_t i = 0; i < frameCount; ++i) {
        auto v = android::audio_utils::intrinsics::vmul(buffer[i], inputAmp);
        const float max_abs_x = android::audio_utils::intrinsics::vmaxv(
                android::audio_utils::intrinsics::vabs(v));
        const float max_abs_x_dB = math::fast_log(std::max(max_abs_x, kMinLogAbsValue));
        // 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_ *= expf(state_ - prev_state);
        const auto x = android::audio_utils::intrinsics::vmul(v, compressor_gain_);
        v = android::audio_utils::intrinsics::vclamp(x, -kFixedPointLimit, kFixedPointLimit);
        buffer[i] = android::audio_utils::intrinsics::vmul(inverseScale, v);
    }
  }

  // The minimum accepted absolute input value and it's natural logarithm. This
  // is to prevent numerical issues when the input is close to zero
  static const float kMinAbsValue;
Loading