Line data Source code
1 : /******************************************************************************************************
2 :
3 : (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
4 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
5 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
6 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
7 : contributors to this repository. All Rights Reserved.
8 :
9 : This software is protected by copyright law and by international treaties.
10 : The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
11 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
12 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
13 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
14 : contributors to this repository retain full ownership rights in their respective contributions in
15 : the software. This notice grants no license of any kind, including but not limited to patent
16 : license, nor is any license granted by implication, estoppel or otherwise.
17 :
18 : Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
19 : contributions.
20 :
21 : This software is provided "AS IS", without any express or implied warranties. The software is in the
22 : development stage. It is intended exclusively for experts who have experience with such software and
23 : solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
24 : and fitness for a particular purpose are hereby disclaimed and excluded.
25 :
26 : Any dispute, controversy or claim arising under or in relation to providing this software shall be
27 : submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
28 : accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
29 : the United Nations Convention on Contracts on the International Sales of Goods.
30 :
31 : *******************************************************************************************************/
32 :
33 : /*====================================================================================
34 : EVS Codec 3GPP TS26.443 Nov 04, 2021. Version 12.14.0 / 13.10.0 / 14.6.0 / 15.4.0 / 16.3.0
35 : ====================================================================================*/
36 :
37 : #include <stdint.h>
38 : #include "options.h"
39 : #ifdef DEBUGGING
40 : #include "debug.h"
41 : #endif
42 : #include "stat_enc.h"
43 : #include "cnst.h"
44 : #include "prot.h"
45 : #include "ivas_prot.h"
46 : #include <assert.h>
47 : #include "wmc_auto.h"
48 :
49 :
50 : /*---------------------------------------------------------------*
51 : * Local constants
52 : *---------------------------------------------------------------*/
53 :
54 : #define MIN_BLOCK_ENERGY 107.37f
55 :
56 : #define THR_HIGH 8.5f
57 : #define THR_NORM_HIGH 8.0f
58 : #define THR_NORM_LOW 4.5f
59 : #define THR_LOW 4.25f
60 : #define THR_LOW_STEP 1.0f
61 :
62 :
63 : /*---------------------------------------------------------------*
64 : * Local function prototypes
65 : *---------------------------------------------------------------*/
66 :
67 : static void InitDelayBuffer( const int16_t nFrameLength, const int16_t nDelay, DelayBuffer *pDelayBuffer );
68 : static void InitSubblockEnergies( const int16_t nFrameLength, const int16_t nDelay, DelayBuffer *pDelayBuffer, SubblockEnergies *pSubblockEnergies );
69 : static void InitTransientDetector( SubblockEnergies *pSubblockEnergies, const int16_t nDelay, const int16_t nSubblocksToCheck, TCheckSubblocksForAttack pCheckSubblocksForAttack, const float attackRatioThreshold, TransientDetector *pTransientDetector );
70 : static void UpdateDelayBuffer( const float *inputconst, const int16_t nSamplesAvailable, DelayBuffer *pDelayBuffer );
71 : static void HighPassFilter( const float *input, const int16_t length, float *pFirState1, float *pFirState2, float *output );
72 : static void UpdateSubblockEnergies( const float *input, const int16_t nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
73 : static void CalculateSubblockEnergies( const float *input, const int16_t nSamplesAvailable, SubblockEnergies *pSubblockEnergies );
74 : static void RunTransientDetector( TransientDetector *pTransientDetector );
75 : static void GetAttackForTCXDecision( const float *pSubblockNrg, const float *pAccSubblockNrg, const int16_t nSubblocks, const int16_t nPastSubblocks, const float attackRatioThreshold, int16_t *pbIsAttackPresent, int16_t *pAttackIndex );
76 :
77 :
78 : /*-------------------------------------------------------------------*
79 : * InitTransientDetection()
80 : *
81 : *
82 : *-------------------------------------------------------------------*/
83 :
84 146320 : void InitTransientDetection(
85 : const int16_t nFrameLength,
86 : const int16_t nTCXDelay,
87 : TRAN_DET_HANDLE hTranDet,
88 : const int16_t ext_mem_flag )
89 : {
90 : /* Init the delay buffer. */
91 146320 : InitDelayBuffer( nFrameLength, nTCXDelay, &hTranDet->delayBuffer );
92 :
93 : /* Init a subblock energies buffer used for the TCX Short/Long decision. */
94 146320 : InitSubblockEnergies( nFrameLength, nTCXDelay, &hTranDet->delayBuffer, &hTranDet->subblockEnergies );
95 :
96 : /* Init the TCX Short/Long transient detector. */
97 146320 : InitTransientDetector( &hTranDet->subblockEnergies, nTCXDelay, NSUBBLOCKS, GetAttackForTCXDecision, 8.5f, &hTranDet->transientDetector );
98 :
99 : /* We need two past subblocks for the TCX TD and NSUBBLOCKS+1 for the temporal flatness measure for the TCX LTP. */
100 146320 : if ( ext_mem_flag )
101 : {
102 146014 : hTranDet->transientDetector.pSubblockEnergies->nDelay += ( NSUBBLOCKS_SHIFT + 1 ) + NSUBBLOCKS + 1;
103 : }
104 : else
105 : {
106 306 : hTranDet->transientDetector.pSubblockEnergies->nDelay += NSUBBLOCKS + 1;
107 : }
108 :
109 146320 : return;
110 : }
111 :
112 :
113 : /*-------------------------------------------------------------------*
114 : * GetTCXAvgTemporalFlatnessMeasure()
115 : *
116 : *
117 : *-------------------------------------------------------------------*/
118 :
119 48732567 : float GetTCXAvgTemporalFlatnessMeasure(
120 : TRAN_DET_HANDLE hTranDet,
121 : const int16_t nCurrentSubblocks,
122 : const int16_t nPrevSubblocks )
123 : {
124 : int16_t i;
125 48732567 : TransientDetector *pTransientDetector = &hTranDet->transientDetector;
126 48732567 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
127 48732567 : const int16_t nDelay = pTransientDetector->nDelay;
128 48732567 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
129 48732567 : const float *pSubblockNrgChange = NULL;
130 : float sumTempFlatness;
131 48732567 : const int16_t nTotBlocks = nCurrentSubblocks + nPrevSubblocks;
132 :
133 : /* Initialization */
134 48732567 : assert( nTotBlocks > 0 );
135 48732567 : sumTempFlatness = 0.0f;
136 :
137 48732567 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
138 48732567 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nRelativeDelay - nPrevSubblocks];
139 507031044 : for ( i = 0; i < nTotBlocks; i++ )
140 : {
141 458298477 : sumTempFlatness += pSubblockNrgChange[i];
142 : }
143 :
144 48732567 : return sumTempFlatness / (float) nTotBlocks;
145 : }
146 :
147 :
148 : /*-------------------------------------------------------------------*
149 : * GetTCXMaxenergyChange()
150 : *
151 : *
152 : *-------------------------------------------------------------------*/
153 :
154 14968012 : float GetTCXMaxenergyChange(
155 : TRAN_DET_HANDLE hTranDet,
156 : const int16_t isTCX10,
157 : const int16_t nCurrentSubblocks,
158 : const int16_t nPrevSubblocks )
159 : {
160 : int16_t i;
161 14968012 : const TransientDetector *pTransientDetector = &hTranDet->transientDetector;
162 14968012 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
163 14968012 : const int16_t nDelay = pTransientDetector->nDelay;
164 14968012 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
165 14968012 : const float *pSubblockNrgChange = NULL;
166 : float maxEnergyChange;
167 14968012 : int16_t nTotBlocks = nCurrentSubblocks + nPrevSubblocks;
168 : /* Initialization */
169 14968012 : assert( nTotBlocks > 0 );
170 14968012 : maxEnergyChange = 0.0f;
171 :
172 14968012 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
173 14968012 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nRelativeDelay - nPrevSubblocks];
174 14968012 : if ( pTransientDetector->bIsAttackPresent || isTCX10 ) /* frame is TCX-10 */
175 : {
176 317392 : const float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nRelativeDelay - nPrevSubblocks];
177 317392 : float nrgMin, nrgMax = pSubblockNrg[0];
178 317392 : int16_t idxMax = 0;
179 : /* find subblock with maximum energy */
180 4042011 : for ( i = 1; i < nTotBlocks; i++ )
181 : {
182 3724619 : if ( nrgMax < pSubblockNrg[i] )
183 : {
184 1037941 : nrgMax = pSubblockNrg[i];
185 1037941 : idxMax = i;
186 : }
187 : }
188 317392 : nrgMin = nrgMax;
189 : /* find minimum energy after maximum */
190 1572378 : for ( i = idxMax + 1; i < nTotBlocks; i++ )
191 : {
192 1254986 : if ( nrgMin > pSubblockNrg[i] )
193 : {
194 750269 : nrgMin = pSubblockNrg[i];
195 : }
196 : }
197 : /* lower maxEnergyChange if energy doesn't decrease much after energy peak */
198 317392 : if ( nrgMin > 0.375f * nrgMax )
199 : {
200 132393 : nTotBlocks = idxMax - 3;
201 : }
202 : }
203 203479112 : for ( i = 0; i < nTotBlocks; i++ )
204 : {
205 188511100 : maxEnergyChange = max( maxEnergyChange, pSubblockNrgChange[i] );
206 : }
207 :
208 14968012 : return maxEnergyChange;
209 : }
210 :
211 :
212 : /*---------------------------------------------------------------*
213 : * RunTransientDetection()
214 : *
215 : * Time Domain Transient Detector for TCX
216 : *---------------------------------------------------------------*/
217 :
218 18531210 : void RunTransientDetection(
219 : const float *input, /* i : input signal */
220 : const int16_t length, /* i : frame length */
221 : TRAN_DET_HANDLE hTranDet /* i/o: transient detection handle */
222 : )
223 : {
224 : float filteredInput[L_FRAME_MAX];
225 18531210 : SubblockEnergies *pSubblockEnergies = &hTranDet->subblockEnergies;
226 18531210 : TransientDetector *pTransientDetector = &hTranDet->transientDetector;
227 : float e0, e1;
228 :
229 18531210 : assert( ( input != NULL ) && ( hTranDet != NULL ) && ( pSubblockEnergies != NULL ) && ( pTransientDetector != NULL ) );
230 :
231 : /* Variable initializations */
232 18531210 : HighPassFilter( input, length, &pSubblockEnergies->firState1, &pSubblockEnergies->firState2, filteredInput );
233 :
234 : /* Update subblock energies. */
235 18531210 : UpdateSubblockEnergies( filteredInput, length, pSubblockEnergies );
236 :
237 : /* Run transient detectors. */
238 18531210 : RunTransientDetector( pTransientDetector );
239 :
240 : /* Update the delay buffer. */
241 18531210 : UpdateDelayBuffer( filteredInput, length, &hTranDet->delayBuffer );
242 :
243 : /* compute ramp up flag */
244 18531210 : pSubblockEnergies->ramp_up_flag = ( ( pSubblockEnergies->ramp_up_flag << 1 ) & 0x0003 );
245 18531210 : e0 = dotp( filteredInput + length / 2, filteredInput + length / 2, pSubblockEnergies->pDelayBuffer->nSubblockSize / 2 ) + 0.5f * MIN_BLOCK_ENERGY;
246 18531210 : e1 = pSubblockEnergies->subblockNrg[pSubblockEnergies->nDelay + 4] - e0;
247 18531210 : if ( e1 > e0 )
248 : {
249 8307738 : pSubblockEnergies->ramp_up_flag |= 0x0001;
250 : }
251 :
252 18531210 : return;
253 : }
254 :
255 :
256 15607215 : static uint16_t isLongTermTransient(
257 : const float frameTFM,
258 : float *lastTFM )
259 : {
260 : float currTFM;
261 :
262 15607215 : if ( frameTFM > *lastTFM )
263 : {
264 8093097 : const float f = ( frameTFM - *lastTFM ) * ( frameTFM - *lastTFM );
265 :
266 8093097 : currTFM = *lastTFM * ( 0.96875f - f ) + frameTFM * ( 0.03125f + f );
267 : }
268 : else
269 : {
270 7514118 : currTFM = *lastTFM * 0.96875f + frameTFM * 0.03125f;
271 : }
272 :
273 15607215 : *lastTFM = max( 0.015625f, currTFM );
274 :
275 15607215 : return ( currTFM < 0.5625f ? 1 : 0 );
276 : }
277 :
278 :
279 : /*-------------------------------------------------------------------*
280 : * SetTCXModeInfo()
281 : *
282 : *
283 : *-------------------------------------------------------------------*/
284 :
285 15692498 : void SetTCXModeInfo(
286 : Encoder_State *st, /* i/o: encoder state structure */
287 : TRAN_DET_HANDLE hTranDet, /* i/o: transient detection handle */
288 : int16_t *tcxModeOverlap /* o : window overlap of current frame */
289 : )
290 : {
291 15692498 : TCX_ENC_HANDLE hTcxEnc = st->hTcxEnc;
292 :
293 15692498 : if ( st->codec_mode == MODE2 || ( st->element_mode > EVS_MONO && st->core != HQ_CORE ) )
294 : {
295 15607215 : assert( hTranDet != NULL );
296 :
297 : /* determine window sequence (1 long or 2 short windows) */
298 15607215 : if ( st->tcx10Enabled && st->tcx20Enabled )
299 : {
300 : /* window switching based on transient detector output */
301 13468339 : if ( ( ( hTranDet->transientDetector.bIsAttackPresent ) || ( st->currEnergyHF > st->prevEnergyHF * 39.0f && st->element_mode != IVAS_CPE_MDCT ) ) &&
302 284078 : ( ( st->last_core != ACELP_CORE ) && ( st->last_core != AMR_WB_CORE ) ) )
303 : {
304 283004 : hTcxEnc->tcxMode = TCX_10;
305 : }
306 : else
307 : {
308 13185335 : hTcxEnc->tcxMode = TCX_20;
309 : }
310 : }
311 : else
312 : {
313 : /* window selection (non-adaptive) based on flags only */
314 2138876 : if ( st->tcx10Enabled )
315 : {
316 0 : hTcxEnc->tcxMode = TCX_10;
317 : }
318 2138876 : else if ( st->tcx20Enabled )
319 : {
320 2138876 : hTcxEnc->tcxMode = TCX_20;
321 : }
322 : else
323 : {
324 0 : hTcxEnc->tcxMode = NO_TCX;
325 : }
326 : }
327 : /* set the left window overlap */
328 15607215 : if ( st->last_core == ACELP_CORE || st->last_core == AMR_WB_CORE )
329 : {
330 136349 : st->hTcxCfg->tcx_last_overlap_mode = TRANSITION_OVERLAP;
331 : }
332 15470866 : else if ( ( hTcxEnc->tcxMode == TCX_10 ) && ( st->hTcxCfg->tcx_curr_overlap_mode == ALDO_WINDOW ) )
333 : {
334 227317 : st->hTcxCfg->tcx_last_overlap_mode = FULL_OVERLAP;
335 : }
336 : else
337 : {
338 15243549 : st->hTcxCfg->tcx_last_overlap_mode = st->hTcxCfg->tcx_curr_overlap_mode;
339 : }
340 :
341 : /* determine the right window overlap */
342 15607215 : if ( hTcxEnc->tcxMode == TCX_10 )
343 : {
344 283004 : if ( hTranDet->transientDetector.attackIndex < 0 )
345 : {
346 71 : *tcxModeOverlap = HALF_OVERLAP;
347 : }
348 : else
349 : {
350 282933 : *tcxModeOverlap = hTranDet->transientDetector.attackIndex % 4;
351 282933 : if ( *tcxModeOverlap == 1 )
352 : {
353 26461 : *tcxModeOverlap = FULL_OVERLAP;
354 : }
355 : }
356 283004 : if ( isLongTermTransient( 1.0f / GetTCXAvgTemporalFlatnessMeasure( hTranDet, NSUBBLOCKS, 0 ), &hTcxEnc->tfm_mem ) && st->element_mode == IVAS_CPE_MDCT )
357 : {
358 6177 : if ( ( *tcxModeOverlap != MIN_OVERLAP ) && ( hTcxEnc->tcxltp_norm_corr_past < 0.5625f ) )
359 : {
360 2598 : *tcxModeOverlap = HALF_OVERLAP;
361 : }
362 : }
363 : }
364 15324211 : else if ( hTcxEnc->tcxMode == TCX_20 )
365 : {
366 15324211 : if ( hTranDet->transientDetector.attackIndex == 7 )
367 : {
368 42479 : *tcxModeOverlap = HALF_OVERLAP;
369 : }
370 15281732 : else if ( hTranDet->transientDetector.attackIndex == 6 )
371 : {
372 36714 : *tcxModeOverlap = MIN_OVERLAP;
373 : }
374 : else
375 : {
376 15245018 : *tcxModeOverlap = ALDO_WINDOW;
377 : }
378 15324211 : if ( isLongTermTransient( 1.0f / GetTCXAvgTemporalFlatnessMeasure( hTranDet, NSUBBLOCKS, 0 ), &hTcxEnc->tfm_mem ) && st->element_mode == IVAS_CPE_MDCT )
379 : {
380 184828 : if ( ( *tcxModeOverlap != MIN_OVERLAP ) && ( hTcxEnc->tcxltp_norm_corr_past < 0.5625f ) )
381 : {
382 87442 : *tcxModeOverlap = HALF_OVERLAP;
383 : }
384 : }
385 : }
386 : else
387 : {
388 : /* NO_TCX */
389 0 : *tcxModeOverlap = TRANSITION_OVERLAP;
390 0 : if ( st->element_mode == IVAS_CPE_MDCT )
391 : {
392 0 : hTcxEnc->tfm_mem = 0.75f;
393 : }
394 : }
395 :
396 : /* for the ACELP -> TCX transition frames use full right window overlap */
397 15607215 : if ( ( st->hTcxCfg->tcx_last_overlap_mode == TRANSITION_OVERLAP ) && ( *tcxModeOverlap == ALDO_WINDOW ) )
398 : {
399 135152 : *tcxModeOverlap = FULL_OVERLAP;
400 : }
401 : }
402 :
403 15692498 : return;
404 : }
405 :
406 :
407 : /*---------------------------------------------------------------*
408 : * Local functions
409 : *---------------------------------------------------------------*/
410 :
411 : /** TCX decision.
412 : * Check if there is an attack in a subblock. Version for TCX Long/Short decision.
413 : * See TCheckSubblocksForAttack for definition of parameters.
414 : * 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.
415 : */
416 18531210 : static void GetAttackForTCXDecision(
417 : const float *pSubblockNrg,
418 : const float *pAccSubblockNrg,
419 : const int16_t nSubblocks,
420 : const int16_t nPastSubblocks,
421 : const float attackRatioThreshold,
422 : int16_t *pbIsAttackPresent,
423 : int16_t *pAttackIndex )
424 : {
425 : int16_t i;
426 : int16_t bIsAttackPresent, attackIndex;
427 :
428 18531210 : assert( nSubblocks >= NSUBBLOCKS );
429 18531210 : assert( nPastSubblocks >= 2 );
430 :
431 18531210 : bIsAttackPresent = FALSE;
432 18531210 : attackIndex = -1;
433 : /* Search for the last attack in the subblocks */
434 18531210 : if ( ( pSubblockNrg[-1] > pAccSubblockNrg[-1] * attackRatioThreshold ) || ( pSubblockNrg[-2] > pAccSubblockNrg[-2] * attackRatioThreshold ) )
435 : {
436 69641 : bIsAttackPresent = TRUE;
437 69641 : attackIndex = 0;
438 : }
439 166780890 : for ( i = 0; i < NSUBBLOCKS; i++ )
440 : {
441 148249680 : if ( pSubblockNrg[i] > pAccSubblockNrg[i] * attackRatioThreshold )
442 : {
443 438021 : if ( i < 6 )
444 : {
445 365727 : bIsAttackPresent = TRUE;
446 : }
447 438021 : if ( ( attackIndex != 2 ) && ( attackIndex != 6 ) )
448 : {
449 433674 : attackIndex = i;
450 433674 : if ( ( pSubblockNrg[i] < pAccSubblockNrg[i] * 1.125f * attackRatioThreshold ) && ( i == 2 || i == 6 ) )
451 : {
452 8298 : attackIndex++; /* avoid minimum overlap to prevent clicks */
453 : }
454 : }
455 : }
456 : else /* no attack, but set index anyway in case of strong energy increase */
457 : {
458 147811659 : if ( ( pSubblockNrg[i] > pSubblockNrg[i - 1] * 1.5f * attackRatioThreshold ) &&
459 229921 : ( pSubblockNrg[i] > pSubblockNrg[i - 2] * 1.5f * attackRatioThreshold ) )
460 : {
461 110711 : if ( ( attackIndex != 2 ) && ( attackIndex != 6 ) )
462 : {
463 110474 : attackIndex = i;
464 :
465 110474 : if ( ( ( pSubblockNrg[i] < pSubblockNrg[i - 1] * 2.0f * attackRatioThreshold ) ||
466 110474 : ( pSubblockNrg[i] < pSubblockNrg[i - 2] * 2.0f * attackRatioThreshold ) ) &&
467 24556 : ( i == 2 || i == 6 ) )
468 : {
469 7676 : attackIndex++; /* avoid minimum overlap to prevent clicks */
470 : }
471 : }
472 : }
473 : }
474 : }
475 :
476 : /* avoid post-echos on click sounds (very short transients) due to TNS aliasing */
477 18531210 : if ( attackIndex == 4 )
478 : {
479 67326 : attackIndex = 7;
480 : }
481 18463884 : else if ( attackIndex == 5 )
482 : {
483 41828 : attackIndex = 6;
484 : }
485 18531210 : *pAttackIndex = attackIndex;
486 18531210 : *pbIsAttackPresent = bIsAttackPresent;
487 :
488 18531210 : return;
489 : }
490 :
491 :
492 146320 : static void InitDelayBuffer(
493 : const int16_t nFrameLength,
494 : const int16_t nDelay,
495 : DelayBuffer *pDelayBuffer )
496 : {
497 146320 : const int16_t nMaxBuffSize = L_FRAME_MAX / NSUBBLOCKS;
498 :
499 146320 : assert( ( nFrameLength > NSUBBLOCKS ) && ( nFrameLength % NSUBBLOCKS == 0 ) && ( nDelay >= 0 ) && ( pDelayBuffer != NULL ) );
500 146320 : pDelayBuffer->nSubblockSize = nFrameLength / NSUBBLOCKS;
501 146320 : assert( pDelayBuffer->nSubblockSize <= nMaxBuffSize );
502 146320 : set_f( pDelayBuffer->buffer, 0.0f, nMaxBuffSize );
503 146320 : pDelayBuffer->nDelay = nDelay % pDelayBuffer->nSubblockSize;
504 146320 : assert( pDelayBuffer->nDelay <= nMaxBuffSize );
505 :
506 146320 : return;
507 : }
508 :
509 :
510 146320 : static void InitSubblockEnergies(
511 : const int16_t nFrameLength,
512 : const int16_t nDelay,
513 : DelayBuffer *pDelayBuffer,
514 : SubblockEnergies *pSubblockEnergies )
515 : {
516 146320 : const int16_t nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
517 :
518 146320 : assert( ( pDelayBuffer != NULL ) && ( pSubblockEnergies != NULL ) && ( pDelayBuffer->nSubblockSize * NSUBBLOCKS == nFrameLength ) && ( pDelayBuffer->nSubblockSize > 0 ) );
519 :
520 146320 : set_f( pSubblockEnergies->subblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize );
521 146320 : set_f( pSubblockEnergies->accSubblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize + 1 );
522 146320 : set_f( pSubblockEnergies->subblockNrgChange, 1.0f, nMaxBuffSize );
523 146320 : pSubblockEnergies->nDelay = nDelay / pDelayBuffer->nSubblockSize;
524 146320 : assert( pSubblockEnergies->nDelay < nMaxBuffSize );
525 146320 : pSubblockEnergies->nPartialDelay = nDelay % pDelayBuffer->nSubblockSize;
526 146320 : pSubblockEnergies->facAccSubblockNrg = 0.8125f; /* Energy accumulation factor */
527 146320 : pSubblockEnergies->firState1 = 0.0f;
528 146320 : pSubblockEnergies->firState2 = 0.0f;
529 :
530 146320 : pSubblockEnergies->pDelayBuffer = pDelayBuffer;
531 146320 : pDelayBuffer->nDelay = max( pDelayBuffer->nDelay, pSubblockEnergies->nPartialDelay );
532 :
533 146320 : return;
534 : }
535 :
536 :
537 : /** Init transient detector.
538 : * Fills TransientDetector structure with sensible content and enable it.
539 : * @param pSubblockEnergies Subblock energies used in this transient detector.
540 : * @param nDelay Delay for this transient detector.
541 : * @param nSubblocksToCheck Number of subblocks to check in this transient detector.
542 : * @param pCheckSubblockForAttack Attack detection function for this transient detector.
543 : * @param pSetAttackPosition Function for finalizing this transient detector.
544 : * @param attackRatioThreshold Attack ratio threshold.
545 : * @param pTransientDetector Structure to be initialized.
546 : */
547 146320 : static void InitTransientDetector(
548 : SubblockEnergies *pSubblockEnergies,
549 : const int16_t nDelay,
550 : const int16_t nSubblocksToCheck,
551 : TCheckSubblocksForAttack pCheckSubblocksForAttack,
552 : const float attackRatioThreshold,
553 : TransientDetector *pTransientDetector )
554 : {
555 146320 : const int16_t nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
556 :
557 146320 : assert( ( pSubblockEnergies != NULL ) && ( pSubblockEnergies->pDelayBuffer != NULL ) && ( pTransientDetector != NULL ) && ( pSubblockEnergies->pDelayBuffer->nSubblockSize != 0 ) );
558 146320 : pTransientDetector->pSubblockEnergies = pSubblockEnergies;
559 146320 : pTransientDetector->nDelay = ( nDelay - pSubblockEnergies->nPartialDelay ) / pSubblockEnergies->pDelayBuffer->nSubblockSize;
560 146320 : assert( nDelay == pTransientDetector->nDelay * pSubblockEnergies->pDelayBuffer->nSubblockSize + pSubblockEnergies->nPartialDelay );
561 146320 : assert( pTransientDetector->nDelay < nMaxBuffSize );
562 146320 : pSubblockEnergies->nDelay = max( pSubblockEnergies->nDelay, pTransientDetector->nDelay );
563 146320 : assert( nSubblocksToCheck <= NSUBBLOCKS + pTransientDetector->nDelay );
564 146320 : pTransientDetector->nSubblocksToCheck = nSubblocksToCheck;
565 146320 : pTransientDetector->CheckSubblocksForAttack = pCheckSubblocksForAttack;
566 146320 : pTransientDetector->attackRatioThreshold = attackRatioThreshold;
567 146320 : pTransientDetector->bIsAttackPresent = FALSE;
568 146320 : pTransientDetector->prev_bIsAttackPresent = FALSE;
569 146320 : pTransientDetector->attackIndex = -1;
570 146320 : pTransientDetector->pSubblockEnergies->ramp_up_flag = 0x0;
571 :
572 146320 : return;
573 : }
574 :
575 :
576 : /* This function should be inlined and WMOPS instrumentation takes that into account, meaning that all references are considered as local variables */
577 15650542080 : static float InlineFilter(
578 : float inValue,
579 : float firState1,
580 : float firState2 )
581 : {
582 15650542080 : return 0.375f * inValue - 0.5f * firState1 + 0.125f * firState2;
583 : }
584 :
585 :
586 18531210 : static void HighPassFilter(
587 : const float *input,
588 : const int16_t length,
589 : float *pFirState1,
590 : float *pFirState2,
591 : float *output )
592 : {
593 : int16_t i;
594 :
595 18531210 : output[0] = InlineFilter( input[0], *pFirState1, *pFirState2 );
596 18531210 : output[1] = InlineFilter( input[1], input[0], *pFirState1 );
597 15632010870 : for ( i = 2; i < length; i++ )
598 : {
599 15613479660 : output[i] = InlineFilter( input[i], input[i - 1], input[i - 2] );
600 : }
601 :
602 : /* update filter states: shift time samples through delay line */
603 18531210 : *pFirState2 = input[length - 2];
604 18531210 : *pFirState1 = input[length - 1];
605 :
606 18531210 : return;
607 : }
608 :
609 :
610 18531210 : static void RunTransientDetector(
611 : TransientDetector *pTransientDetector )
612 : {
613 18531210 : const float attackRatioThreshold = pTransientDetector->attackRatioThreshold;
614 18531210 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
615 18531210 : const int16_t nDelay = pTransientDetector->nDelay;
616 18531210 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
617 18531210 : const float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nRelativeDelay];
618 18531210 : const float *pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nRelativeDelay];
619 :
620 18531210 : assert( ( pTransientDetector->CheckSubblocksForAttack != NULL ) );
621 :
622 : /* Variable initialization */
623 : #define WMC_TOOL_SKIP
624 18531210 : pTransientDetector->CheckSubblocksForAttack( pSubblockNrg, pAccSubblockNrg, NSUBBLOCKS + nDelay, nRelativeDelay, attackRatioThreshold, &pTransientDetector->bIsAttackPresent, &pTransientDetector->attackIndex );
625 : #undef WMC_TOOL_SKIP
626 :
627 18531210 : return;
628 : }
629 :
630 :
631 18531210 : static void UpdateDelayBuffer(
632 : const float *input,
633 : const int16_t nSamplesAvailable,
634 : DelayBuffer *pDelayBuffer )
635 : {
636 : int16_t i;
637 18531210 : int16_t nDelay = pDelayBuffer->nDelay;
638 :
639 18531210 : assert( ( nDelay >= 0 ) && ( nDelay <= (int16_t) sizeof( pDelayBuffer->buffer ) / (int16_t) sizeof( pDelayBuffer->buffer[0] ) ) );
640 18531210 : assert( nSamplesAvailable <= NSUBBLOCKS * pDelayBuffer->nSubblockSize );
641 :
642 : /* If this is not the last frame */
643 18531210 : if ( nSamplesAvailable == NSUBBLOCKS * pDelayBuffer->nSubblockSize )
644 : {
645 : /* Store the newest samples into the delay buffer */
646 21631785 : for ( i = 0; i < nDelay; i++ )
647 : {
648 3100575 : pDelayBuffer->buffer[i] = input[i + nSamplesAvailable - nDelay];
649 : }
650 : }
651 :
652 18531210 : return;
653 : }
654 :
655 :
656 18531210 : static void UpdateSubblockEnergies(
657 : const float *input,
658 : const int16_t nSamplesAvailable,
659 : SubblockEnergies *pSubblockEnergies )
660 : {
661 : int16_t i;
662 :
663 18531210 : assert( ( pSubblockEnergies->nDelay >= 0 ) && ( pSubblockEnergies->nDelay + NSUBBLOCKS <= (int16_t) sizeof( pSubblockEnergies->subblockNrg ) / (int16_t) sizeof( pSubblockEnergies->subblockNrg[0] ) ) );
664 18531210 : assert( pSubblockEnergies->nPartialDelay <= pSubblockEnergies->pDelayBuffer->nDelay );
665 : /* At least one block delay is required when subblock energy change is required */
666 18531210 : assert( pSubblockEnergies->nDelay >= 1 );
667 :
668 : /* Shift old subblock energies */
669 259028668 : for ( i = 0; i < pSubblockEnergies->nDelay; i++ )
670 : {
671 240497458 : pSubblockEnergies->subblockNrg[i] = pSubblockEnergies->subblockNrg[i + NSUBBLOCKS];
672 240497458 : pSubblockEnergies->accSubblockNrg[i] = pSubblockEnergies->accSubblockNrg[i + NSUBBLOCKS];
673 240497458 : pSubblockEnergies->subblockNrgChange[i] = pSubblockEnergies->subblockNrgChange[i + NSUBBLOCKS];
674 : }
675 :
676 : /* Compute filtered subblock energies for the new samples */
677 18531210 : CalculateSubblockEnergies( input, nSamplesAvailable, pSubblockEnergies );
678 :
679 18531210 : return;
680 : }
681 :
682 :
683 : /* This function should be inlined and WMOPS instrumentation takes that into account, meaning that all references are considered as local variables */
684 148249680 : static void UpdatedAndStoreAccWindowNrg(
685 : float newWindowNrgF,
686 : float *pAccSubblockNrg,
687 : float facAccSubblockNrg,
688 : float *pOutAccWindowNrgF )
689 : {
690 : /* Store the accumulated energy */
691 148249680 : *pOutAccWindowNrgF = *pAccSubblockNrg;
692 :
693 : /* Update the accumulated energy: maximum of the current and the accumulated energy */
694 148249680 : *pAccSubblockNrg *= facAccSubblockNrg;
695 148249680 : if ( newWindowNrgF > *pAccSubblockNrg )
696 : {
697 92842133 : *pAccSubblockNrg = newWindowNrgF;
698 : }
699 :
700 148249680 : return;
701 : }
702 :
703 :
704 18531210 : static void CalculateSubblockEnergies(
705 : const float *input,
706 : const int16_t nSamplesAvailable,
707 : SubblockEnergies *pSubblockEnergies )
708 : {
709 18531210 : DelayBuffer *pDelayBuffer = pSubblockEnergies->pDelayBuffer; /* */
710 18531210 : const int16_t nSubblockSize = pDelayBuffer->nSubblockSize; /* */
711 18531210 : const int16_t nDelay = pSubblockEnergies->nDelay; /* */
712 18531210 : const int16_t nPartialDelay = pSubblockEnergies->nPartialDelay; /* */
713 18531210 : const float *delayBuffer = &pDelayBuffer->buffer[pDelayBuffer->nDelay - nPartialDelay]; /* */
714 18531210 : const float facAccSubblockNrg = pSubblockEnergies->facAccSubblockNrg; /* */
715 18531210 : float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nDelay]; /* */
716 18531210 : float *pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nDelay]; /* */
717 18531210 : float *pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nDelay]; /* */
718 : float *pAccSubblockTmp;
719 : int16_t nWindows;
720 : int16_t i, w, k;
721 : /* Variable initializations */
722 18531210 : nWindows = ( nSamplesAvailable + nPartialDelay ) / nSubblockSize;
723 18531210 : pAccSubblockTmp = &pAccSubblockNrg[nWindows];
724 :
725 18531210 : set_f( pSubblockNrg, MIN_BLOCK_ENERGY, NSUBBLOCKS );
726 :
727 18531210 : if ( nWindows > 0 )
728 : {
729 : /* Process left over samples from the previous frame. */
730 21631785 : for ( k = 0; k < nPartialDelay; k++ )
731 : {
732 3100575 : pSubblockNrg[0] += delayBuffer[k] * delayBuffer[k];
733 : }
734 18531210 : k = 0;
735 :
736 : /* Process new samples in the 0. subblock. */
737 1971748395 : for ( i = nPartialDelay; i < nSubblockSize; i++, k++ )
738 : {
739 1953217185 : pSubblockNrg[0] += input[k] * input[k];
740 : }
741 :
742 : /* Set accumulated subblock energy at this point. */
743 18531210 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[0], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[0] );
744 :
745 148249680 : for ( w = 1; w < nWindows; w++ )
746 : {
747 : /* Process new samples in the w. subblock. */
748 13823942790 : for ( i = 0; i < nSubblockSize; i++, k++ )
749 : {
750 13694224320 : pSubblockNrg[w] += input[k] * input[k];
751 : }
752 : /* Set accumulated subblock energy at this point. */
753 129718470 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[w], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[w] );
754 : }
755 :
756 : /* Calculate energy change for each block. */
757 166780890 : for ( w = 0; w < nWindows; w++ )
758 : {
759 148249680 : if ( pSubblockNrg[w] > pSubblockNrg[w - 1] )
760 : {
761 66223916 : pSubblockNrgChange[w] = pSubblockNrg[w] / pSubblockNrg[w - 1];
762 : }
763 : else
764 : {
765 82025764 : pSubblockNrgChange[w] = pSubblockNrg[w - 1] / pSubblockNrg[w];
766 : }
767 : }
768 : }
769 :
770 18531210 : return;
771 : }
772 :
773 :
774 : /*-------------------------------------------------------------------*
775 : * set_transient_stereo()
776 : *
777 : *
778 : *-------------------------------------------------------------------*/
779 :
780 901572 : void set_transient_stereo(
781 : CPE_ENC_HANDLE hCPE, /* i : CPE structure */
782 : float currFlatness[] /* i/o: current flatness */
783 : )
784 : {
785 : int16_t n, attackIsPresent;
786 : float currFlatnessMax;
787 : Encoder_State **sts;
788 :
789 901572 : sts = hCPE->hCoreCoder;
790 :
791 : /* for DFT/TD based stereo ,map avg. flatness to individual stereo channels (M/S or X/Y) */
792 901572 : maximum( currFlatness, CPE_CHANNELS, &currFlatnessMax );
793 901572 : attackIsPresent = 0;
794 :
795 2704716 : for ( n = 0; n < CPE_CHANNELS; n++ )
796 : {
797 1803144 : attackIsPresent = max( attackIsPresent, sts[n]->hTranDet->transientDetector.bIsAttackPresent );
798 : }
799 :
800 901572 : set_f( currFlatness, currFlatnessMax, CPE_CHANNELS );
801 :
802 2704716 : for ( n = 0; n < CPE_CHANNELS; n++ )
803 : {
804 1803144 : sts[n]->hTranDet->transientDetector.bIsAttackPresent = attackIsPresent;
805 : }
806 :
807 901572 : if ( hCPE->hStereoDft != NULL )
808 : {
809 871096 : if ( hCPE->hStereoDft->attackPresent )
810 : {
811 28728 : hCPE->hStereoDft->wasTransient = 1;
812 : }
813 842368 : else if ( hCPE->hStereoDft->wasTransient )
814 : {
815 23533 : hCPE->hStereoDft->wasTransient = 0;
816 : }
817 :
818 871096 : hCPE->hStereoDft->attackPresent = attackIsPresent;
819 :
820 871096 : hCPE->hStereoDft->hItd->currFlatness = 0;
821 2613288 : for ( n = 0; n < CPE_CHANNELS; n++ )
822 : {
823 1742192 : hCPE->hStereoDft->hItd->currFlatness = max( hCPE->hStereoDft->hItd->currFlatness, currFlatness[n] );
824 : }
825 : }
826 :
827 901572 : if ( hCPE->hStereoMdct != NULL )
828 : {
829 0 : hCPE->hStereoMdct->hItd->currFlatness = 0;
830 0 : for ( n = 0; n < CPE_CHANNELS; n++ )
831 : {
832 0 : hCPE->hStereoMdct->hItd->currFlatness = max( hCPE->hStereoMdct->hItd->currFlatness, currFlatness[n] );
833 : }
834 : }
835 :
836 901572 : return;
837 : }
838 :
839 : /*-------------------------------------------------------------------*
840 : * transient_analysis()
841 : *
842 : *
843 : *-------------------------------------------------------------------*/
844 :
845 : /*! r: preliminary flag to force ACELP */
846 119571 : int16_t transient_analysis(
847 : TRAN_DET_HANDLE hTranDet, /* i : handle transient detection */
848 : const float cor_map_LT[], /* i : LT correlation map */
849 : const float multi_harm_limit /* i : multi harminic threshold */
850 : )
851 : {
852 : const float *pSubblockNrg;
853 : float accSubblockNrgRev[NSUBBLOCKS]; /* store acc Nrg in reversed signal */
854 : float *pTmp; /* point to acc Nrg */
855 : int16_t offset;
856 : int16_t i;
857 : float thr_fwd;
858 : float thr_rev;
859 119571 : const int16_t nRelativeDelay = hTranDet->subblockEnergies.nDelay - hTranDet->transientDetector.nDelay;
860 : int16_t prel_force_td;
861 : float cor_map_LT_sum;
862 :
863 119571 : pTmp = &accSubblockNrgRev[NSUBBLOCKS - 1];
864 119571 : offset = nRelativeDelay - 4;
865 119571 : prel_force_td = FALSE;
866 :
867 : /* summation of the LT correlation map */
868 119571 : cor_map_LT_sum = sum_f( cor_map_LT, L_FFT / 2 ); /* Note maybe BE optimized by computing inside noise_est */
869 :
870 119571 : thr_fwd = THR_NORM_HIGH;
871 119571 : if ( cor_map_LT_sum > multi_harm_limit * 0.8f )
872 : {
873 57738 : thr_fwd = THR_HIGH;
874 : }
875 119571 : thr_rev = THR_LOW;
876 119571 : if ( cor_map_LT_sum > multi_harm_limit * 0.6f )
877 : {
878 106024 : thr_rev = THR_NORM_LOW;
879 : }
880 :
881 : /* forward attack analysis */
882 1195710 : for ( i = -2; i < 7; i++ )
883 : {
884 1076139 : if ( hTranDet->subblockEnergies.subblockNrg[nRelativeDelay + i] > hTranDet->subblockEnergies.accSubblockNrg[nRelativeDelay + i] * thr_fwd )
885 : {
886 4955 : prel_force_td |= 0x0001;
887 : }
888 : }
889 119571 : if ( prel_force_td == 0 && hTranDet->transientDetector.prev_bIsAttackPresent == 1 )
890 : {
891 : /* release analysis */
892 5364 : pSubblockNrg = hTranDet->transientDetector.pSubblockEnergies->subblockNrg;
893 5364 : set_zero( accSubblockNrgRev, NSUBBLOCKS );
894 :
895 48276 : for ( i = NSUBBLOCKS - 1; i > -1; i-- )
896 : {
897 42912 : if ( i == NSUBBLOCKS - 1 )
898 : {
899 5364 : accSubblockNrgRev[i] = pSubblockNrg[i + offset];
900 : }
901 : else
902 : {
903 37548 : accSubblockNrgRev[i] = *pTmp;
904 37548 : *pTmp *= hTranDet->transientDetector.pSubblockEnergies->facAccSubblockNrg;
905 37548 : if ( pSubblockNrg[i + offset] > *pTmp )
906 : {
907 25854 : *pTmp = pSubblockNrg[i + offset];
908 : }
909 : }
910 : }
911 :
912 : /* -3 check */
913 5364 : if ( pSubblockNrg[1 + offset] > accSubblockNrgRev[1] * thr_rev )
914 : {
915 131 : prel_force_td |= 0x0002;
916 : }
917 :
918 : /* -4 check */
919 5364 : if ( prel_force_td == 0 && pSubblockNrg[offset] > accSubblockNrgRev[0] * thr_rev )
920 : {
921 146 : if ( pSubblockNrg[offset] > accSubblockNrgRev[0] * ( thr_rev + THR_LOW_STEP ) )
922 : {
923 128 : prel_force_td |= 0x0004;
924 : }
925 18 : else if ( ( hTranDet->subblockEnergies.ramp_up_flag & 0x0002 ) != 0 )
926 : {
927 7 : prel_force_td |= 0x0008;
928 : }
929 : }
930 : }
931 :
932 119571 : return prel_force_td != 0;
933 : }
|