|  | /* | 
|  | *  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/neteq/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/neteq/audio_multi_vector.h" | 
|  | #include "webrtc/modules/audio_coding/neteq/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 |