blob: da087ca3c1766be40c7e6cc03018fc942bed0ff8 [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
pbos@webrtc.org7fad4b82013-05-28 08:11:59 +000015#include "webrtc/modules/audio_processing/agc/digital_agc.h"
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000016
17#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000018#include <string.h>
bjornv@webrtc.orgea297872014-09-23 11:21:39 +000019#ifdef WEBRTC_AGC_DEBUG_DUMP
niklase@google.com470e71d2011-07-07 08:21:25 +000020#include <stdio.h>
21#endif
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000022
pbos@webrtc.org7fad4b82013-05-28 08:11:59 +000023#include "webrtc/modules/audio_processing/agc/include/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;
30// gains = round(2^16*10.^(0.05 * (MinGain + B * ( log(exp(-Knee*A)+exp(-Knee*B)) - log(1+exp(-Knee*B)) ) / log(1/(1+exp(Knee*B))))));
31// fprintf(1, '\t%i, %i, %i, %i,\n', gains);
32// % Matlab code for plotting the gain and input/output level characteristic (copy/paste the following 3 lines):
33// in = 10*log10(lvl); out = 20*log10(gains/65536);
34// subplot(121); plot(in, out); axis([-30, 0, -5, 20]); grid on; xlabel('Input (dB)'); ylabel('Gain (dB)');
35// subplot(122); plot(in, in+out); axis([-30, 0, -30, 5]); grid on; xlabel('Input (dB)'); ylabel('Output (dB)');
36// zoom on;
37
38// Generator table for y=log2(1+e^x) in Q8.
andrew@webrtc.orgd77a6612012-01-04 16:22:24 +000039enum { kGenFuncTableSize = 128 };
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000040static const uint16_t kGenFuncTable[kGenFuncTableSize] = {
niklase@google.com470e71d2011-07-07 08:21:25 +000041 256, 485, 786, 1126, 1484, 1849, 2217, 2586,
42 2955, 3324, 3693, 4063, 4432, 4801, 5171, 5540,
43 5909, 6279, 6648, 7017, 7387, 7756, 8125, 8495,
44 8864, 9233, 9603, 9972, 10341, 10711, 11080, 11449,
45 11819, 12188, 12557, 12927, 13296, 13665, 14035, 14404,
46 14773, 15143, 15512, 15881, 16251, 16620, 16989, 17359,
47 17728, 18097, 18466, 18836, 19205, 19574, 19944, 20313,
48 20682, 21052, 21421, 21790, 22160, 22529, 22898, 23268,
49 23637, 24006, 24376, 24745, 25114, 25484, 25853, 26222,
50 26592, 26961, 27330, 27700, 28069, 28438, 28808, 29177,
51 29546, 29916, 30285, 30654, 31024, 31393, 31762, 32132,
52 32501, 32870, 33240, 33609, 33978, 34348, 34717, 35086,
53 35456, 35825, 36194, 36564, 36933, 37302, 37672, 38041,
54 38410, 38780, 39149, 39518, 39888, 40257, 40626, 40996,
55 41365, 41734, 42104, 42473, 42842, 43212, 43581, 43950,
56 44320, 44689, 45058, 45428, 45797, 46166, 46536, 46905
57};
58
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000059static const int16_t kAvgDecayTime = 250; // frames; < 3000
niklase@google.com470e71d2011-07-07 08:21:25 +000060
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000061int32_t WebRtcAgc_CalculateGainTable(int32_t *gainTable, // Q16
62 int16_t digCompGaindB, // Q0
63 int16_t targetLevelDbfs,// Q0
64 uint8_t limiterEnable,
65 int16_t analogTarget) // Q0
niklase@google.com470e71d2011-07-07 08:21:25 +000066{
67 // This function generates the compressor gain table used in the fixed digital part.
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +000068 uint32_t tmpU32no1, tmpU32no2, absInLevel, logApprox;
69 int32_t inLevel, limiterLvl;
70 int32_t tmp32, tmp32no1, tmp32no2, numFIX, den, y32;
71 const uint16_t kLog10 = 54426; // log2(10) in Q14
72 const uint16_t kLog10_2 = 49321; // 10*log10(2) in Q14
73 const uint16_t kLogE_1 = 23637; // log2(e) in Q14
74 uint16_t constMaxGain;
75 uint16_t tmpU16, intPart, fracPart;
76 const int16_t kCompRatio = 3;
77 const int16_t kSoftLimiterLeft = 1;
78 int16_t limiterOffset = 0; // Limiter offset
79 int16_t limiterIdx, limiterLvlX;
80 int16_t constLinApprox, zeroGainLvl, maxGain, diffGain;
81 int16_t i, tmp16, tmp16no1;
niklase@google.com470e71d2011-07-07 08:21:25 +000082 int zeros, zerosScale;
83
84 // Constants
85// kLogE_1 = 23637; // log2(e) in Q14
86// kLog10 = 54426; // log2(10) in Q14
87// kLog10_2 = 49321; // 10*log10(2) in Q14
88
89 // Calculate maximum digital gain and zero gain level
90 tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB - analogTarget, kCompRatio - 1);
91 tmp16no1 = analogTarget - targetLevelDbfs;
92 tmp16no1 += WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
93 maxGain = WEBRTC_SPL_MAX(tmp16no1, (analogTarget - targetLevelDbfs));
94 tmp32no1 = WEBRTC_SPL_MUL_16_16(maxGain, kCompRatio);
95 zeroGainLvl = digCompGaindB;
96 zeroGainLvl -= WebRtcSpl_DivW32W16ResW16(tmp32no1 + ((kCompRatio - 1) >> 1),
97 kCompRatio - 1);
98 if ((digCompGaindB <= analogTarget) && (limiterEnable))
99 {
100 zeroGainLvl += (analogTarget - digCompGaindB + kSoftLimiterLeft);
101 limiterOffset = 0;
102 }
103
104 // Calculate the difference between maximum gain and gain at 0dB0v:
105 // diffGain = maxGain + (compRatio-1)*zeroGainLvl/compRatio
106 // = (compRatio-1)*digCompGaindB/compRatio
107 tmp32no1 = WEBRTC_SPL_MUL_16_16(digCompGaindB, kCompRatio - 1);
108 diffGain = WebRtcSpl_DivW32W16ResW16(tmp32no1 + (kCompRatio >> 1), kCompRatio);
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +0000109 if (diffGain < 0 || diffGain >= kGenFuncTableSize)
niklase@google.com470e71d2011-07-07 08:21:25 +0000110 {
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +0000111 assert(0);
niklase@google.com470e71d2011-07-07 08:21:25 +0000112 return -1;
113 }
114
115 // Calculate the limiter level and index:
116 // limiterLvlX = analogTarget - limiterOffset
117 // limiterLvl = targetLevelDbfs + limiterOffset/compRatio
118 limiterLvlX = analogTarget - limiterOffset;
119 limiterIdx = 2
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000120 + WebRtcSpl_DivW32W16ResW16(WEBRTC_SPL_LSHIFT_W32((int32_t)limiterLvlX, 13),
bjornv@webrtc.org18026ab2014-06-11 06:53:20 +0000121 (kLog10_2 / 2));
niklase@google.com470e71d2011-07-07 08:21:25 +0000122 tmp16no1 = WebRtcSpl_DivW32W16ResW16(limiterOffset + (kCompRatio >> 1), kCompRatio);
123 limiterLvl = targetLevelDbfs + tmp16no1;
124
125 // Calculate (through table lookup):
126 // constMaxGain = log2(1+2^(log2(e)*diffGain)); (in Q8)
127 constMaxGain = kGenFuncTable[diffGain]; // in Q8
128
129 // Calculate a parameter used to approximate the fractional part of 2^x with a
130 // piecewise linear function in Q14:
131 // constLinApprox = round(3/2*(4*(3-2*sqrt(2))/(log(2)^2)-0.5)*2^14);
132 constLinApprox = 22817; // in Q14
133
134 // Calculate a denominator used in the exponential part to convert from dB to linear scale:
135 // den = 20*constMaxGain (in Q8)
136 den = WEBRTC_SPL_MUL_16_U16(20, constMaxGain); // in Q8
137
138 for (i = 0; i < 32; i++)
139 {
140 // Calculate scaled input level (compressor):
141 // inLevel = fix((-constLog10_2*(compRatio-1)*(1-i)+fix(compRatio/2))/compRatio)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000142 tmp16 = (int16_t)WEBRTC_SPL_MUL_16_16(kCompRatio - 1, i - 1); // Q0
niklase@google.com470e71d2011-07-07 08:21:25 +0000143 tmp32 = WEBRTC_SPL_MUL_16_U16(tmp16, kLog10_2) + 1; // Q14
144 inLevel = WebRtcSpl_DivW32W16(tmp32, kCompRatio); // Q14
145
146 // Calculate diffGain-inLevel, to map using the genFuncTable
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000147 inLevel = WEBRTC_SPL_LSHIFT_W32((int32_t)diffGain, 14) - inLevel; // Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000148
149 // Make calculations on abs(inLevel) and compensate for the sign afterwards.
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000150 absInLevel = (uint32_t)WEBRTC_SPL_ABS_W32(inLevel); // Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000151
152 // LUT with interpolation
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000153 intPart = (uint16_t)WEBRTC_SPL_RSHIFT_U32(absInLevel, 14);
154 fracPart = (uint16_t)(absInLevel & 0x00003FFF); // extract the fractional part
niklase@google.com470e71d2011-07-07 08:21:25 +0000155 tmpU16 = kGenFuncTable[intPart + 1] - kGenFuncTable[intPart]; // Q8
bjornv@webrtc.org37c39f32014-09-08 11:21:56 +0000156 tmpU32no1 = tmpU16 * fracPart; // Q22
bjornv@webrtc.orgc2c41172014-09-05 06:01:53 +0000157 tmpU32no1 += (uint32_t)kGenFuncTable[intPart] << 14; // Q22
niklase@google.com470e71d2011-07-07 08:21:25 +0000158 logApprox = WEBRTC_SPL_RSHIFT_U32(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 {
163 zeros = WebRtcSpl_NormU32(absInLevel);
164 zerosScale = 0;
165 if (zeros < 15)
166 {
167 // Not enough space for multiplication
168 tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(absInLevel, 15 - zeros); // Q(zeros-1)
169 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(tmpU32no2, kLogE_1); // Q(zeros+13)
170 if (zeros < 9)
171 {
172 tmpU32no1 = WEBRTC_SPL_RSHIFT_U32(tmpU32no1, 9 - zeros); // Q(zeros+13)
173 zerosScale = 9 - zeros;
174 } else
175 {
176 tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, zeros - 9); // Q22
177 }
178 } else
179 {
180 tmpU32no2 = WEBRTC_SPL_UMUL_32_16(absInLevel, kLogE_1); // Q28
181 tmpU32no2 = WEBRTC_SPL_RSHIFT_U32(tmpU32no2, 6); // Q22
182 }
183 logApprox = 0;
184 if (tmpU32no2 < tmpU32no1)
185 {
186 logApprox = WEBRTC_SPL_RSHIFT_U32(tmpU32no1 - tmpU32no2, 8 - zerosScale); //Q14
187 }
188 }
189 numFIX = WEBRTC_SPL_LSHIFT_W32(WEBRTC_SPL_MUL_16_U16(maxGain, constMaxGain), 6); // Q14
bjornv@webrtc.org926707b2014-08-25 11:42:42 +0000190 numFIX -= (int32_t)logApprox * diffGain; // Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000191
192 // Calculate ratio
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +0000193 // Shift |numFIX| as much as possible.
194 // Ensure we avoid wrap-around in |den| as well.
195 if (numFIX > (den >> 8)) // |den| is Q8.
196 {
197 zeros = WebRtcSpl_NormW32(numFIX);
198 } else
199 {
200 zeros = WebRtcSpl_NormW32(den) + 8;
201 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000202 numFIX = WEBRTC_SPL_LSHIFT_W32(numFIX, zeros); // Q(14+zeros)
203
204 // Shift den so we end up in Qy1
205 tmp32no1 = WEBRTC_SPL_SHIFT_W32(den, zeros - 8); // Q(zeros)
206 if (numFIX < 0)
207 {
208 numFIX -= WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1);
209 } else
210 {
211 numFIX += WEBRTC_SPL_RSHIFT_W32(tmp32no1, 1);
212 }
bjornv@webrtc.orgdf9fef62014-08-28 12:57:32 +0000213 y32 = numFIX / tmp32no1; // in Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000214 if (limiterEnable && (i < limiterIdx))
215 {
216 tmp32 = WEBRTC_SPL_MUL_16_U16(i - 1, kLog10_2); // Q14
217 tmp32 -= WEBRTC_SPL_LSHIFT_W32(limiterLvl, 14); // Q14
218 y32 = WebRtcSpl_DivW32W16(tmp32 + 10, 20);
219 }
220 if (y32 > 39000)
221 {
222 tmp32 = WEBRTC_SPL_MUL(y32 >> 1, kLog10) + 4096; // in Q27
223 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 13); // in Q14
224 } else
225 {
226 tmp32 = WEBRTC_SPL_MUL(y32, kLog10) + 8192; // in Q28
227 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 14); // in Q14
228 }
229 tmp32 += WEBRTC_SPL_LSHIFT_W32(16, 14); // in Q14 (Make sure final output is in Q16)
230
231 // Calculate power
232 if (tmp32 > 0)
233 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000234 intPart = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 14);
235 fracPart = (uint16_t)(tmp32 & 0x00003FFF); // in Q14
niklase@google.com470e71d2011-07-07 08:21:25 +0000236 if (WEBRTC_SPL_RSHIFT_W32(fracPart, 13))
237 {
238 tmp16 = WEBRTC_SPL_LSHIFT_W16(2, 14) - constLinApprox;
239 tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - fracPart;
bjornv@webrtc.org926707b2014-08-25 11:42:42 +0000240 tmp32no2 *= tmp16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000241 tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13);
242 tmp32no2 = WEBRTC_SPL_LSHIFT_W32(1, 14) - tmp32no2;
243 } else
244 {
245 tmp16 = constLinApprox - WEBRTC_SPL_LSHIFT_W16(1, 14);
bjornv@webrtc.org926707b2014-08-25 11:42:42 +0000246 tmp32no2 = fracPart * tmp16;
niklase@google.com470e71d2011-07-07 08:21:25 +0000247 tmp32no2 = WEBRTC_SPL_RSHIFT_W32(tmp32no2, 13);
248 }
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000249 fracPart = (uint16_t)tmp32no2;
niklase@google.com470e71d2011-07-07 08:21:25 +0000250 gainTable[i] = WEBRTC_SPL_LSHIFT_W32(1, intPart)
251 + WEBRTC_SPL_SHIFT_W32(fracPart, intPart - 14);
252 } else
253 {
254 gainTable[i] = 0;
255 }
256 }
257
258 return 0;
259}
260
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000261int32_t WebRtcAgc_InitDigital(DigitalAgc_t *stt, int16_t agcMode)
niklase@google.com470e71d2011-07-07 08:21:25 +0000262{
263
264 if (agcMode == kAgcModeFixedDigital)
265 {
266 // start at minimum to find correct gain faster
267 stt->capacitorSlow = 0;
268 } else
269 {
270 // start out with 0 dB gain
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000271 stt->capacitorSlow = 134217728; // (int32_t)(0.125f * 32768.0f * 32768.0f);
niklase@google.com470e71d2011-07-07 08:21:25 +0000272 }
273 stt->capacitorFast = 0;
274 stt->gain = 65536;
275 stt->gatePrevious = 0;
276 stt->agcMode = agcMode;
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000277#ifdef WEBRTC_AGC_DEBUG_DUMP
niklase@google.com470e71d2011-07-07 08:21:25 +0000278 stt->frameCounter = 0;
279#endif
280
281 // initialize VADs
282 WebRtcAgc_InitVad(&stt->vadNearend);
283 WebRtcAgc_InitVad(&stt->vadFarend);
284
285 return 0;
286}
287
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000288int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const int16_t *in_far,
289 int16_t nrSamples)
niklase@google.com470e71d2011-07-07 08:21:25 +0000290{
pbos@webrtc.org0117d1c2014-03-03 16:47:03 +0000291 assert(stt != NULL);
niklase@google.com470e71d2011-07-07 08:21:25 +0000292 // VAD for far end
293 WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
294
295 return 0;
296}
297
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000298int32_t WebRtcAgc_ProcessDigital(DigitalAgc_t *stt, const int16_t *in_near,
299 const int16_t *in_near_H, int16_t *out,
300 int16_t *out_H, uint32_t FS,
301 int16_t lowlevelSignal)
niklase@google.com470e71d2011-07-07 08:21:25 +0000302{
303 // array for gains (one value per ms, incl start & end)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000304 int32_t gains[11];
niklase@google.com470e71d2011-07-07 08:21:25 +0000305
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000306 int32_t out_tmp, tmp32;
307 int32_t env[10];
308 int32_t nrg, max_nrg;
309 int32_t cur_level;
310 int32_t gain32, delta;
311 int16_t logratio;
312 int16_t lower_thr, upper_thr;
tommi@webrtc.orgeec6ecd2014-07-11 19:09:59 +0000313 int16_t zeros = 0, zeros_fast, frac = 0;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000314 int16_t decay;
315 int16_t gate, gain_adj;
316 int16_t k, n;
317 int16_t L, L2; // samples/subframe
niklase@google.com470e71d2011-07-07 08:21:25 +0000318
319 // determine number of samples per ms
320 if (FS == 8000)
321 {
322 L = 8;
323 L2 = 3;
324 } else if (FS == 16000)
325 {
326 L = 16;
327 L2 = 4;
328 } else if (FS == 32000)
329 {
330 L = 16;
331 L2 = 4;
332 } else
333 {
334 return -1;
335 }
336
andrew@webrtc.org64235092011-08-19 21:22:08 +0000337 // TODO(andrew): again, we don't need input and output pointers...
338 if (in_near != out)
339 {
340 // Only needed if they don't already point to the same place.
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000341 memcpy(out, in_near, 10 * L * sizeof(int16_t));
andrew@webrtc.org64235092011-08-19 21:22:08 +0000342 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000343 if (FS == 32000)
344 {
andrew@webrtc.org64235092011-08-19 21:22:08 +0000345 if (in_near_H != out_H)
346 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000347 memcpy(out_H, in_near_H, 10 * L * sizeof(int16_t));
andrew@webrtc.org64235092011-08-19 21:22:08 +0000348 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000349 }
350 // VAD for near end
351 logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out, L * 10);
352
353 // Account for far end VAD
354 if (stt->vadFarend.counter > 10)
355 {
356 tmp32 = WEBRTC_SPL_MUL_16_16(3, logratio);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000357 logratio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 - stt->vadFarend.logRatio, 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000358 }
359
360 // Determine decay factor depending on VAD
361 // upper_thr = 1.0f;
362 // lower_thr = 0.25f;
363 upper_thr = 1024; // Q10
364 lower_thr = 0; // Q10
365 if (logratio > upper_thr)
366 {
367 // decay = -2^17 / DecayTime; -> -65
368 decay = -65;
369 } else if (logratio < lower_thr)
370 {
371 decay = 0;
372 } else
373 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000374 // decay = (int16_t)(((lower_thr - logratio)
niklase@google.com470e71d2011-07-07 08:21:25 +0000375 // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10);
376 // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65
377 tmp32 = WEBRTC_SPL_MUL_16_16((lower_thr - logratio), 65);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000378 decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 10);
niklase@google.com470e71d2011-07-07 08:21:25 +0000379 }
380
381 // adjust decay factor for long silence (detected as low standard deviation)
382 // This is only done in the adaptive modes
383 if (stt->agcMode != kAgcModeFixedDigital)
384 {
385 if (stt->vadNearend.stdLongTerm < 4000)
386 {
387 decay = 0;
388 } else if (stt->vadNearend.stdLongTerm < 8096)
389 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000390 // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000391 tmp32 = WEBRTC_SPL_MUL_16_16((stt->vadNearend.stdLongTerm - 4000), decay);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000392 decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000393 }
394
395 if (lowlevelSignal != 0)
396 {
397 decay = 0;
398 }
399 }
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000400#ifdef WEBRTC_AGC_DEBUG_DUMP
niklase@google.com470e71d2011-07-07 08:21:25 +0000401 stt->frameCounter++;
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000402 fprintf(stt->logFile,
403 "%5.2f\t%d\t%d\t%d\t",
404 (float)(stt->frameCounter) / 100,
405 logratio,
406 decay,
407 stt->vadNearend.stdLongTerm);
niklase@google.com470e71d2011-07-07 08:21:25 +0000408#endif
409 // Find max amplitude per sub frame
410 // iterate over sub frames
411 for (k = 0; k < 10; k++)
412 {
413 // iterate over samples
414 max_nrg = 0;
415 for (n = 0; n < L; n++)
416 {
417 nrg = WEBRTC_SPL_MUL_16_16(out[k * L + n], out[k * L + n]);
418 if (nrg > max_nrg)
419 {
420 max_nrg = nrg;
421 }
422 }
423 env[k] = max_nrg;
424 }
425
426 // Calculate gain per sub frame
427 gains[0] = stt->gain;
428 for (k = 0; k < 10; k++)
429 {
430 // Fast envelope follower
431 // decay time = -131000 / -1000 = 131 (ms)
432 stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
433 if (env[k] > stt->capacitorFast)
434 {
435 stt->capacitorFast = env[k];
436 }
437 // Slow envelope follower
438 if (env[k] > stt->capacitorSlow)
439 {
440 // increase capacitorSlow
441 stt->capacitorSlow
442 = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow);
443 } else
444 {
445 // decrease capacitorSlow
446 stt->capacitorSlow
447 = AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
448 }
449
450 // use maximum of both capacitors as current level
451 if (stt->capacitorFast > stt->capacitorSlow)
452 {
453 cur_level = stt->capacitorFast;
454 } else
455 {
456 cur_level = stt->capacitorSlow;
457 }
458 // Translate signal level into gain, using a piecewise linear approximation
459 // find number of leading zeros
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000460 zeros = WebRtcSpl_NormU32((uint32_t)cur_level);
niklase@google.com470e71d2011-07-07 08:21:25 +0000461 if (cur_level == 0)
462 {
463 zeros = 31;
464 }
465 tmp32 = (WEBRTC_SPL_LSHIFT_W32(cur_level, zeros) & 0x7FFFFFFF);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000466 frac = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 19); // Q12
niklase@google.com470e71d2011-07-07 08:21:25 +0000467 tmp32 = WEBRTC_SPL_MUL((stt->gainTable[zeros-1] - stt->gainTable[zeros]), frac);
468 gains[k + 1] = stt->gainTable[zeros] + WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
bjornv@webrtc.orgea297872014-09-23 11:21:39 +0000469#ifdef WEBRTC_AGC_DEBUG_DUMP
470 if (k == 0) {
471 fprintf(stt->logFile,
472 "%d\t%d\t%d\t%d\t%d\n",
473 env[0],
474 cur_level,
475 stt->capacitorFast,
476 stt->capacitorSlow,
477 zeros);
niklase@google.com470e71d2011-07-07 08:21:25 +0000478 }
479#endif
480 }
481
482 // Gate processing (lower gain during absence of speech)
483 zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3);
484 // find number of leading zeros
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000485 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
niklase@google.com470e71d2011-07-07 08:21:25 +0000486 if (stt->capacitorFast == 0)
487 {
488 zeros_fast = 31;
489 }
490 tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF);
491 zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000492 zeros_fast -= (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 22);
niklase@google.com470e71d2011-07-07 08:21:25 +0000493
494 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
495
496 if (gate < 0)
497 {
498 stt->gatePrevious = 0;
499 } else
500 {
501 tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000502 gate = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)gate + tmp32, 3);
niklase@google.com470e71d2011-07-07 08:21:25 +0000503 stt->gatePrevious = gate;
504 }
505 // gate < 0 -> no gate
506 // gate > 2500 -> max gate
507 if (gate > 0)
508 {
509 if (gate < 2500)
510 {
511 gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5);
512 } else
513 {
514 gain_adj = 0;
515 }
516 for (k = 0; k < 10; k++)
517 {
518 if ((gains[k + 1] - stt->gainTable[0]) > 8388608)
519 {
520 // To prevent wraparound
521 tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8);
522 tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj));
523 } else
524 {
525 tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj));
526 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8);
527 }
528 gains[k + 1] = stt->gainTable[0] + tmp32;
529 }
530 }
531
532 // Limit gain to avoid overload distortion
533 for (k = 0; k < 10; k++)
534 {
535 // To prevent wrap around
536 zeros = 10;
537 if (gains[k + 1] > 47453132)
538 {
539 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
540 }
541 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
542 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
543 // check for overflow
544 while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000545 > WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000546 {
547 // multiply by 253/256 ==> -0.1 dB
548 if (gains[k + 1] > 8388607)
549 {
550 // Prevent wrap around
551 gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253);
552 } else
553 {
554 gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8);
555 }
556 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
557 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
558 }
559 }
560 // gain reductions should be done 1 ms earlier than gain increases
561 for (k = 1; k < 10; k++)
562 {
563 if (gains[k] > gains[k + 1])
564 {
565 gains[k] = gains[k + 1];
566 }
567 }
568 // save start gain for next frame
569 stt->gain = gains[10];
570
571 // Apply gain
572 // handle first sub frame separately
573 delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2));
574 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4);
575 // iterate over samples
576 for (n = 0; n < L; n++)
577 {
578 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000579 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
niklase@google.com470e71d2011-07-07 08:21:25 +0000580 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
581 if (out_tmp > 4095)
582 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000583 out[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000584 } else if (out_tmp < -4096)
585 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000586 out[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000587 } else
588 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000589 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4));
590 out[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000591 }
592 // For higher band
593 if (FS == 32000)
594 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000595 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000596 WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
597 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
598 if (out_tmp > 4095)
599 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000600 out_H[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000601 } else if (out_tmp < -4096)
602 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000603 out_H[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000604 } else
605 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000606 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000607 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000608 out_H[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000609 }
610 }
611 //
612
613 gain32 += delta;
614 }
615 // iterate over subframes
616 for (k = 1; k < 10; k++)
617 {
618 delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2));
619 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4);
620 // iterate over samples
621 for (n = 0; n < L; n++)
622 {
623 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000624 tmp32 = WEBRTC_SPL_MUL((int32_t)out[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000625 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000626 out[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000627 // For higher band
628 if (FS == 32000)
629 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000630 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000631 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000632 out_H[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000633 }
634 gain32 += delta;
635 }
636 }
637
638 return 0;
639}
640
641void WebRtcAgc_InitVad(AgcVad_t *state)
642{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000643 int16_t k;
niklase@google.com470e71d2011-07-07 08:21:25 +0000644
645 state->HPstate = 0; // state of high pass filter
646 state->logRatio = 0; // log( P(active) / P(inactive) )
647 // average input level (Q10)
648 state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
649
650 // variance of input level (Q8)
651 state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
652
653 state->stdLongTerm = 0; // standard deviation of input level in dB
654 // short-term average input level (Q10)
655 state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
656
657 // short-term variance of input level (Q8)
658 state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
659
660 state->stdShortTerm = 0; // short-term standard deviation of input level in dB
661 state->counter = 3; // counts updates
662 for (k = 0; k < 8; k++)
663 {
664 // downsampling filter
665 state->downState[k] = 0;
666 }
667}
668
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000669int16_t WebRtcAgc_ProcessVad(AgcVad_t *state, // (i) VAD state
670 const int16_t *in, // (i) Speech signal
671 int16_t nrSamples) // (i) number of samples
niklase@google.com470e71d2011-07-07 08:21:25 +0000672{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000673 int32_t out, nrg, tmp32, tmp32b;
674 uint16_t tmpU16;
675 int16_t k, subfr, tmp16;
676 int16_t buf1[8];
677 int16_t buf2[4];
678 int16_t HPstate;
679 int16_t zeros, dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000680
681 // process in 10 sub frames of 1 ms (to save on memory)
682 nrg = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000683 HPstate = state->HPstate;
684 for (subfr = 0; subfr < 10; subfr++)
685 {
686 // downsample to 4 kHz
687 if (nrSamples == 160)
688 {
689 for (k = 0; k < 8; k++)
690 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000691 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000692 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000693 buf1[k] = (int16_t)tmp32;
niklase@google.com470e71d2011-07-07 08:21:25 +0000694 }
695 in += 16;
696
697 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
698 } else
699 {
700 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
701 in += 8;
702 }
703
704 // high pass filter and compute energy
705 for (k = 0; k < 4; k++)
706 {
707 out = buf2[k] + HPstate;
708 tmp32 = WEBRTC_SPL_MUL(600, out);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000709 HPstate = (int16_t)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000710 tmp32 = WEBRTC_SPL_MUL(out, out);
711 nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
712 }
713 }
714 state->HPstate = HPstate;
715
716 // find number of leading zeros
717 if (!(0xFFFF0000 & nrg))
718 {
719 zeros = 16;
720 } else
721 {
722 zeros = 0;
723 }
724 if (!(0xFF000000 & (nrg << zeros)))
725 {
726 zeros += 8;
727 }
728 if (!(0xF0000000 & (nrg << zeros)))
729 {
730 zeros += 4;
731 }
732 if (!(0xC0000000 & (nrg << zeros)))
733 {
734 zeros += 2;
735 }
736 if (!(0x80000000 & (nrg << zeros)))
737 {
738 zeros += 1;
739 }
740
741 // energy level (range {-32..30}) (Q10)
742 dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11);
743
744 // Update statistics
745
746 if (state->counter < kAvgDecayTime)
747 {
748 // decay time = AvgDecTime * 10 ms
749 state->counter++;
750 }
751
752 // update short-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000753 tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (int32_t)dB);
754 state->meanShortTerm = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000755
756 // update short-term estimate of variance in energy level (Q8)
757 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
758 tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15);
759 state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
760
761 // update short-term estimate of standard deviation in energy level (Q10)
762 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm);
763 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000764 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000765
766 // update long-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000767 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (int32_t)dB;
bjornv@webrtc.org6e71d172014-08-25 07:44:52 +0000768 state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(
769 tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000770
771 // update long-term estimate of variance in energy level (Q8)
772 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
773 tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter);
bjornv@webrtc.org6e71d172014-08-25 07:44:52 +0000774 state->varianceLongTerm = WebRtcSpl_DivW32W16(
775 tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000776
777 // update long-term estimate of standard deviation in energy level (Q10)
778 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm);
779 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000780 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000781
782 // update voice activity measure (Q10)
783 tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12);
784 tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm));
785 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
bjornv@webrtc.org721f9702014-06-16 10:30:14 +0000786 tmpU16 = (13 << 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000787 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
788 tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10);
789
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000790 state->logRatio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
niklase@google.com470e71d2011-07-07 08:21:25 +0000791
792 // limit
793 if (state->logRatio > 2048)
794 {
795 state->logRatio = 2048;
796 }
797 if (state->logRatio < -2048)
798 {
799 state->logRatio = -2048;
800 }
801
802 return state->logRatio; // Q10
803}