Line data Source code
1 : /*====================================================================================
2 : EVS Codec 3GPP TS26.452 Aug 12, 2021. Version 16.3.0
3 : ====================================================================================*/
4 :
5 :
6 : #include "options.h"
7 : #include "stl.h"
8 : #include "basop_util.h"
9 : // #include "prot_fx.h"
10 : #include "rom_com.h"
11 : #include <assert.h>
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include <stdarg.h>
15 : #include "prot_fx.h" /* Function prototypes */
16 : #include "ivas_prot_fx.h" /* Function prototypes */
17 : #include "prot_fx_enc.h" /* Function prototypes */
18 :
19 :
20 : /* Exponent of attack threshold. Picked according to current threshold values. */
21 : #define ATTACKTHRESHOLD_E 4
22 : /* Exponent of subblock energies and accumulated subblock energies.
23 : The current value of 2 does not prevent saturations to happen in all cases. */
24 : #define SUBBLOCK_NRG_E 4
25 : /* Exponent of the subblock energy change.
26 : This value is coupled to the transient detector API. */
27 : #define SUBBLOCK_NRG_CHANGE_E NRG_CHANGE_E
28 :
29 : #define MIN_BLOCK_ENERGY ( (Word32) 1 )
30 :
31 : #define MIN_BLOCK_ENERGY_FX 107
32 : #define THR_HIGH_FX 17408 /* 8.5f in Q11 */
33 : #define THR_NORM_HIGH_FX 16384 /* 8 in Q11 */
34 : #define THR_NORM_LOW_FX 9216 /* 4.5f in Q11 */
35 : #define THR_LOW_FX 8704 /* 4.25f in Q11 */
36 : #define THR_LOW_STEP_FX ONE_IN_Q11 /* 1 in Q11 */
37 : #define MIN_BLOCK_ENERGY_IVAS_FX_Q7 13743 /* 107.37f in Q7 */
38 :
39 : /************************************************/
40 : /* */
41 : /* Internal functions prototypes */
42 : /* */
43 : /************************************************/
44 :
45 : static void InitDelayBuffer( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer );
46 : static void InitSubblockEnergies( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer, SubblockEnergies *pSubblockEnergies );
47 : static void InitSubblockEnergies_ivas_fx( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer, SubblockEnergies *pSubblockEnergies );
48 : static void InitTransientDetector_fx( SubblockEnergies *pSubblockEnergies, Word16 nDelay, Word16 nSubblocksToCheck, TCheckSubblocksForAttack_fx pCheckSubblocksForAttack, Word16 attackRatioThreshold, TransientDetector *pTransientDetector );
49 : static void InitTransientDetector_ivas_fx( SubblockEnergies *pSubblockEnergies, Word16 nDelay, Word16 nSubblocksToCheck, TCheckSubblocksForAttack_fx pCheckSubblocksForAttack, Word16 attackRatioThreshold, TransientDetector *pTransientDetector );
50 : static void UpdateDelayBuffer( Word16 const *input, Word16 nSamplesAvailable, DelayBuffer *pDelayBuffer );
51 : static void HighPassFilter_fx( Word16 const *input, Word16 length, Word16 *pFirState1, Word16 *pFirState2, Word16 *output );
52 : static void UpdateSubblockEnergies( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
53 : static void CalculateSubblockEnergies( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
54 : static void RunTransientDetector_fx( TransientDetector *pTransientDetector );
55 :
56 : static void CalculateSubblockEnergies_ivas_fx( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
57 :
58 : static void UpdateSubblockEnergies_ivas_fx( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
59 :
60 : /************************************************/
61 : /* */
62 : /* Functions that define transient detectors */
63 : /* */
64 : /************************************************/
65 :
66 : /** TCX decision.
67 : * Check IF there is an attack in a subblock. Version FOR TCX Long/Short decision.
68 : * See TCheckSubblocksForAttack_fx FOR definition of parameters.
69 : * It is assumed that the delay of MDCT overlap was not taken into account, so that the last subblock corresponds to the newest input subblock.
70 : */
71 3100 : static void GetAttackForTCXDecision( Word32 const *pSubblockNrg, Word32 const *pAccSubblockNrg, Word16 nSubblocks, Word16 nPastSubblocks, Word16 attackRatioThreshold, Word16 *pbIsAttackPresent, Word16 *pAttackIndex )
72 : {
73 : Word16 i;
74 : Word16 bIsAttackPresent, attackIndex;
75 : Word16 attackRatioThreshold_1_5;
76 :
77 : (void) nPastSubblocks;
78 : (void) nSubblocks;
79 3100 : assert( nSubblocks >= NSUBBLOCKS );
80 3100 : assert( nPastSubblocks >= 2 );
81 :
82 : /* attackRatioThreshold_1_5 = attackRatioThreshold * 1.5, exponent is ATTACKTHRESHOLD_E+1 */
83 3100 : attackRatioThreshold_1_5 = add( shr( attackRatioThreshold, 2 ), shr( attackRatioThreshold, 1 ) );
84 :
85 3100 : move16();
86 3100 : move16();
87 3100 : bIsAttackPresent = FALSE;
88 3100 : attackIndex = 0;
89 : /* Search for the last attack in the subblocks */
90 3100 : if ( s_or( (Word16) GT_32( L_shr( pSubblockNrg[-1], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-1], attackRatioThreshold ) ),
91 3100 : L_sub( L_shr( pSubblockNrg[-2], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-2], attackRatioThreshold ) ) > 0 ) )
92 : {
93 22 : move16();
94 22 : bIsAttackPresent = TRUE;
95 : }
96 :
97 27900 : FOR( i = 0; i < NSUBBLOCKS; i++ )
98 : {
99 24800 : IF( GT_32( L_shr( pSubblockNrg[i], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[i], attackRatioThreshold ) ) )
100 : {
101 96 : if ( i < 6 )
102 : {
103 73 : move16();
104 73 : bIsAttackPresent = TRUE;
105 : }
106 :
107 96 : if ( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
108 : {
109 95 : move16();
110 95 : attackIndex = i;
111 : }
112 : }
113 : ELSE /* no attack, but set index anyway in case of strong energy increase */
114 : {
115 24704 : IF( s_and( ( (Word16) GT_32( L_shr( pSubblockNrg[i], 1 + ATTACKTHRESHOLD_E ), Mpy_32_16_1( pSubblockNrg[sub( i, 1 )], attackRatioThreshold_1_5 ) ) ),
116 : ( L_sub( L_shr( pSubblockNrg[i], 1 + ATTACKTHRESHOLD_E ), Mpy_32_16_1( pSubblockNrg[sub( i, 2 )], attackRatioThreshold_1_5 ) ) > 0 ) ) )
117 : {
118 :
119 26 : if ( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
120 : {
121 26 : move16();
122 26 : attackIndex = i;
123 : }
124 : }
125 : }
126 : }
127 : /* avoid post-echos on click sounds (very short transients) due to TNS aliasing */
128 3100 : if ( EQ_16( attackIndex, 4 ) )
129 : {
130 13 : move16();
131 13 : attackIndex = 7;
132 : }
133 3100 : if ( EQ_16( attackIndex, 5 ) )
134 : {
135 22 : move16();
136 22 : attackIndex = 6;
137 : }
138 :
139 3100 : move16();
140 3100 : move16();
141 3100 : *pAttackIndex = attackIndex;
142 3100 : *pbIsAttackPresent = bIsAttackPresent;
143 3100 : }
144 :
145 1170853 : static void GetAttackForTCXDecision_ivas_fx( Word32 const *pSubblockNrg, Word32 const *pAccSubblockNrg, Word16 nSubblocks, Word16 nPastSubblocks, Word16 attackRatioThreshold, Word16 *pbIsAttackPresent, Word16 *pAttackIndex )
146 : {
147 : Word16 i;
148 : Word16 bIsAttackPresent, attackIndex;
149 : Word16 attackRatioThreshold_1_5;
150 : Word64 W_tmp1, W_tmp2, W_tmp3;
151 :
152 : (void) nPastSubblocks;
153 : (void) nSubblocks;
154 1170853 : assert( nSubblocks >= NSUBBLOCKS );
155 1170853 : assert( nPastSubblocks >= 2 );
156 :
157 : /* attackRatioThreshold_1_5 = attackRatioThreshold * 1.5, exponent is ATTACKTHRESHOLD_E+1 */
158 1170853 : attackRatioThreshold_1_5 = add( shr( attackRatioThreshold, 2 ), shr( attackRatioThreshold, 1 ) );
159 :
160 1170853 : move16();
161 1170853 : move16();
162 1170853 : bIsAttackPresent = FALSE;
163 1170853 : attackIndex = -1;
164 : /* Search for the last attack in the subblocks */
165 1170853 : IF( s_or( (Word16) GT_32( L_shr( pSubblockNrg[-1], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-1], attackRatioThreshold ) ),
166 : L_sub( L_shr( pSubblockNrg[-2], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-2], attackRatioThreshold ) ) > 0 ) )
167 : {
168 4566 : move16();
169 4566 : bIsAttackPresent = TRUE;
170 4566 : attackIndex = 0;
171 4566 : move16();
172 : }
173 :
174 10537677 : FOR( i = 0; i < NSUBBLOCKS; i++ )
175 : {
176 9366824 : W_tmp2 = W_shr( W_mult_32_16( pAccSubblockNrg[i], attackRatioThreshold ), 1 );
177 9366824 : W_tmp1 = W_shl( pSubblockNrg[i], ( 15 - ATTACKTHRESHOLD_E ) );
178 :
179 9366824 : IF( GT_64( W_tmp1, W_tmp2 ) )
180 : {
181 26373 : if ( i < 6 )
182 : {
183 21736 : move16();
184 21736 : bIsAttackPresent = TRUE;
185 : }
186 :
187 26373 : IF( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
188 : {
189 26003 : move16();
190 26003 : attackIndex = i;
191 26003 : W_tmp2 = W_shr( W_mult_32_16( pAccSubblockNrg[i], attackRatioThreshold ), 1 );
192 26003 : W_tmp2 = W_add( W_tmp2, W_shr( W_tmp2, 3 ) ); // pAccSubblockNrg[i] * 1.125f
193 26003 : W_tmp1 = W_shl( pSubblockNrg[i], ( 15 - ATTACKTHRESHOLD_E ) );
194 26003 : if ( s_and( (Word16) LT_64( W_tmp1, W_tmp2 ), s_or( (Word16) EQ_16( i, 2 ), (Word16) EQ_16( i, 6 ) ) ) )
195 : {
196 444 : attackIndex = add( attackIndex, 1 ); /* avoid minimum overlap to prevent clicks */
197 : }
198 : }
199 : }
200 : ELSE /* no attack, but set index anyway in case of strong energy increase */
201 : {
202 9340451 : W_tmp2 = W_shr( W_mult_32_16( pSubblockNrg[i - 1], attackRatioThreshold_1_5 ), 1 );
203 9340451 : W_tmp1 = W_shl( pSubblockNrg[i], ( 15 - ( ATTACKTHRESHOLD_E + 1 ) ) );
204 9340451 : W_tmp3 = W_shr( W_mult_32_16( pSubblockNrg[i - 2], attackRatioThreshold_1_5 ), 1 );
205 :
206 9340451 : IF( s_and( ( (Word16) GT_64( W_tmp1, W_tmp2 ) ),
207 : ( W_sub( W_tmp1, W_tmp3 ) > 0 ) ) )
208 : {
209 :
210 6436 : IF( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
211 : {
212 6425 : move16();
213 6425 : attackIndex = i;
214 :
215 6425 : W_tmp2 = W_mult_32_16( pSubblockNrg[i - 1], attackRatioThreshold );
216 6425 : W_tmp3 = W_mult_32_16( pSubblockNrg[i - 2], attackRatioThreshold );
217 6425 : W_tmp1 = W_shl( pSubblockNrg[i], ( 15 - ATTACKTHRESHOLD_E ) );
218 :
219 6425 : if ( s_and( (Word16) s_or( (Word16) LT_64( W_tmp1, W_tmp2 ), (Word16)
220 6425 : LT_64( W_tmp1, W_tmp3 ) ),
221 6425 : s_or( (Word16) EQ_16( i, 2 ), (Word16) EQ_16( i, 6 ) ) ) )
222 : {
223 835 : attackIndex = add( attackIndex, 1 ); /* avoid minimum overlap to prevent clicks */
224 : }
225 : }
226 : }
227 : }
228 : }
229 : /* avoid post-echos on click sounds (very short transients) due to TNS aliasing */
230 1170853 : if ( EQ_16( attackIndex, 4 ) )
231 : {
232 2550 : move16();
233 2550 : attackIndex = 7;
234 : }
235 1170853 : if ( EQ_16( attackIndex, 5 ) )
236 : {
237 2827 : move16();
238 2827 : attackIndex = 6;
239 : }
240 :
241 1170853 : move16();
242 1170853 : move16();
243 1170853 : *pAttackIndex = attackIndex;
244 1170853 : *pbIsAttackPresent = bIsAttackPresent;
245 1170853 : }
246 :
247 0 : void GetAttackForTCXDecision_fx( Word32 const *pSubblockNrg, Word32 const *pAccSubblockNrg, Word16 nSubblocks, Word16 nPastSubblocks, Word16 attackRatioThreshold, Word16 *pbIsAttackPresent, Word16 *pAttackIndex )
248 : {
249 : Word16 i;
250 : Word16 bIsAttackPresent, attackIndex;
251 : Word16 attackRatioThreshold_1_5;
252 :
253 : (void) nPastSubblocks;
254 : (void) nSubblocks;
255 0 : assert( nSubblocks >= NSUBBLOCKS );
256 0 : assert( nPastSubblocks >= 2 );
257 :
258 : /* attackRatioThreshold_1_5 = attackRatioThreshold * 1.5, exponent is ATTACKTHRESHOLD_E+1 */
259 0 : attackRatioThreshold_1_5 = add( shr( attackRatioThreshold, 2 ), shr( attackRatioThreshold, 1 ) );
260 :
261 0 : move16();
262 0 : move16();
263 0 : bIsAttackPresent = FALSE;
264 0 : attackIndex = 0;
265 : /* Search for the last attack in the subblocks */
266 0 : if ( s_or( (Word16) GT_32( L_shr( pSubblockNrg[-1], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-1], attackRatioThreshold ) ),
267 0 : L_sub( L_shr( pSubblockNrg[-2], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[-2], attackRatioThreshold ) ) > 0 ) )
268 : {
269 0 : move16();
270 0 : bIsAttackPresent = TRUE;
271 : }
272 :
273 0 : FOR( i = 0; i < NSUBBLOCKS; i++ )
274 : {
275 0 : IF( GT_32( L_shr( pSubblockNrg[i], ATTACKTHRESHOLD_E ), Mpy_32_16_1( pAccSubblockNrg[i], attackRatioThreshold ) ) )
276 : {
277 0 : if ( i < 6 )
278 : {
279 0 : move16();
280 0 : bIsAttackPresent = TRUE;
281 : }
282 :
283 0 : if ( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
284 : {
285 0 : move16();
286 0 : attackIndex = i;
287 : }
288 : }
289 : ELSE /* no attack, but set index anyway in case of strong energy increase */
290 : {
291 0 : IF( s_and( ( (Word16) GT_32( L_shr( pSubblockNrg[i], 1 + ATTACKTHRESHOLD_E ), Mpy_32_16_1( pSubblockNrg[sub( i, 1 )], attackRatioThreshold_1_5 ) ) ),
292 : ( L_sub( L_shr( pSubblockNrg[i], 1 + ATTACKTHRESHOLD_E ), Mpy_32_16_1( pSubblockNrg[sub( i, 2 )], attackRatioThreshold_1_5 ) ) > 0 ) ) )
293 : {
294 :
295 0 : if ( s_and( (Word16) NE_16( attackIndex, 2 ), (Word16) NE_16( attackIndex, 6 ) ) )
296 : {
297 0 : move16();
298 0 : attackIndex = i;
299 : }
300 : }
301 : }
302 : }
303 : /* avoid post-echos on click sounds (very short transients) due to TNS aliasing */
304 0 : if ( EQ_16( attackIndex, 4 ) )
305 : {
306 0 : move16();
307 0 : attackIndex = 7;
308 : }
309 0 : if ( EQ_16( attackIndex, 5 ) )
310 : {
311 0 : move16();
312 0 : attackIndex = 6;
313 : }
314 :
315 0 : move16();
316 0 : move16();
317 0 : *pAttackIndex = attackIndex;
318 0 : *pbIsAttackPresent = bIsAttackPresent;
319 0 : }
320 :
321 : /** Initialize TCX transient detector.
322 : * See InitTransientDetector_fx for definition of parameters.
323 : */
324 3 : static void InitTCXTransientDetector( Word16 nDelay, SubblockEnergies *pSubblockEnergies, TransientDetector *pTransientDetector )
325 : {
326 3 : InitTransientDetector_fx( pSubblockEnergies, nDelay, NSUBBLOCKS, GetAttackForTCXDecision, 17408 /*8.5f/(1<<ATTACKTHRESHOLD_E) Q15*/, pTransientDetector );
327 3 : }
328 :
329 :
330 : /************************************************/
331 : /* */
332 : /* Interface functions */
333 : /* */
334 : /************************************************/
335 :
336 3 : void InitTransientDetection_fx( Word16 nFrameLength,
337 : Word16 nTCXDelay,
338 : TransientDetection *pTransientDetection )
339 : {
340 : /* Init the delay buffer. */
341 3 : InitDelayBuffer( nFrameLength, nTCXDelay, &pTransientDetection->delayBuffer );
342 : /* Init a subblock energies buffer used for the TCX Short/Long decision. */
343 3 : InitSubblockEnergies( nFrameLength, nTCXDelay, &pTransientDetection->delayBuffer, &pTransientDetection->subblockEnergies );
344 : /* Init the TCX Short/Long transient detector. */
345 3 : InitTCXTransientDetector( nTCXDelay, &pTransientDetection->subblockEnergies, &pTransientDetection->transientDetector );
346 : /* We need two past subblocks for the TCX TD and NSUBBLOCKS+1 for the temporal flatness measure FOR the TCX LTP. */
347 6 : pTransientDetection->transientDetector.pSubblockEnergies->nDelay =
348 3 : add( pTransientDetection->transientDetector.pSubblockEnergies->nDelay, NSUBBLOCKS + 1 );
349 3 : }
350 :
351 8183 : void InitTransientDetection_ivas_fx( Word16 nFrameLength,
352 : Word16 nTCXDelay,
353 : TRAN_DET_HANDLE pTransientDetection,
354 : const Word16 ext_mem_flag )
355 : {
356 : /* Init the delay buffer. */
357 8183 : InitDelayBuffer( nFrameLength, nTCXDelay, &pTransientDetection->delayBuffer );
358 : /* Init a subblock energies buffer used for the TCX Short/Long decision. */
359 8183 : InitSubblockEnergies_ivas_fx( nFrameLength, nTCXDelay, &pTransientDetection->delayBuffer, &pTransientDetection->subblockEnergies );
360 : /* Init the TCX Short/Long transient detector. */
361 8183 : InitTransientDetector_ivas_fx( &pTransientDetection->subblockEnergies, nTCXDelay, NSUBBLOCKS, GetAttackForTCXDecision_ivas_fx, 17408 /*8.5f/(1<<ATTACKTHRESHOLD_E) Q15*/, &pTransientDetection->transientDetector );
362 : /* We need two past subblocks for the TCX TD and NSUBBLOCKS+1 for the temporal flatness measure FOR the TCX LTP. */
363 8183 : IF( ext_mem_flag )
364 : {
365 16366 : pTransientDetection->transientDetector.pSubblockEnergies->nDelay =
366 8183 : add( pTransientDetection->transientDetector.pSubblockEnergies->nDelay, add( ( NSUBBLOCKS + 1 ), ( NSUBBLOCKS_SHIFT + 1 ) ) );
367 8183 : move16();
368 : }
369 : ELSE
370 : {
371 0 : pTransientDetection->transientDetector.pSubblockEnergies->nDelay =
372 0 : add( pTransientDetection->transientDetector.pSubblockEnergies->nDelay, NSUBBLOCKS + 1 );
373 0 : move16();
374 : }
375 8183 : }
376 :
377 : /**
378 : * \brief Calculate average of temporal energy change.
379 : * \return average temporal energy change with exponent = 8
380 : */
381 4410 : Word16 GetTCXAvgTemporalFlatnessMeasure_fx( struct TransientDetection const *pTransientDetection, Word16 nCurrentSubblocks, Word16 nPrevSubblocks )
382 : {
383 : Word16 i;
384 : TransientDetector const *pTransientDetector;
385 : SubblockEnergies const *pSubblockEnergies;
386 : Word16 nDelay;
387 : Word16 nRelativeDelay;
388 : Word16 const *pSubblockNrgChange;
389 : Word32 sumTempFlatness;
390 : Word16 nTotBlocks;
391 :
392 :
393 : /* Initialization */
394 4410 : pTransientDetector = &pTransientDetection->transientDetector;
395 4410 : pSubblockEnergies = pTransientDetector->pSubblockEnergies;
396 4410 : move16();
397 4410 : nDelay = pTransientDetector->nDelay;
398 4410 : nRelativeDelay = sub( pSubblockEnergies->nDelay, nDelay );
399 4410 : pSubblockNrgChange = NULL;
400 4410 : nTotBlocks = add( nCurrentSubblocks, nPrevSubblocks );
401 :
402 4410 : assert( nTotBlocks > 0 );
403 :
404 4410 : sumTempFlatness = L_deposit_l( 0 );
405 :
406 4410 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
407 :
408 4410 : move16();
409 4410 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[sub( nRelativeDelay, nPrevSubblocks )];
410 :
411 45565 : FOR( i = 0; i < nTotBlocks; i++ )
412 : {
413 41155 : sumTempFlatness = L_add( sumTempFlatness, L_deposit_l( pSubblockNrgChange[i] ) );
414 : }
415 :
416 : /* exponent = AVG_FLAT_E */
417 4410 : i = div_l( L_shl( sumTempFlatness, 16 - 15 + SUBBLOCK_NRG_CHANGE_E - AVG_FLAT_E ), nTotBlocks );
418 :
419 4410 : return i;
420 : }
421 :
422 2951968 : Word32 GetTCXAvgTemporalFlatnessMeasure_ivas_fx( struct TransientDetection const *pTransientDetection, Word16 nCurrentSubblocks, Word16 nPrevSubblocks )
423 : {
424 : Word32 i;
425 : TransientDetector const *pTransientDetector;
426 : SubblockEnergies const *pSubblockEnergies;
427 : Word16 nDelay;
428 : Word16 nRelativeDelay;
429 : Word32 const *pSubblockNrgChange;
430 : Word16 const *pSubblockNrgChange_exp;
431 : Word32 sumTempFlatness;
432 : Word16 nTotBlocks, sumTempFlatness_exp, exp;
433 :
434 : /* Initialization */
435 2951968 : pTransientDetector = &pTransientDetection->transientDetector;
436 2951968 : pSubblockEnergies = pTransientDetector->pSubblockEnergies;
437 2951968 : nDelay = pTransientDetector->nDelay;
438 2951968 : move16();
439 2951968 : nRelativeDelay = sub( pSubblockEnergies->nDelay, nDelay );
440 2951968 : pSubblockNrgChange = NULL;
441 2951968 : nTotBlocks = add( nCurrentSubblocks, nPrevSubblocks );
442 :
443 2951968 : assert( nTotBlocks > 0 );
444 :
445 2951968 : sumTempFlatness = L_deposit_l( 0 );
446 :
447 2951968 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
448 :
449 2951968 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange_32fx[( nRelativeDelay - nPrevSubblocks )];
450 2951968 : pSubblockNrgChange_exp = &pSubblockEnergies->subblockNrgChange_exp[( nRelativeDelay - nPrevSubblocks )];
451 2951968 : sumTempFlatness = 0;
452 2951968 : move32();
453 2951968 : sumTempFlatness_exp = 0;
454 2951968 : move16();
455 30567032 : FOR( i = 0; i < nTotBlocks; i++ )
456 : {
457 27615064 : sumTempFlatness = BASOP_Util_Add_Mant32Exp( sumTempFlatness, sumTempFlatness_exp, pSubblockNrgChange[i], pSubblockNrgChange_exp[i], &sumTempFlatness_exp );
458 : }
459 :
460 : /* exponent = AVG_FLAT_E */
461 2951968 : i = BASOP_Util_Divide3232_Scale_newton( sumTempFlatness, nTotBlocks, &exp );
462 2951968 : exp = add( exp, sub( sumTempFlatness_exp, 31 ) );
463 2951968 : i = L_shl_sat( i, sub( exp, 10 ) ); // Can be saturated, since it is used for comparision againt 3.25/20.0f, Q21
464 2951968 : return i;
465 : }
466 :
467 1310 : Word16 GetTCXMaxenergyChange_fx( struct TransientDetection const *pTransientDetection,
468 : const Word8 isTCX10,
469 : const Word16 nCurrentSubblocks,
470 : const Word16 nPrevSubblocks )
471 : {
472 : Word16 i;
473 : TransientDetector const *pTransientDetector;
474 : SubblockEnergies const *pSubblockEnergies;
475 : Word16 nDelay;
476 : Word16 nRelativeDelay;
477 : Word16 const *pSubblockNrgChange;
478 : Word16 maxEnergyChange;
479 : Word16 nTotBlocks;
480 :
481 :
482 1310 : pTransientDetector = &pTransientDetection->transientDetector;
483 1310 : pSubblockEnergies = pTransientDetector->pSubblockEnergies;
484 1310 : move16();
485 1310 : nDelay = pTransientDetector->nDelay;
486 1310 : nRelativeDelay = sub( pSubblockEnergies->nDelay, nDelay );
487 1310 : pSubblockNrgChange = NULL;
488 1310 : nTotBlocks = nCurrentSubblocks + nPrevSubblocks;
489 1310 : move16();
490 :
491 1310 : assert( nTotBlocks > 0 );
492 1310 : maxEnergyChange = 0 /*0.0f Q7*/;
493 1310 : move16();
494 :
495 1310 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
496 1310 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nRelativeDelay - nPrevSubblocks];
497 :
498 1310 : IF( s_or( pTransientDetector->bIsAttackPresent, isTCX10 ) ) /* frame is TCX-10 */
499 : {
500 39 : Word32 const *pSubblockNrg = &pSubblockEnergies->subblockNrg[sub( nRelativeDelay, nPrevSubblocks )];
501 : Word32 nrgMin, nrgMax;
502 39 : Word16 idxMax = 0;
503 :
504 39 : move16();
505 :
506 39 : nrgMax = L_add( pSubblockNrg[0], 0 );
507 :
508 : /* find subblock with maximum energy */
509 484 : FOR( i = 1; i < nTotBlocks; i++ )
510 : {
511 445 : if ( LT_32( nrgMax, pSubblockNrg[i] ) )
512 : {
513 113 : idxMax = i;
514 113 : move16();
515 : }
516 445 : nrgMax = L_max( nrgMax, pSubblockNrg[i] );
517 : }
518 :
519 39 : nrgMin = L_add( nrgMax, 0 );
520 :
521 : /* find minimum energy after maximum */
522 191 : FOR( i = idxMax + 1; i < nTotBlocks; i++ )
523 : {
524 152 : nrgMin = L_min( nrgMin, pSubblockNrg[i] );
525 : }
526 : /* lower maxEnergyChange if energy doesn't decrease much after energy peak */
527 : /* if (nrgMin > 0.375f * nrgMax) */
528 39 : if ( LT_32( Mpy_32_16_1( nrgMax, 12288 /*0.375f Q15*/ ), nrgMin ) )
529 : {
530 10 : nTotBlocks = sub( idxMax, 3 );
531 : }
532 : }
533 :
534 17609 : FOR( i = 0; i < nTotBlocks; i++ )
535 : {
536 16299 : maxEnergyChange = s_max( maxEnergyChange, pSubblockNrgChange[i] );
537 : }
538 :
539 1310 : move16();
540 1310 : i = maxEnergyChange;
541 :
542 1310 : return i;
543 : }
544 :
545 :
546 878643 : Word16 GetTCXMaxenergyChange_ivas_fx( TRAN_DET_HANDLE hTranDet,
547 : const Word8 isTCX10,
548 : const Word16 nCurrentSubblocks,
549 : const Word16 nPrevSubblocks )
550 : {
551 : Word16 i;
552 : TransientDetector const *pTransientDetector;
553 : SubblockEnergies const *pSubblockEnergies;
554 : Word16 nDelay;
555 : Word16 nRelativeDelay;
556 : Word32 const *pSubblockNrgChange;
557 : Word16 const *pSubblockNrgChange_exp;
558 : Word16 maxEnergyChange;
559 : Word16 nTotBlocks;
560 :
561 :
562 878643 : pTransientDetector = &hTranDet->transientDetector;
563 878643 : pSubblockEnergies = pTransientDetector->pSubblockEnergies;
564 878643 : move16();
565 878643 : nDelay = pTransientDetector->nDelay;
566 878643 : nRelativeDelay = sub( pSubblockEnergies->nDelay, nDelay );
567 878643 : pSubblockNrgChange = NULL;
568 878643 : nTotBlocks = add( nCurrentSubblocks, nPrevSubblocks );
569 :
570 878643 : assert( nTotBlocks > 0 );
571 878643 : maxEnergyChange = 0 /*0.0f Q3*/;
572 878643 : move16();
573 878643 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
574 878643 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange_32fx[nRelativeDelay - nPrevSubblocks];
575 878643 : pSubblockNrgChange_exp = &pSubblockEnergies->subblockNrgChange_exp[nRelativeDelay - nPrevSubblocks];
576 :
577 878643 : IF( s_or( pTransientDetector->bIsAttackPresent, isTCX10 ) ) /* frame is TCX-10 */
578 : {
579 16598 : Word32 const *pSubblockNrg = &pSubblockEnergies->subblockNrg[sub( nRelativeDelay, nPrevSubblocks )];
580 : Word32 nrgMin, nrgMax;
581 16598 : Word16 idxMax = 0;
582 16598 : move16();
583 :
584 16598 : nrgMax = L_add( pSubblockNrg[0], 0 );
585 :
586 : /* find subblock with maximum energy */
587 211431 : FOR( i = 1; i < nTotBlocks; i++ )
588 : {
589 194833 : if ( LT_32( nrgMax, pSubblockNrg[i] ) )
590 : {
591 54084 : idxMax = i;
592 54084 : move16();
593 : }
594 194833 : nrgMax = L_max( nrgMax, pSubblockNrg[i] );
595 : }
596 :
597 16598 : nrgMin = nrgMax;
598 16598 : move32();
599 : /* find minimum energy after maximum */
600 85818 : FOR( i = idxMax + 1; i < nTotBlocks; i++ )
601 : {
602 69220 : nrgMin = L_min( nrgMin, pSubblockNrg[i] );
603 : }
604 : /* lower maxEnergyChange if energy doesn't decrease much after energy peak */
605 : /* if (nrgMin > 0.375f * nrgMax) */
606 16598 : if ( LT_32( Mpy_32_16_1( nrgMax, 12288 /*0.375f Q15*/ ), nrgMin ) )
607 : {
608 6270 : nTotBlocks = sub( idxMax, 3 );
609 : }
610 : }
611 :
612 11927120 : FOR( i = 0; i < nTotBlocks; i++ )
613 : {
614 11048477 : maxEnergyChange = s_max( maxEnergyChange, extract_l( L_shl_sat( pSubblockNrgChange[i], sub( pSubblockNrgChange_exp[i], 28 ) ) ) ); // Q3
615 : }
616 :
617 878643 : move16();
618 878643 : i = maxEnergyChange; // Q3
619 :
620 878643 : return i;
621 : }
622 :
623 3100 : void RunTransientDetection_fx( Word16 const *input, Word16 nSamplesAvailable, TransientDetection *pTransientDetection )
624 : {
625 : Word16 filteredInput[L_FRAME48k];
626 3100 : SubblockEnergies *pSubblockEnergies = &pTransientDetection->subblockEnergies;
627 3100 : TransientDetector *pTransientDetector = &pTransientDetection->transientDetector;
628 :
629 3100 : assert( ( input != NULL ) && ( pTransientDetection != NULL ) && ( pSubblockEnergies != NULL ) && ( pTransientDetector != NULL ) );
630 :
631 3100 : HighPassFilter_fx( input, nSamplesAvailable, &pSubblockEnergies->firState1, &pSubblockEnergies->firState2, filteredInput );
632 :
633 : /* Update subblock energies. */
634 3100 : UpdateSubblockEnergies( filteredInput, nSamplesAvailable, pSubblockEnergies );
635 :
636 : /* Run transient detectors. */
637 3100 : RunTransientDetector_fx( pTransientDetector );
638 :
639 : /* Update the delay buffer. */
640 3100 : UpdateDelayBuffer( filteredInput, nSamplesAvailable, &pTransientDetection->delayBuffer );
641 3100 : }
642 :
643 :
644 1170853 : void RunTransientDetection_ivas_fx(
645 : Word16 *input_fx, /* i : input signal Q: q_input */
646 : const Word16 length, /* i : frame length */
647 : TRAN_DET_HANDLE hTranDet, /* i/o: transient detection handle */
648 : Word16 q_input /*i: stores q for input_fx*/
649 : )
650 : {
651 :
652 : Word16 filteredInput_fx[L_FRAME_MAX];
653 1170853 : SubblockEnergies *pSubblockEnergies = &hTranDet->subblockEnergies;
654 1170853 : TransientDetector *pTransientDetector = &hTranDet->transientDetector;
655 :
656 : Word32 e0_fx, e1_fx;
657 1170853 : Word16 exp = 0, exp1 = 0, q_old_inp = q_input;
658 1170853 : move16();
659 1170853 : move16();
660 1170853 : move16();
661 :
662 1170853 : assert( ( input_fx != NULL ) && ( hTranDet != NULL ) && ( pSubblockEnergies != NULL ) && ( pTransientDetector != NULL ) );
663 :
664 : /* Variable initializations */
665 1170853 : Word16 shift = norm_s( pSubblockEnergies->firState1 );
666 1170853 : shift = s_min( shift, norm_s( pSubblockEnergies->firState2 ) );
667 :
668 1170853 : IF( GT_16( sub( q_input, pSubblockEnergies->q_firState ), shift ) )
669 : {
670 71239 : Scale_sig( input_fx, length, add( sub( pSubblockEnergies->q_firState, q_input ), shift ) ); // q_firState + shift
671 71239 : q_input = add( pSubblockEnergies->q_firState, shift ); // q_firState + shift
672 71239 : pSubblockEnergies->firState1 = shl( pSubblockEnergies->firState1, shift ); // q_firState + shift
673 71239 : move16();
674 71239 : pSubblockEnergies->firState2 = shl( pSubblockEnergies->firState2, shift ); // q_firState + shift
675 71239 : move16();
676 71239 : pSubblockEnergies->q_firState = add( pSubblockEnergies->q_firState, shift ); // q_firState + shift
677 71239 : move16();
678 : }
679 : ELSE
680 : {
681 1099614 : Word16 norm = norm_arr( input_fx, length );
682 1099614 : IF( norm == 0 )
683 : {
684 771957 : Scale_sig( input_fx, length, -1 );
685 771957 : q_input = sub( q_input, 1 );
686 : }
687 1099614 : pSubblockEnergies->firState1 = shl( pSubblockEnergies->firState1, sub( q_input, pSubblockEnergies->q_firState ) ); // q_input
688 1099614 : move16();
689 1099614 : pSubblockEnergies->firState2 = shl( pSubblockEnergies->firState2, sub( q_input, pSubblockEnergies->q_firState ) ); // q_input
690 1099614 : move16();
691 1099614 : pSubblockEnergies->q_firState = q_input;
692 1099614 : move16();
693 : }
694 :
695 1170853 : HighPassFilter_fx( input_fx, length, &pSubblockEnergies->firState1, &pSubblockEnergies->firState2, filteredInput_fx ); // q_input
696 :
697 1170853 : IF( NE_16( q_input, q_old_inp ) )
698 : {
699 843196 : Scale_sig( input_fx, length, sub( q_old_inp, q_input ) ); // q_old_inp
700 : }
701 :
702 : /* Update subblock energies. */
703 1170853 : Scale_sig( filteredInput_fx, length, sub( 0, q_input ) ); // q0
704 1170853 : UpdateSubblockEnergies_ivas_fx( filteredInput_fx, length, pSubblockEnergies );
705 :
706 : /* Run transient detectors. */
707 1170853 : RunTransientDetector_fx( pTransientDetector );
708 :
709 : /* Update the delay buffer. */
710 1170853 : UpdateDelayBuffer( filteredInput_fx, length, &hTranDet->delayBuffer );
711 :
712 : /* compute ramp up flag */
713 1170853 : pSubblockEnergies->ramp_up_flag = (UWord16) L_and( L_shl( pSubblockEnergies->ramp_up_flag, 1 ), 0x0003 );
714 1170853 : move16();
715 :
716 1170853 : e0_fx = dotp_fx( filteredInput_fx + /* length / 2 */ shr( length, 1 ), filteredInput_fx + /* length / 2 */ shr( length, 1 ), shr( pSubblockEnergies->pDelayBuffer->nSubblockSize, 1 ) /* pSubblockEnergies->pDelayBuffer->nSubblockSize / 2 */, &exp );
717 1170853 : exp = sub( exp, 2 * 0 - 1 ); // Q = 2*0 + (30-exp), E = 31 - (2*0 + 30 - exp) = 1 + exp - 2*0 = exp - (2*0-1)
718 1170853 : e0_fx = BASOP_Util_Add_Mant32Exp( MIN_BLOCK_ENERGY_IVAS_FX_Q7 / 2, 31 - Q7, e0_fx, exp, &exp );
719 1170853 : e1_fx = BASOP_Util_Add_Mant32Exp( pSubblockEnergies->subblockNrg[pSubblockEnergies->nDelay + 4], 32, L_negate( e0_fx ), exp, &exp1 );
720 1170853 : IF( BASOP_Util_Cmp_Mant32Exp( e1_fx, exp1, e0_fx, exp ) > 0 )
721 : {
722 504314 : pSubblockEnergies->ramp_up_flag = (UWord16) L_or( pSubblockEnergies->ramp_up_flag, 0x0001 );
723 504314 : move16();
724 : }
725 :
726 1170853 : return;
727 : }
728 :
729 : /*-------------------------------------------------------------------*
730 : * isLongTermTransient_fx()
731 : *
732 : *
733 : *-------------------------------------------------------------------*/
734 921877 : static Word16 isLongTermTransient_fx(
735 : const Word32 frameTFM,
736 : Word32 *lastTFM )
737 : {
738 : Word32 currTFM, f;
739 :
740 921877 : IF( GT_32( frameTFM, *lastTFM ) )
741 : {
742 473540 : f = Mpy_32_32( L_sub( frameTFM, *lastTFM ), L_sub( frameTFM, *lastTFM ) ); // Q31
743 473540 : currTFM = L_add( Mpy_32_32( *lastTFM, L_sub( 2080374784 /* 0.96875f in Q31 */, f ) ), Mpy_32_32( frameTFM, L_add( 67108864 /* 0.03125f in Q31 */, f ) ) ); // Q31
744 : }
745 : ELSE
746 : {
747 448337 : currTFM = L_add( Mpy_32_32( *lastTFM, 2080374784 /* 0.96875f in Q31 */ ), Mpy_32_32( frameTFM, 67108864 /* 0.03125f in Q31 */ ) ); // Q31
748 : }
749 :
750 921877 : *lastTFM = L_max( 33554432 /* 0.015625f in Q31 */, currTFM );
751 921877 : move32();
752 :
753 921877 : IF( LT_32( currTFM, 1207959552 /* 0.5625f in Q31 */ ) )
754 : {
755 16875 : return 1;
756 : }
757 905002 : return 0;
758 : }
759 :
760 :
761 929692 : void SetTCXModeInfo_ivas_fx(
762 : Encoder_State *st, /* i/o: encoder state structure */
763 : TRAN_DET_HANDLE hTranDet, /* i/o: transient detection handle */
764 : Word16 *tcxModeOverlap /* o : window overlap of current frame */
765 : )
766 : {
767 929692 : TCX_ENC_HANDLE hTcxEnc = st->hTcxEnc;
768 : Word16 tmp, exp_diff;
769 :
770 929692 : test();
771 929692 : test();
772 929692 : IF( EQ_16( st->codec_mode, MODE2 ) || ( GT_16( st->element_mode, EVS_MONO ) && NE_16( st->core, HQ_CORE ) ) )
773 : {
774 921877 : assert( hTranDet != NULL );
775 :
776 : /* determine window sequence (1 long or 2 short windows) */
777 921877 : test();
778 921877 : IF( st->tcx10Enabled && st->tcx20Enabled )
779 : {
780 : /* window switching based on transient detector output */
781 785703 : test();
782 785703 : test();
783 785703 : test();
784 785703 : test();
785 : /* window switching based on transient detector output */
786 785703 : IF( ( ( hTranDet->transientDetector.bIsAttackPresent ) || ( EQ_32( BASOP_Util_Cmp_Mant32Exp( Mpy_32_32( st->currEnergyHF_fx, 55063683 /*1.0f/39.0f Q31*/ ), st->currEnergyHF_e_fx, st->prevEnergyHF_fx, 17 ), 1 ) && NE_16( st->element_mode, IVAS_CPE_MDCT ) ) ) &&
787 : ( ( NE_16( st->last_core, ACELP_CORE ) ) && ( NE_16( st->last_core, AMR_WB_CORE ) ) ) )
788 : {
789 14982 : hTcxEnc->tcxMode = TCX_10;
790 14982 : move16();
791 : }
792 : ELSE
793 : {
794 770721 : hTcxEnc->tcxMode = TCX_20;
795 770721 : move16();
796 : }
797 : }
798 : ELSE
799 : {
800 : /* window selection (non-adaptive) based on flags only */
801 136174 : IF( st->tcx10Enabled )
802 : {
803 0 : hTcxEnc->tcxMode = TCX_10;
804 0 : move16();
805 : }
806 136174 : ELSE IF( st->tcx20Enabled )
807 : {
808 136174 : hTcxEnc->tcxMode = TCX_20;
809 136174 : move16();
810 : }
811 : ELSE
812 : {
813 0 : hTcxEnc->tcxMode = NO_TCX;
814 0 : move16();
815 : }
816 : }
817 :
818 : #ifdef SUPPORT_FORCE_TCX10_TCX20
819 : #ifdef DEBUGGING
820 : if ( st->force == FORCE_TCX10 )
821 : {
822 : hTcxEnc->tcxMode = TCX_10;
823 : }
824 : else if ( st->force == FORCE_TCX20 )
825 : {
826 : hTcxEnc->tcxMode = TCX_20;
827 : }
828 : #endif
829 : #endif
830 :
831 : /* set the left window overlap */
832 921877 : test();
833 921877 : test();
834 921877 : IF( EQ_16( st->last_core, ACELP_CORE ) || EQ_16( st->last_core, AMR_WB_CORE ) )
835 : {
836 9217 : st->hTcxCfg->tcx_last_overlap_mode = TRANSITION_OVERLAP;
837 9217 : move16();
838 : }
839 912660 : ELSE IF( ( EQ_16( hTcxEnc->tcxMode, TCX_10 ) ) && ( EQ_16( st->hTcxCfg->tcx_curr_overlap_mode, ALDO_WINDOW ) ) )
840 : {
841 11487 : st->hTcxCfg->tcx_last_overlap_mode = FULL_OVERLAP;
842 11487 : move16();
843 : }
844 : ELSE
845 : {
846 901173 : st->hTcxCfg->tcx_last_overlap_mode = st->hTcxCfg->tcx_curr_overlap_mode;
847 901173 : move16();
848 : }
849 :
850 : /* determine the right window overlap */
851 921877 : IF( EQ_16( hTcxEnc->tcxMode, TCX_10 ) )
852 : {
853 14982 : IF( hTranDet->transientDetector.attackIndex < 0 )
854 : {
855 0 : *tcxModeOverlap = HALF_OVERLAP;
856 0 : move16();
857 : }
858 : ELSE
859 : {
860 14982 : *tcxModeOverlap = s_and( hTranDet->transientDetector.attackIndex, 3 );
861 14982 : move16();
862 14982 : if ( EQ_16( *tcxModeOverlap, 1 ) )
863 : {
864 2504 : *tcxModeOverlap = FULL_OVERLAP;
865 2504 : move16();
866 : }
867 : }
868 14982 : tmp = BASOP_Util_Divide3232_Scale( ONE_IN_Q21, GetTCXAvgTemporalFlatnessMeasure_ivas_fx( (const TransientDetection *) hTranDet, NSUBBLOCKS, 0 ), &exp_diff );
869 14982 : tmp = shl_sat( tmp, exp_diff ); // Q15
870 14982 : test();
871 14982 : IF( isLongTermTransient_fx( L_deposit_h( tmp ), &hTcxEnc->tfm_mem_fx ) && EQ_16( st->element_mode, IVAS_CPE_MDCT ) )
872 : {
873 126 : test();
874 126 : IF( NE_16( *tcxModeOverlap, MIN_OVERLAP ) && LT_16( hTcxEnc->tcxltp_norm_corr_past, 18432 /* 0.5625f in Q15 */ ) )
875 : {
876 51 : *tcxModeOverlap = HALF_OVERLAP;
877 51 : move16();
878 : }
879 : }
880 : }
881 906895 : ELSE IF( EQ_16( hTcxEnc->tcxMode, TCX_20 ) )
882 : {
883 906895 : IF( EQ_16( hTranDet->transientDetector.attackIndex, 7 ) )
884 : {
885 3164 : *tcxModeOverlap = HALF_OVERLAP;
886 3164 : move16();
887 : }
888 903731 : ELSE IF( EQ_16( hTranDet->transientDetector.attackIndex, 6 ) )
889 : {
890 2427 : *tcxModeOverlap = MIN_OVERLAP;
891 2427 : move16();
892 : }
893 : ELSE
894 : {
895 901304 : *tcxModeOverlap = ALDO_WINDOW;
896 901304 : move16();
897 : }
898 906895 : tmp = BASOP_Util_Divide3232_Scale( ONE_IN_Q21, GetTCXAvgTemporalFlatnessMeasure_ivas_fx( (const TransientDetection *) hTranDet, NSUBBLOCKS, 0 ), &exp_diff );
899 906895 : tmp = shl_sat( tmp, exp_diff ); // Q15
900 906895 : test();
901 906895 : IF( isLongTermTransient_fx( L_deposit_h( tmp ), &hTcxEnc->tfm_mem_fx ) && EQ_16( st->element_mode, IVAS_CPE_MDCT ) )
902 : {
903 6818 : test();
904 6818 : if ( NE_16( *tcxModeOverlap, MIN_OVERLAP ) && LT_16( hTcxEnc->tcxltp_norm_corr_past, 18432 /* 0.5625f in Q15 */ ) )
905 : {
906 3702 : *tcxModeOverlap = HALF_OVERLAP;
907 3702 : move16();
908 : }
909 : }
910 : }
911 : ELSE
912 : {
913 : /* NO_TCX */
914 0 : *tcxModeOverlap = TRANSITION_OVERLAP;
915 0 : move16();
916 0 : if ( EQ_16( st->element_mode, IVAS_CPE_MDCT ) )
917 : {
918 0 : hTcxEnc->tfm_mem_fx = 1610612736; /* 0.75f in Q31 */
919 0 : move16();
920 : }
921 : }
922 :
923 : /* for the ACELP -> TCX transition frames use full right window overlap */
924 921877 : test();
925 921877 : IF( ( EQ_16( st->hTcxCfg->tcx_last_overlap_mode, TRANSITION_OVERLAP ) ) && ( EQ_16( *tcxModeOverlap, ALDO_WINDOW ) ) )
926 : {
927 9124 : *tcxModeOverlap = FULL_OVERLAP;
928 9124 : move16();
929 : }
930 : }
931 :
932 929692 : return;
933 : }
934 :
935 :
936 1310 : void SetTCXModeInfo_fx( Encoder_State *st,
937 : TransientDetection const *pTransientDetection,
938 : Word16 *tcxModeOverlap )
939 : {
940 1310 : TCX_ENC_HANDLE hTcxEnc = st->hTcxEnc;
941 :
942 1310 : assert( pTransientDetection != NULL );
943 :
944 1310 : IF( EQ_16( st->codec_mode, MODE2 ) )
945 : {
946 :
947 : /* determine window sequence (1 long or 2 short windows) */
948 :
949 1266 : test();
950 1266 : IF( st->tcx10Enabled != 0 && st->tcx20Enabled != 0 )
951 : {
952 : /* window switching based on transient detector output */
953 0 : test();
954 0 : test();
955 0 : test();
956 0 : IF( ( ( pTransientDetection->transientDetector.bIsAttackPresent != 0 ) || ( GT_32( Mpy_32_16_1( st->currEnergyHF_fx, 840 /*1.0f/39.0f Q15*/ ), st->prevEnergyHF_fx ) ) ) && ( ( NE_16( st->last_core, ACELP_CORE ) ) && ( NE_16( st->last_core, AMR_WB_CORE ) ) ) )
957 : {
958 0 : move16();
959 0 : hTcxEnc->tcxMode = TCX_10;
960 : }
961 : ELSE
962 : {
963 0 : move16();
964 0 : hTcxEnc->tcxMode = TCX_20;
965 : }
966 : }
967 : ELSE
968 : {
969 : /* window selection (non-adaptive) based on flags only */
970 1266 : IF( st->tcx10Enabled )
971 : {
972 0 : move16();
973 0 : hTcxEnc->tcxMode = TCX_10;
974 : }
975 1266 : ELSE IF( st->tcx20Enabled )
976 : {
977 1266 : move16();
978 1266 : hTcxEnc->tcxMode = TCX_20;
979 : }
980 : ELSE
981 : {
982 0 : move16();
983 0 : hTcxEnc->tcxMode = NO_TCX;
984 : }
985 : }
986 1266 : test();
987 1266 : test();
988 1266 : IF( st->last_core == ACELP_CORE || st->last_core == AMR_WB_CORE )
989 : {
990 613 : move16();
991 613 : st->hTcxCfg->tcx_last_overlap_mode = TRANSITION_OVERLAP;
992 : }
993 653 : ELSE IF( ( EQ_16( hTcxEnc->tcxMode, TCX_10 ) ) && ( EQ_16( st->hTcxCfg->tcx_curr_overlap_mode, ALDO_WINDOW ) ) )
994 : {
995 0 : move16();
996 0 : st->hTcxCfg->tcx_last_overlap_mode = FULL_OVERLAP;
997 : }
998 : ELSE
999 : {
1000 653 : move16();
1001 653 : st->hTcxCfg->tcx_last_overlap_mode = st->hTcxCfg->tcx_curr_overlap_mode;
1002 : }
1003 :
1004 : /* determine window overlaps (0 full, 2 none, or 3 half) */
1005 :
1006 1266 : IF( EQ_16( hTcxEnc->tcxMode, TCX_10 ) )
1007 : {
1008 0 : IF( pTransientDetection->transientDetector.attackIndex < 0 )
1009 : {
1010 0 : move16();
1011 0 : *tcxModeOverlap = HALF_OVERLAP;
1012 : }
1013 : ELSE
1014 : {
1015 0 : move16();
1016 0 : *tcxModeOverlap = s_and( pTransientDetection->transientDetector.attackIndex, 3 );
1017 0 : if ( EQ_16( *tcxModeOverlap, 1 ) )
1018 : {
1019 0 : move16();
1020 0 : *tcxModeOverlap = FULL_OVERLAP;
1021 : }
1022 : }
1023 : }
1024 1266 : ELSE IF( EQ_16( hTcxEnc->tcxMode, TCX_20 ) )
1025 : {
1026 1266 : IF( EQ_16( pTransientDetection->transientDetector.attackIndex, 7 ) )
1027 : {
1028 14 : move16();
1029 14 : *tcxModeOverlap = HALF_OVERLAP;
1030 : }
1031 1252 : ELSE IF( EQ_16( pTransientDetection->transientDetector.attackIndex, 6 ) )
1032 : {
1033 18 : move16();
1034 18 : *tcxModeOverlap = MIN_OVERLAP;
1035 : }
1036 : ELSE
1037 : {
1038 1234 : move16();
1039 1234 : *tcxModeOverlap = ALDO_WINDOW;
1040 : }
1041 : }
1042 : ELSE /* NO_TCX */
1043 : {
1044 0 : move16();
1045 0 : *tcxModeOverlap = TRANSITION_OVERLAP;
1046 : }
1047 1266 : test();
1048 1266 : if ( ( EQ_16( st->hTcxCfg->tcx_last_overlap_mode, TRANSITION_OVERLAP ) ) && ( EQ_16( *tcxModeOverlap, ALDO_WINDOW ) ) )
1049 : {
1050 597 : move16();
1051 597 : *tcxModeOverlap = FULL_OVERLAP;
1052 : }
1053 :
1054 : /* Sanity check */
1055 1266 : assert( *tcxModeOverlap != 1 );
1056 : }
1057 1310 : }
1058 :
1059 : /************************************************/
1060 : /* */
1061 : /* Internal functions */
1062 : /* */
1063 : /************************************************/
1064 :
1065 8186 : static void InitDelayBuffer( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer )
1066 : {
1067 8186 : Word16 const nMaxBuffSize = sizeof( pDelayBuffer->buffer ) / sizeof( pDelayBuffer->buffer[0] );
1068 :
1069 :
1070 8186 : move16();
1071 8186 : move16();
1072 8186 : assert( ( nFrameLength > NSUBBLOCKS ) && ( nFrameLength % NSUBBLOCKS == 0 ) && ( nDelay >= 0 ) && ( pDelayBuffer != NULL ) );
1073 8186 : pDelayBuffer->nSubblockSize = nFrameLength / NSUBBLOCKS;
1074 8186 : assert( pDelayBuffer->nSubblockSize <= nMaxBuffSize );
1075 8186 : set16_fx( pDelayBuffer->buffer, 0, nMaxBuffSize );
1076 8186 : pDelayBuffer->nDelay = nDelay % pDelayBuffer->nSubblockSize;
1077 8186 : assert( pDelayBuffer->nDelay <= nMaxBuffSize );
1078 8186 : }
1079 :
1080 3 : static void InitSubblockEnergies( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer, SubblockEnergies *pSubblockEnergies )
1081 : {
1082 3 : Word16 const nMaxBuffSize = sizeof( pSubblockEnergies->subblockNrg ) / sizeof( pSubblockEnergies->subblockNrg[0] );
1083 3 : move16();
1084 : (void) nFrameLength;
1085 :
1086 :
1087 3 : assert( ( pDelayBuffer != NULL ) && ( pSubblockEnergies != NULL ) && ( pDelayBuffer->nSubblockSize * NSUBBLOCKS == nFrameLength ) && ( pDelayBuffer->nSubblockSize > 0 ) );
1088 :
1089 3 : set32_fx( pSubblockEnergies->subblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize );
1090 3 : set32_fx( pSubblockEnergies->accSubblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize + 1 );
1091 3 : set16_fx( pSubblockEnergies->subblockNrgChange, 0x7fff, nMaxBuffSize );
1092 3 : pSubblockEnergies->nDelay = idiv1616_1( nDelay, pDelayBuffer->nSubblockSize );
1093 3 : assert( pSubblockEnergies->nDelay < nMaxBuffSize );
1094 3 : pSubblockEnergies->nPartialDelay = nDelay % pDelayBuffer->nSubblockSize;
1095 3 : pSubblockEnergies->facAccSubblockNrg = 26624 /*0.8125f Q15*/; /* Energy accumulation factor */
1096 3 : pSubblockEnergies->firState1 = 0;
1097 3 : pSubblockEnergies->firState2 = 0;
1098 3 : move16();
1099 3 : move16();
1100 3 : move16();
1101 3 : move16();
1102 3 : move16();
1103 :
1104 3 : pSubblockEnergies->pDelayBuffer = pDelayBuffer;
1105 3 : pDelayBuffer->nDelay = s_max( pDelayBuffer->nDelay, pSubblockEnergies->nPartialDelay );
1106 3 : }
1107 :
1108 8183 : static void InitSubblockEnergies_ivas_fx( Word16 nFrameLength, Word16 nDelay, DelayBuffer *pDelayBuffer, SubblockEnergies *pSubblockEnergies )
1109 : {
1110 8183 : Word16 const nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
1111 8183 : move16();
1112 :
1113 8183 : assert( ( pDelayBuffer != NULL ) && ( pSubblockEnergies != NULL ) && ( pDelayBuffer->nSubblockSize * NSUBBLOCKS == nFrameLength ) && ( pDelayBuffer->nSubblockSize > 0 ) );
1114 :
1115 8183 : set32_fx( pSubblockEnergies->subblockNrg, 54 /* 107.37 in Q(-1) */, nMaxBuffSize );
1116 8183 : set32_fx( pSubblockEnergies->accSubblockNrg, 54 /* 107.37 in Q(-1) */, nMaxBuffSize + 1 );
1117 8183 : set32_fx( pSubblockEnergies->subblockNrgChange_32fx, ONE_IN_Q15, nMaxBuffSize );
1118 8183 : set16_fx( pSubblockEnergies->subblockNrgChange_exp, 16, nMaxBuffSize );
1119 8183 : IF( nDelay != 0 )
1120 : {
1121 0 : pSubblockEnergies->nDelay = idiv1616( nDelay, pDelayBuffer->nSubblockSize );
1122 0 : move16();
1123 : }
1124 : ELSE
1125 : {
1126 8183 : pSubblockEnergies->nDelay = 0;
1127 8183 : move16();
1128 : }
1129 8183 : assert( pSubblockEnergies->nDelay < nMaxBuffSize );
1130 8183 : pSubblockEnergies->nPartialDelay = nDelay % pDelayBuffer->nSubblockSize;
1131 8183 : move16();
1132 8183 : pSubblockEnergies->facAccSubblockNrg = 26624 /*0.8125f Q15*/; /* Energy accumulation factor */
1133 8183 : move16();
1134 8183 : pSubblockEnergies->firState1 = 0;
1135 8183 : move16();
1136 8183 : pSubblockEnergies->firState2 = 0;
1137 8183 : move16();
1138 8183 : pSubblockEnergies->q_firState = 15;
1139 8183 : move16();
1140 :
1141 8183 : pSubblockEnergies->pDelayBuffer = pDelayBuffer;
1142 8183 : pDelayBuffer->nDelay = s_max( pDelayBuffer->nDelay, pSubblockEnergies->nPartialDelay );
1143 8183 : move16();
1144 8183 : }
1145 :
1146 : /** Init transient detector.
1147 : * Fills TransientDetector structure with sensible content and enable it.
1148 : * @param pSubblockEnergies Subblock energies used in this transient detector.
1149 : * @param nDelay Delay FOR this transient detector.
1150 : * @param nSubblocksToCheck Number of subblocks to check in this transient detector.
1151 : * @param pCheckSubblockForAttack Attack detection function FOR this transient detector.
1152 : * @param pSetAttackPosition Function FOR finalizing this transient detector.
1153 : * @param attackRatioThreshold Attack ratio threshold with exponent ATTACKTHRESHOLD_E.
1154 : * @param pTransientDetector Structure to be initialized.
1155 : */
1156 3 : static void InitTransientDetector_fx( SubblockEnergies *pSubblockEnergies, Word16 nDelay, Word16 nSubblocksToCheck, TCheckSubblocksForAttack_fx pCheckSubblocksForAttack, Word16 attackRatioThreshold, TransientDetector *pTransientDetector )
1157 : {
1158 : Word16 nMaxBuffSize;
1159 :
1160 3 : move16();
1161 3 : nMaxBuffSize = sizeof( pSubblockEnergies->subblockNrg ) / sizeof( pSubblockEnergies->subblockNrg[0] );
1162 :
1163 3 : assert( ( pSubblockEnergies != NULL ) && ( pSubblockEnergies->pDelayBuffer != NULL ) && ( pTransientDetector != NULL ) && ( pSubblockEnergies->pDelayBuffer->nSubblockSize != 0 ) );
1164 3 : pTransientDetector->pSubblockEnergies = pSubblockEnergies;
1165 3 : pTransientDetector->nDelay = ( nDelay - pSubblockEnergies->nPartialDelay ) / pSubblockEnergies->pDelayBuffer->nSubblockSize;
1166 3 : move16();
1167 3 : assert( nDelay == pTransientDetector->nDelay * pSubblockEnergies->pDelayBuffer->nSubblockSize + pSubblockEnergies->nPartialDelay );
1168 3 : assert( pTransientDetector->nDelay < nMaxBuffSize );
1169 3 : pSubblockEnergies->nDelay = s_max( pSubblockEnergies->nDelay, pTransientDetector->nDelay );
1170 3 : assert( nSubblocksToCheck <= NSUBBLOCKS + pTransientDetector->nDelay );
1171 3 : pTransientDetector->nSubblocksToCheck = nSubblocksToCheck;
1172 3 : move16();
1173 3 : pTransientDetector->CheckSubblocksForAttack_fx = pCheckSubblocksForAttack;
1174 3 : pTransientDetector->attackRatioThreshold = attackRatioThreshold;
1175 3 : move16();
1176 3 : pTransientDetector->prev_bIsAttackPresent = FALSE;
1177 3 : move16();
1178 3 : pTransientDetector->bIsAttackPresent = FALSE;
1179 3 : move16();
1180 3 : pTransientDetector->attackIndex = -1;
1181 3 : move16();
1182 3 : }
1183 :
1184 8183 : static void InitTransientDetector_ivas_fx( SubblockEnergies *pSubblockEnergies, Word16 nDelay, Word16 nSubblocksToCheck, TCheckSubblocksForAttack_fx pCheckSubblocksForAttack, Word16 attackRatioThreshold, TransientDetector *pTransientDetector )
1185 : {
1186 8183 : const Word16 nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
1187 8183 : move16();
1188 :
1189 8183 : assert( ( pSubblockEnergies != NULL ) && ( pSubblockEnergies->pDelayBuffer != NULL ) && ( pTransientDetector != NULL ) && ( pSubblockEnergies->pDelayBuffer->nSubblockSize != 0 ) );
1190 8183 : pTransientDetector->pSubblockEnergies = pSubblockEnergies;
1191 8183 : IF( sub( nDelay, pSubblockEnergies->nPartialDelay ) != 0 )
1192 : {
1193 0 : pTransientDetector->nDelay = idiv1616( sub( nDelay, pSubblockEnergies->nPartialDelay ), pSubblockEnergies->pDelayBuffer->nSubblockSize );
1194 0 : move16();
1195 : }
1196 : ELSE
1197 : {
1198 8183 : pTransientDetector->nDelay = 0;
1199 8183 : move16();
1200 : }
1201 8183 : assert( nDelay == pTransientDetector->nDelay * pSubblockEnergies->pDelayBuffer->nSubblockSize + pSubblockEnergies->nPartialDelay );
1202 8183 : assert( pTransientDetector->nDelay < nMaxBuffSize );
1203 8183 : pSubblockEnergies->nDelay = s_max( pSubblockEnergies->nDelay, pTransientDetector->nDelay );
1204 8183 : move16();
1205 8183 : assert( nSubblocksToCheck <= NSUBBLOCKS + pTransientDetector->nDelay );
1206 8183 : pTransientDetector->nSubblocksToCheck = nSubblocksToCheck;
1207 8183 : move16();
1208 8183 : pTransientDetector->CheckSubblocksForAttack_fx = pCheckSubblocksForAttack;
1209 8183 : pTransientDetector->attackRatioThreshold = attackRatioThreshold;
1210 8183 : move16();
1211 8183 : pTransientDetector->bIsAttackPresent = FALSE;
1212 8183 : move16();
1213 8183 : pTransientDetector->prev_bIsAttackPresent = FALSE;
1214 8183 : move16();
1215 8183 : pTransientDetector->attackIndex = -1;
1216 8183 : move16();
1217 8183 : pTransientDetector->pSubblockEnergies->ramp_up_flag = 0x0;
1218 8183 : move16();
1219 8183 : }
1220 :
1221 : /* This function should be inlined and WMOPS instrumentation takes that into account, meaning that all references are considered as local variables */
1222 996604480 : static Word32 InlineFilter( Word16 inValue, Word16 firState1, Word16 firState2 )
1223 : {
1224 : /* 0.375f * inValue - 0.5f * firState1 + 0.125f * firState2 */
1225 :
1226 996604480 : return L_msu( L_mac( L_mult( firState2, 4096 /*0.125f Q15*/ ), inValue, 12288 /*0.375f Q15*/ ), firState1, 16384 /*0.5f Q15*/ );
1227 : }
1228 :
1229 1173953 : static void HighPassFilter_fx( Word16 const *input, Word16 length, Word16 *pFirState1, Word16 *pFirState2, Word16 *output )
1230 : {
1231 : Word16 i;
1232 :
1233 1173953 : output[0] = round_fx( InlineFilter( input[0], *pFirState1, *pFirState2 ) );
1234 1173953 : move16();
1235 1173953 : output[1] = round_fx( InlineFilter( input[1], input[0], *pFirState1 ) );
1236 1173953 : move16();
1237 :
1238 995430527 : FOR( i = 2; i < length; i++ )
1239 : {
1240 994256574 : output[i] = round_fx( InlineFilter( input[i], input[i - 1], input[i - 2] ) );
1241 994256574 : move16();
1242 : }
1243 :
1244 : /* update filter states: shift time samples through delay line */
1245 1173953 : move16();
1246 1173953 : move16();
1247 1173953 : *pFirState2 = input[length - 2];
1248 1173953 : *pFirState1 = input[length - 1];
1249 1173953 : }
1250 :
1251 1173953 : static void RunTransientDetector_fx( TransientDetector *pTransientDetector )
1252 : {
1253 1173953 : Word16 const attackRatioThreshold = pTransientDetector->attackRatioThreshold;
1254 1173953 : move16();
1255 1173953 : SubblockEnergies const *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
1256 1173953 : Word16 const nDelay = pTransientDetector->nDelay;
1257 1173953 : move16();
1258 1173953 : Word16 const nRelativeDelay = sub( pSubblockEnergies->nDelay, nDelay );
1259 1173953 : move16();
1260 1173953 : Word32 const *pSubblockNrg = &pSubblockEnergies->subblockNrg[nRelativeDelay];
1261 1173953 : Word32 const *pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nRelativeDelay];
1262 :
1263 1173953 : assert( ( pTransientDetector->CheckSubblocksForAttack_fx != NULL ) );
1264 :
1265 : #define WMC_TOOL_SKIP
1266 1173953 : pTransientDetector->CheckSubblocksForAttack_fx( pSubblockNrg, pAccSubblockNrg,
1267 1173953 : NSUBBLOCKS + nDelay, nRelativeDelay,
1268 : attackRatioThreshold,
1269 : &pTransientDetector->bIsAttackPresent, &pTransientDetector->attackIndex );
1270 : #undef WMC_TOOL_SKIP
1271 1173953 : }
1272 :
1273 1173953 : static void UpdateDelayBuffer( Word16 const *input, Word16 nSamplesAvailable, DelayBuffer *pDelayBuffer )
1274 : {
1275 : Word16 i;
1276 : Word16 nDelay;
1277 :
1278 :
1279 1173953 : move16();
1280 1173953 : nDelay = pDelayBuffer->nDelay;
1281 :
1282 1173953 : assert( ( nDelay >= 0 ) && ( nDelay <= (Word32) sizeof( pDelayBuffer->buffer ) / (Word32) sizeof( pDelayBuffer->buffer[0] ) ) );
1283 1173953 : assert( nSamplesAvailable <= NSUBBLOCKS * pDelayBuffer->nSubblockSize );
1284 : /* If this is not the last frame */
1285 1173953 : IF( EQ_16( nSamplesAvailable, imult1616( NSUBBLOCKS, pDelayBuffer->nSubblockSize ) ) )
1286 : {
1287 : /* Store the newest samples into the delay buffer */
1288 1297703 : FOR( i = 0; i < nDelay; i++ )
1289 : {
1290 123750 : move16();
1291 123750 : pDelayBuffer->buffer[i] = input[i + nSamplesAvailable - nDelay];
1292 : }
1293 : }
1294 1173953 : }
1295 :
1296 3100 : static void UpdateSubblockEnergies( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies )
1297 : {
1298 : Word16 i;
1299 :
1300 :
1301 3100 : assert( ( pSubblockEnergies->nDelay >= 0 ) && ( pSubblockEnergies->nDelay + NSUBBLOCKS <= (Word32) sizeof( pSubblockEnergies->subblockNrg ) / (Word32) sizeof( pSubblockEnergies->subblockNrg[0] ) ) );
1302 3100 : assert( pSubblockEnergies->nPartialDelay <= pSubblockEnergies->pDelayBuffer->nDelay );
1303 : /* At least one block delay is required when subblock energy change is required */
1304 3100 : assert( pSubblockEnergies->nDelay >= 1 );
1305 :
1306 : /* Shift old subblock energies */
1307 31000 : FOR( i = 0; i < pSubblockEnergies->nDelay; i++ )
1308 : {
1309 27900 : move32();
1310 27900 : move32();
1311 27900 : move16();
1312 27900 : pSubblockEnergies->subblockNrg[i] = pSubblockEnergies->subblockNrg[i + NSUBBLOCKS];
1313 27900 : pSubblockEnergies->accSubblockNrg[i] = pSubblockEnergies->accSubblockNrg[i + NSUBBLOCKS];
1314 27900 : pSubblockEnergies->subblockNrgChange[i] = pSubblockEnergies->subblockNrgChange[i + NSUBBLOCKS];
1315 : }
1316 :
1317 : /* Compute filtered subblock energies for the new samples */
1318 3100 : CalculateSubblockEnergies( input, nSamplesAvailable, pSubblockEnergies );
1319 3100 : }
1320 :
1321 1170853 : static void UpdateSubblockEnergies_ivas_fx( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies )
1322 : {
1323 : Word16 i;
1324 :
1325 :
1326 1170853 : assert( ( pSubblockEnergies->nDelay >= 0 ) && ( pSubblockEnergies->nDelay + NSUBBLOCKS <= (Word32) sizeof( pSubblockEnergies->subblockNrg ) / (Word32) sizeof( pSubblockEnergies->subblockNrg[0] ) ) );
1327 1170853 : assert( pSubblockEnergies->nPartialDelay <= pSubblockEnergies->pDelayBuffer->nDelay );
1328 : /* At least one block delay is required when subblock energy change is required */
1329 1170853 : assert( pSubblockEnergies->nDelay >= 1 );
1330 :
1331 : /* Shift old subblock energies */
1332 16391942 : FOR( i = 0; i < pSubblockEnergies->nDelay; i++ )
1333 : {
1334 15221089 : move32();
1335 15221089 : move32();
1336 15221089 : move32();
1337 15221089 : move16();
1338 15221089 : pSubblockEnergies->subblockNrg[i] = pSubblockEnergies->subblockNrg[i + NSUBBLOCKS];
1339 15221089 : pSubblockEnergies->accSubblockNrg[i] = pSubblockEnergies->accSubblockNrg[i + NSUBBLOCKS];
1340 15221089 : pSubblockEnergies->subblockNrgChange_32fx[i] = pSubblockEnergies->subblockNrgChange_32fx[i + NSUBBLOCKS];
1341 15221089 : pSubblockEnergies->subblockNrgChange_exp[i] = pSubblockEnergies->subblockNrgChange_exp[i + NSUBBLOCKS];
1342 : }
1343 :
1344 : /* Compute filtered subblock energies for the new samples */
1345 1170853 : CalculateSubblockEnergies_ivas_fx( input, nSamplesAvailable, pSubblockEnergies );
1346 1170853 : }
1347 :
1348 : /* This function should be inlined and WMOPS instrumentation takes that into account, meaning that all references are considered as local variables */
1349 9391624 : static void UpdatedAndStoreAccWindowNrg( Word32 newWindowNrgF, Word32 *pAccSubblockNrg, Word16 facAccSubblockNrg, Word32 *pOutAccWindowNrgF )
1350 : {
1351 : /* Store the accumulated energy */
1352 9391624 : move32();
1353 9391624 : *pOutAccWindowNrgF = *pAccSubblockNrg;
1354 : /* Update the accumulated energy: maximum of the current and the accumulated energy */
1355 9391624 : *pAccSubblockNrg = Mpy_32_16_1( *pAccSubblockNrg, facAccSubblockNrg );
1356 :
1357 9391624 : if ( GT_32( newWindowNrgF, *pAccSubblockNrg ) )
1358 : {
1359 5804783 : move32();
1360 5804783 : *pAccSubblockNrg = newWindowNrgF;
1361 : }
1362 9391624 : }
1363 :
1364 3100 : static void CalculateSubblockEnergies( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies )
1365 : {
1366 : DelayBuffer *pDelayBuffer;
1367 : Word16 nSubblockSize;
1368 : Word16 nDelay;
1369 : Word16 nPartialDelay;
1370 : Word16 *delayBuffer;
1371 : Word16 facAccSubblockNrg;
1372 : Word32 *pSubblockNrg;
1373 : Word32 *pAccSubblockNrg;
1374 : Word16 *pSubblockNrgChange;
1375 : Word32 *pAccSubblockTmp;
1376 : Word16 nWindows;
1377 : Word16 w, k, k2, tmp;
1378 : Word16 firState1, firState2;
1379 : Word32 w0, w1;
1380 : Word32 accu;
1381 :
1382 3100 : move16();
1383 3100 : pDelayBuffer = pSubblockEnergies->pDelayBuffer;
1384 3100 : facAccSubblockNrg = pSubblockEnergies->facAccSubblockNrg;
1385 :
1386 3100 : move16();
1387 3100 : move16();
1388 3100 : move16();
1389 3100 : nSubblockSize = pDelayBuffer->nSubblockSize;
1390 3100 : nDelay = pSubblockEnergies->nDelay;
1391 3100 : nPartialDelay = pSubblockEnergies->nPartialDelay;
1392 :
1393 3100 : delayBuffer = &pDelayBuffer->buffer[sub( pDelayBuffer->nDelay, nPartialDelay )];
1394 3100 : pSubblockNrg = &pSubblockEnergies->subblockNrg[nDelay];
1395 3100 : pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nDelay];
1396 3100 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nDelay];
1397 :
1398 3100 : move16();
1399 3100 : move16();
1400 : /* nWindows = (nSamplesAvailable + nPartialDelay) / nSubblockSize */
1401 3100 : nWindows = shr( div_s( add( nSamplesAvailable, nPartialDelay ), shl( nSubblockSize, 7 ) ), 8 );
1402 3100 : firState1 = pSubblockEnergies->firState1;
1403 3100 : firState2 = pSubblockEnergies->firState2;
1404 3100 : pAccSubblockTmp = &pAccSubblockNrg[nWindows];
1405 :
1406 3100 : IF( nWindows > 0 )
1407 : {
1408 : /* Process left over samples from the previous frame. */
1409 3100 : accu = L_add( MIN_BLOCK_ENERGY, 0 );
1410 : assert( ( SUBBLOCK_NRG_E & 1 ) == 0 );
1411 126850 : FOR( k = 0; k < nPartialDelay; k++ )
1412 : {
1413 123750 : tmp = shr( delayBuffer[k], SUBBLOCK_NRG_E / 2 );
1414 123750 : accu = L_mac0( accu, tmp, tmp );
1415 : }
1416 :
1417 : /* Process new samples in the 0. subblock. */
1418 3100 : w = sub( nSubblockSize, nPartialDelay );
1419 : assert( ( SUBBLOCK_NRG_E & 1 ) == 0 );
1420 209350 : FOR( k = 0; k < w; k++ )
1421 : {
1422 206250 : tmp = shr( input[k], SUBBLOCK_NRG_E / 2 );
1423 206250 : accu = L_mac0_sat( accu, tmp, tmp );
1424 : }
1425 :
1426 3100 : move32();
1427 3100 : pSubblockNrg[0] = accu;
1428 : /* Set accumulated subblock energy at this point. */
1429 3100 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[0], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[0] );
1430 :
1431 24800 : FOR( w = 1; w < nWindows; w++ )
1432 : {
1433 21700 : accu = L_add( MIN_BLOCK_ENERGY, 0 );
1434 : /* Process new samples in the w. subblock. */
1435 21700 : k2 = add( k, nSubblockSize );
1436 : assert( ( SUBBLOCK_NRG_E & 1 ) == 0 );
1437 2331700 : FOR( ; k < k2; k++ )
1438 : {
1439 2310000 : tmp = shr( input[k], SUBBLOCK_NRG_E / 2 );
1440 2310000 : accu = L_mac0_sat( accu, tmp, tmp );
1441 : }
1442 21700 : move32();
1443 21700 : pSubblockNrg[w] = accu;
1444 : /* Set accumulated subblock energy at this point. */
1445 21700 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[w], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[w] );
1446 : }
1447 :
1448 : /* Calculate energy change for each block. */
1449 27900 : FOR( w = 0; w < nWindows; w++ )
1450 : {
1451 24800 : w0 = L_add( pSubblockNrg[w], 0 );
1452 24800 : w1 = L_add( pSubblockNrg[sub( w, 1 )], 0 );
1453 :
1454 24800 : IF( GT_32( w0, w1 ) )
1455 : {
1456 11470 : k2 = BASOP_Util_Divide3232_uu_1616_Scale( w0, w1, &k );
1457 : }
1458 : ELSE
1459 : {
1460 13330 : k2 = BASOP_Util_Divide3232_uu_1616_Scale( w1, w0, &k );
1461 : }
1462 24800 : move16();
1463 24800 : pSubblockNrgChange[w] = MAX_16;
1464 24800 : IF( LT_16( k, SUBBLOCK_NRG_CHANGE_E ) )
1465 : {
1466 24779 : move16();
1467 24779 : pSubblockNrgChange[w] = shr( k2, sub( SUBBLOCK_NRG_CHANGE_E, k ) );
1468 : }
1469 : }
1470 : }
1471 :
1472 3100 : move16();
1473 3100 : move16();
1474 3100 : pSubblockEnergies->firState1 = firState1;
1475 3100 : pSubblockEnergies->firState2 = firState2;
1476 3100 : }
1477 :
1478 1170853 : static void CalculateSubblockEnergies_ivas_fx( Word16 const *input, Word16 nSamplesAvailable, SubblockEnergies *pSubblockEnergies )
1479 : {
1480 : DelayBuffer *pDelayBuffer;
1481 : Word16 nSubblockSize;
1482 : Word16 nDelay;
1483 : Word16 nPartialDelay;
1484 : Word16 *delayBuffer;
1485 : Word16 facAccSubblockNrg;
1486 : Word32 *pSubblockNrg;
1487 : Word32 *pAccSubblockNrg;
1488 : Word32 *pSubblockNrgChange;
1489 : Word16 *pSubblockNrgChange_exp;
1490 : Word32 *pAccSubblockTmp;
1491 : Word16 nWindows;
1492 : Word16 w, k, k2;
1493 : Word32 w0, w1;
1494 : Word64 accu;
1495 :
1496 1170853 : move16();
1497 1170853 : pDelayBuffer = pSubblockEnergies->pDelayBuffer;
1498 1170853 : facAccSubblockNrg = pSubblockEnergies->facAccSubblockNrg;
1499 :
1500 1170853 : move16();
1501 1170853 : move16();
1502 1170853 : move16();
1503 1170853 : nSubblockSize = pDelayBuffer->nSubblockSize;
1504 1170853 : nDelay = pSubblockEnergies->nDelay;
1505 1170853 : nPartialDelay = pSubblockEnergies->nPartialDelay;
1506 :
1507 1170853 : delayBuffer = &pDelayBuffer->buffer[sub( pDelayBuffer->nDelay, nPartialDelay )];
1508 1170853 : pSubblockNrg = &pSubblockEnergies->subblockNrg[nDelay];
1509 1170853 : pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nDelay];
1510 1170853 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange_32fx[nDelay];
1511 1170853 : pSubblockNrgChange_exp = &pSubblockEnergies->subblockNrgChange_exp[nDelay];
1512 :
1513 : /* nWindows = (nSamplesAvailable + nPartialDelay) / nSubblockSize */
1514 1170853 : nWindows = shr( div_s( add( nSamplesAvailable, nPartialDelay ), shl( nSubblockSize, 7 ) ), 8 );
1515 1170853 : pAccSubblockTmp = &pAccSubblockNrg[nWindows];
1516 :
1517 1170853 : IF( nWindows > 0 )
1518 : {
1519 : /* Process left over samples from the previous frame. */
1520 1170853 : accu = 215; // 107.37f in Q1
1521 1170853 : move64();
1522 1170853 : FOR( k = 0; k < nPartialDelay; k++ )
1523 : {
1524 0 : accu = W_mac_16_16( accu, delayBuffer[k], delayBuffer[k] ); // Q1
1525 : }
1526 :
1527 : /* Process new samples in the 0. subblock. */
1528 125416413 : FOR( k = 0; k < ( nSubblockSize - nPartialDelay ); k++ )
1529 : {
1530 124245560 : accu = W_mac_16_16( accu, input[k], input[k] ); // Q1
1531 : }
1532 :
1533 1170853 : pSubblockNrg[0] = W_shl_sat_l( accu, -2 ); // Q(-1)
1534 1170853 : move32();
1535 : /* Set accumulated subblock energy at this point. */
1536 1170853 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[0], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[0] );
1537 :
1538 9366824 : FOR( w = 1; w < nWindows; w++ )
1539 : {
1540 8195971 : accu = 215; // 107.37f in Q1
1541 8195971 : move64();
1542 : /* Process new samples in the w. subblock. */
1543 8195971 : k2 = add( k, nSubblockSize );
1544 877914891 : FOR( ; k < k2; k++ )
1545 : {
1546 869718920 : accu = W_mac_16_16( accu, input[k], input[k] ); // Q1
1547 : }
1548 8195971 : pSubblockNrg[w] = W_shl_sat_l( accu, -2 ); // Q(-1)
1549 8195971 : move32();
1550 : /* Set accumulated subblock energy at this point. */
1551 8195971 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[w], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[w] );
1552 : }
1553 :
1554 : /* Calculate energy change for each block. */
1555 10537677 : FOR( w = 0; w < nWindows; w++ )
1556 : {
1557 9366824 : w0 = L_add( pSubblockNrg[w], 0 );
1558 9366824 : w1 = L_add( pSubblockNrg[sub( w, 1 )], 0 );
1559 :
1560 9366824 : IF( GT_32( w0, w1 ) )
1561 : {
1562 4183602 : pSubblockNrgChange[w] = BASOP_Util_Divide3232_Scale_newton( w0, w1, &k );
1563 4183602 : pSubblockNrgChange_exp[w] = k;
1564 : }
1565 : ELSE
1566 : {
1567 5183222 : pSubblockNrgChange[w] = BASOP_Util_Divide3232_Scale_newton( w1, w0, &k );
1568 5183222 : pSubblockNrgChange_exp[w] = k;
1569 : }
1570 9366824 : move32();
1571 9366824 : move16();
1572 : }
1573 : }
1574 1170853 : }
1575 :
1576 : /*-------------------------------------------------------------------*
1577 : * transient_analysis()
1578 : *
1579 : *
1580 : *-------------------------------------------------------------------*/
1581 :
1582 : /*! r: preliminary flag to force ACELP */
1583 10390 : Word16 transient_analysis_ivas_fx(
1584 : TRAN_DET_HANDLE hTranDet, /* i : handle transient detection */
1585 : const Word16 cor_map_LT[], /* i : LT correlation map Q_cor_map */
1586 : Word16 Q_cor_map,
1587 : const Word16 multi_harm_limit, /* i : multi harmonic threshold Q_multi_harm_limit */
1588 : Word16 Q_multi_harm_limit )
1589 : {
1590 : const Word32 *pSubblockNrg;
1591 : Word32 accSubblockNrgRev_fx[NSUBBLOCKS];
1592 : Word32 *pTmp_fx;
1593 : Word16 offset;
1594 : Word16 i;
1595 : Word16 thr_fwd_fx;
1596 : Word16 thr_rev_fx;
1597 10390 : const Word16 nRelativeDelay = sub( hTranDet->subblockEnergies.nDelay, hTranDet->transientDetector.nDelay );
1598 : Word16 prel_force_td;
1599 10390 : Word32 cor_map_LT_sum = 0;
1600 10390 : move32();
1601 :
1602 : /* Set pointer to the reverse accumulator buffer */
1603 10390 : pTmp_fx = &accSubblockNrgRev_fx[NSUBBLOCKS - 1];
1604 10390 : offset = sub( nRelativeDelay, 4 );
1605 10390 : prel_force_td = FALSE;
1606 10390 : move16();
1607 :
1608 10390 : cor_map_LT_sum = sum16_32_fx( cor_map_LT, L_FFT / 2 );
1609 :
1610 10390 : thr_fwd_fx = THR_NORM_HIGH_FX; // THR_NORM_HIGH_FX is in Q11 Format
1611 10390 : move16();
1612 10390 : Word16 shift = 0;
1613 10390 : move16();
1614 : /* Q_cor_map: cor_map_LT is in Q_cor_map format */
1615 : /* Q_multi_harm_limit: multi_harm_limit is in Q_multi_harm_limit format */
1616 10390 : IF( GT_16( Q_cor_map, Q_multi_harm_limit ) )
1617 : {
1618 10390 : shift = sub( Q_cor_map, Q_multi_harm_limit );
1619 :
1620 10390 : if ( GT_32( L_shr( cor_map_LT_sum, shift ), L_deposit_l( mult( multi_harm_limit, 26214 ) ) ) ) // 26214 is 0.8 in Q15 format
1621 : {
1622 2921 : thr_fwd_fx = THR_HIGH_FX; // THR_HIGH_FX is in Q11 Format
1623 2921 : move16();
1624 : }
1625 : }
1626 : ELSE
1627 : {
1628 0 : shift = sub( Q_multi_harm_limit, Q_cor_map );
1629 :
1630 0 : if ( GT_32( cor_map_LT_sum, L_deposit_l( shr( mult( multi_harm_limit, 26214 ), shift ) ) ) ) // 26214 is 0.8 in Q15 format
1631 : {
1632 0 : thr_fwd_fx = THR_HIGH_FX; // THR_HIGH_FX is in Q11 Format
1633 0 : move16();
1634 : }
1635 : }
1636 :
1637 10390 : thr_rev_fx = THR_LOW_FX; // THR_LOW_FX is in Q11 Format
1638 10390 : move16();
1639 10390 : IF( GT_16( Q_cor_map, Q_multi_harm_limit ) )
1640 : {
1641 10390 : shift = sub( Q_cor_map, Q_multi_harm_limit );
1642 :
1643 10390 : if ( GT_32( L_shr( cor_map_LT_sum, shift ), L_deposit_l( mult( multi_harm_limit, 19661 ) ) ) ) // 19661 is 0.6 in Q15 format
1644 : {
1645 9307 : thr_rev_fx = THR_NORM_LOW_FX; // THR_NORM_LOW_FX is in Q11 Format
1646 9307 : move16();
1647 : }
1648 : }
1649 : ELSE
1650 : {
1651 0 : shift = sub( Q_multi_harm_limit, Q_cor_map );
1652 :
1653 0 : if ( GT_32( cor_map_LT_sum, L_deposit_l( shr( mult( multi_harm_limit, 19661 ), shift ) ) ) ) // 19661 is 0.6 in Q15 format
1654 : {
1655 0 : thr_rev_fx = THR_NORM_LOW_FX; // THR_NORM_LOW_FX is in Q11 Format
1656 0 : move16();
1657 : }
1658 : }
1659 :
1660 : /* Forward attack analysis */
1661 103900 : FOR( i = -2; i < 7; i++ )
1662 : {
1663 93510 : IF( BASOP_Util_Cmp_Mant32Exp( hTranDet->subblockEnergies.subblockNrg[nRelativeDelay + i], 32, Mpy_32_16_1( hTranDet->subblockEnergies.accSubblockNrg[nRelativeDelay + i], thr_fwd_fx ), ( 32 + 4 ) ) > 0 )
1664 : {
1665 432 : prel_force_td = s_or( prel_force_td, 0x0001 );
1666 : }
1667 : }
1668 :
1669 10390 : test();
1670 10390 : IF( prel_force_td == 0 && EQ_16( hTranDet->transientDetector.prev_bIsAttackPresent, 1 ) )
1671 : {
1672 : /* Release analysis */
1673 371 : pSubblockNrg = hTranDet->transientDetector.pSubblockEnergies->subblockNrg;
1674 :
1675 371 : set32_fx( accSubblockNrgRev_fx, 0, NSUBBLOCKS );
1676 :
1677 3339 : FOR( i = NSUBBLOCKS - 1; i > -1; i-- )
1678 : {
1679 :
1680 2968 : IF( EQ_16( i, NSUBBLOCKS - 1 ) )
1681 : {
1682 371 : accSubblockNrgRev_fx[i] = pSubblockNrg[i + offset];
1683 371 : move32();
1684 : }
1685 : ELSE
1686 : {
1687 2597 : accSubblockNrgRev_fx[i] = *pTmp_fx;
1688 2597 : *pTmp_fx = Mpy_32_16_1( *pTmp_fx, hTranDet->transientDetector.pSubblockEnergies->facAccSubblockNrg );
1689 2597 : move32();
1690 2597 : if ( GT_32( pSubblockNrg[i + offset], *pTmp_fx ) )
1691 : {
1692 1668 : *pTmp_fx = pSubblockNrg[i + offset];
1693 1668 : move32();
1694 : }
1695 : }
1696 : }
1697 :
1698 : /* -3 check */
1699 371 : test();
1700 371 : IF( BASOP_Util_Cmp_Mant32Exp( pSubblockNrg[1 + offset], 32, Mpy_32_16_1( accSubblockNrgRev_fx[1], thr_rev_fx ), ( 32 + 4 ) ) > 0 )
1701 : {
1702 1 : prel_force_td = s_or( prel_force_td, 0x0002 );
1703 1 : move16();
1704 : }
1705 :
1706 : /* -4 check */
1707 371 : test();
1708 371 : IF( prel_force_td == 0 && BASOP_Util_Cmp_Mant32Exp( pSubblockNrg[offset], 32, Mpy_32_16_1( accSubblockNrgRev_fx[0], thr_rev_fx ), ( 32 + 4 ) ) > 0 )
1709 : {
1710 :
1711 19 : IF( BASOP_Util_Cmp_Mant32Exp( pSubblockNrg[offset], 32, Mpy_32_16_1( accSubblockNrgRev_fx[0], add( thr_rev_fx, THR_LOW_STEP_FX ) ), ( 32 + 4 ) ) > 0 )
1712 : {
1713 17 : prel_force_td = s_or( prel_force_td, 0x0004 );
1714 : }
1715 2 : ELSE IF( ( s_and( hTranDet->subblockEnergies.ramp_up_flag, 0x0002 ) ) != 0 )
1716 : {
1717 2 : prel_force_td = s_or( prel_force_td, 0x0008 );
1718 : }
1719 : }
1720 : }
1721 :
1722 :
1723 10390 : return prel_force_td != 0;
1724 : }
1725 :
1726 : /*-------------------------------------------------------------------*
1727 : * set_transient_stereo()
1728 : *
1729 : *
1730 : *-------------------------------------------------------------------*/
1731 63470 : void set_transient_stereo_fx(
1732 : CPE_ENC_HANDLE hCPE, /* i : CPE structure */
1733 : Word32 currFlatness[] /* i/o: current flatness */
1734 : )
1735 : {
1736 : Word16 n, attackIsPresent;
1737 : Word32 currFlatnessMax;
1738 : Encoder_State **sts;
1739 :
1740 63470 : sts = hCPE->hCoreCoder;
1741 :
1742 : /* for DFT/TD based stereo ,map avg. flatness to individual stereo channels (M/S or X/Y) */
1743 63470 : maximum_32_fx( currFlatness, CPE_CHANNELS, &currFlatnessMax );
1744 63470 : attackIsPresent = 0;
1745 63470 : move16();
1746 :
1747 190410 : FOR( n = 0; n < CPE_CHANNELS; n++ )
1748 : {
1749 126940 : attackIsPresent = s_max( attackIsPresent, sts[n]->hTranDet->transientDetector.bIsAttackPresent );
1750 : }
1751 :
1752 63470 : set32_fx( currFlatness, currFlatnessMax, CPE_CHANNELS );
1753 :
1754 190410 : FOR( n = 0; n < CPE_CHANNELS; n++ )
1755 : {
1756 126940 : sts[n]->hTranDet->transientDetector.bIsAttackPresent = attackIsPresent;
1757 126940 : move16();
1758 : }
1759 :
1760 63470 : IF( hCPE->hStereoDft != NULL )
1761 : {
1762 59643 : IF( hCPE->hStereoDft->attackPresent )
1763 : {
1764 1842 : hCPE->hStereoDft->wasTransient = 1;
1765 1842 : move16();
1766 : }
1767 57801 : ELSE IF( hCPE->hStereoDft->wasTransient )
1768 : {
1769 1606 : hCPE->hStereoDft->wasTransient = 0;
1770 1606 : move16();
1771 : }
1772 :
1773 59643 : hCPE->hStereoDft->attackPresent = attackIsPresent;
1774 59643 : move16();
1775 :
1776 59643 : hCPE->hStereoDft->hItd->currFlatness_fx = 0;
1777 59643 : move16();
1778 178929 : FOR( n = 0; n < CPE_CHANNELS; n++ )
1779 : {
1780 119286 : hCPE->hStereoDft->hItd->currFlatness_fx = L_max( hCPE->hStereoDft->hItd->currFlatness_fx, currFlatness[n] );
1781 119286 : move32();
1782 : }
1783 : }
1784 :
1785 63470 : IF( hCPE->hStereoMdct != NULL )
1786 : {
1787 0 : hCPE->hStereoMdct->hItd->currFlatness_fx = 0;
1788 0 : move16();
1789 0 : FOR( n = 0; n < CPE_CHANNELS; n++ )
1790 : {
1791 0 : hCPE->hStereoMdct->hItd->currFlatness_fx = L_max( hCPE->hStereoMdct->hItd->currFlatness_fx, currFlatness[n] );
1792 0 : move32();
1793 : }
1794 : }
1795 :
1796 63470 : return;
1797 : }
|