blob: f3c1cf74fd25a6bd72a71547a57db427bfbb0deb [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>
bjornv@webrtc.orgea297872014-09-23 11:21:39 +000018#ifdef WEBRTC_AGC_DEBUG_DUMP
niklase@google.com470e71d2011-07-07 08:21:25 +000019#include <stdio.h>
20#endif
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000021
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/checks.h"
23#include "modules/audio_processing/agc/legacy/gain_control.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000024
25// To generate the gaintable, copy&paste the following lines to a Matlab window:
26// MaxGain = 6; MinGain = 0; CompRatio = 3; Knee = 1;
27// zeros = 0:31; lvl = 2.^(1-zeros);
28// A = -10*log10(lvl) * (CompRatio - 1) / CompRatio;
29// B = MaxGain - MinGain;
minyuecac94aa2016-05-20 08:42:22 -070030// gains = round(2^16*10.^(0.05 * (MinGain + B * (
31// log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) /
32// log(1/(1+exp(Knee*B))))));
niklase@google.com470e71d2011-07-07 08:21:25 +000033// fprintf(1, '\t%i, %i, %i, %i,\n', gains);
minyuecac94aa2016-05-20 08:42:22 -070034// % Matlab code for plotting the gain and input/output level characteristic
35// (copy/paste the following 3 lines):
niklase@google.com470e71d2011-07-07 08:21:25 +000036// in = 10*log10(lvl); out = 20*log10(gains/65536);
minyuecac94aa2016-05-20 08:42:22 -070037// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input
38// (dB)'); ylabel('Gain (dB)');
39// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on;
40// xlabel('Input (dB)'); ylabel('Output (dB)');
niklase@google.com470e71d2011-07-07 08:21:25 +000041// zoom on;
42
43// Generator table for y=log2(1+e^x) in Q8.
andrew@webrtc.orgd77a6612012-01-04 16:22:24 +000044enum { kGenFuncTableSize = 128 };
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000045static const uint16_t kGenFuncTable[kGenFuncTableSize] = {
minyuecac94aa2016-05-20 08:42:22 -070046 256, 485, 786, 1126, 1484, 1849, 2217, 2586, 2955, 3324, 3693,
47 4063, 4432, 4801, 5171, 5540, 5909, 6279, 6648, 7017, 7387, 7756,
48 8125, 8495, 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449, 11819,
49 12188, 12557, 12927, 13296, 13665, 14035, 14404, 14773, 15143, 15512, 15881,
50 16251, 16620, 16989, 17359, 17728, 18097, 18466, 18836, 19205, 19574, 19944,
51 20313, 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268, 23637, 24006,
52 24376, 24745, 25114, 25484, 25853, 26222, 26592, 26961, 27330, 27700, 28069,
53 28438, 28808, 29177, 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132,
54 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086, 35456, 35825, 36194,
55 36564, 36933, 37302, 37672, 38041, 38410, 38780, 39149, 39518, 39888, 40257,
56 40626, 40996, 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950, 44320,
57 44689, 45058, 45428, 45797, 46166, 46536, 46905};
niklase@google.com470e71d2011-07-07 08:21:25 +000058
minyuecac94aa2016-05-20 08:42:22 -070059static const int16_t kAvgDecayTime = 250; // frames; < 3000
niklase@google.com470e71d2011-07-07 08:21:25 +000060
minyuecac94aa2016-05-20 08:42:22 -070061int32_t WebRtcAgc_CalculateGainTable(int32_t* gainTable, // Q16
62 int16_t digCompGaindB, // Q0
63 int16_t targetLevelDbfs, // Q0
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000064 uint8_t limiterEnable,
minyuecac94aa2016-05-20 08:42:22 -070065 int16_t analogTarget) // Q0
niklase@google.com470e71d2011-07-07 08:21:25 +000066{
minyuecac94aa2016-05-20 08:42:22 -070067 // This function generates the compressor gain table used in the fixed digital
68 // part.
69 uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox;
70 int32_t inLevel, limiterLvl;
71 int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32;
72 const uint16_t kLog10 = 54426; // log2(10) in Q14
73 const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14
74 const uint16_t kLogE_1 = 23637; // log2(e) in Q14
75 uint16_t constMaxGain;
76 uint16_t tmpU16, intPart, fracPart;
77 const int16_t kCompRatio = 3;
78 const int16_t kSoftLimiterLeft = 1;
79 int16_t limiterOffset = 0; // Limiter offset
80 int16_t limiterIdx, limiterLvlX;
81 int16_t constLinApprox, zeroGainLvl, maxGain, diffGain;
82 int16_t i, tmp16, tmp16no1;
83 int zeros, zerosScale;
niklase@google.com470e71d2011-07-07 08:21:25 +000084
minyuecac94aa2016-05-20 08:42:22 -070085 // Constants
86 // kLogE_1 = 23637; // log2(e) in Q14
87 // kLog10 = 54426; // log2(10) in Q14
88 // kLog10_2 = 49321; // 10*log10(2) in Q14
niklase@google.com470e71d2011-07-07 08:21:25 +000089
minyuecac94aa2016-05-20 08:42:22 -070090 // Calculate maximum digital gain and zero gain level
91 tmp32no1 = (digCompGaindB - analogTarget) * (kCompRatio - 1);
92 tmp16no1 = analogTarget - targetLevelDbfs;
93 tmp16no1 +=
94 WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
95 maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs));
96 tmp32no1 = maxGain * kCompRatio;
97 zeroGainLvl = digCompGaindB;
98 zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1),
99 kCompRatio - 1);
100 if ((digCompGaindB <= analogTarget) && (limiterEnable)) {
101 zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft);
102 limiterOffset = 0;
103 }
104
105 // Calculate the difference between maximum gain and gain at 0dB0v:
106 // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio
107 // = (compRatio-1)*digCompGaindB/compRatio
108 tmp32no1 = digCompGaindB * (kCompRatio - 1);
109 diffGain =
110 WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
111 if (diffGain < 0 || diffGain >= kGenFuncTableSize) {
kwiberg1e8ed4a2016-08-26 04:33:34 -0700112 RTC_DCHECK(0);
minyuecac94aa2016-05-20 08:42:22 -0700113 return -1;
114 }
115
116 // Calculate the limiter level and index:
117 // limiterLvlX = analogTarget - limiterOffset
118 // limiterLvl = targetLevelDbfs + limiterOffset/compRatio
119 limiterLvlX = analogTarget - limiterOffset;
120 limiterIdx = 2 + WebRtcSpl_DivW32W16ResW16((int32_t)limiterLvlX * (1 << 13),
121 kLog10_2 / 2);
122 tmp16no1 =
123 WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio);
124 limiterLvl = targetLevelDbfs + tmp16no1;
125
126 // Calculate (through table lookup):
127 // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8)
128 constMaxGain = kGenFuncTable[diffGain]; // in Q8
129
130 // Calculate a parameter used to approximate the fractional part of 2^x with a
131 // piecewise linear function in Q14:
132 // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14);
133 constLinApprox = 22817; // in Q14
134
135 // Calculate a denominator used in the exponential part to convert from dB to
136 // linear scale:
137 // den = 20*constMaxGain (in Q8)
138 den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8
139
140 for (i = 0; i < 32; i++) {
141 // Calculate scaled input level (compressor):
142 // inLevel =
143 // fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio)
144 tmp16 = (int16_t)((kCompRatio - 1) * (i - 1)); // Q0
145 tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14
146 inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14
147
148 // Calculate diffGain-inLevel, to map using the genFuncTable
149 inLevel = (int32_t)diffGain * (1 << 14) - inLevel; // Q14
150
151 // Make calculations on abs(inLevel) and compensate for the sign afterwards.
152 absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14
153
154 // LUT with interpolation
155 intPart = (uint16_t)(absInLevel >> 14);
156 fracPart =
157 (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part
158 tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8
159 tmpU32no1 = tmpU16 * fracPart; // Q22
160 tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22
161 logApprox = tmpU32no1 >> 8; // Q14
162 // Compensate for negative exponent using the relation:
163 // log2(1 + 2^-x) = log2(1 + 2^x) - x
164 if (inLevel < 0) {
165 zeros = WebRtcSpl_NormU32(absInLevel);
166 zerosScale = 0;
167 if (zeros < 15) {
168 // Not enough space for multiplication
169 tmpU32no2 = absInLevel >> (15 - zeros); // Q(zeros-1)
170 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13)
171 if (zeros < 9) {
172 zerosScale = 9 - zeros;
173 tmpU32no1 >>= zerosScale; // Q(zeros+13)
174 } else {
175 tmpU32no2 >>= zeros - 9; // Q22
176 }
177 } else {
178 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28
179 tmpU32no2 >>= 6; // Q22
180 }
181 logApprox = 0;
182 if (tmpU32no2 < tmpU32no1) {
183 logApprox = (tmpU32no1 - tmpU32no2) >> (8 - zerosScale); // Q14
184 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000185 }
minyuecac94aa2016-05-20 08:42:22 -0700186 numFIX = (maxGain * constMaxGain) * (1 << 6); // Q14
187 numFIX -= (int32_t)logApprox * diffGain; // Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000188
minyuecac94aa2016-05-20 08:42:22 -0700189 // Calculate ratio
190 // Shift |numFIX| as much as possible.
191 // Ensure we avoid wrap-around in |den| as well.
minyuefd634c42016-06-17 04:36:10 -0700192 if (numFIX > (den >> 8) || -numFIX > (den >> 8)) // |den| is Q8.
niklase@google.com470e71d2011-07-07 08:21:25 +0000193 {
minyuecac94aa2016-05-20 08:42:22 -0700194 zeros = WebRtcSpl_NormW32(numFIX);
195 } else {
196 zeros = WebRtcSpl_NormW32(den) + 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000197 }
minyuecac94aa2016-05-20 08:42:22 -0700198 numFIX *= 1 << zeros; // Q(14+zeros)
niklase@google.com470e71d2011-07-07 08:21:25 +0000199
minyuecac94aa2016-05-20 08:42:22 -0700200 // Shift den so we end up in Qy1
minyuefd634c42016-06-17 04:36:10 -0700201 tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 9); // Q(zeros - 1)
202 y32 = numFIX / tmp32no1; // in Q15
203 // This is to do rounding in Q14.
204 y32 = y32 >= 0 ? (y32 + 1) >> 1 : -((-y32 + 1) >> 1);
205
minyuecac94aa2016-05-20 08:42:22 -0700206 if (limiterEnable && (i < limiterIdx)) {
207 tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14
208 tmp32 -= limiterLvl * (1 << 14); // Q14
209 y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20);
210 }
211 if (y32 > 39000) {
212 tmp32 = (y32 >> 1) * kLog10 + 4096; // in Q27
213 tmp32 >>= 13; // In Q14.
214 } else {
215 tmp32 = y32 * kLog10 + 8192; // in Q28
216 tmp32 >>= 14; // In Q14.
217 }
218 tmp32 += 16 << 14; // in Q14 (Make sure final output is in Q16)
niklase@google.com470e71d2011-07-07 08:21:25 +0000219
minyuecac94aa2016-05-20 08:42:22 -0700220 // Calculate power
221 if (tmp32 > 0) {
222 intPart = (int16_t)(tmp32 >> 14);
223 fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14
224 if ((fracPart >> 13) != 0) {
225 tmp16 = (2 << 14) - constLinApprox;
226 tmp32no2 = (1 << 14) - fracPart;
227 tmp32no2 *= tmp16;
228 tmp32no2 >>= 13;
229 tmp32no2 = (1 << 14) - tmp32no2;
230 } else {
231 tmp16 = constLinApprox - (1 << 14);
232 tmp32no2 = (fracPart * tmp16) >> 13;
233 }
234 fracPart = (uint16_t)tmp32no2;
235 gainTable[i] =
236 (1 << intPart) + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14);
237 } else {
238 gainTable[i] = 0;
239 }
240 }
241
242 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000243}
244
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000245int32_t WebRtcAgc_InitDigital(DigitalAgc* stt, int16_t agcMode) {
minyuecac94aa2016-05-20 08:42:22 -0700246 if (agcMode == kAgcModeFixedDigital) {
247 // start at minimum to find correct gain faster
248 stt->capacitorSlow = 0;
249 } else {
250 // start out with 0 dB gain
251 stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f);
252 }
253 stt->capacitorFast = 0;
254 stt->gain = 65536;
255 stt->gatePrevious = 0;
256 stt->agcMode = agcMode;
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000257#ifdef WEBRTC_AGC_DEBUG_DUMP
minyuecac94aa2016-05-20 08:42:22 -0700258 stt->frameCounter = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000259#endif
260
minyuecac94aa2016-05-20 08:42:22 -0700261 // initialize VADs
262 WebRtcAgc_InitVad(&stt->vadNearend);
263 WebRtcAgc_InitVad(&stt->vadFarend);
niklase@google.com470e71d2011-07-07 08:21:25 +0000264
minyuecac94aa2016-05-20 08:42:22 -0700265 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000266}
267
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000268int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc* stt,
269 const int16_t* in_far,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700270 size_t nrSamples) {
kwiberg1e8ed4a2016-08-26 04:33:34 -0700271 RTC_DCHECK(stt);
minyuecac94aa2016-05-20 08:42:22 -0700272 // VAD for far end
273 WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
niklase@google.com470e71d2011-07-07 08:21:25 +0000274
minyuecac94aa2016-05-20 08:42:22 -0700275 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000276}
277
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000278int32_t WebRtcAgc_ProcessDigital(DigitalAgc* stt,
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000279 const int16_t* const* in_near,
Peter Kastingdce40cf2015-08-24 14:52:23 -0700280 size_t num_bands,
aluebs@webrtc.orgcf6d0b62014-12-16 20:56:09 +0000281 int16_t* const* out,
282 uint32_t FS,
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000283 int16_t lowlevelSignal) {
minyuecac94aa2016-05-20 08:42:22 -0700284 // array for gains (one value per ms, incl start & end)
285 int32_t gains[11];
niklase@google.com470e71d2011-07-07 08:21:25 +0000286
minyuecac94aa2016-05-20 08:42:22 -0700287 int32_t out_tmp, tmp32;
288 int32_t env[10];
289 int32_t max_nrg;
290 int32_t cur_level;
291 int32_t gain32, delta;
292 int16_t logratio;
293 int16_t lower_thr, upper_thr;
294 int16_t zeros = 0, zeros_fast, frac = 0;
295 int16_t decay;
296 int16_t gate, gain_adj;
297 int16_t k;
298 size_t n, i, L;
299 int16_t L2; // samples/subframe
niklase@google.com470e71d2011-07-07 08:21:25 +0000300
minyuecac94aa2016-05-20 08:42:22 -0700301 // determine number of samples per ms
302 if (FS == 8000) {
303 L = 8;
304 L2 = 3;
305 } else if (FS == 16000 || FS == 32000 || FS == 48000) {
306 L = 16;
307 L2 = 4;
308 } else {
309 return -1;
310 }
311
312 for (i = 0; i < num_bands; ++i) {
313 if (in_near[i] != out[i]) {
314 // Only needed if they don't already point to the same place.
315 memcpy(out[i], in_near[i], 10 * L * sizeof(in_near[i][0]));
316 }
317 }
318 // VAD for near end
319 logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out[0], L * 10);
320
321 // Account for far end VAD
322 if (stt->vadFarend.counter > 10) {
323 tmp32 = 3 * logratio;
324 logratio = (int16_t)((tmp32 - stt->vadFarend.logRatio) >> 2);
325 }
326
327 // Determine decay factor depending on VAD
328 // upper_thr = 1.0f;
329 // lower_thr = 0.25f;
330 upper_thr = 1024; // Q10
331 lower_thr = 0; // Q10
332 if (logratio > upper_thr) {
333 // decay = -2^17 / DecayTime; -> -65
334 decay = -65;
335 } else if (logratio < lower_thr) {
336 decay = 0;
337 } else {
338 // decay = (int16_t)(((lower_thr - logratio)
339 // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10);
340 // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65
341 tmp32 = (lower_thr - logratio) * 65;
342 decay = (int16_t)(tmp32 >> 10);
343 }
344
345 // adjust decay factor for long silence (detected as low standard deviation)
346 // This is only done in the adaptive modes
347 if (stt->agcMode != kAgcModeFixedDigital) {
348 if (stt->vadNearend.stdLongTerm < 4000) {
349 decay = 0;
350 } else if (stt->vadNearend.stdLongTerm < 8096) {
351 // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >>
352 // 12);
353 tmp32 = (stt->vadNearend.stdLongTerm - 4000) * decay;
354 decay = (int16_t)(tmp32 >> 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000355 }
356
minyuecac94aa2016-05-20 08:42:22 -0700357 if (lowlevelSignal != 0) {
358 decay = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000359 }
minyuecac94aa2016-05-20 08:42:22 -0700360 }
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000361#ifdef WEBRTC_AGC_DEBUG_DUMP
minyuecac94aa2016-05-20 08:42:22 -0700362 stt->frameCounter++;
363 fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100,
364 logratio, decay, stt->vadNearend.stdLongTerm);
niklase@google.com470e71d2011-07-07 08:21:25 +0000365#endif
minyuecac94aa2016-05-20 08:42:22 -0700366 // Find max amplitude per sub frame
367 // iterate over sub frames
368 for (k = 0; k < 10; k++) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000369 // iterate over samples
minyuecac94aa2016-05-20 08:42:22 -0700370 max_nrg = 0;
371 for (n = 0; n < L; n++) {
372 int32_t nrg = out[0][k * L + n] * out[0][k * L + n];
373 if (nrg > max_nrg) {
374 max_nrg = nrg;
375 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000376 }
minyuecac94aa2016-05-20 08:42:22 -0700377 env[k] = max_nrg;
378 }
379
380 // Calculate gain per sub frame
381 gains[0] = stt->gain;
382 for (k = 0; k < 10; k++) {
383 // Fast envelope follower
384 // decay time = -131000 / -1000 = 131 (ms)
385 stt->capacitorFast =
386 AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
387 if (env[k] > stt->capacitorFast) {
388 stt->capacitorFast = env[k];
389 }
390 // Slow envelope follower
391 if (env[k] > stt->capacitorSlow) {
392 // increase capacitorSlow
393 stt->capacitorSlow = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow),
394 stt->capacitorSlow);
395 } else {
396 // decrease capacitorSlow
397 stt->capacitorSlow =
398 AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
niklase@google.com470e71d2011-07-07 08:21:25 +0000399 }
400
minyuecac94aa2016-05-20 08:42:22 -0700401 // use maximum of both capacitors as current level
402 if (stt->capacitorFast > stt->capacitorSlow) {
403 cur_level = stt->capacitorFast;
404 } else {
405 cur_level = stt->capacitorSlow;
406 }
407 // Translate signal level into gain, using a piecewise linear approximation
408 // find number of leading zeros
409 zeros = WebRtcSpl_NormU32((uint32_t)cur_level);
410 if (cur_level == 0) {
411 zeros = 31;
412 }
Alex Loikoc531af72017-10-24 10:41:48 +0200413 tmp32 = ((uint32_t)cur_level << zeros) & 0x7FFFFFFF;
minyuecac94aa2016-05-20 08:42:22 -0700414 frac = (int16_t)(tmp32 >> 19); // Q12.
Sam Zackrisson762289e2018-06-26 11:21:22 +0200415 // Interpolate between gainTable[zeros] and gainTable[zeros-1].
416 tmp32 = ((stt->gainTable[zeros - 1] - stt->gainTable[zeros]) *
417 (int64_t)frac) >> 12;
418 gains[k + 1] = stt->gainTable[zeros] + tmp32;
minyuecac94aa2016-05-20 08:42:22 -0700419#ifdef WEBRTC_AGC_DEBUG_DUMP
420 if (k == 0) {
421 fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level,
422 stt->capacitorFast, stt->capacitorSlow, zeros);
423 }
424#endif
425 }
426
427 // Gate processing (lower gain during absence of speech)
428 zeros = (zeros << 9) - (frac >> 3);
429 // find number of leading zeros
430 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
431 if (stt->capacitorFast == 0) {
432 zeros_fast = 31;
433 }
Alex Loikoc531af72017-10-24 10:41:48 +0200434 tmp32 = ((uint32_t)stt->capacitorFast << zeros_fast) & 0x7FFFFFFF;
minyuecac94aa2016-05-20 08:42:22 -0700435 zeros_fast <<= 9;
436 zeros_fast -= (int16_t)(tmp32 >> 22);
437
438 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
439
440 if (gate < 0) {
441 stt->gatePrevious = 0;
442 } else {
443 tmp32 = stt->gatePrevious * 7;
444 gate = (int16_t)((gate + tmp32) >> 3);
445 stt->gatePrevious = gate;
446 }
447 // gate < 0 -> no gate
448 // gate > 2500 -> max gate
449 if (gate > 0) {
450 if (gate < 2500) {
451 gain_adj = (2500 - gate) >> 5;
452 } else {
453 gain_adj = 0;
454 }
455 for (k = 0; k < 10; k++) {
456 if ((gains[k + 1] - stt->gainTable[0]) > 8388608) {
457 // To prevent wraparound
458 tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8;
459 tmp32 *= 178 + gain_adj;
460 } else {
461 tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj);
462 tmp32 >>= 8;
463 }
464 gains[k + 1] = stt->gainTable[0] + tmp32;
465 }
466 }
467
468 // Limit gain to avoid overload distortion
469 for (k = 0; k < 10; k++) {
470 // To prevent wrap around
471 zeros = 10;
472 if (gains[k + 1] > 47453132) {
473 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
474 }
475 gain32 = (gains[k + 1] >> zeros) + 1;
476 gain32 *= gain32;
477 // check for overflow
478 while (AGC_MUL32((env[k] >> 12) + 1, gain32) >
479 WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) {
480 // multiply by 253/256 ==> -0.1 dB
481 if (gains[k + 1] > 8388607) {
482 // Prevent wrap around
483 gains[k + 1] = (gains[k + 1] / 256) * 253;
484 } else {
485 gains[k + 1] = (gains[k + 1] * 253) / 256;
486 }
487 gain32 = (gains[k + 1] >> zeros) + 1;
488 gain32 *= gain32;
489 }
490 }
491 // gain reductions should be done 1 ms earlier than gain increases
492 for (k = 1; k < 10; k++) {
493 if (gains[k] > gains[k + 1]) {
494 gains[k] = gains[k + 1];
495 }
496 }
497 // save start gain for next frame
498 stt->gain = gains[10];
499
500 // Apply gain
501 // handle first sub frame separately
502 delta = (gains[1] - gains[0]) * (1 << (4 - L2));
503 gain32 = gains[0] * (1 << 4);
504 // iterate over samples
505 for (n = 0; n < L; n++) {
506 for (i = 0; i < num_bands; ++i) {
Sam Zackrisson46f858a2018-07-02 15:01:11 +0200507 out_tmp = (int64_t)out[i][n] * ((gain32 + 127) >> 7) >> 16;
minyuecac94aa2016-05-20 08:42:22 -0700508 if (out_tmp > 4095) {
509 out[i][n] = (int16_t)32767;
510 } else if (out_tmp < -4096) {
511 out[i][n] = (int16_t)-32768;
512 } else {
Sam Zackrisson46f858a2018-07-02 15:01:11 +0200513 tmp32 = ((int64_t)out[i][n] * (gain32 >> 4)) >> 16;
514 out[i][n] = (int16_t)tmp32;
minyuecac94aa2016-05-20 08:42:22 -0700515 }
516 }
minyuecac94aa2016-05-20 08:42:22 -0700517
518 gain32 += delta;
519 }
520 // iterate over subframes
521 for (k = 1; k < 10; k++) {
522 delta = (gains[k + 1] - gains[k]) * (1 << (4 - L2));
523 gain32 = gains[k] * (1 << 4);
524 // iterate over samples
525 for (n = 0; n < L; n++) {
526 for (i = 0; i < num_bands; ++i) {
peahfb2fa3f2017-09-13 06:28:16 -0700527 int64_t tmp64 = ((int64_t)(out[i][k * L + n])) * (gain32 >> 4);
528 tmp64 = tmp64 >> 16;
529 if (tmp64 > 32767) {
530 out[i][k * L + n] = 32767;
531 }
532 else if (tmp64 < -32768) {
533 out[i][k * L + n] = -32768;
534 }
535 else {
536 out[i][k * L + n] = (int16_t)(tmp64);
537 }
minyuecac94aa2016-05-20 08:42:22 -0700538 }
539 gain32 += delta;
540 }
541 }
542
543 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000544}
545
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000546void WebRtcAgc_InitVad(AgcVad* state) {
minyuecac94aa2016-05-20 08:42:22 -0700547 int16_t k;
niklase@google.com470e71d2011-07-07 08:21:25 +0000548
minyuecac94aa2016-05-20 08:42:22 -0700549 state->HPstate = 0; // state of high pass filter
550 state->logRatio = 0; // log( P(active) / P(inactive) )
551 // average input level (Q10)
552 state->meanLongTerm = 15 << 10;
niklase@google.com470e71d2011-07-07 08:21:25 +0000553
minyuecac94aa2016-05-20 08:42:22 -0700554 // variance of input level (Q8)
555 state->varianceLongTerm = 500 << 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000556
minyuecac94aa2016-05-20 08:42:22 -0700557 state->stdLongTerm = 0; // standard deviation of input level in dB
558 // short-term average input level (Q10)
559 state->meanShortTerm = 15 << 10;
niklase@google.com470e71d2011-07-07 08:21:25 +0000560
minyuecac94aa2016-05-20 08:42:22 -0700561 // short-term variance of input level (Q8)
562 state->varianceShortTerm = 500 << 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000563
minyuecac94aa2016-05-20 08:42:22 -0700564 state->stdShortTerm =
565 0; // short-term standard deviation of input level in dB
566 state->counter = 3; // counts updates
567 for (k = 0; k < 8; k++) {
568 // downsampling filter
569 state->downState[k] = 0;
570 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000571}
572
pbos@webrtc.orge468bc92014-12-18 09:11:33 +0000573int16_t WebRtcAgc_ProcessVad(AgcVad* state, // (i) VAD state
574 const int16_t* in, // (i) Speech signal
minyuecac94aa2016-05-20 08:42:22 -0700575 size_t nrSamples) // (i) number of samples
niklase@google.com470e71d2011-07-07 08:21:25 +0000576{
Alex Loikoc7b18fe2017-10-27 14:57:38 +0200577 uint32_t nrg;
578 int32_t out, tmp32, tmp32b;
minyuecac94aa2016-05-20 08:42:22 -0700579 uint16_t tmpU16;
580 int16_t k, subfr, tmp16;
581 int16_t buf1[8];
582 int16_t buf2[4];
583 int16_t HPstate;
584 int16_t zeros, dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000585
minyuecac94aa2016-05-20 08:42:22 -0700586 // process in 10 sub frames of 1 ms (to save on memory)
587 nrg = 0;
588 HPstate = state->HPstate;
589 for (subfr = 0; subfr < 10; subfr++) {
590 // downsample to 4 kHz
591 if (nrSamples == 160) {
592 for (k = 0; k < 8; k++) {
593 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
594 tmp32 >>= 1;
595 buf1[k] = (int16_t)tmp32;
596 }
597 in += 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000598
minyuecac94aa2016-05-20 08:42:22 -0700599 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
600 } else {
601 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
602 in += 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000603 }
604
minyuecac94aa2016-05-20 08:42:22 -0700605 // high pass filter and compute energy
606 for (k = 0; k < 4; k++) {
607 out = buf2[k] + HPstate;
608 tmp32 = 600 * out;
609 HPstate = (int16_t)((tmp32 >> 10) - buf2[k]);
Alex Loiko7cfbf3a2017-11-07 16:34:32 +0100610
611 // Add 'out * out / 2**6' to 'nrg' in a non-overflowing
612 // way. Guaranteed to work as long as 'out * out / 2**6' fits in
613 // an int32_t.
614 nrg += out * (out / (1 << 6));
615 nrg += out * (out % (1 << 6)) / (1 << 6);
niklase@google.com470e71d2011-07-07 08:21:25 +0000616 }
minyuecac94aa2016-05-20 08:42:22 -0700617 }
618 state->HPstate = HPstate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000619
minyuecac94aa2016-05-20 08:42:22 -0700620 // find number of leading zeros
621 if (!(0xFFFF0000 & nrg)) {
622 zeros = 16;
623 } else {
624 zeros = 0;
625 }
626 if (!(0xFF000000 & (nrg << zeros))) {
627 zeros += 8;
628 }
629 if (!(0xF0000000 & (nrg << zeros))) {
630 zeros += 4;
631 }
632 if (!(0xC0000000 & (nrg << zeros))) {
633 zeros += 2;
634 }
635 if (!(0x80000000 & (nrg << zeros))) {
636 zeros += 1;
637 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000638
minyuecac94aa2016-05-20 08:42:22 -0700639 // energy level (range {-32..30}) (Q10)
Alex Loikob9f53612017-10-24 09:58:00 +0200640 dB = (15 - zeros) * (1 << 11);
niklase@google.com470e71d2011-07-07 08:21:25 +0000641
minyuecac94aa2016-05-20 08:42:22 -0700642 // Update statistics
niklase@google.com470e71d2011-07-07 08:21:25 +0000643
minyuecac94aa2016-05-20 08:42:22 -0700644 if (state->counter < kAvgDecayTime) {
645 // decay time = AvgDecTime * 10 ms
646 state->counter++;
647 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000648
minyuecac94aa2016-05-20 08:42:22 -0700649 // update short-term estimate of mean energy level (Q10)
650 tmp32 = state->meanShortTerm * 15 + dB;
651 state->meanShortTerm = (int16_t)(tmp32 >> 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000652
minyuecac94aa2016-05-20 08:42:22 -0700653 // update short-term estimate of variance in energy level (Q8)
654 tmp32 = (dB * dB) >> 12;
655 tmp32 += state->varianceShortTerm * 15;
656 state->varianceShortTerm = tmp32 / 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000657
minyuecac94aa2016-05-20 08:42:22 -0700658 // update short-term estimate of standard deviation in energy level (Q10)
659 tmp32 = state->meanShortTerm * state->meanShortTerm;
660 tmp32 = (state->varianceShortTerm << 12) - tmp32;
661 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000662
minyuecac94aa2016-05-20 08:42:22 -0700663 // update long-term estimate of mean energy level (Q10)
664 tmp32 = state->meanLongTerm * state->counter + dB;
665 state->meanLongTerm =
666 WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000667
minyuecac94aa2016-05-20 08:42:22 -0700668 // update long-term estimate of variance in energy level (Q8)
669 tmp32 = (dB * dB) >> 12;
670 tmp32 += state->varianceLongTerm * state->counter;
671 state->varianceLongTerm =
672 WebRtcSpl_DivW32W16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000673
minyuecac94aa2016-05-20 08:42:22 -0700674 // update long-term estimate of standard deviation in energy level (Q10)
675 tmp32 = state->meanLongTerm * state->meanLongTerm;
676 tmp32 = (state->varianceLongTerm << 12) - tmp32;
677 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
678
679 // update voice activity measure (Q10)
680 tmp16 = 3 << 12;
681 // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in
682 // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16()
683 // was used, which did an intermediate cast to (int16_t), hence losing
684 // significant bits. This cause logRatio to max out positive, rather than
685 // negative. This is a bug, but has very little significance.
686 tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm);
687 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
688 tmpU16 = (13 << 12);
689 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
690 tmp32 += tmp32b >> 10;
691
692 state->logRatio = (int16_t)(tmp32 >> 6);
693
694 // limit
695 if (state->logRatio > 2048) {
696 state->logRatio = 2048;
697 }
698 if (state->logRatio < -2048) {
699 state->logRatio = -2048;
700 }
701
702 return state->logRatio; // Q10
niklase@google.com470e71d2011-07-07 08:21:25 +0000703}