blob: e408b15030e96b6c326e413caa29876850d4ac9c [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
2 * Copyright (c) 2011 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/* digital_agc.c
12 *
13 */
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "modules/audio_processing/agc/legacy/digital_agc.h"
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000016
niklase@google.com470e71d2011-07-07 08:21:25 +000017#include <string.h>
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000018
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "rtc_base/checks.h"
20#include "modules/audio_processing/agc/legacy/gain_control.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000021
22// To generate the gaintable, copy&paste the following lines to a Matlab window:
23// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1;
24// zeros = 0:31; lvl = 2.^(1-zeros);
25// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio;
26// B = MaxGain - MinGain;
minyuecac94aa2016-05-20 08:42:22 -070027// gains = round(2^16*10.^(0.05 * (MinGain + B * (
28// log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) /
29// log(1/(1+exp(Knee*B))))));
niklase@google.com470e71d2011-07-07 08:21:25 +000030// fprintf(1, '\t%i, %i, %i, %i,\n', gains);
minyuecac94aa2016-05-20 08:42:22 -070031// % Matlab code for plotting the gain and input/output level characteristic
32// (copy/paste the following 3 lines):
niklase@google.com470e71d2011-07-07 08:21:25 +000033// in = 10*log10(lvl); out = 20*log10(gains/65536);
minyuecac94aa2016-05-20 08:42:22 -070034// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input
35// (dB)'); ylabel('Gain (dB)');
36// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on;
37// xlabel('Input (dB)'); ylabel('Output (dB)');
niklase@google.com470e71d2011-07-07 08:21:25 +000038// zoom on;
39
40// Generator table for y=log2(1+e^x) in Q8.
andrew@webrtc.orgd77a6612012-01-04 16:22:24 +000041enum { kGenFuncTableSize = 128 };
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000042static const uint16_t kGenFuncTable[kGenFuncTableSize] = {
minyuecac94aa2016-05-20 08:42:22 -070043 256, 485, 786, 1126, 1484, 1849, 2217, 2586, 2955, 3324, 3693,
44 4063, 4432, 4801, 5171, 5540, 5909, 6279, 6648, 7017, 7387, 7756,
45 8125, 8495, 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, 11819,
46 12188, 12557, 12927, 13296, 13665, 14035, 14404, 14773, 15143, 15512, 15881,
47 16251, 16620, 16989, 17359, 17728, 18097, 18466, 18836, 19205, 19574, 19944,
48 20313, 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, 23637, 24006,
49 24376, 24745, 25114, 25484, 25853, 26222, 26592, 26961, 27330, 27700, 28069,
50 28438, 28808, 29177, 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132,
51 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, 35456, 35825, 36194,
52 36564, 36933, 37302, 37672, 38041, 38410, 38780, 39149, 39518, 39888, 40257,
53 40626, 40996, 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, 44320,
54 44689, 45058, 45428, 45797, 46166, 46536, 46905};
niklase@google.com470e71d2011-07-07 08:21:25 +000055
minyuecac94aa2016-05-20 08:42:22 -070056static const int16_t kAvgDecayTime = 250; // frames; < 3000
niklase@google.com470e71d2011-07-07 08:21:25 +000057
minyuecac94aa2016-05-20 08:42:22 -070058int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16
59 int16_t digCompGaindB, // Q0
60 int16_t targetLevelDbfs, // Q0
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000061 uint8_t limiterEnable,
minyuecac94aa2016-05-20 08:42:22 -070062 int16_t analogTarget) // Q0
niklase@google.com470e71d2011-07-07 08:21:25 +000063{
minyuecac94aa2016-05-20 08:42:22 -070064 // This function generates the compressor gain table used in the fixed digital
65 // part.
66 uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox;
67 int32_t inLevel, limiterLvl;
68 int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32;
69 const uint16_t kLog10 = 54426; // log2(10) in Q14
70 const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14
71 const uint16_t kLogE_1 = 23637; // log2(e) in Q14
72 uint16_t constMaxGain;
73 uint16_t tmpU16, intPart, fracPart;
74 const int16_t kCompRatio = 3;
75 const int16_t kSoftLimiterLeft = 1;
76 int16_t limiterOffset = 0; // Limiter offset
77 int16_t limiterIdx, limiterLvlX;
78 int16_t constLinApprox, zeroGainLvl, maxGain, diffGain;
79 int16_t i, tmp16, tmp16no1;
80 int zeros, zerosScale;
niklase@google.com470e71d2011-07-07 08:21:25 +000081
minyuecac94aa2016-05-20 08:42:22 -070082 // Constants
83 // kLogE_1 = 23637; // log2(e) in Q14
84 // kLog10 = 54426; // log2(10) in Q14
85 // kLog10_2 = 49321; // 10*log10(2) in Q14
niklase@google.com470e71d2011-07-07 08:21:25 +000086
minyuecac94aa2016-05-20 08:42:22 -070087 // Calculate maximum digital gain and zero gain level
88 tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1);
89 tmp16no1 = analogTarget - targetLevelDbfs;
90 tmp16no1 +=
91 WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
92 maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs));
93 tmp32no1 = maxGain * kCompRatio;
94 zeroGainLvl = digCompGaindB;
95 zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1),
96 kCompRatio - 1);
97 if ((digCompGaindB <= analogTarget) && (limiterEnable)) {
98 zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft);
99 limiterOffset = 0;
100 }
101
102 // Calculate the difference between maximum gain and gain at 0dB0v:
103 // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio
104 // = (compRatio-1)*digCompGaindB/compRatio
105 tmp32no1 = digCompGaindB * (kCompRatio - 1);
106 diffGain =
107 WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
108 if (diffGain < 0 || diffGain >= kGenFuncTableSize) {
kwiberg1e8ed4a2016-08-26 04:33:34 -0700109 RTC_DCHECK(0);
minyuecac94aa2016-05-20 08:42:22 -0700110 return -1;
111 }
112
113 // Calculate the limiter level and index:
114 // limiterLvlX = analogTarget - limiterOffset
115 // limiterLvl = targetLevelDbfs + limiterOffset/compRatio
116 limiterLvlX = analogTarget - limiterOffset;
117 limiterIdx = 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX * (1 << 13),
118 kLog10_2 / 2);
119 tmp16no1 =
120 WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio);
121 limiterLvl = targetLevelDbfs + tmp16no1;
122
123 // Calculate (through table lookup):
124 // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8)
125 constMaxGain = kGenFuncTable[diffGain]; // in Q8
126
127 // Calculate a parameter used to approximate the fractional part of 2^x with a
128 // piecewise linear function in Q14:
129 // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14);
130 constLinApprox = 22817; // in Q14
131
132 // Calculate a denominator used in the exponential part to convert from dB to
133 // linear scale:
134 // den = 20*constMaxGain (in Q8)
135 den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8
136
137 for (i = 0; i < 32; i++) {
138 // Calculate scaled input level (compressor):
139 // inLevel =
140 // fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio)
141 tmp16 = (int16_t)((kCompRatio - 1) * (i - 1)); // Q0
142 tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14
143 inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14
144
145 // Calculate diffGain-inLevel, to map using the genFuncTable
146 inLevel = (int32_t)diffGain * (1 << 14) - inLevel; // Q14
147
148 // Make calculations on abs(inLevel) and compensate for the sign afterwards.
149 absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14
150
151 // LUT with interpolation
152 intPart = (uint16_t)(absInLevel >> 14);
153 fracPart =
154 (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part
155 tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8
156 tmpU32no1 = tmpU16 * fracPart; // Q22
157 tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22
158 logApprox = tmpU32no1 >> 8; // Q14
159 // Compensate for negative exponent using the relation:
160 // log2(1 + 2^-x) = log2(1 + 2^x) - x
161 if (inLevel < 0) {
162 zeros = WebRtcSpl_NormU32(absInLevel);
163 zerosScale = 0;
164 if (zeros < 15) {
165 // Not enough space for multiplication
166 tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1)
167 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13)
168 if (zeros < 9) {
169 zerosScale = 9 - zeros;
170 tmpU32no1 >>= zerosScale; // Q(zeros+13)
171 } else {
172 tmpU32no2 >>= zeros - 9; // Q22
173 }
174 } else {
175 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28
176 tmpU32no2 >>= 6; // Q22
177 }
178 logApprox = 0;
179 if (tmpU32no2 < tmpU32no1) {
180 logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); // Q14
181 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000182 }
minyuecac94aa2016-05-20 08:42:22 -0700183 numFIX = (maxGain * constMaxGain) * (1 << 6); // Q14
184 numFIX -= (int32_t)logApprox * diffGain; // Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000185
minyuecac94aa2016-05-20 08:42:22 -0700186 // Calculate ratio
187 // Shift |numFIX| as much as possible.
188 // Ensure we avoid wrap-around in |den| as well.
minyuefd634c42016-06-17 04:36:10 -0700189 if (numFIX > (den >> 8) || -numFIX > (den >> 8)) // |den| is Q8.
niklase@google.com470e71d2011-07-07 08:21:25 +0000190 {
minyuecac94aa2016-05-20 08:42:22 -0700191 zeros = WebRtcSpl_NormW32(numFIX);
192 } else {
193 zeros = WebRtcSpl_NormW32(den) + 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000194 }
minyuecac94aa2016-05-20 08:42:22 -0700195 numFIX *= 1 << zeros; // Q(14+zeros)
niklase@google.com470e71d2011-07-07 08:21:25 +0000196
minyuecac94aa2016-05-20 08:42:22 -0700197 // Shift den so we end up in Qy1
minyuefd634c42016-06-17 04:36:10 -0700198 tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 9); // Q(zeros - 1)
199 y32 = numFIX / tmp32no1; // in Q15
200 // This is to do rounding in Q14.
201 y32 = y32 >= 0 ? (y32 + 1) >> 1 : -((-y32 + 1) >> 1);
202
minyuecac94aa2016-05-20 08:42:22 -0700203 if (limiterEnable && (i < limiterIdx)) {
204 tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14
205 tmp32 -= limiterLvl * (1 << 14); // Q14
206 y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20);
207 }
208 if (y32 > 39000) {
209 tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27
210 tmp32 >>= 13; // In Q14.
211 } else {
212 tmp32 = y32 * kLog10 + 8192; // in Q28
213 tmp32 >>= 14; // In Q14.
214 }
215 tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16)
niklase@google.com470e71d2011-07-07 08:21:25 +0000216
minyuecac94aa2016-05-20 08:42:22 -0700217 // Calculate power
218 if (tmp32 > 0) {
219 intPart = (int16_t)(tmp32 >> 14);
220 fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14
221 if ((fracPart >> 13) != 0) {
222 tmp16 = (2 << 14) - constLinApprox;
223 tmp32no2 = (1 << 14) - fracPart;
224 tmp32no2 *= tmp16;
225 tmp32no2 >>= 13;
226 tmp32no2 = (1 << 14) - tmp32no2;
227 } else {
228 tmp16 = constLinApprox - (1 << 14);
229 tmp32no2 = (fracPart * tmp16) >> 13;
230 }
231 fracPart = (uint16_t)tmp32no2;
232 gainTable[i] =
233 (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14);
234 } else {
235 gainTable[i] = 0;
236 }
237 }
238
239 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000240}
241
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000242int32_t WebRtcAgc_InitDigital(DigitalAgc* stt, int16_t agcMode) {
minyuecac94aa2016-05-20 08:42:22 -0700243 if (agcMode == kAgcModeFixedDigital) {
244 // start at minimum to find correct gain faster
245 stt->capacitorSlow = 0;
246 } else {
247 // start out with 0 dB gain
248 stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f);
249 }
250 stt->capacitorFast = 0;
251 stt->gain = 65536;
252 stt->gatePrevious = 0;
253 stt->agcMode = agcMode;
niklase@google.com470e71d2011-07-07 08:21:25 +0000254
minyuecac94aa2016-05-20 08:42:22 -0700255 // initialize VADs
256 WebRtcAgc_InitVad(&stt->vadNearend);
257 WebRtcAgc_InitVad(&stt->vadFarend);
niklase@google.com470e71d2011-07-07 08:21:25 +0000258
minyuecac94aa2016-05-20 08:42:22 -0700259 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000260}
261
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000262int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* stt,
263 const int16_t* in_far,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700264 size_t nrSamples) {
kwiberg1e8ed4a2016-08-26 04:33:34 -0700265 RTC_DCHECK(stt);
minyuecac94aa2016-05-20 08:42:22 -0700266 // VAD for far end
267 WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
niklase@google.com470e71d2011-07-07 08:21:25 +0000268
minyuecac94aa2016-05-20 08:42:22 -0700269 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000270}
271
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100272// Gains is an 11 element long array (one value per ms, incl start & end).
273int32_t WebRtcAgc_ComputeDigitalGains(DigitalAgc* stt,
274 const int16_t* const* in_near,
275 size_t num_bands,
276 uint32_t FS,
277 int16_t lowlevelSignal,
278 int32_t gains[11]) {
279 int32_t tmp32;
minyuecac94aa2016-05-20 08:42:22 -0700280 int32_t env[10];
281 int32_t max_nrg;
282 int32_t cur_level;
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100283 int32_t gain32;
minyuecac94aa2016-05-20 08:42:22 -0700284 int16_t logratio;
285 int16_t lower_thr, upper_thr;
286 int16_t zeros = 0, zeros_fast, frac = 0;
287 int16_t decay;
288 int16_t gate, gain_adj;
289 int16_t k;
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100290 size_t n, L;
minyuecac94aa2016-05-20 08:42:22 -0700291 int16_t L2; // samples/subframe
niklase@google.com470e71d2011-07-07 08:21:25 +0000292
minyuecac94aa2016-05-20 08:42:22 -0700293 // determine number of samples per ms
294 if (FS == 8000) {
295 L = 8;
296 L2 = 3;
297 } else if (FS == 16000 || FS == 32000 || FS == 48000) {
298 L = 16;
299 L2 = 4;
300 } else {
301 return -1;
302 }
303
minyuecac94aa2016-05-20 08:42:22 -0700304 // VAD for near end
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100305 logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, in_near[0], L * 10);
minyuecac94aa2016-05-20 08:42:22 -0700306
307 // Account for far end VAD
308 if (stt->vadFarend.counter > 10) {
309 tmp32 = 3 * logratio;
310 logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2);
311 }
312
313 // Determine decay factor depending on VAD
314 // upper_thr = 1.0f;
315 // lower_thr = 0.25f;
316 upper_thr = 1024; // Q10
317 lower_thr = 0; // Q10
318 if (logratio > upper_thr) {
319 // decay = -2^17 / DecayTime; -> -65
320 decay = -65;
321 } else if (logratio < lower_thr) {
322 decay = 0;
323 } else {
324 // decay = (int16_t)(((lower_thr - logratio)
325 // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10);
326 // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65
327 tmp32 = (lower_thr - logratio) * 65;
328 decay = (int16_t)(tmp32 >> 10);
329 }
330
331 // adjust decay factor for long silence (detected as low standard deviation)
332 // This is only done in the adaptive modes
333 if (stt->agcMode != kAgcModeFixedDigital) {
334 if (stt->vadNearend.stdLongTerm < 4000) {
335 decay = 0;
336 } else if (stt->vadNearend.stdLongTerm < 8096) {
337 // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >>
338 // 12);
339 tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay;
340 decay = (int16_t)(tmp32 >> 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000341 }
342
minyuecac94aa2016-05-20 08:42:22 -0700343 if (lowlevelSignal != 0) {
344 decay = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000345 }
minyuecac94aa2016-05-20 08:42:22 -0700346 }
minyuecac94aa2016-05-20 08:42:22 -0700347 // Find max amplitude per sub frame
348 // iterate over sub frames
349 for (k = 0; k < 10; k++) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000350 // iterate over samples
minyuecac94aa2016-05-20 08:42:22 -0700351 max_nrg = 0;
352 for (n = 0; n < L; n++) {
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100353 int32_t nrg = in_near[0][k * L + n] * in_near[0][k * L + n];
minyuecac94aa2016-05-20 08:42:22 -0700354 if (nrg > max_nrg) {
355 max_nrg = nrg;
356 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000357 }
minyuecac94aa2016-05-20 08:42:22 -0700358 env[k] = max_nrg;
359 }
360
361 // Calculate gain per sub frame
362 gains[0] = stt->gain;
363 for (k = 0; k < 10; k++) {
364 // Fast envelope follower
365 // decay time = -131000 / -1000 = 131 (ms)
366 stt->capacitorFast =
367 AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
368 if (env[k] > stt->capacitorFast) {
369 stt->capacitorFast = env[k];
370 }
371 // Slow envelope follower
372 if (env[k] > stt->capacitorSlow) {
373 // increase capacitorSlow
374 stt->capacitorSlow = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow),
375 stt->capacitorSlow);
376 } else {
377 // decrease capacitorSlow
378 stt->capacitorSlow =
379 AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
niklase@google.com470e71d2011-07-07 08:21:25 +0000380 }
381
minyuecac94aa2016-05-20 08:42:22 -0700382 // use maximum of both capacitors as current level
383 if (stt->capacitorFast > stt->capacitorSlow) {
384 cur_level = stt->capacitorFast;
385 } else {
386 cur_level = stt->capacitorSlow;
387 }
388 // Translate signal level into gain, using a piecewise linear approximation
389 // find number of leading zeros
390 zeros = WebRtcSpl_NormU32((uint32_t)cur_level);
391 if (cur_level == 0) {
392 zeros = 31;
393 }
Alex Loikoc531af72017-10-24 10:41:48 +0200394 tmp32 = ((uint32_t)cur_level << zeros) & 0x7FFFFFFF;
minyuecac94aa2016-05-20 08:42:22 -0700395 frac = (int16_t)(tmp32 >> 19); // Q12.
Sam Zackrisson762289e2018-06-26 11:21:22 +0200396 // Interpolate between gainTable[zeros] and gainTable[zeros-1].
397 tmp32 = ((stt->gainTable[zeros - 1] - stt->gainTable[zeros]) *
398 (int64_t)frac) >> 12;
399 gains[k + 1] = stt->gainTable[zeros] + tmp32;
minyuecac94aa2016-05-20 08:42:22 -0700400 }
401
402 // Gate processing (lower gain during absence of speech)
403 zeros = (zeros << 9) - (frac >> 3);
404 // find number of leading zeros
405 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
406 if (stt->capacitorFast == 0) {
407 zeros_fast = 31;
408 }
Alex Loikoc531af72017-10-24 10:41:48 +0200409 tmp32 = ((uint32_t)stt->capacitorFast << zeros_fast) & 0x7FFFFFFF;
minyuecac94aa2016-05-20 08:42:22 -0700410 zeros_fast <<= 9;
411 zeros_fast -= (int16_t)(tmp32 >> 22);
412
413 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
414
415 if (gate < 0) {
416 stt->gatePrevious = 0;
417 } else {
418 tmp32 = stt->gatePrevious * 7;
419 gate = (int16_t)((gate + tmp32) >> 3);
420 stt->gatePrevious = gate;
421 }
422 // gate < 0 -> no gate
423 // gate > 2500 -> max gate
424 if (gate > 0) {
425 if (gate < 2500) {
426 gain_adj = (2500 - gate) >> 5;
427 } else {
428 gain_adj = 0;
429 }
430 for (k = 0; k < 10; k++) {
431 if ((gains[k + 1] - stt->gainTable[0]) > 8388608) {
432 // To prevent wraparound
433 tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8;
434 tmp32 *= 178 + gain_adj;
435 } else {
436 tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj);
437 tmp32 >>= 8;
438 }
439 gains[k + 1] = stt->gainTable[0] + tmp32;
440 }
441 }
442
443 // Limit gain to avoid overload distortion
444 for (k = 0; k < 10; k++) {
Sam Zackrisson71729eb2018-07-09 16:06:28 +0200445 // Find a shift of gains[k + 1] such that it can be squared without
446 // overflow, but at least by 10 bits.
minyuecac94aa2016-05-20 08:42:22 -0700447 zeros = 10;
Sam Zackrisson71729eb2018-07-09 16:06:28 +0200448 if (gains[k + 1] > 47452159) {
minyuecac94aa2016-05-20 08:42:22 -0700449 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
450 }
451 gain32 = (gains[k + 1] >> zeros) + 1;
452 gain32 *= gain32;
453 // check for overflow
454 while (AGC_MUL32((env[k] >> 12) + 1, gain32) >
455 WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) {
456 // multiply by 253/256 ==> -0.1 dB
457 if (gains[k + 1] > 8388607) {
458 // Prevent wrap around
459 gains[k + 1] = (gains[k + 1] / 256) * 253;
460 } else {
461 gains[k + 1] = (gains[k + 1] * 253) / 256;
462 }
463 gain32 = (gains[k + 1] >> zeros) + 1;
464 gain32 *= gain32;
465 }
466 }
467 // gain reductions should be done 1 ms earlier than gain increases
468 for (k = 1; k < 10; k++) {
469 if (gains[k] > gains[k + 1]) {
470 gains[k] = gains[k + 1];
471 }
472 }
473 // save start gain for next frame
474 stt->gain = gains[10];
475
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100476 return 0;
477}
478
479int32_t WebRtcAgc_ApplyDigitalGains(const int32_t gains[11], size_t num_bands,
480 uint32_t FS, const int16_t* const* in_near,
481 int16_t* const* out) {
minyuecac94aa2016-05-20 08:42:22 -0700482 // Apply gain
483 // handle first sub frame separately
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100484 size_t L;
485 int16_t L2; // samples/subframe
486
487 // determine number of samples per ms
488 if (FS == 8000) {
489 L = 8;
490 L2 = 3;
491 } else if (FS == 16000 || FS == 32000 || FS == 48000) {
492 L = 16;
493 L2 = 4;
494 } else {
495 return -1;
496 }
497
498 for (size_t i = 0; i < num_bands; ++i) {
499 if (in_near[i] != out[i]) {
500 // Only needed if they don't already point to the same place.
501 memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0]));
502 }
503 }
504
minyuecac94aa2016-05-20 08:42:22 -0700505 // iterate over samples
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100506 int32_t delta = (gains[1] - gains[0]) * (1 << (4 - L2));
507 int32_t gain32 = gains[0] * (1 << 4);
508 for (size_t n = 0; n < L; n++) {
509 for (size_t i = 0; i < num_bands; ++i) {
510 int32_t out_tmp = (int64_t)out[i][n] * ((gain32 + 127) >> 7) >> 16;
minyuecac94aa2016-05-20 08:42:22 -0700511 if (out_tmp > 4095) {
512 out[i][n] = (int16_t)32767;
513 } else if (out_tmp < -4096) {
514 out[i][n] = (int16_t)-32768;
515 } else {
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100516 int32_t tmp32 = ((int64_t)out[i][n] * (gain32 >> 4)) >> 16;
Sam Zackrisson46f858a2018-07-02 15:01:11 +0200517 out[i][n] = (int16_t)tmp32;
minyuecac94aa2016-05-20 08:42:22 -0700518 }
519 }
minyuecac94aa2016-05-20 08:42:22 -0700520
521 gain32 += delta;
522 }
523 // iterate over subframes
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100524 for (int k = 1; k < 10; k++) {
minyuecac94aa2016-05-20 08:42:22 -0700525 delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2));
526 gain32 = gains[k] * (1 << 4);
527 // iterate over samples
Per Ã…hgren77dc1992019-11-23 00:14:31 +0100528 for (size_t n = 0; n < L; n++) {
529 for (size_t i = 0; i < num_bands; ++i) {
peahfb2fa3f2017-09-13 06:28:16 -0700530 int64_t tmp64 = ((int64_t)(out[i][k * L + n])) * (gain32 >> 4);
531 tmp64 = tmp64 >> 16;
532 if (tmp64 > 32767) {
533 out[i][k * L + n] = 32767;
534 }
535 else if (tmp64 < -32768) {
536 out[i][k * L + n] = -32768;
537 }
538 else {
539 out[i][k * L + n] = (int16_t)(tmp64);
540 }
minyuecac94aa2016-05-20 08:42:22 -0700541 }
542 gain32 += delta;
543 }
544 }
minyuecac94aa2016-05-20 08:42:22 -0700545 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000546}
547
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000548void WebRtcAgc_InitVad(AgcVad* state) {
minyuecac94aa2016-05-20 08:42:22 -0700549 int16_t k;
niklase@google.com470e71d2011-07-07 08:21:25 +0000550
minyuecac94aa2016-05-20 08:42:22 -0700551 state->HPstate = 0; // state of high pass filter
552 state->logRatio = 0; // log( P(active) / P(inactive) )
553 // average input level (Q10)
554 state->meanLongTerm = 15 << 10;
niklase@google.com470e71d2011-07-07 08:21:25 +0000555
minyuecac94aa2016-05-20 08:42:22 -0700556 // variance of input level (Q8)
557 state->varianceLongTerm = 500 << 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000558
minyuecac94aa2016-05-20 08:42:22 -0700559 state->stdLongTerm = 0; // standard deviation of input level in dB
560 // short-term average input level (Q10)
561 state->meanShortTerm = 15 << 10;
niklase@google.com470e71d2011-07-07 08:21:25 +0000562
minyuecac94aa2016-05-20 08:42:22 -0700563 // short-term variance of input level (Q8)
564 state->varianceShortTerm = 500 << 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000565
minyuecac94aa2016-05-20 08:42:22 -0700566 state->stdShortTerm =
567 0; // short-term standard deviation of input level in dB
568 state->counter = 3; // counts updates
569 for (k = 0; k < 8; k++) {
570 // downsampling filter
571 state->downState[k] = 0;
572 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000573}
574
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000575int16_t WebRtcAgc_ProcessVad(AgcVad* state, // (i) VAD state
576 const int16_t* in, // (i) Speech signal
minyuecac94aa2016-05-20 08:42:22 -0700577 size_t nrSamples) // (i) number of samples
niklase@google.com470e71d2011-07-07 08:21:25 +0000578{
Alex Loikoc7b18fe2017-10-27 14:57:38 +0200579 uint32_t nrg;
580 int32_t out, tmp32, tmp32b;
minyuecac94aa2016-05-20 08:42:22 -0700581 uint16_t tmpU16;
582 int16_t k, subfr, tmp16;
583 int16_t buf1[8];
584 int16_t buf2[4];
585 int16_t HPstate;
586 int16_t zeros, dB;
Alex Loikoe714ed62018-07-19 13:08:23 +0200587 int64_t tmp64;
niklase@google.com470e71d2011-07-07 08:21:25 +0000588
minyuecac94aa2016-05-20 08:42:22 -0700589 // process in 10 sub frames of 1 ms (to save on memory)
590 nrg = 0;
591 HPstate = state->HPstate;
592 for (subfr = 0; subfr < 10; subfr++) {
593 // downsample to 4 kHz
594 if (nrSamples == 160) {
595 for (k = 0; k < 8; k++) {
596 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
597 tmp32 >>= 1;
598 buf1[k] = (int16_t)tmp32;
599 }
600 in += 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000601
minyuecac94aa2016-05-20 08:42:22 -0700602 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
603 } else {
604 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
605 in += 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000606 }
607
minyuecac94aa2016-05-20 08:42:22 -0700608 // high pass filter and compute energy
609 for (k = 0; k < 4; k++) {
610 out = buf2[k] + HPstate;
611 tmp32 = 600 * out;
612 HPstate = (int16_t)((tmp32 >> 10) - buf2[k]);
Alex Loiko7cfbf3a2017-11-07 16:34:32 +0100613
614 // Add 'out * out / 2**6' to 'nrg' in a non-overflowing
615 // way. Guaranteed to work as long as 'out * out / 2**6' fits in
616 // an int32_t.
617 nrg += out * (out / (1 << 6));
618 nrg += out * (out % (1 << 6)) / (1 << 6);
niklase@google.com470e71d2011-07-07 08:21:25 +0000619 }
minyuecac94aa2016-05-20 08:42:22 -0700620 }
621 state->HPstate = HPstate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000622
minyuecac94aa2016-05-20 08:42:22 -0700623 // find number of leading zeros
624 if (!(0xFFFF0000 & nrg)) {
625 zeros = 16;
626 } else {
627 zeros = 0;
628 }
629 if (!(0xFF000000 & (nrg << zeros))) {
630 zeros += 8;
631 }
632 if (!(0xF0000000 & (nrg << zeros))) {
633 zeros += 4;
634 }
635 if (!(0xC0000000 & (nrg << zeros))) {
636 zeros += 2;
637 }
638 if (!(0x80000000 & (nrg << zeros))) {
639 zeros += 1;
640 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000641
minyuecac94aa2016-05-20 08:42:22 -0700642 // energy level (range {-32..30}) (Q10)
Alex Loikob9f53612017-10-24 09:58:00 +0200643 dB = (15 - zeros) * (1 << 11);
niklase@google.com470e71d2011-07-07 08:21:25 +0000644
minyuecac94aa2016-05-20 08:42:22 -0700645 // Update statistics
niklase@google.com470e71d2011-07-07 08:21:25 +0000646
minyuecac94aa2016-05-20 08:42:22 -0700647 if (state->counter < kAvgDecayTime) {
648 // decay time = AvgDecTime * 10 ms
649 state->counter++;
650 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000651
minyuecac94aa2016-05-20 08:42:22 -0700652 // update short-term estimate of mean energy level (Q10)
653 tmp32 = state->meanShortTerm * 15 + dB;
654 state->meanShortTerm = (int16_t)(tmp32 >> 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000655
minyuecac94aa2016-05-20 08:42:22 -0700656 // update short-term estimate of variance in energy level (Q8)
657 tmp32 = (dB * dB) >> 12;
658 tmp32 += state->varianceShortTerm * 15;
659 state->varianceShortTerm = tmp32 / 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000660
minyuecac94aa2016-05-20 08:42:22 -0700661 // update short-term estimate of standard deviation in energy level (Q10)
662 tmp32 = state->meanShortTerm * state->meanShortTerm;
663 tmp32 = (state->varianceShortTerm << 12) - tmp32;
664 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000665
minyuecac94aa2016-05-20 08:42:22 -0700666 // update long-term estimate of mean energy level (Q10)
667 tmp32 = state->meanLongTerm * state->counter + dB;
668 state->meanLongTerm =
669 WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000670
minyuecac94aa2016-05-20 08:42:22 -0700671 // update long-term estimate of variance in energy level (Q8)
672 tmp32 = (dB * dB) >> 12;
673 tmp32 += state->varianceLongTerm * state->counter;
674 state->varianceLongTerm =
675 WebRtcSpl_DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000676
minyuecac94aa2016-05-20 08:42:22 -0700677 // update long-term estimate of standard deviation in energy level (Q10)
678 tmp32 = state->meanLongTerm * state->meanLongTerm;
679 tmp32 = (state->varianceLongTerm << 12) - tmp32;
680 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
681
682 // update voice activity measure (Q10)
683 tmp16 = 3 << 12;
684 // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in
685 // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16()
686 // was used, which did an intermediate cast to (int16_t), hence losing
687 // significant bits. This cause logRatio to max out positive, rather than
688 // negative. This is a bug, but has very little significance.
689 tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm);
690 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
691 tmpU16 = (13 << 12);
692 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
Alex Loikoe714ed62018-07-19 13:08:23 +0200693 tmp64 = tmp32;
694 tmp64 += tmp32b >> 10;
695 tmp64 >>= 6;
minyuecac94aa2016-05-20 08:42:22 -0700696
697 // limit
Alex Loikoe714ed62018-07-19 13:08:23 +0200698 if (tmp64 > 2048) {
699 tmp64 = 2048;
700 } else if (tmp64 < -2048) {
701 tmp64 = -2048;
minyuecac94aa2016-05-20 08:42:22 -0700702 }
Alex Loikoe714ed62018-07-19 13:08:23 +0200703 state->logRatio = (int16_t)tmp64;
minyuecac94aa2016-05-20 08:42:22 -0700704
705 return state->logRatio; // Q10
niklase@google.com470e71d2011-07-07 08:21:25 +0000706}