580b307fd7
Sync opus codec to upstream commit 02fed471a4568852d6618e041c4f2af0d7730ee2 (August 30 2013) This brings in a lot of optimizations but also makes the diff between our codec and the upstream much smaller as most of our optimizations have been upstreamed or supeceded. Speedups across the board for CELT mode files: 64kbps 128kbps H300 9.82MHz 15.48MHz c200 4.86MHz 9.63MHz fuze v1 10.32MHz 15.92MHz For the silk mode test file (16kbps) arm targets get a speedup of about 2MHz while the H300 is 7.8MHz slower, likely because it's now using the pseudostack more rather than the real stack which is in iram. Patches to get around that are upcomming. Change-Id: Ifecf963e461c51ac42e09dac1e91bc4bc3b12fa3
423 lines
19 KiB
C
423 lines
19 KiB
C
/***********************************************************************
|
|
Copyright (c) 2006-2011, Skype Limited. All rights reserved.
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
- Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
|
names of specific contributors, may be used to endorse or promote
|
|
products derived from this software without specific prior written
|
|
permission.
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
***********************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "main.h"
|
|
#include "stack_alloc.h"
|
|
#include "PLC.h"
|
|
|
|
#define NB_ATT 2
|
|
static const opus_int16 HARM_ATT_Q15[NB_ATT] = { 32440, 31130 }; /* 0.99, 0.95 */
|
|
static const opus_int16 PLC_RAND_ATTENUATE_V_Q15[NB_ATT] = { 31130, 26214 }; /* 0.95, 0.8 */
|
|
static const opus_int16 PLC_RAND_ATTENUATE_UV_Q15[NB_ATT] = { 32440, 29491 }; /* 0.99, 0.9 */
|
|
|
|
static inline void silk_PLC_update(
|
|
silk_decoder_state *psDec, /* I/O Decoder state */
|
|
silk_decoder_control *psDecCtrl /* I/O Decoder control */
|
|
);
|
|
|
|
static inline void silk_PLC_conceal(
|
|
silk_decoder_state *psDec, /* I/O Decoder state */
|
|
silk_decoder_control *psDecCtrl, /* I/O Decoder control */
|
|
opus_int16 frame[] /* O LPC residual signal */
|
|
);
|
|
|
|
|
|
void silk_PLC_Reset(
|
|
silk_decoder_state *psDec /* I/O Decoder state */
|
|
)
|
|
{
|
|
psDec->sPLC.pitchL_Q8 = silk_LSHIFT( psDec->frame_length, 8 - 1 );
|
|
psDec->sPLC.prevGain_Q16[ 0 ] = SILK_FIX_CONST( 1, 16 );
|
|
psDec->sPLC.prevGain_Q16[ 1 ] = SILK_FIX_CONST( 1, 16 );
|
|
psDec->sPLC.subfr_length = 20;
|
|
psDec->sPLC.nb_subfr = 2;
|
|
}
|
|
|
|
void silk_PLC(
|
|
silk_decoder_state *psDec, /* I/O Decoder state */
|
|
silk_decoder_control *psDecCtrl, /* I/O Decoder control */
|
|
opus_int16 frame[], /* I/O signal */
|
|
opus_int lost /* I Loss flag */
|
|
)
|
|
{
|
|
/* PLC control function */
|
|
if( psDec->fs_kHz != psDec->sPLC.fs_kHz ) {
|
|
silk_PLC_Reset( psDec );
|
|
psDec->sPLC.fs_kHz = psDec->fs_kHz;
|
|
}
|
|
|
|
if( lost ) {
|
|
/****************************/
|
|
/* Generate Signal */
|
|
/****************************/
|
|
silk_PLC_conceal( psDec, psDecCtrl, frame );
|
|
|
|
psDec->lossCnt++;
|
|
} else {
|
|
/****************************/
|
|
/* Update state */
|
|
/****************************/
|
|
silk_PLC_update( psDec, psDecCtrl );
|
|
}
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Update state of PLC */
|
|
/**************************************************/
|
|
static inline void silk_PLC_update(
|
|
silk_decoder_state *psDec, /* I/O Decoder state */
|
|
silk_decoder_control *psDecCtrl /* I/O Decoder control */
|
|
)
|
|
{
|
|
opus_int32 LTP_Gain_Q14, temp_LTP_Gain_Q14;
|
|
opus_int i, j;
|
|
silk_PLC_struct *psPLC;
|
|
|
|
psPLC = &psDec->sPLC;
|
|
|
|
/* Update parameters used in case of packet loss */
|
|
psDec->prevSignalType = psDec->indices.signalType;
|
|
LTP_Gain_Q14 = 0;
|
|
if( psDec->indices.signalType == TYPE_VOICED ) {
|
|
/* Find the parameters for the last subframe which contains a pitch pulse */
|
|
for( j = 0; j * psDec->subfr_length < psDecCtrl->pitchL[ psDec->nb_subfr - 1 ]; j++ ) {
|
|
if( j == psDec->nb_subfr ) {
|
|
break;
|
|
}
|
|
temp_LTP_Gain_Q14 = 0;
|
|
for( i = 0; i < LTP_ORDER; i++ ) {
|
|
temp_LTP_Gain_Q14 += psDecCtrl->LTPCoef_Q14[ ( psDec->nb_subfr - 1 - j ) * LTP_ORDER + i ];
|
|
}
|
|
if( temp_LTP_Gain_Q14 > LTP_Gain_Q14 ) {
|
|
LTP_Gain_Q14 = temp_LTP_Gain_Q14;
|
|
silk_memcpy( psPLC->LTPCoef_Q14,
|
|
&psDecCtrl->LTPCoef_Q14[ silk_SMULBB( psDec->nb_subfr - 1 - j, LTP_ORDER ) ],
|
|
LTP_ORDER * sizeof( opus_int16 ) );
|
|
|
|
psPLC->pitchL_Q8 = silk_LSHIFT( psDecCtrl->pitchL[ psDec->nb_subfr - 1 - j ], 8 );
|
|
}
|
|
}
|
|
|
|
silk_memset( psPLC->LTPCoef_Q14, 0, LTP_ORDER * sizeof( opus_int16 ) );
|
|
psPLC->LTPCoef_Q14[ LTP_ORDER / 2 ] = LTP_Gain_Q14;
|
|
|
|
/* Limit LT coefs */
|
|
if( LTP_Gain_Q14 < V_PITCH_GAIN_START_MIN_Q14 ) {
|
|
opus_int scale_Q10;
|
|
opus_int32 tmp;
|
|
|
|
tmp = silk_LSHIFT( V_PITCH_GAIN_START_MIN_Q14, 10 );
|
|
scale_Q10 = silk_DIV32( tmp, silk_max( LTP_Gain_Q14, 1 ) );
|
|
for( i = 0; i < LTP_ORDER; i++ ) {
|
|
psPLC->LTPCoef_Q14[ i ] = silk_RSHIFT( silk_SMULBB( psPLC->LTPCoef_Q14[ i ], scale_Q10 ), 10 );
|
|
}
|
|
} else if( LTP_Gain_Q14 > V_PITCH_GAIN_START_MAX_Q14 ) {
|
|
opus_int scale_Q14;
|
|
opus_int32 tmp;
|
|
|
|
tmp = silk_LSHIFT( V_PITCH_GAIN_START_MAX_Q14, 14 );
|
|
scale_Q14 = silk_DIV32( tmp, silk_max( LTP_Gain_Q14, 1 ) );
|
|
for( i = 0; i < LTP_ORDER; i++ ) {
|
|
psPLC->LTPCoef_Q14[ i ] = silk_RSHIFT( silk_SMULBB( psPLC->LTPCoef_Q14[ i ], scale_Q14 ), 14 );
|
|
}
|
|
}
|
|
} else {
|
|
psPLC->pitchL_Q8 = silk_LSHIFT( silk_SMULBB( psDec->fs_kHz, 18 ), 8 );
|
|
silk_memset( psPLC->LTPCoef_Q14, 0, LTP_ORDER * sizeof( opus_int16 ));
|
|
}
|
|
|
|
/* Save LPC coeficients */
|
|
silk_memcpy( psPLC->prevLPC_Q12, psDecCtrl->PredCoef_Q12[ 1 ], psDec->LPC_order * sizeof( opus_int16 ) );
|
|
psPLC->prevLTP_scale_Q14 = psDecCtrl->LTP_scale_Q14;
|
|
|
|
/* Save last two gains */
|
|
silk_memcpy( psPLC->prevGain_Q16, &psDecCtrl->Gains_Q16[ psDec->nb_subfr - 2 ], 2 * sizeof( opus_int32 ) );
|
|
|
|
psPLC->subfr_length = psDec->subfr_length;
|
|
psPLC->nb_subfr = psDec->nb_subfr;
|
|
}
|
|
|
|
static inline void silk_PLC_conceal(
|
|
silk_decoder_state *psDec, /* I/O Decoder state */
|
|
silk_decoder_control *psDecCtrl, /* I/O Decoder control */
|
|
opus_int16 frame[] /* O LPC residual signal */
|
|
)
|
|
{
|
|
opus_int i, j, k;
|
|
opus_int lag, idx, sLTP_buf_idx, shift1, shift2;
|
|
opus_int32 rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30;
|
|
opus_int32 energy1, energy2, *rand_ptr, *pred_lag_ptr;
|
|
opus_int32 LPC_pred_Q10, LTP_pred_Q12;
|
|
opus_int16 rand_scale_Q14;
|
|
opus_int16 *B_Q14, *exc_buf_ptr;
|
|
opus_int32 *sLPC_Q14_ptr;
|
|
VARDECL( opus_int16, exc_buf );
|
|
opus_int16 A_Q12[ MAX_LPC_ORDER ];
|
|
VARDECL( opus_int16, sLTP );
|
|
VARDECL( opus_int32, sLTP_Q14 );
|
|
silk_PLC_struct *psPLC = &psDec->sPLC;
|
|
opus_int32 prevGain_Q10[2];
|
|
SAVE_STACK;
|
|
|
|
ALLOC( exc_buf, 2*psPLC->subfr_length, opus_int16 );
|
|
ALLOC( sLTP, psDec->ltp_mem_length, opus_int16 );
|
|
ALLOC( sLTP_Q14, psDec->ltp_mem_length + psDec->frame_length, opus_int32 );
|
|
|
|
prevGain_Q10[0] = silk_RSHIFT( psPLC->prevGain_Q16[ 0 ], 6);
|
|
prevGain_Q10[1] = silk_RSHIFT( psPLC->prevGain_Q16[ 1 ], 6);
|
|
|
|
if( psDec->first_frame_after_reset ) {
|
|
silk_memset( psPLC->prevLPC_Q12, 0, sizeof( psPLC->prevLPC_Q12 ) );
|
|
}
|
|
|
|
/* Find random noise component */
|
|
/* Scale previous excitation signal */
|
|
exc_buf_ptr = exc_buf;
|
|
for( k = 0; k < 2; k++ ) {
|
|
for( i = 0; i < psPLC->subfr_length; i++ ) {
|
|
exc_buf_ptr[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT(
|
|
silk_SMULWW( psDec->exc_Q14[ i + ( k + psPLC->nb_subfr - 2 ) * psPLC->subfr_length ], prevGain_Q10[ k ] ), 8 ) );
|
|
}
|
|
exc_buf_ptr += psPLC->subfr_length;
|
|
}
|
|
/* Find the subframe with lowest energy of the last two and use that as random noise generator */
|
|
silk_sum_sqr_shift( &energy1, &shift1, exc_buf, psPLC->subfr_length );
|
|
silk_sum_sqr_shift( &energy2, &shift2, &exc_buf[ psPLC->subfr_length ], psPLC->subfr_length );
|
|
|
|
if( silk_RSHIFT( energy1, shift2 ) < silk_RSHIFT( energy2, shift1 ) ) {
|
|
/* First sub-frame has lowest energy */
|
|
rand_ptr = &psDec->exc_Q14[ silk_max_int( 0, ( psPLC->nb_subfr - 1 ) * psPLC->subfr_length - RAND_BUF_SIZE ) ];
|
|
} else {
|
|
/* Second sub-frame has lowest energy */
|
|
rand_ptr = &psDec->exc_Q14[ silk_max_int( 0, psPLC->nb_subfr * psPLC->subfr_length - RAND_BUF_SIZE ) ];
|
|
}
|
|
|
|
/* Set up Gain to random noise component */
|
|
B_Q14 = psPLC->LTPCoef_Q14;
|
|
rand_scale_Q14 = psPLC->randScale_Q14;
|
|
|
|
/* Set up attenuation gains */
|
|
harm_Gain_Q15 = HARM_ATT_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ];
|
|
if( psDec->prevSignalType == TYPE_VOICED ) {
|
|
rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ];
|
|
} else {
|
|
rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[ silk_min_int( NB_ATT - 1, psDec->lossCnt ) ];
|
|
}
|
|
|
|
/* LPC concealment. Apply BWE to previous LPC */
|
|
silk_bwexpander( psPLC->prevLPC_Q12, psDec->LPC_order, SILK_FIX_CONST( BWE_COEF, 16 ) );
|
|
|
|
/* Preload LPC coeficients to array on stack. Gives small performance gain */
|
|
silk_memcpy( A_Q12, psPLC->prevLPC_Q12, psDec->LPC_order * sizeof( opus_int16 ) );
|
|
|
|
/* First Lost frame */
|
|
if( psDec->lossCnt == 0 ) {
|
|
rand_scale_Q14 = 1 << 14;
|
|
|
|
/* Reduce random noise Gain for voiced frames */
|
|
if( psDec->prevSignalType == TYPE_VOICED ) {
|
|
for( i = 0; i < LTP_ORDER; i++ ) {
|
|
rand_scale_Q14 -= B_Q14[ i ];
|
|
}
|
|
rand_scale_Q14 = silk_max_16( 3277, rand_scale_Q14 ); /* 0.2 */
|
|
rand_scale_Q14 = (opus_int16)silk_RSHIFT( silk_SMULBB( rand_scale_Q14, psPLC->prevLTP_scale_Q14 ), 14 );
|
|
} else {
|
|
/* Reduce random noise for unvoiced frames with high LPC gain */
|
|
opus_int32 invGain_Q30, down_scale_Q30;
|
|
|
|
invGain_Q30 = silk_LPC_inverse_pred_gain( psPLC->prevLPC_Q12, psDec->LPC_order );
|
|
|
|
down_scale_Q30 = silk_min_32( silk_RSHIFT( (opus_int32)1 << 30, LOG2_INV_LPC_GAIN_HIGH_THRES ), invGain_Q30 );
|
|
down_scale_Q30 = silk_max_32( silk_RSHIFT( (opus_int32)1 << 30, LOG2_INV_LPC_GAIN_LOW_THRES ), down_scale_Q30 );
|
|
down_scale_Q30 = silk_LSHIFT( down_scale_Q30, LOG2_INV_LPC_GAIN_HIGH_THRES );
|
|
|
|
rand_Gain_Q15 = silk_RSHIFT( silk_SMULWB( down_scale_Q30, rand_Gain_Q15 ), 14 );
|
|
}
|
|
}
|
|
|
|
rand_seed = psPLC->rand_seed;
|
|
lag = silk_RSHIFT_ROUND( psPLC->pitchL_Q8, 8 );
|
|
sLTP_buf_idx = psDec->ltp_mem_length;
|
|
|
|
/* Rewhiten LTP state */
|
|
idx = psDec->ltp_mem_length - lag - psDec->LPC_order - LTP_ORDER / 2;
|
|
silk_assert( idx > 0 );
|
|
silk_LPC_analysis_filter( &sLTP[ idx ], &psDec->outBuf[ idx ], A_Q12, psDec->ltp_mem_length - idx, psDec->LPC_order );
|
|
/* Scale LTP state */
|
|
inv_gain_Q30 = silk_INVERSE32_varQ( psPLC->prevGain_Q16[ 1 ], 46 );
|
|
inv_gain_Q30 = silk_min( inv_gain_Q30, silk_int32_MAX >> 1 );
|
|
for( i = idx + psDec->LPC_order; i < psDec->ltp_mem_length; i++ ) {
|
|
sLTP_Q14[ i ] = silk_SMULWB( inv_gain_Q30, sLTP[ i ] );
|
|
}
|
|
|
|
/***************************/
|
|
/* LTP synthesis filtering */
|
|
/***************************/
|
|
for( k = 0; k < psDec->nb_subfr; k++ ) {
|
|
/* Set up pointer */
|
|
pred_lag_ptr = &sLTP_Q14[ sLTP_buf_idx - lag + LTP_ORDER / 2 ];
|
|
for( i = 0; i < psDec->subfr_length; i++ ) {
|
|
/* Unrolled loop */
|
|
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
|
|
LTP_pred_Q12 = 2;
|
|
LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ 0 ], B_Q14[ 0 ] );
|
|
LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -1 ], B_Q14[ 1 ] );
|
|
LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -2 ], B_Q14[ 2 ] );
|
|
LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -3 ], B_Q14[ 3 ] );
|
|
LTP_pred_Q12 = silk_SMLAWB( LTP_pred_Q12, pred_lag_ptr[ -4 ], B_Q14[ 4 ] );
|
|
pred_lag_ptr++;
|
|
|
|
/* Generate LPC excitation */
|
|
rand_seed = silk_RAND( rand_seed );
|
|
idx = silk_RSHIFT( rand_seed, 25 ) & RAND_BUF_MASK;
|
|
sLTP_Q14[ sLTP_buf_idx ] = silk_LSHIFT32( silk_SMLAWB( LTP_pred_Q12, rand_ptr[ idx ], rand_scale_Q14 ), 2 );
|
|
sLTP_buf_idx++;
|
|
}
|
|
|
|
/* Gradually reduce LTP gain */
|
|
for( j = 0; j < LTP_ORDER; j++ ) {
|
|
B_Q14[ j ] = silk_RSHIFT( silk_SMULBB( harm_Gain_Q15, B_Q14[ j ] ), 15 );
|
|
}
|
|
/* Gradually reduce excitation gain */
|
|
rand_scale_Q14 = silk_RSHIFT( silk_SMULBB( rand_scale_Q14, rand_Gain_Q15 ), 15 );
|
|
|
|
/* Slowly increase pitch lag */
|
|
psPLC->pitchL_Q8 = silk_SMLAWB( psPLC->pitchL_Q8, psPLC->pitchL_Q8, PITCH_DRIFT_FAC_Q16 );
|
|
psPLC->pitchL_Q8 = silk_min_32( psPLC->pitchL_Q8, silk_LSHIFT( silk_SMULBB( MAX_PITCH_LAG_MS, psDec->fs_kHz ), 8 ) );
|
|
lag = silk_RSHIFT_ROUND( psPLC->pitchL_Q8, 8 );
|
|
}
|
|
|
|
/***************************/
|
|
/* LPC synthesis filtering */
|
|
/***************************/
|
|
sLPC_Q14_ptr = &sLTP_Q14[ psDec->ltp_mem_length - MAX_LPC_ORDER ];
|
|
|
|
/* Copy LPC state */
|
|
silk_memcpy( sLPC_Q14_ptr, psDec->sLPC_Q14_buf, MAX_LPC_ORDER * sizeof( opus_int32 ) );
|
|
|
|
silk_assert( psDec->LPC_order >= 10 ); /* check that unrolling works */
|
|
for( i = 0; i < psDec->frame_length; i++ ) {
|
|
/* partly unrolled */
|
|
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
|
|
LPC_pred_Q10 = silk_RSHIFT( psDec->LPC_order, 1 );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 1 ], A_Q12[ 0 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 2 ], A_Q12[ 1 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 3 ], A_Q12[ 2 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 4 ], A_Q12[ 3 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 5 ], A_Q12[ 4 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 6 ], A_Q12[ 5 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 7 ], A_Q12[ 6 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 8 ], A_Q12[ 7 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 9 ], A_Q12[ 8 ] );
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - 10 ], A_Q12[ 9 ] );
|
|
for( j = 10; j < psDec->LPC_order; j++ ) {
|
|
LPC_pred_Q10 = silk_SMLAWB( LPC_pred_Q10, sLPC_Q14_ptr[ MAX_LPC_ORDER + i - j - 1 ], A_Q12[ j ] );
|
|
}
|
|
|
|
/* Add prediction to LPC excitation */
|
|
sLPC_Q14_ptr[ MAX_LPC_ORDER + i ] = silk_ADD_LSHIFT32( sLPC_Q14_ptr[ MAX_LPC_ORDER + i ], LPC_pred_Q10, 4 );
|
|
|
|
/* Scale with Gain */
|
|
frame[ i ] = (opus_int16)silk_SAT16( silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( sLPC_Q14_ptr[ MAX_LPC_ORDER + i ], prevGain_Q10[ 1 ] ), 8 ) ) );
|
|
}
|
|
|
|
/* Save LPC state */
|
|
silk_memcpy( psDec->sLPC_Q14_buf, &sLPC_Q14_ptr[ psDec->frame_length ], MAX_LPC_ORDER * sizeof( opus_int32 ) );
|
|
|
|
/**************************************/
|
|
/* Update states */
|
|
/**************************************/
|
|
psPLC->rand_seed = rand_seed;
|
|
psPLC->randScale_Q14 = rand_scale_Q14;
|
|
for( i = 0; i < MAX_NB_SUBFR; i++ ) {
|
|
psDecCtrl->pitchL[ i ] = lag;
|
|
}
|
|
RESTORE_STACK;
|
|
}
|
|
|
|
/* Glues concealed frames with new good received frames */
|
|
void silk_PLC_glue_frames(
|
|
silk_decoder_state *psDec, /* I/O decoder state */
|
|
opus_int16 frame[], /* I/O signal */
|
|
opus_int length /* I length of signal */
|
|
)
|
|
{
|
|
opus_int i, energy_shift;
|
|
opus_int32 energy;
|
|
silk_PLC_struct *psPLC;
|
|
psPLC = &psDec->sPLC;
|
|
|
|
if( psDec->lossCnt ) {
|
|
/* Calculate energy in concealed residual */
|
|
silk_sum_sqr_shift( &psPLC->conc_energy, &psPLC->conc_energy_shift, frame, length );
|
|
|
|
psPLC->last_frame_lost = 1;
|
|
} else {
|
|
if( psDec->sPLC.last_frame_lost ) {
|
|
/* Calculate residual in decoded signal if last frame was lost */
|
|
silk_sum_sqr_shift( &energy, &energy_shift, frame, length );
|
|
|
|
/* Normalize energies */
|
|
if( energy_shift > psPLC->conc_energy_shift ) {
|
|
psPLC->conc_energy = silk_RSHIFT( psPLC->conc_energy, energy_shift - psPLC->conc_energy_shift );
|
|
} else if( energy_shift < psPLC->conc_energy_shift ) {
|
|
energy = silk_RSHIFT( energy, psPLC->conc_energy_shift - energy_shift );
|
|
}
|
|
|
|
/* Fade in the energy difference */
|
|
if( energy > psPLC->conc_energy ) {
|
|
opus_int32 frac_Q24, LZ;
|
|
opus_int32 gain_Q16, slope_Q16;
|
|
|
|
LZ = silk_CLZ32( psPLC->conc_energy );
|
|
LZ = LZ - 1;
|
|
psPLC->conc_energy = silk_LSHIFT( psPLC->conc_energy, LZ );
|
|
energy = silk_RSHIFT( energy, silk_max_32( 24 - LZ, 0 ) );
|
|
|
|
frac_Q24 = silk_DIV32( psPLC->conc_energy, silk_max( energy, 1 ) );
|
|
|
|
gain_Q16 = silk_LSHIFT( silk_SQRT_APPROX( frac_Q24 ), 4 );
|
|
slope_Q16 = silk_DIV32_16( ( (opus_int32)1 << 16 ) - gain_Q16, length );
|
|
/* Make slope 4x steeper to avoid missing onsets after DTX */
|
|
slope_Q16 = silk_LSHIFT( slope_Q16, 2 );
|
|
|
|
for( i = 0; i < length; i++ ) {
|
|
frame[ i ] = silk_SMULWB( gain_Q16, frame[ i ] );
|
|
gain_Q16 += slope_Q16;
|
|
if( gain_Q16 > (opus_int32)1 << 16 ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
psPLC->last_frame_lost = 0;
|
|
}
|
|
}
|