blob: 7b515a57dec416c3df66ee3856b77019753208a5 [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>
19#ifdef AGC_DEBUG
20#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
156 tmpU32no1 = WEBRTC_SPL_UMUL_16_16(tmpU16, fracPart); // Q22
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000157 tmpU32no1 += WEBRTC_SPL_LSHIFT_U32((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 }
213 y32 = WEBRTC_SPL_DIV(numFIX, tmp32no1); // in Q14
214 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;
277#ifdef AGC_DEBUG
278 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 }
400#ifdef AGC_DEBUG
401 stt->frameCounter++;
402 fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100, logratio, decay, stt->vadNearend.stdLongTerm);
403#endif
404 // Find max amplitude per sub frame
405 // iterate over sub frames
406 for (k = 0; k < 10; k++)
407 {
408 // iterate over samples
409 max_nrg = 0;
410 for (n = 0; n < L; n++)
411 {
412 nrg = WEBRTC_SPL_MUL_16_16(out[k * L + n], out[k * L + n]);
413 if (nrg > max_nrg)
414 {
415 max_nrg = nrg;
416 }
417 }
418 env[k] = max_nrg;
419 }
420
421 // Calculate gain per sub frame
422 gains[0] = stt->gain;
423 for (k = 0; k < 10; k++)
424 {
425 // Fast envelope follower
426 // decay time = -131000 / -1000 = 131 (ms)
427 stt->capacitorFast = AGC_SCALEDIFF32(-1000, stt->capacitorFast, stt->capacitorFast);
428 if (env[k] > stt->capacitorFast)
429 {
430 stt->capacitorFast = env[k];
431 }
432 // Slow envelope follower
433 if (env[k] > stt->capacitorSlow)
434 {
435 // increase capacitorSlow
436 stt->capacitorSlow
437 = AGC_SCALEDIFF32(500, (env[k] - stt->capacitorSlow), stt->capacitorSlow);
438 } else
439 {
440 // decrease capacitorSlow
441 stt->capacitorSlow
442 = AGC_SCALEDIFF32(decay, stt->capacitorSlow, stt->capacitorSlow);
443 }
444
445 // use maximum of both capacitors as current level
446 if (stt->capacitorFast > stt->capacitorSlow)
447 {
448 cur_level = stt->capacitorFast;
449 } else
450 {
451 cur_level = stt->capacitorSlow;
452 }
453 // Translate signal level into gain, using a piecewise linear approximation
454 // find number of leading zeros
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000455 zeros = WebRtcSpl_NormU32((uint32_t)cur_level);
niklase@google.com470e71d2011-07-07 08:21:25 +0000456 if (cur_level == 0)
457 {
458 zeros = 31;
459 }
460 tmp32 = (WEBRTC_SPL_LSHIFT_W32(cur_level, zeros) & 0x7FFFFFFF);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000461 frac = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 19); // Q12
niklase@google.com470e71d2011-07-07 08:21:25 +0000462 tmp32 = WEBRTC_SPL_MUL((stt->gainTable[zeros-1] - stt->gainTable[zeros]), frac);
463 gains[k + 1] = stt->gainTable[zeros] + WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
464#ifdef AGC_DEBUG
465 if (k == 0)
466 {
467 fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros);
468 }
469#endif
470 }
471
472 // Gate processing (lower gain during absence of speech)
473 zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3);
474 // find number of leading zeros
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000475 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
niklase@google.com470e71d2011-07-07 08:21:25 +0000476 if (stt->capacitorFast == 0)
477 {
478 zeros_fast = 31;
479 }
480 tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF);
481 zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000482 zeros_fast -= (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 22);
niklase@google.com470e71d2011-07-07 08:21:25 +0000483
484 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
485
486 if (gate < 0)
487 {
488 stt->gatePrevious = 0;
489 } else
490 {
491 tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000492 gate = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)gate + tmp32, 3);
niklase@google.com470e71d2011-07-07 08:21:25 +0000493 stt->gatePrevious = gate;
494 }
495 // gate < 0 -> no gate
496 // gate > 2500 -> max gate
497 if (gate > 0)
498 {
499 if (gate < 2500)
500 {
501 gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5);
502 } else
503 {
504 gain_adj = 0;
505 }
506 for (k = 0; k < 10; k++)
507 {
508 if ((gains[k + 1] - stt->gainTable[0]) > 8388608)
509 {
510 // To prevent wraparound
511 tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8);
512 tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj));
513 } else
514 {
515 tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj));
516 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8);
517 }
518 gains[k + 1] = stt->gainTable[0] + tmp32;
519 }
520 }
521
522 // Limit gain to avoid overload distortion
523 for (k = 0; k < 10; k++)
524 {
525 // To prevent wrap around
526 zeros = 10;
527 if (gains[k + 1] > 47453132)
528 {
529 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
530 }
531 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
532 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
533 // check for overflow
534 while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000535 > WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000536 {
537 // multiply by 253/256 ==> -0.1 dB
538 if (gains[k + 1] > 8388607)
539 {
540 // Prevent wrap around
541 gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253);
542 } else
543 {
544 gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8);
545 }
546 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
547 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
548 }
549 }
550 // gain reductions should be done 1 ms earlier than gain increases
551 for (k = 1; k < 10; k++)
552 {
553 if (gains[k] > gains[k + 1])
554 {
555 gains[k] = gains[k + 1];
556 }
557 }
558 // save start gain for next frame
559 stt->gain = gains[10];
560
561 // Apply gain
562 // handle first sub frame separately
563 delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2));
564 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4);
565 // iterate over samples
566 for (n = 0; n < L; n++)
567 {
568 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000569 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
niklase@google.com470e71d2011-07-07 08:21:25 +0000570 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
571 if (out_tmp > 4095)
572 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000573 out[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000574 } else if (out_tmp < -4096)
575 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000576 out[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000577 } else
578 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000579 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4));
580 out[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000581 }
582 // For higher band
583 if (FS == 32000)
584 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000585 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000586 WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
587 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
588 if (out_tmp > 4095)
589 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000590 out_H[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000591 } else if (out_tmp < -4096)
592 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000593 out_H[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000594 } else
595 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000596 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000597 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000598 out_H[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000599 }
600 }
601 //
602
603 gain32 += delta;
604 }
605 // iterate over subframes
606 for (k = 1; k < 10; k++)
607 {
608 delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2));
609 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4);
610 // iterate over samples
611 for (n = 0; n < L; n++)
612 {
613 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000614 tmp32 = WEBRTC_SPL_MUL((int32_t)out[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000615 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000616 out[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000617 // For higher band
618 if (FS == 32000)
619 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000620 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000621 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000622 out_H[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000623 }
624 gain32 += delta;
625 }
626 }
627
628 return 0;
629}
630
631void WebRtcAgc_InitVad(AgcVad_t *state)
632{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000633 int16_t k;
niklase@google.com470e71d2011-07-07 08:21:25 +0000634
635 state->HPstate = 0; // state of high pass filter
636 state->logRatio = 0; // log( P(active) / P(inactive) )
637 // average input level (Q10)
638 state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
639
640 // variance of input level (Q8)
641 state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
642
643 state->stdLongTerm = 0; // standard deviation of input level in dB
644 // short-term average input level (Q10)
645 state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
646
647 // short-term variance of input level (Q8)
648 state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
649
650 state->stdShortTerm = 0; // short-term standard deviation of input level in dB
651 state->counter = 3; // counts updates
652 for (k = 0; k < 8; k++)
653 {
654 // downsampling filter
655 state->downState[k] = 0;
656 }
657}
658
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000659int16_t WebRtcAgc_ProcessVad(AgcVad_t *state, // (i) VAD state
660 const int16_t *in, // (i) Speech signal
661 int16_t nrSamples) // (i) number of samples
niklase@google.com470e71d2011-07-07 08:21:25 +0000662{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000663 int32_t out, nrg, tmp32, tmp32b;
664 uint16_t tmpU16;
665 int16_t k, subfr, tmp16;
666 int16_t buf1[8];
667 int16_t buf2[4];
668 int16_t HPstate;
669 int16_t zeros, dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000670
671 // process in 10 sub frames of 1 ms (to save on memory)
672 nrg = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000673 HPstate = state->HPstate;
674 for (subfr = 0; subfr < 10; subfr++)
675 {
676 // downsample to 4 kHz
677 if (nrSamples == 160)
678 {
679 for (k = 0; k < 8; k++)
680 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000681 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000682 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000683 buf1[k] = (int16_t)tmp32;
niklase@google.com470e71d2011-07-07 08:21:25 +0000684 }
685 in += 16;
686
687 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
688 } else
689 {
690 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
691 in += 8;
692 }
693
694 // high pass filter and compute energy
695 for (k = 0; k < 4; k++)
696 {
697 out = buf2[k] + HPstate;
698 tmp32 = WEBRTC_SPL_MUL(600, out);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000699 HPstate = (int16_t)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000700 tmp32 = WEBRTC_SPL_MUL(out, out);
701 nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
702 }
703 }
704 state->HPstate = HPstate;
705
706 // find number of leading zeros
707 if (!(0xFFFF0000 & nrg))
708 {
709 zeros = 16;
710 } else
711 {
712 zeros = 0;
713 }
714 if (!(0xFF000000 & (nrg << zeros)))
715 {
716 zeros += 8;
717 }
718 if (!(0xF0000000 & (nrg << zeros)))
719 {
720 zeros += 4;
721 }
722 if (!(0xC0000000 & (nrg << zeros)))
723 {
724 zeros += 2;
725 }
726 if (!(0x80000000 & (nrg << zeros)))
727 {
728 zeros += 1;
729 }
730
731 // energy level (range {-32..30}) (Q10)
732 dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11);
733
734 // Update statistics
735
736 if (state->counter < kAvgDecayTime)
737 {
738 // decay time = AvgDecTime * 10 ms
739 state->counter++;
740 }
741
742 // update short-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000743 tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (int32_t)dB);
744 state->meanShortTerm = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000745
746 // update short-term estimate of variance in energy level (Q8)
747 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
748 tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15);
749 state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
750
751 // update short-term estimate of standard deviation in energy level (Q10)
752 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm);
753 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000754 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000755
756 // update long-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000757 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (int32_t)dB;
bjornv@webrtc.org6e71d172014-08-25 07:44:52 +0000758 state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(
759 tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000760
761 // update long-term estimate of variance in energy level (Q8)
762 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
763 tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter);
bjornv@webrtc.org6e71d172014-08-25 07:44:52 +0000764 state->varianceLongTerm = WebRtcSpl_DivW32W16(
765 tmp32, WebRtcSpl_AddSatW16(state->counter, 1));
niklase@google.com470e71d2011-07-07 08:21:25 +0000766
767 // update long-term estimate of standard deviation in energy level (Q10)
768 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm);
769 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000770 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000771
772 // update voice activity measure (Q10)
773 tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12);
774 tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm));
775 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
bjornv@webrtc.org721f9702014-06-16 10:30:14 +0000776 tmpU16 = (13 << 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000777 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
778 tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10);
779
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000780 state->logRatio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
niklase@google.com470e71d2011-07-07 08:21:25 +0000781
782 // limit
783 if (state->logRatio > 2048)
784 {
785 state->logRatio = 2048;
786 }
787 if (state->logRatio < -2048)
788 {
789 state->logRatio = -2048;
790 }
791
792 return state->logRatio; // Q10
793}