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 "webrtc/modules/audio_coding/neteq4/background_noise.h"
  
  #include <assert.h>
  #include <string.h>  // memcpy
  
  #include <algorithm>  // min, max
  
  #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
  #include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h"
  #include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h"
  
  namespace webrtc {
  
  BackgroundNoise::BackgroundNoise(size_t num_channels)
      : num_channels_(num_channels),
        channel_parameters_(new ChannelParameters[num_channels_]),
        mode_(kBgnOn) {
    Reset();
  }
  
  BackgroundNoise::~BackgroundNoise() {}
  
  void BackgroundNoise::Reset() {
    initialized_ = false;
    for (size_t channel = 0; channel < num_channels_; ++channel) {
      channel_parameters_[channel].Reset();
    }
    // Keep _bgnMode as it is.
  }
  
  void BackgroundNoise::Update(const AudioMultiVector& input,
                               const PostDecodeVad& vad) {
    if (vad.running() && vad.active_speech()) {
      // Do not update the background noise parameters if we know that the signal
      // is active speech.
      return;
    }
  
    int32_t auto_correlation[kMaxLpcOrder + 1];
    int16_t fiter_output[kMaxLpcOrder + kResidualLength];
    int16_t reflection_coefficients[kMaxLpcOrder];
    int16_t lpc_coefficients[kMaxLpcOrder + 1];
  
    for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) {
      ChannelParameters& parameters = channel_parameters_[channel_ix];
      int16_t temp_signal_array[kVecLen + kMaxLpcOrder] = {0};
      int16_t* temp_signal = &temp_signal_array[kMaxLpcOrder];
      memcpy(temp_signal,
             &input[channel_ix][input.Size() - kVecLen],
             sizeof(int16_t) * kVecLen);
  
      int32_t sample_energy = CalculateAutoCorrelation(temp_signal, kVecLen,
                                                       auto_correlation);
  
      if ((!vad.running() &&
          sample_energy < parameters.energy_update_threshold) ||
          (vad.running() && !vad.active_speech())) {
        // Generate LPC coefficients.
        if (auto_correlation[0] > 0) {
          // Regardless of whether the filter is actually updated or not,
          // update energy threshold levels, since we have in fact observed
          // a low energy signal.
          if (sample_energy < parameters.energy_update_threshold) {
            // Never go under 1.0 in average sample energy.
            parameters.energy_update_threshold = std::max(sample_energy, 1);
            parameters.low_energy_update_threshold = 0;
          }
  
          // Only update BGN if filter is stable, i.e., if return value from
          // Levinson-Durbin function is 1.
          if (WebRtcSpl_LevinsonDurbin(auto_correlation, lpc_coefficients,
                                       reflection_coefficients,
                                       kMaxLpcOrder) != 1) {
            return;
          }
        } else {
          // Center value in auto-correlation is not positive. Do not update.
          return;
        }
  
        // Generate the CNG gain factor by looking at the energy of the residual.
        WebRtcSpl_FilterMAFastQ12(temp_signal + kVecLen - kResidualLength,
                                  fiter_output, lpc_coefficients,
                                  kMaxLpcOrder + 1, kResidualLength);
        int32_t residual_energy = WebRtcSpl_DotProductWithScale(fiter_output,
                                                                fiter_output,
                                                                kResidualLength,
                                                                0);
  
        // Check spectral flatness.
        // Comparing the residual variance with the input signal variance tells
        // if the spectrum is flat or not.
        // If 20 * residual_energy >= sample_energy << 6, the spectrum is flat
        // enough.  Also ensure that the energy is non-zero.
        if ((residual_energy * 20 >= (sample_energy << 6)) &&
            (sample_energy > 0)) {
          // Spectrum is flat enough; save filter parameters.
          // |temp_signal| + |kVecLen| - |kMaxLpcOrder| points at the first of the
          // |kMaxLpcOrder| samples in the residual signal, which will form the
          // filter state for the next noise generation.
          SaveParameters(channel_ix, lpc_coefficients,
                         temp_signal + kVecLen - kMaxLpcOrder, sample_energy,
                         residual_energy);
        }
      } else {
        // Will only happen if post-decode VAD is disabled and |sample_energy| is
        // not low enough. Increase the threshold for update so that it increases
        // by a factor 4 in 4 seconds.
        IncrementEnergyThreshold(channel_ix, sample_energy);
      }
    }
    return;
  }
  
  int32_t BackgroundNoise::Energy(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].energy;
  }
  
  void BackgroundNoise::SetMuteFactor(size_t channel, int16_t value) {
    assert(channel < num_channels_);
    channel_parameters_[channel].mute_factor = value;
  }
  
  int16_t BackgroundNoise::MuteFactor(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].mute_factor;
  }
  
  const int16_t* BackgroundNoise::Filter(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].filter;
  }
  
  const int16_t* BackgroundNoise::FilterState(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].filter_state;
  }
  
  void BackgroundNoise::SetFilterState(size_t channel, const int16_t* input,
                                       size_t length) {
    assert(channel < num_channels_);
    length = std::min(length, static_cast<size_t>(kMaxLpcOrder));
    memcpy(channel_parameters_[channel].filter_state, input,
           length * sizeof(int16_t));
  }
  
  int16_t BackgroundNoise::Scale(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].scale;
  }
  int16_t BackgroundNoise::ScaleShift(size_t channel) const {
    assert(channel < num_channels_);
    return channel_parameters_[channel].scale_shift;
  }
  
  int32_t BackgroundNoise::CalculateAutoCorrelation(
      const int16_t* signal, int length, int32_t* auto_correlation) const {
    int16_t signal_max = WebRtcSpl_MaxAbsValueW16(signal, length);
    int correlation_scale = kLogVecLen -
        WebRtcSpl_NormW32(signal_max * signal_max);
    correlation_scale = std::max(0, correlation_scale);
  
    static const int kCorrelationStep = -1;
    WebRtcSpl_CrossCorrelation(auto_correlation, signal, signal, length,
                               kMaxLpcOrder + 1, correlation_scale,
                               kCorrelationStep);
  
    // Number of shifts to normalize energy to energy/sample.
    int energy_sample_shift = kLogVecLen - correlation_scale;
    return auto_correlation[0] >> energy_sample_shift;
  }
  
  void BackgroundNoise::IncrementEnergyThreshold(size_t channel,
                                                 int32_t sample_energy) {
    // TODO(hlundin): Simplify the below threshold update. What this code
    // does is simply "threshold += (increment * threshold) >> 16", but due
    // to the limited-width operations, it is not exactly the same. The
    // difference should be inaudible, but bit-exactness would not be
    // maintained.
    assert(channel < num_channels_);
    ChannelParameters& parameters = channel_parameters_[channel];
    int32_t temp_energy =
        WEBRTC_SPL_MUL_16_16_RSFT(kThresholdIncrement,
                                  parameters.low_energy_update_threshold, 16);
    temp_energy += kThresholdIncrement *
        (parameters.energy_update_threshold & 0xFF);
    temp_energy += (kThresholdIncrement *
        ((parameters.energy_update_threshold>>8) & 0xFF)) << 8;
    parameters.low_energy_update_threshold += temp_energy;
  
    parameters.energy_update_threshold += kThresholdIncrement *
        (parameters.energy_update_threshold>>16);
    parameters.energy_update_threshold +=
        parameters.low_energy_update_threshold >> 16;
    parameters.low_energy_update_threshold =
        parameters.low_energy_update_threshold & 0x0FFFF;
  
    // Update maximum energy.
    // Decrease by a factor 1/1024 each time.
    parameters.max_energy = parameters.max_energy -
        (parameters.max_energy >> 10);
    if (sample_energy > parameters.max_energy) {
      parameters.max_energy = sample_energy;
    }
  
    // Set |energy_update_threshold| to no less than 60 dB lower than
    // |max_energy_|. Adding 524288 assures proper rounding.
    int32_t energy_update_threshold = (parameters.max_energy + 524288) >> 20;
    if (energy_update_threshold > parameters.energy_update_threshold) {
      parameters.energy_update_threshold = energy_update_threshold;
    }
  }
  
  void BackgroundNoise::SaveParameters(size_t channel,
                                       const int16_t* lpc_coefficients,
                                       const int16_t* filter_state,
                                       int32_t sample_energy,
                                       int32_t residual_energy) {
    assert(channel < num_channels_);
    ChannelParameters& parameters = channel_parameters_[channel];
    memcpy(parameters.filter, lpc_coefficients,
           (kMaxLpcOrder+1) * sizeof(int16_t));
    memcpy(parameters.filter_state, filter_state,
           kMaxLpcOrder * sizeof(int16_t));
    // Save energy level and update energy threshold levels.
    // Never get under 1.0 in average sample energy.
    parameters.energy = std::max(sample_energy, 1);
    parameters.energy_update_threshold = parameters.energy;
    parameters.low_energy_update_threshold = 0;
  
    // Normalize residual_energy to 29 or 30 bits before sqrt.
    int norm_shift = WebRtcSpl_NormW32(residual_energy) - 1;
    if (norm_shift & 0x1) {
      norm_shift -= 1;  // Even number of shifts required.
    }
    assert(norm_shift >= 0);  // Should always be positive.
    residual_energy = residual_energy << norm_shift;
  
    // Calculate scale and shift factor.
    parameters.scale = WebRtcSpl_SqrtFloor(residual_energy);
    // Add 13 to the |scale_shift_|, since the random numbers table is in
    // Q13.
    // TODO(hlundin): Move the "13" to where the |scale_shift_| is used?
    parameters.scale_shift = 13 + ((kLogResidualLength + norm_shift) / 2);
  
    initialized_ = true;
  }
  
  }  // namespace webrtc
  