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