blob: 60463512961876cc766f9014cb6e99bd784876ce [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
andrew@webrtc.org3905b0c2012-01-04 15:47:20 +000015#include "digital_agc.h"
16
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
niklase@google.com470e71d2011-07-07 08:21:25 +000023#include "gain_control.h"
24
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),
niklase@google.com470e71d2011-07-07 08:21:25 +0000121 WEBRTC_SPL_RSHIFT_U16(kLog10_2, 1));
122 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
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000190 numFIX -= WEBRTC_SPL_MUL_32_16((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;
240 tmp32no2 = WEBRTC_SPL_MUL_32_16(tmp32no2, tmp16);
241 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);
246 tmp32no2 = WEBRTC_SPL_MUL_32_16(fracPart, tmp16);
247 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{
291 // Check for valid pointer
292 if (&stt->vadFarend == NULL)
293 {
294 return -1;
295 }
296
297 // VAD for far end
298 WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
299
300 return 0;
301}
302
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000303int32_t WebRtcAgc_ProcessDigital(DigitalAgc_t *stt, const int16_t *in_near,
304 const int16_t *in_near_H, int16_t *out,
305 int16_t *out_H, uint32_t FS,
306 int16_t lowlevelSignal)
niklase@google.com470e71d2011-07-07 08:21:25 +0000307{
308 // array for gains (one value per ms, incl start & end)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000309 int32_t gains[11];
niklase@google.com470e71d2011-07-07 08:21:25 +0000310
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000311 int32_t out_tmp, tmp32;
312 int32_t env[10];
313 int32_t nrg, max_nrg;
314 int32_t cur_level;
315 int32_t gain32, delta;
316 int16_t logratio;
317 int16_t lower_thr, upper_thr;
318 int16_t zeros, zeros_fast, frac;
319 int16_t decay;
320 int16_t gate, gain_adj;
321 int16_t k, n;
322 int16_t L, L2; // samples/subframe
niklase@google.com470e71d2011-07-07 08:21:25 +0000323
324 // determine number of samples per ms
325 if (FS == 8000)
326 {
327 L = 8;
328 L2 = 3;
329 } else if (FS == 16000)
330 {
331 L = 16;
332 L2 = 4;
333 } else if (FS == 32000)
334 {
335 L = 16;
336 L2 = 4;
337 } else
338 {
339 return -1;
340 }
341
andrew@webrtc.org64235092011-08-19 21:22:08 +0000342 // TODO(andrew): again, we don't need input and output pointers...
343 if (in_near != out)
344 {
345 // Only needed if they don't already point to the same place.
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000346 memcpy(out, in_near, 10 * L * sizeof(int16_t));
andrew@webrtc.org64235092011-08-19 21:22:08 +0000347 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000348 if (FS == 32000)
349 {
andrew@webrtc.org64235092011-08-19 21:22:08 +0000350 if (in_near_H != out_H)
351 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000352 memcpy(out_H, in_near_H, 10 * L * sizeof(int16_t));
andrew@webrtc.org64235092011-08-19 21:22:08 +0000353 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000354 }
355 // VAD for near end
356 logratio = WebRtcAgc_ProcessVad(&stt->vadNearend, out, L * 10);
357
358 // Account for far end VAD
359 if (stt->vadFarend.counter > 10)
360 {
361 tmp32 = WEBRTC_SPL_MUL_16_16(3, logratio);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000362 logratio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 - stt->vadFarend.logRatio, 2);
niklase@google.com470e71d2011-07-07 08:21:25 +0000363 }
364
365 // Determine decay factor depending on VAD
366 // upper_thr = 1.0f;
367 // lower_thr = 0.25f;
368 upper_thr = 1024; // Q10
369 lower_thr = 0; // Q10
370 if (logratio > upper_thr)
371 {
372 // decay = -2^17 / DecayTime; -> -65
373 decay = -65;
374 } else if (logratio < lower_thr)
375 {
376 decay = 0;
377 } else
378 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000379 // decay = (int16_t)(((lower_thr - logratio)
niklase@google.com470e71d2011-07-07 08:21:25 +0000380 // * (2^27/(DecayTime*(upper_thr-lower_thr)))) >> 10);
381 // SUBSTITUTED: 2^27/(DecayTime*(upper_thr-lower_thr)) -> 65
382 tmp32 = WEBRTC_SPL_MUL_16_16((lower_thr - logratio), 65);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000383 decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 10);
niklase@google.com470e71d2011-07-07 08:21:25 +0000384 }
385
386 // adjust decay factor for long silence (detected as low standard deviation)
387 // This is only done in the adaptive modes
388 if (stt->agcMode != kAgcModeFixedDigital)
389 {
390 if (stt->vadNearend.stdLongTerm < 4000)
391 {
392 decay = 0;
393 } else if (stt->vadNearend.stdLongTerm < 8096)
394 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000395 // decay = (int16_t)(((stt->vadNearend.stdLongTerm - 4000) * decay) >> 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000396 tmp32 = WEBRTC_SPL_MUL_16_16((stt->vadNearend.stdLongTerm - 4000), decay);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000397 decay = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000398 }
399
400 if (lowlevelSignal != 0)
401 {
402 decay = 0;
403 }
404 }
405#ifdef AGC_DEBUG
406 stt->frameCounter++;
407 fprintf(stt->logFile, "%5.2f\t%d\t%d\t%d\t", (float)(stt->frameCounter) / 100, logratio, decay, stt->vadNearend.stdLongTerm);
408#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);
469#ifdef AGC_DEBUG
470 if (k == 0)
471 {
472 fprintf(stt->logFile, "%d\t%d\t%d\t%d\t%d\n", env[0], cur_level, stt->capacitorFast, stt->capacitorSlow, zeros);
473 }
474#endif
475 }
476
477 // Gate processing (lower gain during absence of speech)
478 zeros = WEBRTC_SPL_LSHIFT_W16(zeros, 9) - WEBRTC_SPL_RSHIFT_W16(frac, 3);
479 // find number of leading zeros
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000480 zeros_fast = WebRtcSpl_NormU32((uint32_t)stt->capacitorFast);
niklase@google.com470e71d2011-07-07 08:21:25 +0000481 if (stt->capacitorFast == 0)
482 {
483 zeros_fast = 31;
484 }
485 tmp32 = (WEBRTC_SPL_LSHIFT_W32(stt->capacitorFast, zeros_fast) & 0x7FFFFFFF);
486 zeros_fast = WEBRTC_SPL_LSHIFT_W16(zeros_fast, 9);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000487 zeros_fast -= (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 22);
niklase@google.com470e71d2011-07-07 08:21:25 +0000488
489 gate = 1000 + zeros_fast - zeros - stt->vadNearend.stdShortTerm;
490
491 if (gate < 0)
492 {
493 stt->gatePrevious = 0;
494 } else
495 {
496 tmp32 = WEBRTC_SPL_MUL_16_16(stt->gatePrevious, 7);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000497 gate = (int16_t)WEBRTC_SPL_RSHIFT_W32((int32_t)gate + tmp32, 3);
niklase@google.com470e71d2011-07-07 08:21:25 +0000498 stt->gatePrevious = gate;
499 }
500 // gate < 0 -> no gate
501 // gate > 2500 -> max gate
502 if (gate > 0)
503 {
504 if (gate < 2500)
505 {
506 gain_adj = WEBRTC_SPL_RSHIFT_W16(2500 - gate, 5);
507 } else
508 {
509 gain_adj = 0;
510 }
511 for (k = 0; k < 10; k++)
512 {
513 if ((gains[k + 1] - stt->gainTable[0]) > 8388608)
514 {
515 // To prevent wraparound
516 tmp32 = WEBRTC_SPL_RSHIFT_W32((gains[k+1] - stt->gainTable[0]), 8);
517 tmp32 = WEBRTC_SPL_MUL(tmp32, (178 + gain_adj));
518 } else
519 {
520 tmp32 = WEBRTC_SPL_MUL((gains[k+1] - stt->gainTable[0]), (178 + gain_adj));
521 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 8);
522 }
523 gains[k + 1] = stt->gainTable[0] + tmp32;
524 }
525 }
526
527 // Limit gain to avoid overload distortion
528 for (k = 0; k < 10; k++)
529 {
530 // To prevent wrap around
531 zeros = 10;
532 if (gains[k + 1] > 47453132)
533 {
534 zeros = 16 - WebRtcSpl_NormW32(gains[k + 1]);
535 }
536 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
537 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
538 // check for overflow
539 while (AGC_MUL32(WEBRTC_SPL_RSHIFT_W32(env[k], 12) + 1, gain32)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000540 > WEBRTC_SPL_SHIFT_W32((int32_t)32767, 2 * (1 - zeros + 10)))
niklase@google.com470e71d2011-07-07 08:21:25 +0000541 {
542 // multiply by 253/256 ==> -0.1 dB
543 if (gains[k + 1] > 8388607)
544 {
545 // Prevent wrap around
546 gains[k + 1] = WEBRTC_SPL_MUL(WEBRTC_SPL_RSHIFT_W32(gains[k+1], 8), 253);
547 } else
548 {
549 gains[k + 1] = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL(gains[k+1], 253), 8);
550 }
551 gain32 = WEBRTC_SPL_RSHIFT_W32(gains[k+1], zeros) + 1;
552 gain32 = WEBRTC_SPL_MUL(gain32, gain32);
553 }
554 }
555 // gain reductions should be done 1 ms earlier than gain increases
556 for (k = 1; k < 10; k++)
557 {
558 if (gains[k] > gains[k + 1])
559 {
560 gains[k] = gains[k + 1];
561 }
562 }
563 // save start gain for next frame
564 stt->gain = gains[10];
565
566 // Apply gain
567 // handle first sub frame separately
568 delta = WEBRTC_SPL_LSHIFT_W32(gains[1] - gains[0], (4 - L2));
569 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[0], 4);
570 // iterate over samples
571 for (n = 0; n < L; n++)
572 {
573 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000574 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
niklase@google.com470e71d2011-07-07 08:21:25 +0000575 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
576 if (out_tmp > 4095)
577 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000578 out[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000579 } else if (out_tmp < -4096)
580 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000581 out[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000582 } else
583 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000584 tmp32 = WEBRTC_SPL_MUL((int32_t)out[n], WEBRTC_SPL_RSHIFT_W32(gain32, 4));
585 out[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000586 }
587 // For higher band
588 if (FS == 32000)
589 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000590 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000591 WEBRTC_SPL_RSHIFT_W32(gain32 + 127, 7));
592 out_tmp = WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
593 if (out_tmp > 4095)
594 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000595 out_H[n] = (int16_t)32767;
niklase@google.com470e71d2011-07-07 08:21:25 +0000596 } else if (out_tmp < -4096)
597 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000598 out_H[n] = (int16_t)-32768;
niklase@google.com470e71d2011-07-07 08:21:25 +0000599 } else
600 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000601 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000602 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000603 out_H[n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000604 }
605 }
606 //
607
608 gain32 += delta;
609 }
610 // iterate over subframes
611 for (k = 1; k < 10; k++)
612 {
613 delta = WEBRTC_SPL_LSHIFT_W32(gains[k+1] - gains[k], (4 - L2));
614 gain32 = WEBRTC_SPL_LSHIFT_W32(gains[k], 4);
615 // iterate over samples
616 for (n = 0; n < L; n++)
617 {
618 // For lower band
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000619 tmp32 = WEBRTC_SPL_MUL((int32_t)out[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000620 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000621 out[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000622 // For higher band
623 if (FS == 32000)
624 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000625 tmp32 = WEBRTC_SPL_MUL((int32_t)out_H[k * L + n],
niklase@google.com470e71d2011-07-07 08:21:25 +0000626 WEBRTC_SPL_RSHIFT_W32(gain32, 4));
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000627 out_H[k * L + n] = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32 , 16);
niklase@google.com470e71d2011-07-07 08:21:25 +0000628 }
629 gain32 += delta;
630 }
631 }
632
633 return 0;
634}
635
636void WebRtcAgc_InitVad(AgcVad_t *state)
637{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000638 int16_t k;
niklase@google.com470e71d2011-07-07 08:21:25 +0000639
640 state->HPstate = 0; // state of high pass filter
641 state->logRatio = 0; // log( P(active) / P(inactive) )
642 // average input level (Q10)
643 state->meanLongTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
644
645 // variance of input level (Q8)
646 state->varianceLongTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
647
648 state->stdLongTerm = 0; // standard deviation of input level in dB
649 // short-term average input level (Q10)
650 state->meanShortTerm = WEBRTC_SPL_LSHIFT_W16(15, 10);
651
652 // short-term variance of input level (Q8)
653 state->varianceShortTerm = WEBRTC_SPL_LSHIFT_W32(500, 8);
654
655 state->stdShortTerm = 0; // short-term standard deviation of input level in dB
656 state->counter = 3; // counts updates
657 for (k = 0; k < 8; k++)
658 {
659 // downsampling filter
660 state->downState[k] = 0;
661 }
662}
663
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000664int16_t WebRtcAgc_ProcessVad(AgcVad_t *state, // (i) VAD state
665 const int16_t *in, // (i) Speech signal
666 int16_t nrSamples) // (i) number of samples
niklase@google.com470e71d2011-07-07 08:21:25 +0000667{
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000668 int32_t out, nrg, tmp32, tmp32b;
669 uint16_t tmpU16;
670 int16_t k, subfr, tmp16;
671 int16_t buf1[8];
672 int16_t buf2[4];
673 int16_t HPstate;
674 int16_t zeros, dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000675
676 // process in 10 sub frames of 1 ms (to save on memory)
677 nrg = 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000678 HPstate = state->HPstate;
679 for (subfr = 0; subfr < 10; subfr++)
680 {
681 // downsample to 4 kHz
682 if (nrSamples == 160)
683 {
684 for (k = 0; k < 8; k++)
685 {
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000686 tmp32 = (int32_t)in[2 * k] + (int32_t)in[2 * k + 1];
niklase@google.com470e71d2011-07-07 08:21:25 +0000687 tmp32 = WEBRTC_SPL_RSHIFT_W32(tmp32, 1);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000688 buf1[k] = (int16_t)tmp32;
niklase@google.com470e71d2011-07-07 08:21:25 +0000689 }
690 in += 16;
691
692 WebRtcSpl_DownsampleBy2(buf1, 8, buf2, state->downState);
693 } else
694 {
695 WebRtcSpl_DownsampleBy2(in, 8, buf2, state->downState);
696 in += 8;
697 }
698
699 // high pass filter and compute energy
700 for (k = 0; k < 4; k++)
701 {
702 out = buf2[k] + HPstate;
703 tmp32 = WEBRTC_SPL_MUL(600, out);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000704 HPstate = (int16_t)(WEBRTC_SPL_RSHIFT_W32(tmp32, 10) - buf2[k]);
niklase@google.com470e71d2011-07-07 08:21:25 +0000705 tmp32 = WEBRTC_SPL_MUL(out, out);
706 nrg += WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
707 }
708 }
709 state->HPstate = HPstate;
710
711 // find number of leading zeros
712 if (!(0xFFFF0000 & nrg))
713 {
714 zeros = 16;
715 } else
716 {
717 zeros = 0;
718 }
719 if (!(0xFF000000 & (nrg << zeros)))
720 {
721 zeros += 8;
722 }
723 if (!(0xF0000000 & (nrg << zeros)))
724 {
725 zeros += 4;
726 }
727 if (!(0xC0000000 & (nrg << zeros)))
728 {
729 zeros += 2;
730 }
731 if (!(0x80000000 & (nrg << zeros)))
732 {
733 zeros += 1;
734 }
735
736 // energy level (range {-32..30}) (Q10)
737 dB = WEBRTC_SPL_LSHIFT_W16(15 - zeros, 11);
738
739 // Update statistics
740
741 if (state->counter < kAvgDecayTime)
742 {
743 // decay time = AvgDecTime * 10 ms
744 state->counter++;
745 }
746
747 // update short-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000748 tmp32 = (WEBRTC_SPL_MUL_16_16(state->meanShortTerm, 15) + (int32_t)dB);
749 state->meanShortTerm = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
niklase@google.com470e71d2011-07-07 08:21:25 +0000750
751 // update short-term estimate of variance in energy level (Q8)
752 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
753 tmp32 += WEBRTC_SPL_MUL(state->varianceShortTerm, 15);
754 state->varianceShortTerm = WEBRTC_SPL_RSHIFT_W32(tmp32, 4);
755
756 // update short-term estimate of standard deviation in energy level (Q10)
757 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanShortTerm, state->meanShortTerm);
758 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceShortTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000759 state->stdShortTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000760
761 // update long-term estimate of mean energy level (Q10)
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000762 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->counter) + (int32_t)dB;
niklase@google.com470e71d2011-07-07 08:21:25 +0000763 state->meanLongTerm = WebRtcSpl_DivW32W16ResW16(tmp32,
764 WEBRTC_SPL_ADD_SAT_W16(state->counter, 1));
765
766 // update long-term estimate of variance in energy level (Q8)
767 tmp32 = WEBRTC_SPL_RSHIFT_W32(WEBRTC_SPL_MUL_16_16(dB, dB), 12);
768 tmp32 += WEBRTC_SPL_MUL(state->varianceLongTerm, state->counter);
769 state->varianceLongTerm = WebRtcSpl_DivW32W16(tmp32,
770 WEBRTC_SPL_ADD_SAT_W16(state->counter, 1));
771
772 // update long-term estimate of standard deviation in energy level (Q10)
773 tmp32 = WEBRTC_SPL_MUL_16_16(state->meanLongTerm, state->meanLongTerm);
774 tmp32 = WEBRTC_SPL_LSHIFT_W32(state->varianceLongTerm, 12) - tmp32;
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000775 state->stdLongTerm = (int16_t)WebRtcSpl_Sqrt(tmp32);
niklase@google.com470e71d2011-07-07 08:21:25 +0000776
777 // update voice activity measure (Q10)
778 tmp16 = WEBRTC_SPL_LSHIFT_W16(3, 12);
779 tmp32 = WEBRTC_SPL_MUL_16_16(tmp16, (dB - state->meanLongTerm));
780 tmp32 = WebRtcSpl_DivW32W16(tmp32, state->stdLongTerm);
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000781 tmpU16 = WEBRTC_SPL_LSHIFT_U16((uint16_t)13, 12);
niklase@google.com470e71d2011-07-07 08:21:25 +0000782 tmp32b = WEBRTC_SPL_MUL_16_U16(state->logRatio, tmpU16);
783 tmp32 += WEBRTC_SPL_RSHIFT_W32(tmp32b, 10);
784
pbos@webrtc.orgb7192b82013-04-10 07:50:54 +0000785 state->logRatio = (int16_t)WEBRTC_SPL_RSHIFT_W32(tmp32, 6);
niklase@google.com470e71d2011-07-07 08:21:25 +0000786
787 // limit
788 if (state->logRatio > 2048)
789 {
790 state->logRatio = 2048;
791 }
792 if (state->logRatio < -2048)
793 {
794 state->logRatio = -2048;
795 }
796
797 return state->logRatio; // Q10
798}