text: /*
   *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
   *
   *  Use of this source code is governed by a BSD-style license
   *  that can be found in the LICENSE file in the root of the source
   *  tree. An additional intellectual property rights grant can be found
   *  in the file PATENTS.  All contributing project authors may
   *  be found in the AUTHORS file in the root of the source tree.
   */
  
  #include "modules/audio_processing/aecm/aecm_core.h"
  
  #include <stddef.h>
  #include <stdlib.h>
  #include <string.h>
  
  extern "C" {
  #include "common_audio/ring_buffer.h"
  #include "common_audio/signal_processing/include/real_fft.h"
  }
  #include "common_audio/signal_processing/include/signal_processing_library.h"
  #include "modules/audio_processing/aecm/echo_control_mobile.h"
  #include "modules/audio_processing/utility/delay_estimator_wrapper.h"
  #include "rtc_base/checks.h"
  #include "rtc_base/numerics/safe_conversions.h"
  
  namespace webrtc {
  
  namespace {
  
  #ifdef AEC_DEBUG
  FILE* dfile;
  FILE* testfile;
  #endif
  
  // Initialization table for echo channel in 8 kHz
  static const int16_t kChannelStored8kHz[PART_LEN1] = {
      2040, 1815, 1590, 1498, 1405, 1395, 1385, 1418, 1451, 1506, 1562,
      1644, 1726, 1804, 1882, 1918, 1953, 1982, 2010, 2025, 2040, 2034,
      2027, 2021, 2014, 1997, 1980, 1925, 1869, 1800, 1732, 1683, 1635,
      1604, 1572, 1545, 1517, 1481, 1444, 1405, 1367, 1331, 1294, 1270,
      1245, 1239, 1233, 1247, 1260, 1282, 1303, 1338, 1373, 1407, 1441,
      1470, 1499, 1524, 1549, 1565, 1582, 1601, 1621, 1649, 1676};
  
  // Initialization table for echo channel in 16 kHz
  static const int16_t kChannelStored16kHz[PART_LEN1] = {
      2040, 1590, 1405, 1385, 1451, 1562, 1726, 1882, 1953, 2010, 2040,
      2027, 2014, 1980, 1869, 1732, 1635, 1572, 1517, 1444, 1367, 1294,
      1245, 1233, 1260, 1303, 1373, 1441, 1499, 1549, 1582, 1621, 1676,
      1741, 1802, 1861, 1921, 1983, 2040, 2102, 2170, 2265, 2375, 2515,
      2651, 2781, 2922, 3075, 3253, 3471, 3738, 3976, 4151, 4258, 4308,
      4288, 4270, 4253, 4237, 4179, 4086, 3947, 3757, 3484, 3153};
  
  }  // namespace
  
  const int16_t WebRtcAecm_kCosTable[] = {
      8192,  8190,  8187,  8180,  8172,  8160,  8147,  8130,  8112,  8091,  8067,
      8041,  8012,  7982,  7948,  7912,  7874,  7834,  7791,  7745,  7697,  7647,
      7595,  7540,  7483,  7424,  7362,  7299,  7233,  7164,  7094,  7021,  6947,
      6870,  6791,  6710,  6627,  6542,  6455,  6366,  6275,  6182,  6087,  5991,
      5892,  5792,  5690,  5586,  5481,  5374,  5265,  5155,  5043,  4930,  4815,
      4698,  4580,  4461,  4341,  4219,  4096,  3971,  3845,  3719,  3591,  3462,
      3331,  3200,  3068,  2935,  2801,  2667,  2531,  2395,  2258,  2120,  1981,
      1842,  1703,  1563,  1422,  1281,  1140,  998,   856,   713,   571,   428,
      285,   142,   0,     -142,  -285,  -428,  -571,  -713,  -856,  -998,  -1140,
      -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395, -2531, -2667,
      -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845, -3971, -4095,
      -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155, -5265, -5374,
      -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275, -6366, -6455,
      -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164, -7233, -7299,
      -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791, -7834, -7874,
      -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130, -8147, -8160,
      -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172, -8160, -8147,
      -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912, -7874, -7834,
      -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362, -7299, -7233,
      -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542, -6455, -6366,
      -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481, -5374, -5265,
      -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219, -4096, -3971,
      -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801, -2667, -2531,
      -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281, -1140, -998,
      -856,  -713,  -571,  -428,  -285,  -142,  0,     142,   285,   428,   571,
      713,   856,   998,   1140,  1281,  1422,  1563,  1703,  1842,  1981,  2120,
      2258,  2395,  2531,  2667,  2801,  2935,  3068,  3200,  3331,  3462,  3591,
      3719,  3845,  3971,  4095,  4219,  4341,  4461,  4580,  4698,  4815,  4930,
      5043,  5155,  5265,  5374,  5481,  5586,  5690,  5792,  5892,  5991,  6087,
      6182,  6275,  6366,  6455,  6542,  6627,  6710,  6791,  6870,  6947,  7021,
      7094,  7164,  7233,  7299,  7362,  7424,  7483,  7540,  7595,  7647,  7697,
      7745,  7791,  7834,  7874,  7912,  7948,  7982,  8012,  8041,  8067,  8091,
      8112,  8130,  8147,  8160,  8172,  8180,  8187,  8190};
  
  const int16_t WebRtcAecm_kSinTable[] = {
      0,     142,   285,   428,   571,   713,   856,   998,   1140,  1281,  1422,
      1563,  1703,  1842,  1981,  2120,  2258,  2395,  2531,  2667,  2801,  2935,
      3068,  3200,  3331,  3462,  3591,  3719,  3845,  3971,  4095,  4219,  4341,
      4461,  4580,  4698,  4815,  4930,  5043,  5155,  5265,  5374,  5481,  5586,
      5690,  5792,  5892,  5991,  6087,  6182,  6275,  6366,  6455,  6542,  6627,
      6710,  6791,  6870,  6947,  7021,  7094,  7164,  7233,  7299,  7362,  7424,
      7483,  7540,  7595,  7647,  7697,  7745,  7791,  7834,  7874,  7912,  7948,
      7982,  8012,  8041,  8067,  8091,  8112,  8130,  8147,  8160,  8172,  8180,
      8187,  8190,  8191,  8190,  8187,  8180,  8172,  8160,  8147,  8130,  8112,
      8091,  8067,  8041,  8012,  7982,  7948,  7912,  7874,  7834,  7791,  7745,
      7697,  7647,  7595,  7540,  7483,  7424,  7362,  7299,  7233,  7164,  7094,
      7021,  6947,  6870,  6791,  6710,  6627,  6542,  6455,  6366,  6275,  6182,
      6087,  5991,  5892,  5792,  5690,  5586,  5481,  5374,  5265,  5155,  5043,
      4930,  4815,  4698,  4580,  4461,  4341,  4219,  4096,  3971,  3845,  3719,
      3591,  3462,  3331,  3200,  3068,  2935,  2801,  2667,  2531,  2395,  2258,
      2120,  1981,  1842,  1703,  1563,  1422,  1281,  1140,  998,   856,   713,
      571,   428,   285,   142,   0,     -142,  -285,  -428,  -571,  -713,  -856,
      -998,  -1140, -1281, -1422, -1563, -1703, -1842, -1981, -2120, -2258, -2395,
      -2531, -2667, -2801, -2935, -3068, -3200, -3331, -3462, -3591, -3719, -3845,
      -3971, -4095, -4219, -4341, -4461, -4580, -4698, -4815, -4930, -5043, -5155,
      -5265, -5374, -5481, -5586, -5690, -5792, -5892, -5991, -6087, -6182, -6275,
      -6366, -6455, -6542, -6627, -6710, -6791, -6870, -6947, -7021, -7094, -7164,
      -7233, -7299, -7362, -7424, -7483, -7540, -7595, -7647, -7697, -7745, -7791,
      -7834, -7874, -7912, -7948, -7982, -8012, -8041, -8067, -8091, -8112, -8130,
      -8147, -8160, -8172, -8180, -8187, -8190, -8191, -8190, -8187, -8180, -8172,
      -8160, -8147, -8130, -8112, -8091, -8067, -8041, -8012, -7982, -7948, -7912,
      -7874, -7834, -7791, -7745, -7697, -7647, -7595, -7540, -7483, -7424, -7362,
      -7299, -7233, -7164, -7094, -7021, -6947, -6870, -6791, -6710, -6627, -6542,
      -6455, -6366, -6275, -6182, -6087, -5991, -5892, -5792, -5690, -5586, -5481,
      -5374, -5265, -5155, -5043, -4930, -4815, -4698, -4580, -4461, -4341, -4219,
      -4096, -3971, -3845, -3719, -3591, -3462, -3331, -3200, -3068, -2935, -2801,
      -2667, -2531, -2395, -2258, -2120, -1981, -1842, -1703, -1563, -1422, -1281,
      -1140, -998,  -856,  -713,  -571,  -428,  -285,  -142};
  
  
  // Moves the pointer to the next entry and inserts |far_spectrum| and
  // corresponding Q-domain in its buffer.
  //
  // Inputs:
  //      - self          : Pointer to the delay estimation instance
  //      - far_spectrum  : Pointer to the far end spectrum
  //      - far_q         : Q-domain of far end spectrum
  //
  void WebRtcAecm_UpdateFarHistory(AecmCore* self,
                                   uint16_t* far_spectrum,
                                   int far_q) {
    // Get new buffer position
    self->far_history_pos++;
    if (self->far_history_pos >= MAX_DELAY) {
      self->far_history_pos = 0;
    }
    // Update Q-domain buffer
    self->far_q_domains[self->far_history_pos] = far_q;
    // Update far end spectrum buffer
    memcpy(&(self->far_history[self->far_history_pos * PART_LEN1]), far_spectrum,
           sizeof(uint16_t) * PART_LEN1);
  }
  
  // Returns a pointer to the far end spectrum aligned to current near end
  // spectrum. The function WebRtc_DelayEstimatorProcessFix(...) should have been
  // called before AlignedFarend(...). Otherwise, you get the pointer to the
  // previous frame. The memory is only valid until the next call of
  // WebRtc_DelayEstimatorProcessFix(...).
  //
  // Inputs:
  //      - self              : Pointer to the AECM instance.
  //      - delay             : Current delay estimate.
  //
  // Output:
  //      - far_q             : The Q-domain of the aligned far end spectrum
  //
  // Return value:
  //      - far_spectrum      : Pointer to the aligned far end spectrum
  //                            NULL - Error
  //
  const uint16_t* WebRtcAecm_AlignedFarend(AecmCore* self,
                                           int* far_q,
                                           int delay) {
    int buffer_position = 0;
    RTC_DCHECK(self);
    buffer_position = self->far_history_pos - delay;
  
    // Check buffer position
    if (buffer_position < 0) {
      buffer_position += MAX_DELAY;
    }
    // Get Q-domain
    *far_q = self->far_q_domains[buffer_position];
    // Return far end spectrum
    return &(self->far_history[buffer_position * PART_LEN1]);
  }
  
  // Declare function pointers.
  CalcLinearEnergies WebRtcAecm_CalcLinearEnergies;
  StoreAdaptiveChannel WebRtcAecm_StoreAdaptiveChannel;
  ResetAdaptiveChannel WebRtcAecm_ResetAdaptiveChannel;
  
  AecmCore* WebRtcAecm_CreateCore() {
    // Allocate zero-filled memory.
    AecmCore* aecm = static_cast<AecmCore*>(calloc(1, sizeof(AecmCore)));
  
    aecm->farFrameBuf =
        WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t));
    if (!aecm->farFrameBuf) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
  
    aecm->nearNoisyFrameBuf =
        WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t));
    if (!aecm->nearNoisyFrameBuf) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
  
    aecm->nearCleanFrameBuf =
        WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t));
    if (!aecm->nearCleanFrameBuf) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
  
    aecm->outFrameBuf =
        WebRtc_CreateBuffer(FRAME_LEN + PART_LEN, sizeof(int16_t));
    if (!aecm->outFrameBuf) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
  
    aecm->delay_estimator_farend =
        WebRtc_CreateDelayEstimatorFarend(PART_LEN1, MAX_DELAY);
    if (aecm->delay_estimator_farend == NULL) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
    aecm->delay_estimator =
        WebRtc_CreateDelayEstimator(aecm->delay_estimator_farend, 0);
    if (aecm->delay_estimator == NULL) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
    // TODO(bjornv): Explicitly disable robust delay validation until no
    // performance regression has been established.  Then remove the line.
    WebRtc_enable_robust_validation(aecm->delay_estimator, 0);
  
    aecm->real_fft = WebRtcSpl_CreateRealFFT(PART_LEN_SHIFT);
    if (aecm->real_fft == NULL) {
      WebRtcAecm_FreeCore(aecm);
      return NULL;
    }
  
    // Init some aecm pointers. 16 and 32 byte alignment is only necessary
    // for Neon code currently.
    aecm->xBuf = (int16_t*)(((uintptr_t)aecm->xBuf_buf + 31) & ~31);
    aecm->dBufClean = (int16_t*)(((uintptr_t)aecm->dBufClean_buf + 31) & ~31);
    aecm->dBufNoisy = (int16_t*)(((uintptr_t)aecm->dBufNoisy_buf + 31) & ~31);
    aecm->outBuf = (int16_t*)(((uintptr_t)aecm->outBuf_buf + 15) & ~15);
    aecm->channelStored =
        (int16_t*)(((uintptr_t)aecm->channelStored_buf + 15) & ~15);
    aecm->channelAdapt16 =
        (int16_t*)(((uintptr_t)aecm->channelAdapt16_buf + 15) & ~15);
    aecm->channelAdapt32 =
        (int32_t*)(((uintptr_t)aecm->channelAdapt32_buf + 31) & ~31);
  
    return aecm;
  }
  
  void WebRtcAecm_InitEchoPathCore(AecmCore* aecm, const int16_t* echo_path) {
    int i = 0;
  
    // Reset the stored channel
    memcpy(aecm->channelStored, echo_path, sizeof(int16_t) * PART_LEN1);
    // Reset the adapted channels
    memcpy(aecm->channelAdapt16, echo_path, sizeof(int16_t) * PART_LEN1);
    for (i = 0; i < PART_LEN1; i++) {
      aecm->channelAdapt32[i] = (int32_t)aecm->channelAdapt16[i] << 16;
    }
  
    // Reset channel storing variables
    aecm->mseAdaptOld = 1000;
    aecm->mseStoredOld = 1000;
    aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX;
    aecm->mseChannelCount = 0;
  }
  
  static void CalcLinearEnergiesC(AecmCore* aecm,
                                  const uint16_t* far_spectrum,
                                  int32_t* echo_est,
                                  uint32_t* far_energy,
                                  uint32_t* echo_energy_adapt,
                                  uint32_t* echo_energy_stored) {
    int i;
  
    // Get energy for the delayed far end signal and estimated
    // echo using both stored and adapted channels.
    for (i = 0; i < PART_LEN1; i++) {
      echo_est[i] =
          WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
      (*far_energy) += (uint32_t)(far_spectrum[i]);
      *echo_energy_adapt += aecm->channelAdapt16[i] * far_spectrum[i];
      (*echo_energy_stored) += (uint32_t)echo_est[i];
    }
  }
  
  static void StoreAdaptiveChannelC(AecmCore* aecm,
                                    const uint16_t* far_spectrum,
                                    int32_t* echo_est) {
    int i;
  
    // During startup we store the channel every block.
    memcpy(aecm->channelStored, aecm->channelAdapt16,
           sizeof(int16_t) * PART_LEN1);
    // Recalculate echo estimate
    for (i = 0; i < PART_LEN; i += 4) {
      echo_est[i] =
          WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
      echo_est[i + 1] =
          WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 1], far_spectrum[i + 1]);
      echo_est[i + 2] =
          WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 2], far_spectrum[i + 2]);
      echo_est[i + 3] =
          WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i + 3], far_spectrum[i + 3]);
    }
    echo_est[i] = WEBRTC_SPL_MUL_16_U16(aecm->channelStored[i], far_spectrum[i]);
  }
  
  static void ResetAdaptiveChannelC(AecmCore* aecm) {
    int i;
  
    // The stored channel has a significantly lower MSE than the adaptive one for
    // two consecutive calculations. Reset the adaptive channel.
    memcpy(aecm->channelAdapt16, aecm->channelStored,
           sizeof(int16_t) * PART_LEN1);
    // Restore the W32 channel
    for (i = 0; i < PART_LEN; i += 4) {
      aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16;
      aecm->channelAdapt32[i + 1] = (int32_t)aecm->channelStored[i + 1] << 16;
      aecm->channelAdapt32[i + 2] = (int32_t)aecm->channelStored[i + 2] << 16;
      aecm->channelAdapt32[i + 3] = (int32_t)aecm->channelStored[i + 3] << 16;
    }
    aecm->channelAdapt32[i] = (int32_t)aecm->channelStored[i] << 16;
  }
  
  // Initialize function pointers for ARM Neon platform.
  #if defined(WEBRTC_HAS_NEON)
  static void WebRtcAecm_InitNeon(void) {
    WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannelNeon;
    WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannelNeon;
    WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergiesNeon;
  }
  #endif
  
  // Initialize function pointers for MIPS platform.
  #if defined(MIPS32_LE)
  static void WebRtcAecm_InitMips(void) {
  #if defined(MIPS_DSP_R1_LE)
    WebRtcAecm_StoreAdaptiveChannel = WebRtcAecm_StoreAdaptiveChannel_mips;
    WebRtcAecm_ResetAdaptiveChannel = WebRtcAecm_ResetAdaptiveChannel_mips;
  #endif
    WebRtcAecm_CalcLinearEnergies = WebRtcAecm_CalcLinearEnergies_mips;
  }
  #endif
  
  // WebRtcAecm_InitCore(...)
  //
  // This function initializes the AECM instant created with
  // WebRtcAecm_CreateCore(...) Input:
  //      - aecm            : Pointer to the Echo Suppression instance
  //      - samplingFreq   : Sampling Frequency
  //
  // Output:
  //      - aecm            : Initialized instance
  //
  // Return value         :  0 - Ok
  //                        -1 - Error
  //
  int WebRtcAecm_InitCore(AecmCore* const aecm, int samplingFreq) {
    int i = 0;
    int32_t tmp32 = PART_LEN1 * PART_LEN1;
    int16_t tmp16 = PART_LEN1;
  
    if (samplingFreq != 8000 && samplingFreq != 16000) {
      samplingFreq = 8000;
      return -1;
    }
    // sanity check of sampling frequency
    aecm->mult = (int16_t)samplingFreq / 8000;
  
    aecm->farBufWritePos = 0;
    aecm->farBufReadPos = 0;
    aecm->knownDelay = 0;
    aecm->lastKnownDelay = 0;
  
    WebRtc_InitBuffer(aecm->farFrameBuf);
    WebRtc_InitBuffer(aecm->nearNoisyFrameBuf);
    WebRtc_InitBuffer(aecm->nearCleanFrameBuf);
    WebRtc_InitBuffer(aecm->outFrameBuf);
  
    memset(aecm->xBuf_buf, 0, sizeof(aecm->xBuf_buf));
    memset(aecm->dBufClean_buf, 0, sizeof(aecm->dBufClean_buf));
    memset(aecm->dBufNoisy_buf, 0, sizeof(aecm->dBufNoisy_buf));
    memset(aecm->outBuf_buf, 0, sizeof(aecm->outBuf_buf));
  
    aecm->seed = 666;
    aecm->totCount = 0;
  
    if (WebRtc_InitDelayEstimatorFarend(aecm->delay_estimator_farend) != 0) {
      return -1;
    }
    if (WebRtc_InitDelayEstimator(aecm->delay_estimator) != 0) {
      return -1;
    }
    // Set far end histories to zero
    memset(aecm->far_history, 0, sizeof(uint16_t) * PART_LEN1 * MAX_DELAY);
    memset(aecm->far_q_domains, 0, sizeof(int) * MAX_DELAY);
    aecm->far_history_pos = MAX_DELAY;
  
    aecm->nlpFlag = 1;
    aecm->fixedDelay = -1;
  
    aecm->dfaCleanQDomain = 0;
    aecm->dfaCleanQDomainOld = 0;
    aecm->dfaNoisyQDomain = 0;
    aecm->dfaNoisyQDomainOld = 0;
  
    memset(aecm->nearLogEnergy, 0, sizeof(aecm->nearLogEnergy));
    aecm->farLogEnergy = 0;
    memset(aecm->echoAdaptLogEnergy, 0, sizeof(aecm->echoAdaptLogEnergy));
    memset(aecm->echoStoredLogEnergy, 0, sizeof(aecm->echoStoredLogEnergy));
  
    // Initialize the echo channels with a stored shape.
    if (samplingFreq == 8000) {
      WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz);
    } else {
      WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz);
    }
  
    memset(aecm->echoFilt, 0, sizeof(aecm->echoFilt));
    memset(aecm->nearFilt, 0, sizeof(aecm->nearFilt));
    aecm->noiseEstCtr = 0;
  
    aecm->cngMode = AecmTrue;
  
    memset(aecm->noiseEstTooLowCtr, 0, sizeof(aecm->noiseEstTooLowCtr));
    memset(aecm->noiseEstTooHighCtr, 0, sizeof(aecm->noiseEstTooHighCtr));
    // Shape the initial noise level to an approximate pink noise.
    for (i = 0; i < (PART_LEN1 >> 1) - 1; i++) {
      aecm->noiseEst[i] = (tmp32 << 8);
      tmp16--;
      tmp32 -= (int32_t)((tmp16 << 1) + 1);
    }
    for (; i < PART_LEN1; i++) {
      aecm->noiseEst[i] = (tmp32 << 8);
    }
  
    aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX;
    aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN;
    aecm->farEnergyMaxMin = 0;
    aecm->farEnergyVAD = FAR_ENERGY_MIN;  // This prevents false speech detection
                                          // at the beginning.
    aecm->farEnergyMSE = 0;
    aecm->currentVADValue = 0;
    aecm->vadUpdateCount = 0;
    aecm->firstVAD = 1;
  
    aecm->startupState = 0;
    aecm->supGain = SUPGAIN_DEFAULT;
    aecm->supGainOld = SUPGAIN_DEFAULT;
  
    aecm->supGainErrParamA = SUPGAIN_ERROR_PARAM_A;
    aecm->supGainErrParamD = SUPGAIN_ERROR_PARAM_D;
    aecm->supGainErrParamDiffAB = SUPGAIN_ERROR_PARAM_A - SUPGAIN_ERROR_PARAM_B;
    aecm->supGainErrParamDiffBD = SUPGAIN_ERROR_PARAM_B - SUPGAIN_ERROR_PARAM_D;
  
    // Assert a preprocessor definition at compile-time. It's an assumption
    // used in assembly code, so check the assembly files before any change.
    static_assert(PART_LEN % 16 == 0, "PART_LEN is not a multiple of 16");
  
    // Initialize function pointers.
    WebRtcAecm_CalcLinearEnergies = CalcLinearEnergiesC;
    WebRtcAecm_StoreAdaptiveChannel = StoreAdaptiveChannelC;
    WebRtcAecm_ResetAdaptiveChannel = ResetAdaptiveChannelC;
  
  #if defined(WEBRTC_HAS_NEON)
    WebRtcAecm_InitNeon();
  #endif
  
  #if defined(MIPS32_LE)
    WebRtcAecm_InitMips();
  #endif
    return 0;
  }
  
  // TODO(bjornv): This function is currently not used. Add support for these
  // parameters from a higher level
  int WebRtcAecm_Control(AecmCore* aecm, int delay, int nlpFlag) {
    aecm->nlpFlag = nlpFlag;
    aecm->fixedDelay = delay;
  
    return 0;
  }
  
  void WebRtcAecm_FreeCore(AecmCore* aecm) {
    if (aecm == NULL) {
      return;
    }
  
    WebRtc_FreeBuffer(aecm->farFrameBuf);
    WebRtc_FreeBuffer(aecm->nearNoisyFrameBuf);
    WebRtc_FreeBuffer(aecm->nearCleanFrameBuf);
    WebRtc_FreeBuffer(aecm->outFrameBuf);
  
    WebRtc_FreeDelayEstimator(aecm->delay_estimator);
    WebRtc_FreeDelayEstimatorFarend(aecm->delay_estimator_farend);
    WebRtcSpl_FreeRealFFT(aecm->real_fft);
  
    free(aecm);
  }
  
  int WebRtcAecm_ProcessFrame(AecmCore* aecm,
                              const int16_t* farend,
                              const int16_t* nearendNoisy,
                              const int16_t* nearendClean,
                              int16_t* out) {
    int16_t outBlock_buf[PART_LEN + 8];  // Align buffer to 8-byte boundary.
    int16_t* outBlock = (int16_t*)(((uintptr_t)outBlock_buf + 15) & ~15);
  
    int16_t farFrame[FRAME_LEN];
    const int16_t* out_ptr = NULL;
    int size = 0;
  
    // Buffer the current frame.
    // Fetch an older one corresponding to the delay.
    WebRtcAecm_BufferFarFrame(aecm, farend, FRAME_LEN);
    WebRtcAecm_FetchFarFrame(aecm, farFrame, FRAME_LEN, aecm->knownDelay);
  
    // Buffer the synchronized far and near frames,
    // to pass the smaller blocks individually.
    WebRtc_WriteBuffer(aecm->farFrameBuf, farFrame, FRAME_LEN);
    WebRtc_WriteBuffer(aecm->nearNoisyFrameBuf, nearendNoisy, FRAME_LEN);
    if (nearendClean != NULL) {
      WebRtc_WriteBuffer(aecm->nearCleanFrameBuf, nearendClean, FRAME_LEN);
    }
  
    // Process as many blocks as possible.
    while (WebRtc_available_read(aecm->farFrameBuf) >= PART_LEN) {
      int16_t far_block[PART_LEN];
      const int16_t* far_block_ptr = NULL;
      int16_t near_noisy_block[PART_LEN];
      const int16_t* near_noisy_block_ptr = NULL;
  
      WebRtc_ReadBuffer(aecm->farFrameBuf, (void**)&far_block_ptr, far_block,
                        PART_LEN);
      WebRtc_ReadBuffer(aecm->nearNoisyFrameBuf, (void**)&near_noisy_block_ptr,
                        near_noisy_block, PART_LEN);
      if (nearendClean != NULL) {
        int16_t near_clean_block[PART_LEN];
        const int16_t* near_clean_block_ptr = NULL;
  
        WebRtc_ReadBuffer(aecm->nearCleanFrameBuf, (void**)&near_clean_block_ptr,
                          near_clean_block, PART_LEN);
        if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr,
                                    near_clean_block_ptr, outBlock) == -1) {
          return -1;
        }
      } else {
        if (WebRtcAecm_ProcessBlock(aecm, far_block_ptr, near_noisy_block_ptr,
                                    NULL, outBlock) == -1) {
          return -1;
        }
      }
  
      WebRtc_WriteBuffer(aecm->outFrameBuf, outBlock, PART_LEN);
    }
  
    // Stuff the out buffer if we have less than a frame to output.
    // This should only happen for the first frame.
    size = (int)WebRtc_available_read(aecm->outFrameBuf);
    if (size < FRAME_LEN) {
      WebRtc_MoveReadPtr(aecm->outFrameBuf, size - FRAME_LEN);
    }
  
    // Obtain an output frame.
    WebRtc_ReadBuffer(aecm->outFrameBuf, (void**)&out_ptr, out, FRAME_LEN);
    if (out_ptr != out) {
      // ReadBuffer() hasn't copied to |out| in this case.
      memcpy(out, out_ptr, FRAME_LEN * sizeof(int16_t));
    }
  
    return 0;
  }
  
  // WebRtcAecm_AsymFilt(...)
  //
  // Performs asymmetric filtering.
  //
  // Inputs:
  //      - filtOld       : Previous filtered value.
  //      - inVal         : New input value.
  //      - stepSizePos   : Step size when we have a positive contribution.
  //      - stepSizeNeg   : Step size when we have a negative contribution.
  //
  // Output:
  //
  // Return: - Filtered value.
  //
  int16_t WebRtcAecm_AsymFilt(const int16_t filtOld,
                              const int16_t inVal,
                              const int16_t stepSizePos,
                              const int16_t stepSizeNeg) {
    int16_t retVal;
  
    if ((filtOld == WEBRTC_SPL_WORD16_MAX) | (filtOld == WEBRTC_SPL_WORD16_MIN)) {
      return inVal;
    }
    retVal = filtOld;
    if (filtOld > inVal) {
      retVal -= (filtOld - inVal) >> stepSizeNeg;
    } else {
      retVal += (inVal - filtOld) >> stepSizePos;
    }
  
    return retVal;
  }
  
  // ExtractFractionPart(a, zeros)
  //
  // returns the fraction part of |a|, with |zeros| number of leading zeros, as an
  // int16_t scaled to Q8. There is no sanity check of |a| in the sense that the
  // number of zeros match.
  static int16_t ExtractFractionPart(uint32_t a, int zeros) {
    return (int16_t)(((a << zeros) & 0x7FFFFFFF) >> 23);
  }
  
  // Calculates and returns the log of |energy| in Q8. The input |energy| is
  // supposed to be in Q(|q_domain|).
  static int16_t LogOfEnergyInQ8(uint32_t energy, int q_domain) {
    static const int16_t kLogLowValue = PART_LEN_SHIFT << 7;
    int16_t log_energy_q8 = kLogLowValue;
    if (energy > 0) {
      int zeros = WebRtcSpl_NormU32(energy);
      int16_t frac = ExtractFractionPart(energy, zeros);
      // log2 of |energy| in Q8.
      log_energy_q8 += ((31 - zeros) << 8) + frac - (q_domain << 8);
    }
    return log_energy_q8;
  }
  
  // WebRtcAecm_CalcEnergies(...)
  //
  // This function calculates the log of energies for nearend, farend and
  // estimated echoes. There is also an update of energy decision levels, i.e.
  // internal VAD.
  //
  //
  // @param  aecm         [i/o]   Handle of the AECM instance.
  // @param  far_spectrum [in]    Pointer to farend spectrum.
  // @param  far_q        [in]    Q-domain of farend spectrum.
  // @param  nearEner     [in]    Near end energy for current block in
  //                              Q(aecm->dfaQDomain).
  // @param  echoEst      [out]   Estimated echo in Q(xfa_q+RESOLUTION_CHANNEL16).
  //
  void WebRtcAecm_CalcEnergies(AecmCore* aecm,
                               const uint16_t* far_spectrum,
                               const int16_t far_q,
                               const uint32_t nearEner,
                               int32_t* echoEst) {
    // Local variables
    uint32_t tmpAdapt = 0;
    uint32_t tmpStored = 0;
    uint32_t tmpFar = 0;
  
    int i;
  
    int16_t tmp16;
    int16_t increase_max_shifts = 4;
    int16_t decrease_max_shifts = 11;
    int16_t increase_min_shifts = 11;
    int16_t decrease_min_shifts = 3;
  
    // Get log of near end energy and store in buffer
  
    // Shift buffer
    memmove(aecm->nearLogEnergy + 1, aecm->nearLogEnergy,
            sizeof(int16_t) * (MAX_BUF_LEN - 1));
  
    // Logarithm of integrated magnitude spectrum (nearEner)
    aecm->nearLogEnergy[0] = LogOfEnergyInQ8(nearEner, aecm->dfaNoisyQDomain);
  
    WebRtcAecm_CalcLinearEnergies(aecm, far_spectrum, echoEst, &tmpFar, &tmpAdapt,
                                  &tmpStored);
  
    // Shift buffers
    memmove(aecm->echoAdaptLogEnergy + 1, aecm->echoAdaptLogEnergy,
            sizeof(int16_t) * (MAX_BUF_LEN - 1));
    memmove(aecm->echoStoredLogEnergy + 1, aecm->echoStoredLogEnergy,
            sizeof(int16_t) * (MAX_BUF_LEN - 1));
  
    // Logarithm of delayed far end energy
    aecm->farLogEnergy = LogOfEnergyInQ8(tmpFar, far_q);
  
    // Logarithm of estimated echo energy through adapted channel
    aecm->echoAdaptLogEnergy[0] =
        LogOfEnergyInQ8(tmpAdapt, RESOLUTION_CHANNEL16 + far_q);
  
    // Logarithm of estimated echo energy through stored channel
    aecm->echoStoredLogEnergy[0] =
        LogOfEnergyInQ8(tmpStored, RESOLUTION_CHANNEL16 + far_q);
  
    // Update farend energy levels (min, max, vad, mse)
    if (aecm->farLogEnergy > FAR_ENERGY_MIN) {
      if (aecm->startupState == 0) {
        increase_max_shifts = 2;
        decrease_min_shifts = 2;
        increase_min_shifts = 8;
      }
  
      aecm->farEnergyMin =
          WebRtcAecm_AsymFilt(aecm->farEnergyMin, aecm->farLogEnergy,
                              increase_min_shifts, decrease_min_shifts);
      aecm->farEnergyMax =
          WebRtcAecm_AsymFilt(aecm->farEnergyMax, aecm->farLogEnergy,
                              increase_max_shifts, decrease_max_shifts);
      aecm->farEnergyMaxMin = (aecm->farEnergyMax - aecm->farEnergyMin);
  
      // Dynamic VAD region size
      tmp16 = 2560 - aecm->farEnergyMin;
      if (tmp16 > 0) {
        tmp16 = (int16_t)((tmp16 * FAR_ENERGY_VAD_REGION) >> 9);
      } else {
        tmp16 = 0;
      }
      tmp16 += FAR_ENERGY_VAD_REGION;
  
      if ((aecm->startupState == 0) | (aecm->vadUpdateCount > 1024)) {
        // In startup phase or VAD update halted
        aecm->farEnergyVAD = aecm->farEnergyMin + tmp16;
      } else {
        if (aecm->farEnergyVAD > aecm->farLogEnergy) {
          aecm->farEnergyVAD +=
              (aecm->farLogEnergy + tmp16 - aecm->farEnergyVAD) >> 6;
          aecm->vadUpdateCount = 0;
        } else {
          aecm->vadUpdateCount++;
        }
      }
      // Put MSE threshold higher than VAD
      aecm->farEnergyMSE = aecm->farEnergyVAD + (1 << 8);
    }
  
    // Update VAD variables
    if (aecm->farLogEnergy > aecm->farEnergyVAD) {
      if ((aecm->startupState == 0) | (aecm->farEnergyMaxMin > FAR_ENERGY_DIFF)) {
        // We are in startup or have significant dynamics in input speech level
        aecm->currentVADValue = 1;
      }
    } else {
      aecm->currentVADValue = 0;
    }
    if ((aecm->currentVADValue) && (aecm->firstVAD)) {
      aecm->firstVAD = 0;
      if (aecm->echoAdaptLogEnergy[0] > aecm->nearLogEnergy[0]) {
        // The estimated echo has higher energy than the near end signal.
        // This means that the initialization was too aggressive. Scale
        // down by a factor 8
        for (i = 0; i < PART_LEN1; i++) {
          aecm->channelAdapt16[i] >>= 3;
        }
        // Compensate the adapted echo energy level accordingly.
        aecm->echoAdaptLogEnergy[0] -= (3 << 8);
        aecm->firstVAD = 1;
      }
    }
  }
  
  // WebRtcAecm_CalcStepSize(...)
  //
  // This function calculates the step size used in channel estimation
  //
  //
  // @param  aecm  [in]    Handle of the AECM instance.
  // @param  mu    [out]   (Return value) Stepsize in log2(), i.e. number of
  // shifts.
  //
  //
  int16_t WebRtcAecm_CalcStepSize(AecmCore* const aecm) {
    int32_t tmp32;
    int16_t tmp16;
    int16_t mu = MU_MAX;
  
    // Here we calculate the step size mu used in the
    // following NLMS based Channel estimation algorithm
    if (!aecm->currentVADValue) {
      // Far end energy level too low, no channel update
      mu = 0;
    } else if (aecm->startupState > 0) {
      if (aecm->farEnergyMin >= aecm->farEnergyMax) {
        mu = MU_MIN;
      } else {
        tmp16 = (aecm->farLogEnergy - aecm->farEnergyMin);
        tmp32 = tmp16 * MU_DIFF;
        tmp32 = WebRtcSpl_DivW32W16(tmp32, aecm->farEnergyMaxMin);
        mu = MU_MIN - 1 - (int16_t)(tmp32);
        // The -1 is an alternative to rounding. This way we get a larger
        // stepsize, so we in some sense compensate for truncation in NLMS
      }
      if (mu < MU_MAX) {
        mu = MU_MAX;  // Equivalent with maximum step size of 2^-MU_MAX
      }
    }
  
    return mu;
  }
  
  // WebRtcAecm_UpdateChannel(...)
  //
  // This function performs channel estimation. NLMS and decision on channel
  // storage.
  //
  //
  // @param  aecm         [i/o]   Handle of the AECM instance.
  // @param  far_spectrum [in]    Absolute value of the farend signal in Q(far_q)
  // @param  far_q        [in]    Q-domain of the farend signal
  // @param  dfa          [in]    Absolute value of the nearend signal
  // (Q[aecm->dfaQDomain])
  // @param  mu           [in]    NLMS step size.
  // @param  echoEst      [i/o]   Estimated echo in Q(far_q+RESOLUTION_CHANNEL16).
  //
  void WebRtcAecm_UpdateChannel(AecmCore* aecm,
                                const uint16_t* far_spectrum,
                                const int16_t far_q,
                                const uint16_t* const dfa,
                                const int16_t mu,
                                int32_t* echoEst) {
    uint32_t tmpU32no1, tmpU32no2;
    int32_t tmp32no1, tmp32no2;
    int32_t mseStored;
    int32_t mseAdapt;
  
    int i;
  
    int16_t zerosFar, zerosNum, zerosCh, zerosDfa;
    int16_t shiftChFar, shiftNum, shift2ResChan;
    int16_t tmp16no1;
    int16_t xfaQ, dfaQ;
  
    // This is the channel estimation algorithm. It is base on NLMS but has a
    // variable step length, which was calculated above.
    if (mu) {
      for (i = 0; i < PART_LEN1; i++) {
        // Determine norm of channel and farend to make sure we don't get overflow
        // in multiplication
        zerosCh = WebRtcSpl_NormU32(aecm->channelAdapt32[i]);
        zerosFar = WebRtcSpl_NormU32((uint32_t)far_spectrum[i]);
        if (zerosCh + zerosFar > 31) {
          // Multiplication is safe
          tmpU32no1 =
              WEBRTC_SPL_UMUL_32_16(aecm->channelAdapt32[i], far_spectrum[i]);
          shiftChFar = 0;
        } else {
          // We need to shift down before multiplication
          shiftChFar = 32 - zerosCh - zerosFar;
          // If zerosCh == zerosFar == 0, shiftChFar is 32. A
          // right shift of 32 is undefined. To avoid that, we
          // do this check.
          tmpU32no1 =
              rtc::dchecked_cast<uint32_t>(
                  shiftChFar >= 32 ? 0 : aecm->channelAdapt32[i] >> shiftChFar) *
              far_spectrum[i];
        }
        // Determine Q-domain of numerator
        zerosNum = WebRtcSpl_NormU32(tmpU32no1);
        if (dfa[i]) {
          zerosDfa = WebRtcSpl_NormU32((uint32_t)dfa[i]);
        } else {
          zerosDfa = 32;
        }
        tmp16no1 = zerosDfa - 2 + aecm->dfaNoisyQDomain - RESOLUTION_CHANNEL32 -
                   far_q + shiftChFar;
        if (zerosNum > tmp16no1 + 1) {
          xfaQ = tmp16no1;
          dfaQ = zerosDfa - 2;
        } else {
          xfaQ = zerosNum - 2;
          dfaQ = RESOLUTION_CHANNEL32 + far_q - aecm->dfaNoisyQDomain -
                 shiftChFar + xfaQ;
        }
        // Add in the same Q-domain
        tmpU32no1 = WEBRTC_SPL_SHIFT_W32(tmpU32no1, xfaQ);
        tmpU32no2 = WEBRTC_SPL_SHIFT_W32((uint32_t)dfa[i], dfaQ);
        tmp32no1 = (int32_t)tmpU32no2 - (int32_t)tmpU32no1;
        zerosNum = WebRtcSpl_NormW32(tmp32no1);
        if ((tmp32no1) && (far_spectrum[i] > (CHANNEL_VAD << far_q))) {
          //
          // Update is needed
          //
          // This is what we would like to compute
          //
          // tmp32no1 = dfa[i] - (aecm->channelAdapt[i] * far_spectrum[i])
          // tmp32norm = (i + 1)
          // aecm->channelAdapt[i] += (2^mu) * tmp32no1
          //                        / (tmp32norm * far_spectrum[i])
          //
  
          // Make sure we don't get overflow in multiplication.
          if (zerosNum + zerosFar > 31) {
            if (tmp32no1 > 0) {
              tmp32no2 =
                  (int32_t)WEBRTC_SPL_UMUL_32_16(tmp32no1, far_spectrum[i]);
            } else {
              tmp32no2 =
                  -(int32_t)WEBRTC_SPL_UMUL_32_16(-tmp32no1, far_spectrum[i]);
            }
            shiftNum = 0;
          } else {
            shiftNum = 32 - (zerosNum + zerosFar);
            if (tmp32no1 > 0) {
              tmp32no2 = (tmp32no1 >> shiftNum) * far_spectrum[i];
            } else {
              tmp32no2 = -((-tmp32no1 >> shiftNum) * far_spectrum[i]);
            }
          }
          // Normalize with respect to frequency bin
          tmp32no2 = WebRtcSpl_DivW32W16(tmp32no2, i + 1);
          // Make sure we are in the right Q-domain
          shift2ResChan =
              shiftNum + shiftChFar - xfaQ - mu - ((30 - zerosFar) << 1);
          if (WebRtcSpl_NormW32(tmp32no2) < shift2ResChan) {
            tmp32no2 = WEBRTC_SPL_WORD32_MAX;
          } else {
            tmp32no2 = WEBRTC_SPL_SHIFT_W32(tmp32no2, shift2ResChan);
          }
          aecm->channelAdapt32[i] =
              WebRtcSpl_AddSatW32(aecm->channelAdapt32[i], tmp32no2);
          if (aecm->channelAdapt32[i] < 0) {
            // We can never have negative channel gain
            aecm->channelAdapt32[i] = 0;
          }
          aecm->channelAdapt16[i] = (int16_t)(aecm->channelAdapt32[i] >> 16);
        }
      }
    }
    // END: Adaptive channel update
  
    // Determine if we should store or restore the channel
    if ((aecm->startupState == 0) & (aecm->currentVADValue)) {
      // During startup we store the channel every block,
      // and we recalculate echo estimate
      WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst);
    } else {
      if (aecm->farLogEnergy < aecm->farEnergyMSE) {
        aecm->mseChannelCount = 0;
      } else {
        aecm->mseChannelCount++;
      }
      // Enough data for validation. Store channel if we can.
      if (aecm->mseChannelCount >= (MIN_MSE_COUNT + 10)) {
        // We have enough data.
        // Calculate MSE of "Adapt" and "Stored" versions.
        // It is actually not MSE, but average absolute error.
        mseStored = 0;
        mseAdapt = 0;
        for (i = 0; i < MIN_MSE_COUNT; i++) {
          tmp32no1 = ((int32_t)aecm->echoStoredLogEnergy[i] -
                      (int32_t)aecm->nearLogEnergy[i]);
          tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1);
          mseStored += tmp32no2;
  
          tmp32no1 = ((int32_t)aecm->echoAdaptLogEnergy[i] -
                      (int32_t)aecm->nearLogEnergy[i]);
          tmp32no2 = WEBRTC_SPL_ABS_W32(tmp32no1);
          mseAdapt += tmp32no2;
        }
        if (((mseStored << MSE_RESOLUTION) < (MIN_MSE_DIFF * mseAdapt)) &
            ((aecm->mseStoredOld << MSE_RESOLUTION) <
             (MIN_MSE_DIFF * aecm->mseAdaptOld))) {
          // The stored channel has a significantly lower MSE than the adaptive
          // one for two consecutive calculations. Reset the adaptive channel.
          WebRtcAecm_ResetAdaptiveChannel(aecm);
        } else if (((MIN_MSE_DIFF * mseStored) > (mseAdapt << MSE_RESOLUTION)) &
                   (mseAdapt < aecm->mseThreshold) &
                   (aecm->mseAdaptOld < aecm->mseThreshold)) {
          // The adaptive channel has a significantly lower MSE than the stored
          // one. The MSE for the adaptive channel has also been low for two
          // consecutive calculations. Store the adaptive channel.
          WebRtcAecm_StoreAdaptiveChannel(aecm, far_spectrum, echoEst);
  
          // Update threshold
          if (aecm->mseThreshold == WEBRTC_SPL_WORD32_MAX) {
            aecm->mseThreshold = (mseAdapt + aecm->mseAdaptOld);
          } else {
            int scaled_threshold = aecm->mseThreshold * 5 / 8;
            aecm->mseThreshold += ((mseAdapt - scaled_threshold) * 205) >> 8;
          }
        }
  
        // Reset counter
        aecm->mseChannelCount = 0;
  
        // Store the MSE values.
        aecm->mseStoredOld = mseStored;
        aecm->mseAdaptOld = mseAdapt;
      }
    }
    // END: Determine if we should store or reset channel estimate.
  }
  
  // CalcSuppressionGain(...)
  //
  // This function calculates the suppression gain that is used in the Wiener
  // filter.
  //
  //
  // @param  aecm     [i/n]   Handle of the AECM instance.
  // @param  supGain  [out]   (Return value) Suppression gain with which to scale
  // the noise
  //                          level (Q14).
  //
  //
  int16_t WebRtcAecm_CalcSuppressionGain(AecmCore* const aecm) {
    int32_t tmp32no1;
  
    int16_t supGain = SUPGAIN_DEFAULT;
    int16_t tmp16no1;
    int16_t dE = 0;
  
    // Determine suppression gain used in the Wiener filter. The gain is based on
    // a mix of far end energy and echo estimation error. Adjust for the far end
    // signal level. A low signal level indicates no far end signal, hence we set
    // the suppression gain to 0
    if (!aecm->currentVADValue) {
      supGain = 0;
    } else {
      // Adjust for possible double talk. If we have large variations in
      // estimation error we likely have double talk (or poor channel).
      tmp16no1 = (aecm->nearLogEnergy[0] - aecm->echoStoredLogEnergy[0] -
                  ENERGY_DEV_OFFSET);
      dE = WEBRTC_SPL_ABS_W16(tmp16no1);
  
      if (dE < ENERGY_DEV_TOL) {
        // Likely no double talk. The better estimation, the more we can suppress
        // signal. Update counters
        if (dE < SUPGAIN_EPC_DT) {
          tmp32no1 = aecm->supGainErrParamDiffAB * dE;
          tmp32no1 += (SUPGAIN_EPC_DT >> 1);
          tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(tmp32no1, SUPGAIN_EPC_DT);
          supGain = aecm->supGainErrParamA - tmp16no1;
        } else {
          tmp32no1 = aecm->supGainErrParamDiffBD * (ENERGY_DEV_TOL - dE);
          tmp32no1 += ((ENERGY_DEV_TOL - SUPGAIN_EPC_DT) >> 1);
          tmp16no1 = (int16_t)WebRtcSpl_DivW32W16(
              tmp32no1, (ENERGY_DEV_TOL - SUPGAIN_EPC_DT));
          supGain = aecm->supGainErrParamD + tmp16no1;
        }
      } else {
        // Likely in double talk. Use default value
        supGain = aecm->supGainErrParamD;
      }
    }
  
    if (supGain > aecm->supGainOld) {
      tmp16no1 = supGain;
    } else {
      tmp16no1 = aecm->supGainOld;
    }
    aecm->supGainOld = supGain;
    if (tmp16no1 < aecm->supGain) {
      aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4);
    } else {
      aecm->supGain += (int16_t)((tmp16no1 - aecm->supGain) >> 4);
    }
  
    // END: Update suppression gain
  
    return aecm->supGain;
  }
  
  void WebRtcAecm_BufferFarFrame(AecmCore* const aecm,
                                 const int16_t* const farend,
                                 const int farLen) {
    int writeLen = farLen, writePos = 0;
  
    // Check if the write position must be wrapped
    while (aecm->farBufWritePos + writeLen > FAR_BUF_LEN) {
      // Write to remaining buffer space before wrapping
      writeLen = FAR_BUF_LEN - aecm->farBufWritePos;
      memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos,
             sizeof(int16_t) * writeLen);
      aecm->farBufWritePos = 0;
      writePos = writeLen;
      writeLen = farLen - writeLen;
    }
  
    memcpy(aecm->farBuf + aecm->farBufWritePos, farend + writePos,
           sizeof(int16_t) * writeLen);
    aecm->farBufWritePos += writeLen;
  }
  
  void WebRtcAecm_FetchFarFrame(AecmCore* const aecm,
                                int16_t* const farend,
                                const int farLen,
                                const int knownDelay) {
    int readLen = farLen;
    int readPos = 0;
    int delayChange = knownDelay - aecm->lastKnownDelay;
  
    aecm->farBufReadPos -= delayChange;
  
    // Check if delay forces a read position wrap
    while (aecm->farBufReadPos < 0) {
      aecm->farBufReadPos += FAR_BUF_LEN;
    }
    while (aecm->farBufReadPos > FAR_BUF_LEN - 1) {
      aecm->farBufReadPos -= FAR_BUF_LEN;
    }
  
    aecm->lastKnownDelay = knownDelay;
  
    // Check if read position must be wrapped
    while (aecm->farBufReadPos + readLen > FAR_BUF_LEN) {
      // Read from remaining buffer space before wrapping
      readLen = FAR_BUF_LEN - aecm->farBufReadPos;
      memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos,
             sizeof(int16_t) * readLen);
      aecm->farBufReadPos = 0;
      readPos = readLen;
      readLen = farLen - readLen;
    }
    memcpy(farend + readPos, aecm->farBuf + aecm->farBufReadPos,
           sizeof(int16_t) * readLen);
    aecm->farBufReadPos += readLen;
  }
  
  }  // namespace webrtc
  