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