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 8767 : 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 8767 : InitDelayBuffer( nFrameLength, nTCXDelay, &hTranDet->delayBuffer );
92 :
93 : /* Init a subblock energies buffer used for the TCX Short/Long decision. */
94 8767 : InitSubblockEnergies( nFrameLength, nTCXDelay, &hTranDet->delayBuffer, &hTranDet->subblockEnergies );
95 :
96 : /* Init the TCX Short/Long transient detector. */
97 8767 : 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 8767 : if ( ext_mem_flag )
101 : {
102 8764 : hTranDet->transientDetector.pSubblockEnergies->nDelay += ( NSUBBLOCKS_SHIFT + 1 ) + NSUBBLOCKS + 1;
103 : }
104 : else
105 : {
106 3 : hTranDet->transientDetector.pSubblockEnergies->nDelay += NSUBBLOCKS + 1;
107 : }
108 :
109 8767 : return;
110 : }
111 :
112 :
113 : /*-------------------------------------------------------------------*
114 : * GetTCXAvgTemporalFlatnessMeasure()
115 : *
116 : *
117 : *-------------------------------------------------------------------*/
118 :
119 3021689 : float GetTCXAvgTemporalFlatnessMeasure(
120 : TRAN_DET_HANDLE hTranDet,
121 : const int16_t nCurrentSubblocks,
122 : const int16_t nPrevSubblocks )
123 : {
124 : int16_t i;
125 3021689 : TransientDetector *pTransientDetector = &hTranDet->transientDetector;
126 3021689 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
127 3021689 : const int16_t nDelay = pTransientDetector->nDelay;
128 3021689 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
129 3021689 : const float *pSubblockNrgChange = NULL;
130 : float sumTempFlatness;
131 3021689 : const int16_t nTotBlocks = nCurrentSubblocks + nPrevSubblocks;
132 :
133 : /* Initialization */
134 3021689 : assert( nTotBlocks > 0 );
135 3021689 : sumTempFlatness = 0.0f;
136 :
137 3021689 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
138 3021689 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nRelativeDelay - nPrevSubblocks];
139 31318351 : for ( i = 0; i < nTotBlocks; i++ )
140 : {
141 28296662 : sumTempFlatness += pSubblockNrgChange[i];
142 : }
143 :
144 3021689 : return sumTempFlatness / (float) nTotBlocks;
145 : }
146 :
147 :
148 : /*-------------------------------------------------------------------*
149 : * GetTCXMaxenergyChange()
150 : *
151 : *
152 : *-------------------------------------------------------------------*/
153 :
154 904335 : 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 904335 : const TransientDetector *pTransientDetector = &hTranDet->transientDetector;
162 904335 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
163 904335 : const int16_t nDelay = pTransientDetector->nDelay;
164 904335 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
165 904335 : const float *pSubblockNrgChange = NULL;
166 : float maxEnergyChange;
167 904335 : int16_t nTotBlocks = nCurrentSubblocks + nPrevSubblocks;
168 : /* Initialization */
169 904335 : assert( nTotBlocks > 0 );
170 904335 : maxEnergyChange = 0.0f;
171 :
172 904335 : assert( ( nPrevSubblocks <= nRelativeDelay ) && ( nCurrentSubblocks <= NSUBBLOCKS + nDelay ) );
173 904335 : pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nRelativeDelay - nPrevSubblocks];
174 904335 : if ( pTransientDetector->bIsAttackPresent || isTCX10 ) /* frame is TCX-10 */
175 : {
176 18528 : const float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nRelativeDelay - nPrevSubblocks];
177 18528 : float nrgMin, nrgMax = pSubblockNrg[0];
178 18528 : int16_t idxMax = 0;
179 : /* find subblock with maximum energy */
180 236148 : for ( i = 1; i < nTotBlocks; i++ )
181 : {
182 217620 : if ( nrgMax < pSubblockNrg[i] )
183 : {
184 60614 : nrgMax = pSubblockNrg[i];
185 60614 : idxMax = i;
186 : }
187 : }
188 18528 : nrgMin = nrgMax;
189 : /* find minimum energy after maximum */
190 94839 : for ( i = idxMax + 1; i < nTotBlocks; i++ )
191 : {
192 76311 : if ( nrgMin > pSubblockNrg[i] )
193 : {
194 42430 : nrgMin = pSubblockNrg[i];
195 : }
196 : }
197 : /* lower maxEnergyChange if energy doesn't decrease much after energy peak */
198 18528 : if ( nrgMin > 0.375f * nrgMax )
199 : {
200 7248 : nTotBlocks = idxMax - 3;
201 : }
202 : }
203 12280308 : for ( i = 0; i < nTotBlocks; i++ )
204 : {
205 11375973 : maxEnergyChange = max( maxEnergyChange, pSubblockNrgChange[i] );
206 : }
207 :
208 904335 : return maxEnergyChange;
209 : }
210 :
211 :
212 : /*---------------------------------------------------------------*
213 : * RunTransientDetection()
214 : *
215 : * Time Domain Transient Detector for TCX
216 : *---------------------------------------------------------------*/
217 :
218 1195723 : 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 1195723 : SubblockEnergies *pSubblockEnergies = &hTranDet->subblockEnergies;
226 1195723 : TransientDetector *pTransientDetector = &hTranDet->transientDetector;
227 : float e0, e1;
228 :
229 1195723 : assert( ( input != NULL ) && ( hTranDet != NULL ) && ( pSubblockEnergies != NULL ) && ( pTransientDetector != NULL ) );
230 :
231 : /* Variable initializations */
232 1195723 : HighPassFilter( input, length, &pSubblockEnergies->firState1, &pSubblockEnergies->firState2, filteredInput );
233 :
234 : /* Update subblock energies. */
235 1195723 : UpdateSubblockEnergies( filteredInput, length, pSubblockEnergies );
236 :
237 : /* Run transient detectors. */
238 1195723 : RunTransientDetector( pTransientDetector );
239 :
240 : /* Update the delay buffer. */
241 1195723 : UpdateDelayBuffer( filteredInput, length, &hTranDet->delayBuffer );
242 :
243 : /* compute ramp up flag */
244 1195723 : pSubblockEnergies->ramp_up_flag = ( ( pSubblockEnergies->ramp_up_flag << 1 ) & 0x0003 );
245 1195723 : e0 = dotp( filteredInput + length / 2, filteredInput + length / 2, pSubblockEnergies->pDelayBuffer->nSubblockSize / 2 ) + 0.5f * MIN_BLOCK_ENERGY;
246 1195723 : e1 = pSubblockEnergies->subblockNrg[pSubblockEnergies->nDelay + 4] - e0;
247 1195723 : if ( e1 > e0 )
248 : {
249 551687 : pSubblockEnergies->ramp_up_flag |= 0x0001;
250 : }
251 :
252 1195723 : return;
253 : }
254 :
255 :
256 942360 : static uint16_t isLongTermTransient(
257 : const float frameTFM,
258 : float *lastTFM )
259 : {
260 : float currTFM;
261 :
262 942360 : if ( frameTFM > *lastTFM )
263 : {
264 476769 : const float f = ( frameTFM - *lastTFM ) * ( frameTFM - *lastTFM );
265 :
266 476769 : currTFM = *lastTFM * ( 0.96875f - f ) + frameTFM * ( 0.03125f + f );
267 : }
268 : else
269 : {
270 465591 : currTFM = *lastTFM * 0.96875f + frameTFM * 0.03125f;
271 : }
272 :
273 942360 : *lastTFM = max( 0.015625f, currTFM );
274 :
275 942360 : return ( currTFM < 0.5625f ? 1 : 0 );
276 : }
277 :
278 :
279 : /*-------------------------------------------------------------------*
280 : * SetTCXModeInfo()
281 : *
282 : *
283 : *-------------------------------------------------------------------*/
284 :
285 950452 : 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 950452 : TCX_ENC_HANDLE hTcxEnc = st->hTcxEnc;
292 :
293 950452 : if ( st->codec_mode == MODE2 || ( st->element_mode > EVS_MONO && st->core != HQ_CORE ) )
294 : {
295 942360 : assert( hTranDet != NULL );
296 :
297 : /* determine window sequence (1 long or 2 short windows) */
298 942360 : if ( st->tcx10Enabled && st->tcx20Enabled )
299 : {
300 : /* window switching based on transient detector output */
301 805501 : if ( ( ( hTranDet->transientDetector.bIsAttackPresent ) || ( st->currEnergyHF > st->prevEnergyHF * 39.0f && st->element_mode != IVAS_CPE_MDCT ) ) &&
302 16128 : ( ( st->last_core != ACELP_CORE ) && ( st->last_core != AMR_WB_CORE ) ) )
303 : {
304 16061 : hTcxEnc->tcxMode = TCX_10;
305 : }
306 : else
307 : {
308 789440 : hTcxEnc->tcxMode = TCX_20;
309 : }
310 : }
311 : else
312 : {
313 : /* window selection (non-adaptive) based on flags only */
314 136859 : if ( st->tcx10Enabled )
315 : {
316 0 : hTcxEnc->tcxMode = TCX_10;
317 : }
318 136859 : else if ( st->tcx20Enabled )
319 : {
320 136859 : hTcxEnc->tcxMode = TCX_20;
321 : }
322 : else
323 : {
324 0 : hTcxEnc->tcxMode = NO_TCX;
325 : }
326 : }
327 : /* set the left window overlap */
328 942360 : if ( st->last_core == ACELP_CORE || st->last_core == AMR_WB_CORE )
329 : {
330 9953 : st->hTcxCfg->tcx_last_overlap_mode = TRANSITION_OVERLAP;
331 : }
332 932407 : else if ( ( hTcxEnc->tcxMode == TCX_10 ) && ( st->hTcxCfg->tcx_curr_overlap_mode == ALDO_WINDOW ) )
333 : {
334 12400 : st->hTcxCfg->tcx_last_overlap_mode = FULL_OVERLAP;
335 : }
336 : else
337 : {
338 920007 : st->hTcxCfg->tcx_last_overlap_mode = st->hTcxCfg->tcx_curr_overlap_mode;
339 : }
340 :
341 : /* determine the right window overlap */
342 942360 : if ( hTcxEnc->tcxMode == TCX_10 )
343 : {
344 16061 : if ( hTranDet->transientDetector.attackIndex < 0 )
345 : {
346 0 : *tcxModeOverlap = HALF_OVERLAP;
347 : }
348 : else
349 : {
350 16061 : *tcxModeOverlap = hTranDet->transientDetector.attackIndex % 4;
351 16061 : if ( *tcxModeOverlap == 1 )
352 : {
353 2570 : *tcxModeOverlap = FULL_OVERLAP;
354 : }
355 : }
356 16061 : if ( isLongTermTransient( 1.0f / GetTCXAvgTemporalFlatnessMeasure( hTranDet, NSUBBLOCKS, 0 ), &hTcxEnc->tfm_mem ) && st->element_mode == IVAS_CPE_MDCT )
357 : {
358 127 : if ( ( *tcxModeOverlap != MIN_OVERLAP ) && ( hTcxEnc->tcxltp_norm_corr_past < 0.5625f ) )
359 : {
360 49 : *tcxModeOverlap = HALF_OVERLAP;
361 : }
362 : }
363 : }
364 926299 : else if ( hTcxEnc->tcxMode == TCX_20 )
365 : {
366 926299 : if ( hTranDet->transientDetector.attackIndex == 7 )
367 : {
368 3244 : *tcxModeOverlap = HALF_OVERLAP;
369 : }
370 923055 : else if ( hTranDet->transientDetector.attackIndex == 6 )
371 : {
372 2491 : *tcxModeOverlap = MIN_OVERLAP;
373 : }
374 : else
375 : {
376 920564 : *tcxModeOverlap = ALDO_WINDOW;
377 : }
378 926299 : if ( isLongTermTransient( 1.0f / GetTCXAvgTemporalFlatnessMeasure( hTranDet, NSUBBLOCKS, 0 ), &hTcxEnc->tfm_mem ) && st->element_mode == IVAS_CPE_MDCT )
379 : {
380 7197 : if ( ( *tcxModeOverlap != MIN_OVERLAP ) && ( hTcxEnc->tcxltp_norm_corr_past < 0.5625f ) )
381 : {
382 3813 : *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 942360 : if ( ( st->hTcxCfg->tcx_last_overlap_mode == TRANSITION_OVERLAP ) && ( *tcxModeOverlap == ALDO_WINDOW ) )
398 : {
399 9848 : *tcxModeOverlap = FULL_OVERLAP;
400 : }
401 : }
402 :
403 950452 : 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 1195723 : 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 1195723 : assert( nSubblocks >= NSUBBLOCKS );
429 1195723 : assert( nPastSubblocks >= 2 );
430 :
431 1195723 : bIsAttackPresent = FALSE;
432 1195723 : attackIndex = -1;
433 : /* Search for the last attack in the subblocks */
434 1195723 : if ( ( pSubblockNrg[-1] > pAccSubblockNrg[-1] * attackRatioThreshold ) || ( pSubblockNrg[-2] > pAccSubblockNrg[-2] * attackRatioThreshold ) )
435 : {
436 4724 : bIsAttackPresent = TRUE;
437 4724 : attackIndex = 0;
438 : }
439 10761507 : for ( i = 0; i < NSUBBLOCKS; i++ )
440 : {
441 9565784 : if ( pSubblockNrg[i] > pAccSubblockNrg[i] * attackRatioThreshold )
442 : {
443 27655 : if ( i < 6 )
444 : {
445 22859 : bIsAttackPresent = TRUE;
446 : }
447 27655 : if ( ( attackIndex != 2 ) && ( attackIndex != 6 ) )
448 : {
449 27272 : attackIndex = i;
450 27272 : if ( ( pSubblockNrg[i] < pAccSubblockNrg[i] * 1.125f * attackRatioThreshold ) && ( i == 2 || i == 6 ) )
451 : {
452 440 : 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 9538129 : if ( ( pSubblockNrg[i] > pSubblockNrg[i - 1] * 1.5f * attackRatioThreshold ) &&
459 15354 : ( pSubblockNrg[i] > pSubblockNrg[i - 2] * 1.5f * attackRatioThreshold ) )
460 : {
461 6680 : if ( ( attackIndex != 2 ) && ( attackIndex != 6 ) )
462 : {
463 6669 : attackIndex = i;
464 :
465 6669 : if ( ( ( pSubblockNrg[i] < pSubblockNrg[i - 1] * 2.0f * attackRatioThreshold ) ||
466 6669 : ( pSubblockNrg[i] < pSubblockNrg[i - 2] * 2.0f * attackRatioThreshold ) ) &&
467 2011 : ( i == 2 || i == 6 ) )
468 : {
469 796 : 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 1195723 : if ( attackIndex == 4 )
478 : {
479 2659 : attackIndex = 7;
480 : }
481 1193064 : else if ( attackIndex == 5 )
482 : {
483 2938 : attackIndex = 6;
484 : }
485 1195723 : *pAttackIndex = attackIndex;
486 1195723 : *pbIsAttackPresent = bIsAttackPresent;
487 :
488 1195723 : return;
489 : }
490 :
491 :
492 8767 : static void InitDelayBuffer(
493 : const int16_t nFrameLength,
494 : const int16_t nDelay,
495 : DelayBuffer *pDelayBuffer )
496 : {
497 8767 : const int16_t nMaxBuffSize = L_FRAME_MAX / NSUBBLOCKS;
498 :
499 8767 : assert( ( nFrameLength > NSUBBLOCKS ) && ( nFrameLength % NSUBBLOCKS == 0 ) && ( nDelay >= 0 ) && ( pDelayBuffer != NULL ) );
500 8767 : pDelayBuffer->nSubblockSize = nFrameLength / NSUBBLOCKS;
501 8767 : assert( pDelayBuffer->nSubblockSize <= nMaxBuffSize );
502 8767 : set_f( pDelayBuffer->buffer, 0.0f, nMaxBuffSize );
503 8767 : pDelayBuffer->nDelay = nDelay % pDelayBuffer->nSubblockSize;
504 8767 : assert( pDelayBuffer->nDelay <= nMaxBuffSize );
505 :
506 8767 : return;
507 : }
508 :
509 :
510 8767 : static void InitSubblockEnergies(
511 : const int16_t nFrameLength,
512 : const int16_t nDelay,
513 : DelayBuffer *pDelayBuffer,
514 : SubblockEnergies *pSubblockEnergies )
515 : {
516 8767 : const int16_t nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
517 :
518 8767 : assert( ( pDelayBuffer != NULL ) && ( pSubblockEnergies != NULL ) && ( pDelayBuffer->nSubblockSize * NSUBBLOCKS == nFrameLength ) && ( pDelayBuffer->nSubblockSize > 0 ) );
519 :
520 8767 : set_f( pSubblockEnergies->subblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize );
521 8767 : set_f( pSubblockEnergies->accSubblockNrg, MIN_BLOCK_ENERGY, nMaxBuffSize + 1 );
522 8767 : set_f( pSubblockEnergies->subblockNrgChange, 1.0f, nMaxBuffSize );
523 8767 : pSubblockEnergies->nDelay = nDelay / pDelayBuffer->nSubblockSize;
524 8767 : assert( pSubblockEnergies->nDelay < nMaxBuffSize );
525 8767 : pSubblockEnergies->nPartialDelay = nDelay % pDelayBuffer->nSubblockSize;
526 8767 : pSubblockEnergies->facAccSubblockNrg = 0.8125f; /* Energy accumulation factor */
527 8767 : pSubblockEnergies->firState1 = 0.0f;
528 8767 : pSubblockEnergies->firState2 = 0.0f;
529 :
530 8767 : pSubblockEnergies->pDelayBuffer = pDelayBuffer;
531 8767 : pDelayBuffer->nDelay = max( pDelayBuffer->nDelay, pSubblockEnergies->nPartialDelay );
532 :
533 8767 : 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 8767 : 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 8767 : const int16_t nMaxBuffSize = NSUBBLOCKS + MAX_TD_DELAY;
556 :
557 8767 : assert( ( pSubblockEnergies != NULL ) && ( pSubblockEnergies->pDelayBuffer != NULL ) && ( pTransientDetector != NULL ) && ( pSubblockEnergies->pDelayBuffer->nSubblockSize != 0 ) );
558 8767 : pTransientDetector->pSubblockEnergies = pSubblockEnergies;
559 8767 : pTransientDetector->nDelay = ( nDelay - pSubblockEnergies->nPartialDelay ) / pSubblockEnergies->pDelayBuffer->nSubblockSize;
560 8767 : assert( nDelay == pTransientDetector->nDelay * pSubblockEnergies->pDelayBuffer->nSubblockSize + pSubblockEnergies->nPartialDelay );
561 8767 : assert( pTransientDetector->nDelay < nMaxBuffSize );
562 8767 : pSubblockEnergies->nDelay = max( pSubblockEnergies->nDelay, pTransientDetector->nDelay );
563 8767 : assert( nSubblocksToCheck <= NSUBBLOCKS + pTransientDetector->nDelay );
564 8767 : pTransientDetector->nSubblocksToCheck = nSubblocksToCheck;
565 8767 : pTransientDetector->CheckSubblocksForAttack = pCheckSubblocksForAttack;
566 8767 : pTransientDetector->attackRatioThreshold = attackRatioThreshold;
567 8767 : pTransientDetector->bIsAttackPresent = FALSE;
568 8767 : pTransientDetector->prev_bIsAttackPresent = FALSE;
569 8767 : pTransientDetector->attackIndex = -1;
570 8767 : pTransientDetector->pSubblockEnergies->ramp_up_flag = 0x0;
571 :
572 8767 : 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 1015807680 : static float InlineFilter(
578 : float inValue,
579 : float firState1,
580 : float firState2 )
581 : {
582 1015807680 : return 0.375f * inValue - 0.5f * firState1 + 0.125f * firState2;
583 : }
584 :
585 :
586 1195723 : 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 1195723 : output[0] = InlineFilter( input[0], *pFirState1, *pFirState2 );
596 1195723 : output[1] = InlineFilter( input[1], input[0], *pFirState1 );
597 1014611957 : for ( i = 2; i < length; i++ )
598 : {
599 1013416234 : output[i] = InlineFilter( input[i], input[i - 1], input[i - 2] );
600 : }
601 :
602 : /* update filter states: shift time samples through delay line */
603 1195723 : *pFirState2 = input[length - 2];
604 1195723 : *pFirState1 = input[length - 1];
605 :
606 1195723 : return;
607 : }
608 :
609 :
610 1195723 : static void RunTransientDetector(
611 : TransientDetector *pTransientDetector )
612 : {
613 1195723 : const float attackRatioThreshold = pTransientDetector->attackRatioThreshold;
614 1195723 : const SubblockEnergies *pSubblockEnergies = pTransientDetector->pSubblockEnergies;
615 1195723 : const int16_t nDelay = pTransientDetector->nDelay;
616 1195723 : const int16_t nRelativeDelay = pSubblockEnergies->nDelay - nDelay;
617 1195723 : const float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nRelativeDelay];
618 1195723 : const float *pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nRelativeDelay];
619 :
620 1195723 : assert( ( pTransientDetector->CheckSubblocksForAttack != NULL ) );
621 :
622 : /* Variable initialization */
623 : #define WMC_TOOL_SKIP
624 1195723 : pTransientDetector->CheckSubblocksForAttack( pSubblockNrg, pAccSubblockNrg, NSUBBLOCKS + nDelay, nRelativeDelay, attackRatioThreshold, &pTransientDetector->bIsAttackPresent, &pTransientDetector->attackIndex );
625 : #undef WMC_TOOL_SKIP
626 :
627 1195723 : return;
628 : }
629 :
630 :
631 1195723 : static void UpdateDelayBuffer(
632 : const float *input,
633 : const int16_t nSamplesAvailable,
634 : DelayBuffer *pDelayBuffer )
635 : {
636 : int16_t i;
637 1195723 : int16_t nDelay = pDelayBuffer->nDelay;
638 :
639 1195723 : assert( ( nDelay >= 0 ) && ( nDelay <= (int16_t) sizeof( pDelayBuffer->buffer ) / (int16_t) sizeof( pDelayBuffer->buffer[0] ) ) );
640 1195723 : assert( nSamplesAvailable <= NSUBBLOCKS * pDelayBuffer->nSubblockSize );
641 :
642 : /* If this is not the last frame */
643 1195723 : if ( nSamplesAvailable == NSUBBLOCKS * pDelayBuffer->nSubblockSize )
644 : {
645 : /* Store the newest samples into the delay buffer */
646 1319473 : for ( i = 0; i < nDelay; i++ )
647 : {
648 123750 : pDelayBuffer->buffer[i] = input[i + nSamplesAvailable - nDelay];
649 : }
650 : }
651 :
652 1195723 : return;
653 : }
654 :
655 :
656 1195723 : static void UpdateSubblockEnergies(
657 : const float *input,
658 : const int16_t nSamplesAvailable,
659 : SubblockEnergies *pSubblockEnergies )
660 : {
661 : int16_t i;
662 :
663 1195723 : assert( ( pSubblockEnergies->nDelay >= 0 ) && ( pSubblockEnergies->nDelay + NSUBBLOCKS <= (int16_t) sizeof( pSubblockEnergies->subblockNrg ) / (int16_t) sizeof( pSubblockEnergies->subblockNrg[0] ) ) );
664 1195723 : assert( pSubblockEnergies->nPartialDelay <= pSubblockEnergies->pDelayBuffer->nDelay );
665 : /* At least one block delay is required when subblock energy change is required */
666 1195723 : assert( pSubblockEnergies->nDelay >= 1 );
667 :
668 : /* Shift old subblock energies */
669 16727722 : for ( i = 0; i < pSubblockEnergies->nDelay; i++ )
670 : {
671 15531999 : pSubblockEnergies->subblockNrg[i] = pSubblockEnergies->subblockNrg[i + NSUBBLOCKS];
672 15531999 : pSubblockEnergies->accSubblockNrg[i] = pSubblockEnergies->accSubblockNrg[i + NSUBBLOCKS];
673 15531999 : pSubblockEnergies->subblockNrgChange[i] = pSubblockEnergies->subblockNrgChange[i + NSUBBLOCKS];
674 : }
675 :
676 : /* Compute filtered subblock energies for the new samples */
677 1195723 : CalculateSubblockEnergies( input, nSamplesAvailable, pSubblockEnergies );
678 :
679 1195723 : 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 9565784 : static void UpdatedAndStoreAccWindowNrg(
685 : float newWindowNrgF,
686 : float *pAccSubblockNrg,
687 : float facAccSubblockNrg,
688 : float *pOutAccWindowNrgF )
689 : {
690 : /* Store the accumulated energy */
691 9565784 : *pOutAccWindowNrgF = *pAccSubblockNrg;
692 :
693 : /* Update the accumulated energy: maximum of the current and the accumulated energy */
694 9565784 : *pAccSubblockNrg *= facAccSubblockNrg;
695 9565784 : if ( newWindowNrgF > *pAccSubblockNrg )
696 : {
697 5882642 : *pAccSubblockNrg = newWindowNrgF;
698 : }
699 :
700 9565784 : return;
701 : }
702 :
703 :
704 1195723 : static void CalculateSubblockEnergies(
705 : const float *input,
706 : const int16_t nSamplesAvailable,
707 : SubblockEnergies *pSubblockEnergies )
708 : {
709 1195723 : DelayBuffer *pDelayBuffer = pSubblockEnergies->pDelayBuffer; /* */
710 1195723 : const int16_t nSubblockSize = pDelayBuffer->nSubblockSize; /* */
711 1195723 : const int16_t nDelay = pSubblockEnergies->nDelay; /* */
712 1195723 : const int16_t nPartialDelay = pSubblockEnergies->nPartialDelay; /* */
713 1195723 : const float *delayBuffer = &pDelayBuffer->buffer[pDelayBuffer->nDelay - nPartialDelay]; /* */
714 1195723 : const float facAccSubblockNrg = pSubblockEnergies->facAccSubblockNrg; /* */
715 1195723 : float *pSubblockNrg = &pSubblockEnergies->subblockNrg[nDelay]; /* */
716 1195723 : float *pAccSubblockNrg = &pSubblockEnergies->accSubblockNrg[nDelay]; /* */
717 1195723 : float *pSubblockNrgChange = &pSubblockEnergies->subblockNrgChange[nDelay]; /* */
718 : float *pAccSubblockTmp;
719 : int16_t nWindows;
720 : int16_t i, w, k;
721 : /* Variable initializations */
722 1195723 : nWindows = ( nSamplesAvailable + nPartialDelay ) / nSubblockSize;
723 1195723 : pAccSubblockTmp = &pAccSubblockNrg[nWindows];
724 :
725 1195723 : set_f( pSubblockNrg, MIN_BLOCK_ENERGY, NSUBBLOCKS );
726 :
727 1195723 : if ( nWindows > 0 )
728 : {
729 : /* Process left over samples from the previous frame. */
730 1319473 : for ( k = 0; k < nPartialDelay; k++ )
731 : {
732 123750 : pSubblockNrg[0] += delayBuffer[k] * delayBuffer[k];
733 : }
734 1195723 : k = 0;
735 :
736 : /* Process new samples in the 0. subblock. */
737 128047933 : for ( i = nPartialDelay; i < nSubblockSize; i++, k++ )
738 : {
739 126852210 : pSubblockNrg[0] += input[k] * input[k];
740 : }
741 :
742 : /* Set accumulated subblock energy at this point. */
743 1195723 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[0], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[0] );
744 :
745 9565784 : for ( w = 1; w < nWindows; w++ )
746 : {
747 : /* Process new samples in the w. subblock. */
748 897201781 : for ( i = 0; i < nSubblockSize; i++, k++ )
749 : {
750 888831720 : pSubblockNrg[w] += input[k] * input[k];
751 : }
752 : /* Set accumulated subblock energy at this point. */
753 8370061 : UpdatedAndStoreAccWindowNrg( pSubblockNrg[w], pAccSubblockTmp, facAccSubblockNrg, &pAccSubblockNrg[w] );
754 : }
755 :
756 : /* Calculate energy change for each block. */
757 10761507 : for ( w = 0; w < nWindows; w++ )
758 : {
759 9565784 : if ( pSubblockNrg[w] > pSubblockNrg[w - 1] )
760 : {
761 4372063 : pSubblockNrgChange[w] = pSubblockNrg[w] / pSubblockNrg[w - 1];
762 : }
763 : else
764 : {
765 5193721 : pSubblockNrgChange[w] = pSubblockNrg[w - 1] / pSubblockNrg[w];
766 : }
767 : }
768 : }
769 :
770 1195723 : return;
771 : }
772 :
773 :
774 : /*-------------------------------------------------------------------*
775 : * set_transient_stereo()
776 : *
777 : *
778 : *-------------------------------------------------------------------*/
779 :
780 63470 : 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 63470 : sts = hCPE->hCoreCoder;
790 :
791 : /* for DFT/TD based stereo ,map avg. flatness to individual stereo channels (M/S or X/Y) */
792 63470 : maximum( currFlatness, CPE_CHANNELS, &currFlatnessMax );
793 63470 : attackIsPresent = 0;
794 :
795 190410 : for ( n = 0; n < CPE_CHANNELS; n++ )
796 : {
797 126940 : attackIsPresent = max( attackIsPresent, sts[n]->hTranDet->transientDetector.bIsAttackPresent );
798 : }
799 :
800 63470 : set_f( currFlatness, currFlatnessMax, CPE_CHANNELS );
801 :
802 190410 : for ( n = 0; n < CPE_CHANNELS; n++ )
803 : {
804 126940 : sts[n]->hTranDet->transientDetector.bIsAttackPresent = attackIsPresent;
805 : }
806 :
807 63470 : if ( hCPE->hStereoDft != NULL )
808 : {
809 59679 : if ( hCPE->hStereoDft->attackPresent )
810 : {
811 1837 : hCPE->hStereoDft->wasTransient = 1;
812 : }
813 57842 : else if ( hCPE->hStereoDft->wasTransient )
814 : {
815 1600 : hCPE->hStereoDft->wasTransient = 0;
816 : }
817 :
818 59679 : hCPE->hStereoDft->attackPresent = attackIsPresent;
819 :
820 59679 : hCPE->hStereoDft->hItd->currFlatness = 0;
821 179037 : for ( n = 0; n < CPE_CHANNELS; n++ )
822 : {
823 119358 : hCPE->hStereoDft->hItd->currFlatness = max( hCPE->hStereoDft->hItd->currFlatness, currFlatness[n] );
824 : }
825 : }
826 :
827 63470 : 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 63470 : return;
837 : }
838 :
839 : /*-------------------------------------------------------------------*
840 : * transient_analysis()
841 : *
842 : *
843 : *-------------------------------------------------------------------*/
844 :
845 : /*! r: preliminary flag to force ACELP */
846 10886 : 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 10886 : const int16_t nRelativeDelay = hTranDet->subblockEnergies.nDelay - hTranDet->transientDetector.nDelay;
860 : int16_t prel_force_td;
861 : float cor_map_LT_sum;
862 :
863 10886 : pTmp = &accSubblockNrgRev[NSUBBLOCKS - 1];
864 10886 : offset = nRelativeDelay - 4;
865 10886 : prel_force_td = FALSE;
866 :
867 : /* summation of the LT correlation map */
868 10886 : cor_map_LT_sum = sum_f( cor_map_LT, L_FFT / 2 ); /* Note maybe BE optimized by computing inside noise_est */
869 :
870 10886 : thr_fwd = THR_NORM_HIGH;
871 10886 : if ( cor_map_LT_sum > multi_harm_limit * 0.8f )
872 : {
873 3310 : thr_fwd = THR_HIGH;
874 : }
875 10886 : thr_rev = THR_LOW;
876 10886 : if ( cor_map_LT_sum > multi_harm_limit * 0.6f )
877 : {
878 9810 : thr_rev = THR_NORM_LOW;
879 : }
880 :
881 : /* forward attack analysis */
882 108860 : for ( i = -2; i < 7; i++ )
883 : {
884 97974 : if ( hTranDet->subblockEnergies.subblockNrg[nRelativeDelay + i] > hTranDet->subblockEnergies.accSubblockNrg[nRelativeDelay + i] * thr_fwd )
885 : {
886 430 : prel_force_td |= 0x0001;
887 : }
888 : }
889 10886 : if ( prel_force_td == 0 && hTranDet->transientDetector.prev_bIsAttackPresent == 1 )
890 : {
891 : /* release analysis */
892 350 : pSubblockNrg = hTranDet->transientDetector.pSubblockEnergies->subblockNrg;
893 350 : set_zero( accSubblockNrgRev, NSUBBLOCKS );
894 :
895 3150 : for ( i = NSUBBLOCKS - 1; i > -1; i-- )
896 : {
897 2800 : if ( i == NSUBBLOCKS - 1 )
898 : {
899 350 : accSubblockNrgRev[i] = pSubblockNrg[i + offset];
900 : }
901 : else
902 : {
903 2450 : accSubblockNrgRev[i] = *pTmp;
904 2450 : *pTmp *= hTranDet->transientDetector.pSubblockEnergies->facAccSubblockNrg;
905 2450 : if ( pSubblockNrg[i + offset] > *pTmp )
906 : {
907 1584 : *pTmp = pSubblockNrg[i + offset];
908 : }
909 : }
910 : }
911 :
912 : /* -3 check */
913 350 : if ( pSubblockNrg[1 + offset] > accSubblockNrgRev[1] * thr_rev )
914 : {
915 2 : prel_force_td |= 0x0002;
916 : }
917 :
918 : /* -4 check */
919 350 : if ( prel_force_td == 0 && pSubblockNrg[offset] > accSubblockNrgRev[0] * thr_rev )
920 : {
921 7 : if ( pSubblockNrg[offset] > accSubblockNrgRev[0] * ( thr_rev + THR_LOW_STEP ) )
922 : {
923 5 : prel_force_td |= 0x0004;
924 : }
925 2 : else if ( ( hTranDet->subblockEnergies.ramp_up_flag & 0x0002 ) != 0 )
926 : {
927 2 : prel_force_td |= 0x0008;
928 : }
929 : }
930 : }
931 :
932 10886 : return prel_force_td != 0;
933 : }
|