blob: 7f59785bdafc92eb8ed73cee52025ae11a256914 [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
bjornv@webrtc.orgb395a5e2014-12-16 10:38:10 +000015#include "webrtc/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
ehmaldonadoeaaae9e2017-07-07 03:09:51 -070022#include "webrtc/rtc_base/checks.h"
bjornv@webrtc.orgb395a5e2014-12-16 10:38:10 +000023#include "webrtc/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 }
413 tmp32 = (cur_level << zeros) & 0x7FFFFFFF;
414 frac = (int16_t)(tmp32 >> 19); // Q12.
415 tmp32 = (stt->gainTable[zeros - 1] - stt->gainTable[zeros]) * frac;
416 gains[k + 1] = stt->gainTable[zeros] + (tmp32 >> 12);
417#ifdef WEBRTC_AGC_DEBUG_DUMP
418 if (k == 0) {
419 fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level,
420 stt->capacitorFast, stt->capacitorSlow, zeros);
421 }
422#endif
423 }
424
425 // Gate processing (lower gain during absence of speech)
426 zeros = (zeros << 9) - (frac >> 3);
427 // find number of leading zeros
428 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
429 if (stt->capacitorFast == 0) {
430 zeros_fast = 31;
431 }
432 tmp32 = (stt->capacitorFast << zeros_fast) & 0x7FFFFFFF;
433 zeros_fast <<= 9;
434 zeros_fast -= (int16_t)(tmp32 >> 22);
435
436 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
437
438 if (gate < 0) {
439 stt->gatePrevious = 0;
440 } else {
441 tmp32 = stt->gatePrevious * 7;
442 gate = (int16_t)((gate + tmp32) >> 3);
443 stt->gatePrevious = gate;
444 }
445 // gate < 0 -> no gate
446 // gate > 2500 -> max gate
447 if (gate > 0) {
448 if (gate < 2500) {
449 gain_adj = (2500 - gate) >> 5;
450 } else {
451 gain_adj = 0;
452 }
453 for (k = 0; k < 10; k++) {
454 if ((gains[k + 1] - stt->gainTable[0]) > 8388608) {
455 // To prevent wraparound
456 tmp32 = (gains[k + 1] - stt->gainTable[0]) >> 8;
457 tmp32 *= 178 + gain_adj;
458 } else {
459 tmp32 = (gains[k + 1] - stt->gainTable[0]) * (178 + gain_adj);
460 tmp32 >>= 8;
461 }
462 gains[k + 1] = stt->gainTable[0] + tmp32;
463 }
464 }
465
466 // Limit gain to avoid overload distortion
467 for (k = 0; k < 10; k++) {
468 // To prevent wrap around
469 zeros = 10;
470 if (gains[k + 1] > 47453132) {
471 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
472 }
473 gain32 = (gains[k + 1] >> zeros) + 1;
474 gain32 *= gain32;
475 // check for overflow
476 while (AGC_MUL32((env[k] >> 12) + 1, gain32) >
477 WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10))) {
478 // multiply by 253/256 ==> -0.1 dB
479 if (gains[k + 1] > 8388607) {
480 // Prevent wrap around
481 gains[k + 1] = (gains[k + 1] / 256) * 253;
482 } else {
483 gains[k + 1] = (gains[k + 1] * 253) / 256;
484 }
485 gain32 = (gains[k + 1] >> zeros) + 1;
486 gain32 *= gain32;
487 }
488 }
489 // gain reductions should be done 1 ms earlier than gain increases
490 for (k = 1; k < 10; k++) {
491 if (gains[k] > gains[k + 1]) {
492 gains[k] = gains[k + 1];
493 }
494 }
495 // save start gain for next frame
496 stt->gain = gains[10];
497
498 // Apply gain
499 // handle first sub frame separately
500 delta = (gains[1] - gains[0]) * (1 << (4 - L2));
501 gain32 = gains[0] * (1 << 4);
502 // iterate over samples
503 for (n = 0; n < L; n++) {
504 for (i = 0; i < num_bands; ++i) {
505 tmp32 = out[i][n] * ((gain32 + 127) >> 7);
506 out_tmp = tmp32 >> 16;
507 if (out_tmp > 4095) {
508 out[i][n] = (int16_t)32767;
509 } else if (out_tmp < -4096) {
510 out[i][n] = (int16_t)-32768;
511 } else {
512 tmp32 = out[i][n] * (gain32 >> 4);
513 out[i][n] = (int16_t)(tmp32 >> 16);
514 }
515 }
516 //
517
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{
minyuecac94aa2016-05-20 08:42:22 -0700577 int32_t out, nrg, tmp32, tmp32b;
578 uint16_t tmpU16;
579 int16_t k, subfr, tmp16;
580 int16_t buf1[8];
581 int16_t buf2[4];
582 int16_t HPstate;
583 int16_t zeros, dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000584
minyuecac94aa2016-05-20 08:42:22 -0700585 // process in 10 sub frames of 1 ms (to save on memory)
586 nrg = 0;
587 HPstate = state->HPstate;
588 for (subfr = 0; subfr < 10; subfr++) {
589 // downsample to 4 kHz
590 if (nrSamples == 160) {
591 for (k = 0; k < 8; k++) {
592 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
593 tmp32 >>= 1;
594 buf1[k] = (int16_t)tmp32;
595 }
596 in += 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000597
minyuecac94aa2016-05-20 08:42:22 -0700598 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
599 } else {
600 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
601 in += 8;
niklase@google.com470e71d2011-07-07 08:21:25 +0000602 }
603
minyuecac94aa2016-05-20 08:42:22 -0700604 // high pass filter and compute energy
605 for (k = 0; k < 4; k++) {
606 out = buf2[k] + HPstate;
607 tmp32 = 600 * out;
608 HPstate = (int16_t)((tmp32 >> 10) - buf2[k]);
609 nrg += (out * out) >> 6;
niklase@google.com470e71d2011-07-07 08:21:25 +0000610 }
minyuecac94aa2016-05-20 08:42:22 -0700611 }
612 state->HPstate = HPstate;
niklase@google.com470e71d2011-07-07 08:21:25 +0000613
minyuecac94aa2016-05-20 08:42:22 -0700614 // find number of leading zeros
615 if (!(0xFFFF0000 & nrg)) {
616 zeros = 16;
617 } else {
618 zeros = 0;
619 }
620 if (!(0xFF000000 & (nrg << zeros))) {
621 zeros += 8;
622 }
623 if (!(0xF0000000 & (nrg << zeros))) {
624 zeros += 4;
625 }
626 if (!(0xC0000000 & (nrg << zeros))) {
627 zeros += 2;
628 }
629 if (!(0x80000000 & (nrg << zeros))) {
630 zeros += 1;
631 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000632
minyuecac94aa2016-05-20 08:42:22 -0700633 // energy level (range {-32..30}) (Q10)
634 dB = (15 - zeros) << 11;
niklase@google.com470e71d2011-07-07 08:21:25 +0000635
minyuecac94aa2016-05-20 08:42:22 -0700636 // Update statistics
niklase@google.com470e71d2011-07-07 08:21:25 +0000637
minyuecac94aa2016-05-20 08:42:22 -0700638 if (state->counter < kAvgDecayTime) {
639 // decay time = AvgDecTime * 10 ms
640 state->counter++;
641 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000642
minyuecac94aa2016-05-20 08:42:22 -0700643 // update short-term estimate of mean energy level (Q10)
644 tmp32 = state->meanShortTerm * 15 + dB;
645 state->meanShortTerm = (int16_t)(tmp32 >> 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000646
minyuecac94aa2016-05-20 08:42:22 -0700647 // update short-term estimate of variance in energy level (Q8)
648 tmp32 = (dB * dB) >> 12;
649 tmp32 += state->varianceShortTerm * 15;
650 state->varianceShortTerm = tmp32 / 16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000651
minyuecac94aa2016-05-20 08:42:22 -0700652 // update short-term estimate of standard deviation in energy level (Q10)
653 tmp32 = state->meanShortTerm * state->meanShortTerm;
654 tmp32 = (state->varianceShortTerm << 12) - tmp32;
655 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000656
minyuecac94aa2016-05-20 08:42:22 -0700657 // update long-term estimate of mean energy level (Q10)
658 tmp32 = state->meanLongTerm * state->counter + dB;
659 state->meanLongTerm =
660 WebRtcSpl_DivW32W16ResW16(tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000661
minyuecac94aa2016-05-20 08:42:22 -0700662 // update long-term estimate of variance in energy level (Q8)
663 tmp32 = (dB * dB) >> 12;
664 tmp32 += state->varianceLongTerm * state->counter;
665 state->varianceLongTerm =
666 WebRtcSpl_DivW32W16(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 standard deviation in energy level (Q10)
669 tmp32 = state->meanLongTerm * state->meanLongTerm;
670 tmp32 = (state->varianceLongTerm << 12) - tmp32;
671 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
672
673 // update voice activity measure (Q10)
674 tmp16 = 3 << 12;
675 // TODO(bjornv): (dB - state->meanLongTerm) can overflow, e.g., in
676 // ApmTest.Process unit test. Previously the macro WEBRTC_SPL_MUL_16_16()
677 // was used, which did an intermediate cast to (int16_t), hence losing
678 // significant bits. This cause logRatio to max out positive, rather than
679 // negative. This is a bug, but has very little significance.
680 tmp32 = tmp16 * (int16_t)(dB - state->meanLongTerm);
681 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
682 tmpU16 = (13 << 12);
683 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
684 tmp32 += tmp32b >> 10;
685
686 state->logRatio = (int16_t)(tmp32 >> 6);
687
688 // limit
689 if (state->logRatio > 2048) {
690 state->logRatio = 2048;
691 }
692 if (state->logRatio < -2048) {
693 state->logRatio = -2048;
694 }
695
696 return state->logRatio; // Q10
niklase@google.com470e71d2011-07-07 08:21:25 +0000697}