blob: 2dfb3c1f39248225a31d103dff5fa096aad59d00 [file] [log] [blame]
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +00001/*
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/neteq4/background_noise.h"
12
13#include <assert.h>
pbos@webrtc.org3f45c2e2013-08-05 16:22:53 +000014#include <string.h> // memcpy
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +000015
16#include <algorithm> // min, max
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +000017
18#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
19#include "webrtc/modules/audio_coding/neteq4/audio_multi_vector.h"
20#include "webrtc/modules/audio_coding/neteq4/post_decode_vad.h"
21
22namespace webrtc {
23
pbos@webrtc.orgdd1b19d2013-07-31 15:54:00 +000024BackgroundNoise::BackgroundNoise(size_t num_channels)
25 : num_channels_(num_channels),
26 channel_parameters_(new ChannelParameters[num_channels_]),
27 mode_(kBgnOn) {
28 Reset();
29}
30
31BackgroundNoise::~BackgroundNoise() {}
32
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +000033void 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
henrik.lundin@webrtc.org0e9c3992013-09-30 20:38:44 +000041void BackgroundNoise::Update(const AudioMultiVector& input,
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +000042 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
125int32_t BackgroundNoise::Energy(size_t channel) const {
126 assert(channel < num_channels_);
127 return channel_parameters_[channel].energy;
128}
129
130void BackgroundNoise::SetMuteFactor(size_t channel, int16_t value) {
131 assert(channel < num_channels_);
132 channel_parameters_[channel].mute_factor = value;
133}
134
135int16_t BackgroundNoise::MuteFactor(size_t channel) const {
136 assert(channel < num_channels_);
137 return channel_parameters_[channel].mute_factor;
138}
139
140const int16_t* BackgroundNoise::Filter(size_t channel) const {
141 assert(channel < num_channels_);
142 return channel_parameters_[channel].filter;
143}
144
145const int16_t* BackgroundNoise::FilterState(size_t channel) const {
146 assert(channel < num_channels_);
147 return channel_parameters_[channel].filter_state;
148}
149
150void 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
158int16_t BackgroundNoise::Scale(size_t channel) const {
159 assert(channel < num_channels_);
160 return channel_parameters_[channel].scale;
161}
162int16_t BackgroundNoise::ScaleShift(size_t channel) const {
163 assert(channel < num_channels_);
164 return channel_parameters_[channel].scale_shift;
165}
166
167int32_t BackgroundNoise::CalculateAutoCorrelation(
turaj@webrtc.org045e45e2013-09-20 16:25:28 +0000168 const int16_t* signal, int length, int32_t* auto_correlation) const {
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +0000169 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;
turaj@webrtc.org045e45e2013-09-20 16:25:28 +0000175 WebRtcSpl_CrossCorrelation(auto_correlation, signal, signal, length,
176 kMaxLpcOrder + 1, correlation_scale,
henrik.lundin@webrtc.org9a400812013-01-29 12:09:21 +0000177 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
184void 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
225void 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