Home | History | Annotate | Download | only in neteq
      1 /*
      2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/modules/audio_coding/neteq/background_noise.h"
     12 
     13 #include <assert.h>
     14 #include <string.h>  // memcpy
     15 
     16 #include <algorithm>  // min, max
     17 
     18 #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
     19 #include "webrtc/modules/audio_coding/neteq/audio_multi_vector.h"
     20 #include "webrtc/modules/audio_coding/neteq/post_decode_vad.h"
     21 
     22 namespace webrtc {
     23 
     24 BackgroundNoise::BackgroundNoise(size_t num_channels)
     25     : num_channels_(num_channels),
     26       channel_parameters_(new ChannelParameters[num_channels_]),
     27       mode_(NetEq::kBgnOn) {
     28   Reset();
     29 }
     30 
     31 BackgroundNoise::~BackgroundNoise() {}
     32 
     33 void BackgroundNoise::Reset() {
     34   initialized_ = false;
     35   for (size_t channel = 0; channel < num_channels_; ++channel) {
     36     channel_parameters_[channel].Reset();
     37   }
     38   // Keep _bgnMode as it is.
     39 }
     40 
     41 void BackgroundNoise::Update(const AudioMultiVector& input,
     42                              const PostDecodeVad& vad) {
     43   if (vad.running() && vad.active_speech()) {
     44     // Do not update the background noise parameters if we know that the signal
     45     // is active speech.
     46     return;
     47   }
     48 
     49   int32_t auto_correlation[kMaxLpcOrder + 1];
     50   int16_t fiter_output[kMaxLpcOrder + kResidualLength];
     51   int16_t reflection_coefficients[kMaxLpcOrder];
     52   int16_t lpc_coefficients[kMaxLpcOrder + 1];
     53 
     54   for (size_t channel_ix = 0; channel_ix < num_channels_; ++channel_ix) {
     55     ChannelParameters& parameters = channel_parameters_[channel_ix];
     56     int16_t temp_signal_array[kVecLen + kMaxLpcOrder] = {0};
     57     int16_t* temp_signal = &temp_signal_array[kMaxLpcOrder];
     58     memcpy(temp_signal,
     59            &input[channel_ix][input.Size() - kVecLen],
     60            sizeof(int16_t) * kVecLen);
     61 
     62     int32_t sample_energy = CalculateAutoCorrelation(temp_signal, kVecLen,
     63                                                      auto_correlation);
     64 
     65     if ((!vad.running() &&
     66         sample_energy < parameters.energy_update_threshold) ||
     67         (vad.running() && !vad.active_speech())) {
     68       // Generate LPC coefficients.
     69       if (auto_correlation[0] > 0) {
     70         // Regardless of whether the filter is actually updated or not,
     71         // update energy threshold levels, since we have in fact observed
     72         // a low energy signal.
     73         if (sample_energy < parameters.energy_update_threshold) {
     74           // Never go under 1.0 in average sample energy.
     75           parameters.energy_update_threshold = std::max(sample_energy, 1);
     76           parameters.low_energy_update_threshold = 0;
     77         }
     78 
     79         // Only update BGN if filter is stable, i.e., if return value from
     80         // Levinson-Durbin function is 1.
     81         if (WebRtcSpl_LevinsonDurbin(auto_correlation, lpc_coefficients,
     82                                      reflection_coefficients,
     83                                      kMaxLpcOrder) != 1) {
     84           return;
     85         }
     86       } else {
     87         // Center value in auto-correlation is not positive. Do not update.
     88         return;
     89       }
     90 
     91       // Generate the CNG gain factor by looking at the energy of the residual.
     92       WebRtcSpl_FilterMAFastQ12(temp_signal + kVecLen - kResidualLength,
     93                                 fiter_output, lpc_coefficients,
     94                                 kMaxLpcOrder + 1, kResidualLength);
     95       int32_t residual_energy = WebRtcSpl_DotProductWithScale(fiter_output,
     96                                                               fiter_output,
     97                                                               kResidualLength,
     98                                                               0);
     99 
    100       // Check spectral flatness.
    101       // Comparing the residual variance with the input signal variance tells
    102       // if the spectrum is flat or not.
    103       // If 20 * residual_energy >= sample_energy << 6, the spectrum is flat
    104       // enough.  Also ensure that the energy is non-zero.
    105       if ((residual_energy * 20 >= (sample_energy << 6)) &&
    106           (sample_energy > 0)) {
    107         // Spectrum is flat enough; save filter parameters.
    108         // |temp_signal| + |kVecLen| - |kMaxLpcOrder| points at the first of the
    109         // |kMaxLpcOrder| samples in the residual signal, which will form the
    110         // filter state for the next noise generation.
    111         SaveParameters(channel_ix, lpc_coefficients,
    112                        temp_signal + kVecLen - kMaxLpcOrder, sample_energy,
    113                        residual_energy);
    114       }
    115     } else {
    116       // Will only happen if post-decode VAD is disabled and |sample_energy| is
    117       // not low enough. Increase the threshold for update so that it increases
    118       // by a factor 4 in 4 seconds.
    119       IncrementEnergyThreshold(channel_ix, sample_energy);
    120     }
    121   }
    122   return;
    123 }
    124 
    125 int32_t BackgroundNoise::Energy(size_t channel) const {
    126   assert(channel < num_channels_);
    127   return channel_parameters_[channel].energy;
    128 }
    129 
    130 void BackgroundNoise::SetMuteFactor(size_t channel, int16_t value) {
    131   assert(channel < num_channels_);
    132   channel_parameters_[channel].mute_factor = value;
    133 }
    134 
    135 int16_t BackgroundNoise::MuteFactor(size_t channel) const {
    136   assert(channel < num_channels_);
    137   return channel_parameters_[channel].mute_factor;
    138 }
    139 
    140 const int16_t* BackgroundNoise::Filter(size_t channel) const {
    141   assert(channel < num_channels_);
    142   return channel_parameters_[channel].filter;
    143 }
    144 
    145 const int16_t* BackgroundNoise::FilterState(size_t channel) const {
    146   assert(channel < num_channels_);
    147   return channel_parameters_[channel].filter_state;
    148 }
    149 
    150 void BackgroundNoise::SetFilterState(size_t channel, const int16_t* input,
    151                                      size_t length) {
    152   assert(channel < num_channels_);
    153   length = std::min(length, static_cast<size_t>(kMaxLpcOrder));
    154   memcpy(channel_parameters_[channel].filter_state, input,
    155          length * sizeof(int16_t));
    156 }
    157 
    158 int16_t BackgroundNoise::Scale(size_t channel) const {
    159   assert(channel < num_channels_);
    160   return channel_parameters_[channel].scale;
    161 }
    162 int16_t BackgroundNoise::ScaleShift(size_t channel) const {
    163   assert(channel < num_channels_);
    164   return channel_parameters_[channel].scale_shift;
    165 }
    166 
    167 int32_t BackgroundNoise::CalculateAutoCorrelation(
    168     const int16_t* signal, int length, int32_t* auto_correlation) const {
    169   int16_t signal_max = WebRtcSpl_MaxAbsValueW16(signal, length);
    170   int correlation_scale = kLogVecLen -
    171       WebRtcSpl_NormW32(signal_max * signal_max);
    172   correlation_scale = std::max(0, correlation_scale);
    173 
    174   static const int kCorrelationStep = -1;
    175   WebRtcSpl_CrossCorrelation(auto_correlation, signal, signal, length,
    176                              kMaxLpcOrder + 1, correlation_scale,
    177                              kCorrelationStep);
    178 
    179   // Number of shifts to normalize energy to energy/sample.
    180   int energy_sample_shift = kLogVecLen - correlation_scale;
    181   return auto_correlation[0] >> energy_sample_shift;
    182 }
    183 
    184 void BackgroundNoise::IncrementEnergyThreshold(size_t channel,
    185                                                int32_t sample_energy) {
    186   // TODO(hlundin): Simplify the below threshold update. What this code
    187   // does is simply "threshold += (increment * threshold) >> 16", but due
    188   // to the limited-width operations, it is not exactly the same. The
    189   // difference should be inaudible, but bit-exactness would not be
    190   // maintained.
    191   assert(channel < num_channels_);
    192   ChannelParameters& parameters = channel_parameters_[channel];
    193   int32_t temp_energy =
    194       WEBRTC_SPL_MUL_16_16_RSFT(kThresholdIncrement,
    195                                 parameters.low_energy_update_threshold, 16);
    196   temp_energy += kThresholdIncrement *
    197       (parameters.energy_update_threshold & 0xFF);
    198   temp_energy += (kThresholdIncrement *
    199       ((parameters.energy_update_threshold>>8) & 0xFF)) << 8;
    200   parameters.low_energy_update_threshold += temp_energy;
    201 
    202   parameters.energy_update_threshold += kThresholdIncrement *
    203       (parameters.energy_update_threshold>>16);
    204   parameters.energy_update_threshold +=
    205       parameters.low_energy_update_threshold >> 16;
    206   parameters.low_energy_update_threshold =
    207       parameters.low_energy_update_threshold & 0x0FFFF;
    208 
    209   // Update maximum energy.
    210   // Decrease by a factor 1/1024 each time.
    211   parameters.max_energy = parameters.max_energy -
    212       (parameters.max_energy >> 10);
    213   if (sample_energy > parameters.max_energy) {
    214     parameters.max_energy = sample_energy;
    215   }
    216 
    217   // Set |energy_update_threshold| to no less than 60 dB lower than
    218   // |max_energy_|. Adding 524288 assures proper rounding.
    219   int32_t energy_update_threshold = (parameters.max_energy + 524288) >> 20;
    220   if (energy_update_threshold > parameters.energy_update_threshold) {
    221     parameters.energy_update_threshold = energy_update_threshold;
    222   }
    223 }
    224 
    225 void BackgroundNoise::SaveParameters(size_t channel,
    226                                      const int16_t* lpc_coefficients,
    227                                      const int16_t* filter_state,
    228                                      int32_t sample_energy,
    229                                      int32_t residual_energy) {
    230   assert(channel < num_channels_);
    231   ChannelParameters& parameters = channel_parameters_[channel];
    232   memcpy(parameters.filter, lpc_coefficients,
    233          (kMaxLpcOrder+1) * sizeof(int16_t));
    234   memcpy(parameters.filter_state, filter_state,
    235          kMaxLpcOrder * sizeof(int16_t));
    236   // Save energy level and update energy threshold levels.
    237   // Never get under 1.0 in average sample energy.
    238   parameters.energy = std::max(sample_energy, 1);
    239   parameters.energy_update_threshold = parameters.energy;
    240   parameters.low_energy_update_threshold = 0;
    241 
    242   // Normalize residual_energy to 29 or 30 bits before sqrt.
    243   int norm_shift = WebRtcSpl_NormW32(residual_energy) - 1;
    244   if (norm_shift & 0x1) {
    245     norm_shift -= 1;  // Even number of shifts required.
    246   }
    247   assert(norm_shift >= 0);  // Should always be positive.
    248   residual_energy = residual_energy << norm_shift;
    249 
    250   // Calculate scale and shift factor.
    251   parameters.scale = WebRtcSpl_SqrtFloor(residual_energy);
    252   // Add 13 to the |scale_shift_|, since the random numbers table is in
    253   // Q13.
    254   // TODO(hlundin): Move the "13" to where the |scale_shift_| is used?
    255   parameters.scale_shift = 13 + ((kLogResidualLength + norm_shift) / 2);
    256 
    257   initialized_ = true;
    258 }
    259 
    260 }  // namespace webrtc
    261