/* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */
void SKP_Silk_HP_variable_cutoff_FIX(
    SKP_Silk_encoder_state_FIX      *psEnc,             /* I/O  Encoder state FIX                           */
    SKP_Silk_encoder_control_FIX    *psEncCtrl,         /* I/O  Encoder control FIX                         */
    SKP_int16                       *out,               /* O    high-pass filtered output signal            */
    const SKP_int16                 *in                 /* I    input signal                                */
)
{
    SKP_int   quality_Q15;
    SKP_int32 B_Q28[ 3 ], A_Q28[ 2 ];
    SKP_int32 Fc_Q19, r_Q28, r_Q22;
    SKP_int32 pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7;

    /*********************************************/
    /* Estimate Low End of Pitch Frequency Range */
    /*********************************************/
    if( psEnc->sCmn.prev_sigtype == SIG_TYPE_VOICED ) {
        /* difference, in log domain */
        pitch_freq_Hz_Q16 = SKP_DIV32_16( SKP_LSHIFT( SKP_MUL( psEnc->sCmn.fs_kHz, 1000 ), 16 ), psEnc->sCmn.prevLag );
        pitch_freq_log_Q7 = SKP_Silk_lin2log( pitch_freq_Hz_Q16 ) - ( 16 << 7 ); //0x70

        /* adjustment based on quality */
        quality_Q15 = psEncCtrl->input_quality_bands_Q15[ 0 ];
        pitch_freq_log_Q7 = SKP_SUB32( pitch_freq_log_Q7, SKP_SMULWB( SKP_SMULWB( SKP_LSHIFT( quality_Q15, 2 ), quality_Q15 ), 
            pitch_freq_log_Q7 - SKP_LOG2_VARIABLE_HP_MIN_FREQ_Q7 ) );
        pitch_freq_log_Q7 = SKP_ADD32( pitch_freq_log_Q7, SKP_RSHIFT( SKP_FIX_CONST( 0.6, 15 ) - quality_Q15, 9 ) );

        //delta_freq = pitch_freq_log - psEnc->variable_HP_smth1;
        delta_freq_Q7 = pitch_freq_log_Q7 - SKP_RSHIFT( psEnc->variable_HP_smth1_Q15, 8 );
        if( delta_freq_Q7 < 0 ) {
            /* less smoothing for decreasing pitch frequency, to track something close to the minimum */
            delta_freq_Q7 = SKP_MUL( delta_freq_Q7, 3 );
        }

        /* limit delta, to reduce impact of outliers */
        delta_freq_Q7 = SKP_LIMIT_32( delta_freq_Q7, -SKP_FIX_CONST( VARIABLE_HP_MAX_DELTA_FREQ, 7 ), SKP_FIX_CONST( VARIABLE_HP_MAX_DELTA_FREQ, 7 ) );

        /* update smoother */
        psEnc->variable_HP_smth1_Q15 = SKP_SMLAWB( psEnc->variable_HP_smth1_Q15, 
            SKP_MUL( SKP_LSHIFT( psEnc->speech_activity_Q8, 1 ), delta_freq_Q7 ), SKP_FIX_CONST( VARIABLE_HP_SMTH_COEF1, 16 ) );
    }
    /* second smoother */
    psEnc->variable_HP_smth2_Q15 = SKP_SMLAWB( psEnc->variable_HP_smth2_Q15, 
        psEnc->variable_HP_smth1_Q15 - psEnc->variable_HP_smth2_Q15, SKP_FIX_CONST( VARIABLE_HP_SMTH_COEF2, 16 ) );

    /* convert from log scale to Hertz */
    psEncCtrl->pitch_freq_low_Hz = SKP_Silk_log2lin( SKP_RSHIFT( psEnc->variable_HP_smth2_Q15, 8 ) );

    /* limit frequency range */
    psEncCtrl->pitch_freq_low_Hz = SKP_LIMIT_32( psEncCtrl->pitch_freq_low_Hz, 
        SKP_FIX_CONST( VARIABLE_HP_MIN_FREQ, 0 ), SKP_FIX_CONST( VARIABLE_HP_MAX_FREQ, 0 ) );

    /********************************/
    /* Compute Filter Coefficients  */
    /********************************/
    /* compute cut-off frequency, in radians */
    //Fc_num   = (SKP_float)( 0.45f * 2.0f * 3.14159265359 * psEncCtrl->pitch_freq_low_Hz );
    //Fc_denom = (SKP_float)( 1e3f * psEnc->sCmn.fs_kHz );
    SKP_assert( psEncCtrl->pitch_freq_low_Hz <= SKP_int32_MAX / SKP_RADIANS_CONSTANT_Q19 );
    Fc_Q19 = SKP_DIV32_16( SKP_SMULBB( SKP_RADIANS_CONSTANT_Q19, psEncCtrl->pitch_freq_low_Hz ), psEnc->sCmn.fs_kHz ); // range: 3704 - 27787, 11-15 bits
    SKP_assert( Fc_Q19 >=  3704 );
    SKP_assert( Fc_Q19 <= 27787 );

    r_Q28 = SKP_FIX_CONST( 1.0, 28 ) - SKP_MUL( SKP_FIX_CONST( 0.92, 9 ), Fc_Q19 );
    SKP_assert( r_Q28 >= 255347779 );
    SKP_assert( r_Q28 <= 266690872 );

    /* b = r * [ 1; -2; 1 ]; */
    /* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */
    B_Q28[ 0 ] = r_Q28;
    B_Q28[ 1 ] = SKP_LSHIFT( -r_Q28, 1 );
    B_Q28[ 2 ] = r_Q28;
    
    // -r * ( 2 - Fc * Fc );
    r_Q22  = SKP_RSHIFT( r_Q28, 6 );
    A_Q28[ 0 ] = SKP_SMULWW( r_Q22, SKP_SMULWW( Fc_Q19, Fc_Q19 ) - SKP_FIX_CONST( 2.0,  22 ) );
    A_Q28[ 1 ] = SKP_SMULWW( r_Q22, r_Q22 );

    /********************************/
    /* High-Pass Filter             */
    /********************************/
    SKP_Silk_biquad_alt( in, B_Q28, A_Q28, psEnc->sCmn.In_HP_State, out, psEnc->sCmn.frame_length );
}
SKP_int SKP_Silk_decode_frame(
    SKP_Silk_decoder_state      *psDec,             /* I/O  Pointer to Silk decoder state               */
    ec_dec                      *psRangeDec,        /* I/O  Compressor data structure                   */
    SKP_int16                   pOut[],             /* O    Pointer to output speech frame              */
    SKP_int32                   *pN,                /* O    Pointer to size of output frame             */
    const SKP_int               nBytes,             /* I    Payload length                              */
    SKP_int                     lostFlag            /* I    0: no loss, 1 loss, 2 decode fec            */
)
{
    SKP_Silk_decoder_control sDecCtrl;
    SKP_int         i, L, mv_len, ret = 0;
    SKP_int8        flags;
    SKP_int32       LBRR_symbol;
    SKP_int         pulses[ MAX_FRAME_LENGTH ];

TIC(DECODE_FRAME)

    L = psDec->frame_length;
    sDecCtrl.LTP_scale_Q14 = 0;

    /* Safety checks */
    SKP_assert( L > 0 && L <= MAX_FRAME_LENGTH );

    /********************************************/
    /* Decode Frame if packet is not lost       */
    /********************************************/
    if( lostFlag != PACKET_LOST && psDec->nFramesDecoded == 0 ) {
        /* First decoder call for this payload */
        /* Decode VAD flags and LBRR flag */
        flags = SKP_RSHIFT( psRangeDec->buf[ 0 ], 7 - psDec->nFramesPerPacket ) & 
            ( SKP_LSHIFT( 1, psDec->nFramesPerPacket + 1 ) - 1 );
        psDec->LBRR_flag = flags & 1;
        for( i = psDec->nFramesPerPacket - 1; i >= 0 ; i-- ) {
            flags = SKP_RSHIFT( flags, 1 );
            psDec->VAD_flags[ i ] = flags & 1;
        }
        for( i = 0; i < psDec->nFramesPerPacket + 1; i++ ) {
            ec_dec_icdf( psRangeDec, SKP_Silk_uniform2_iCDF, 8 );
        }
       
        /* Decode LBRR flags */
        SKP_memset( psDec->LBRR_flags, 0, sizeof( psDec->LBRR_flags ) );
        if( psDec->LBRR_flag ) {
            if( psDec->nFramesPerPacket == 1 ) {
                psDec->LBRR_flags[ 0 ] = 1;
            } else {
                LBRR_symbol = ec_dec_icdf( psRangeDec, SKP_Silk_LBRR_flags_iCDF_ptr[ psDec->nFramesPerPacket - 2 ], 8 ) + 1;
                for( i = 0; i < psDec->nFramesPerPacket; i++ ) {
                    psDec->LBRR_flags[ i ] = SKP_RSHIFT( LBRR_symbol, i ) & 1;
                }
            }
        }

        if( lostFlag == DECODE_NORMAL ) {
            /* Regular decoding: skip all LBRR data */
            for( i = 0; i < psDec->nFramesPerPacket; i++ ) {
                if( psDec->LBRR_flags[ i ] ) {
                    SKP_Silk_decode_indices( psDec, psRangeDec, i, 1 );
                    SKP_Silk_decode_pulses( psRangeDec, pulses, psDec->indices.signalType, 
                        psDec->indices.quantOffsetType, psDec->frame_length );
                }
            }
        }

    }

    if( lostFlag == DECODE_LBRR && psDec->LBRR_flags[ psDec->nFramesDecoded ] == 0 ) {
        /* Treat absent LBRR data as lost frame */
        lostFlag = PACKET_LOST;
        psDec->nFramesDecoded++;
    }

    if( lostFlag != PACKET_LOST ) {
        /*********************************************/
        /* Decode quantization indices of side info  */
        /*********************************************/
TIC(decode_indices)
        SKP_Silk_decode_indices( psDec, psRangeDec, psDec->nFramesDecoded, lostFlag );
TOC(decode_indices)

        /*********************************************/
        /* Decode quantization indices of excitation */
        /*********************************************/
TIC(decode_pulses)
        SKP_Silk_decode_pulses( psRangeDec, pulses, psDec->indices.signalType, 
                psDec->indices.quantOffsetType, psDec->frame_length );
TOC(decode_pulses)

        /********************************************/
        /* Decode parameters and pulse signal       */
        /********************************************/
TIC(decode_params)
        SKP_Silk_decode_parameters( psDec, &sDecCtrl );
TOC(decode_params)

        /* Update length. Sampling frequency may have changed */
        L = psDec->frame_length;

        /********************************************************/
        /* Run inverse NSQ                                      */
        /********************************************************/
TIC(decode_core)
        SKP_Silk_decode_core( psDec, &sDecCtrl, pOut, pulses );
TOC(decode_core)

        /********************************************************/
        /* Update PLC state                                     */
        /********************************************************/
        SKP_Silk_PLC( psDec, &sDecCtrl, pOut, L, 0 );

        psDec->lossCnt = 0;
        psDec->prevSignalType = psDec->indices.signalType;
        SKP_assert( psDec->prevSignalType >= 0 && psDec->prevSignalType <= 2 );

        /* A frame has been decoded without errors */
        psDec->first_frame_after_reset = 0;
        psDec->nFramesDecoded++;
    } else {
        /* Handle packet loss by extrapolation */
        SKP_Silk_PLC( psDec, &sDecCtrl, pOut, L, 1 );
    }

    /*************************/
    /* Update output buffer. */
    /*************************/
    SKP_assert( psDec->ltp_mem_length >= psDec->frame_length );
    mv_len = psDec->ltp_mem_length - psDec->frame_length;
    SKP_memmove( psDec->outBuf, &psDec->outBuf[ psDec->frame_length ], mv_len * sizeof(SKP_int16) );
    SKP_memcpy( &psDec->outBuf[ mv_len ], pOut, psDec->frame_length * sizeof( SKP_int16 ) );

    /****************************************************************/
    /* Ensure smooth connection of extrapolated and good frames     */
    /****************************************************************/
    SKP_Silk_PLC_glue_frames( psDec, &sDecCtrl, pOut, L );

    /************************************************/
    /* Comfort noise generation / estimation        */
    /************************************************/
    SKP_Silk_CNG( psDec, &sDecCtrl, pOut, L );

    /********************************************/
    /* HP filter output                            */
    /********************************************/
TIC(HP_out)
    SKP_Silk_biquad_alt( pOut, psDec->HP_B, psDec->HP_A, psDec->HPState, pOut, L );
TOC(HP_out)

    /* Update some decoder state variables */
    psDec->lagPrev = sDecCtrl.pitchL[ psDec->nb_subfr - 1 ];

    /********************************************/
    /* set output frame length                    */
    /********************************************/
    *pN = ( SKP_int16 )L;

TOC(DECODE_FRAME)

    return ret;
}
/* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */
void SKP_Silk_HP_variable_cutoff_FLP(
    SKP_Silk_encoder_state_FLP      *psEnc,             /* I/O  Encoder state FLP                       */
    SKP_Silk_encoder_control_FLP    *psEncCtrl,         /* I/O  Encoder control FLP                     */
          SKP_int16                 *out,               /* O    High-pass filtered output signal        */
    const SKP_int16                 *in                 /* I    Input signal                            */
)
{
    SKP_float pitch_freq_Hz, pitch_freq_log, quality, delta_freq, smth_coef, Fc, r;
    SKP_int32 B_Q28[ 3 ], A_Q28[ 2 ];

    /*********************************************/
    /* Estimate low end of pitch frequency range */
    /*********************************************/
    if( psEnc->sCmn.prev_sigtype == SIG_TYPE_VOICED ) {

        /* Difference, in log domain */
        pitch_freq_Hz  = 1e3f * psEnc->sCmn.fs_kHz / psEnc->sCmn.prevLag;
        pitch_freq_log = SKP_Silk_log2( pitch_freq_Hz );

        /* Adjustment based on quality */
        quality = psEncCtrl->input_quality_bands[ 0 ];
        pitch_freq_log -= quality * quality * ( pitch_freq_log - SKP_Silk_log2( VARIABLE_HP_MIN_FREQ ) );
        pitch_freq_log += 0.5f * ( 0.6f - quality );

        delta_freq = pitch_freq_log - psEnc->variable_HP_smth1;
        if( delta_freq < 0.0 ) {
            /* Less smoothing for decreasing pitch frequency, to track something close to the minimum */
            delta_freq *= 3.0f;
        }

        /* Limit delta, to reduce impact of outliers */
        delta_freq = SKP_LIMIT_float( delta_freq, -VARIABLE_HP_MAX_DELTA_FREQ, VARIABLE_HP_MAX_DELTA_FREQ );
    
        /* Update smoother */
        smth_coef = VARIABLE_HP_SMTH_COEF1 * psEnc->speech_activity;
        psEnc->variable_HP_smth1 += smth_coef * delta_freq;
    }

    /* Second smoother */
    psEnc->variable_HP_smth2 += VARIABLE_HP_SMTH_COEF2 * ( psEnc->variable_HP_smth1 - psEnc->variable_HP_smth2 );

    /* Convert from log scale to Hertz */
    psEncCtrl->pitch_freq_low_Hz = ( SKP_float )pow( 2.0f, psEnc->variable_HP_smth2 );

    /* Limit frequency range */
    psEncCtrl->pitch_freq_low_Hz = SKP_LIMIT_float( psEncCtrl->pitch_freq_low_Hz, VARIABLE_HP_MIN_FREQ, VARIABLE_HP_MAX_FREQ );

    /*******************************/
    /* Compute filter coefficients */
    /*******************************/
    /* Compute cut-off frequency, in radians */
    Fc = ( SKP_float )( 0.45f * 2.0f * 3.14159265359 * psEncCtrl->pitch_freq_low_Hz / ( 1e3f * psEnc->sCmn.fs_kHz ) );

    /* 2nd order ARMA coefficients */
    r = 1.0f - 0.92f * Fc;

    /* b = r * [1; -2; 1]; */
    /* a = [1; -2 * r * (1 - 0.5 * Fc^2); r^2]; */
    B_Q28[ 0 ] = SKP_float2int( ( 1 << 28 ) * r );
    B_Q28[ 1 ] = SKP_float2int( ( 1 << 28 ) * -2.0f * r );
    B_Q28[ 2 ] = B_Q28[ 0 ];
    A_Q28[ 0 ] = SKP_float2int( ( 1 << 28 ) * -2.0f * r * ( 1.0f - 0.5f * Fc * Fc ) );
    A_Q28[ 1 ] = SKP_float2int( ( 1 << 28 ) * r * r );

    /********************/
    /* High-pass filter */
    /********************/
    SKP_Silk_biquad_alt( in, B_Q28, A_Q28, psEnc->sCmn.In_HP_State, out, psEnc->sCmn.frame_length );
}
/* Deactivate by setting psEncC->transition_frame_no = 0;   */
void SKP_Silk_LP_variable_cutoff(
    SKP_Silk_LP_state               *psLP,          /* I/O  LP filter state                     */
    SKP_int16                       *out,           /* O    Low-pass filtered output signal     */
    const SKP_int16                 *in,            /* I    Input signal                        */
    const SKP_int                   frame_length    /* I    Frame length                        */
)
{
    SKP_int32   B_Q28[ TRANSITION_NB ], A_Q28[ TRANSITION_NA ], fac_Q16 = 0;
    SKP_int     ind = 0;

    SKP_assert( psLP->transition_frame_no >= 0 );
    SKP_assert( ( ( ( psLP->transition_frame_no <= TRANSITION_FRAMES_DOWN ) && ( psLP->mode == 0 ) ) || 
                  ( ( psLP->transition_frame_no <= TRANSITION_FRAMES_UP   ) && ( psLP->mode == 1 ) ) ) );

    /* Interpolate filter coefficients if needed */
    if( psLP->transition_frame_no > 0 ) {
        if( psLP->mode == 0 ) {
            if( psLP->transition_frame_no < TRANSITION_FRAMES_DOWN ) {
                /* Calculate index and interpolation factor for interpolation */
#if( TRANSITION_INT_STEPS_DOWN == 32 )
                fac_Q16 = SKP_LSHIFT( psLP->transition_frame_no, 16 - 5 );
#else
                fac_Q16 = SKP_DIV32_16( SKP_LSHIFT( psLP->transition_frame_no, 16 ), TRANSITION_INT_STEPS_DOWN );
#endif
                ind      = SKP_RSHIFT( fac_Q16, 16 );
                fac_Q16 -= SKP_LSHIFT( ind, 16 );

                SKP_assert( ind >= 0 );
                SKP_assert( ind < TRANSITION_INT_NUM );

                /* Interpolate filter coefficients */
                SKP_Silk_LP_interpolate_filter_taps( B_Q28, A_Q28, ind, fac_Q16 );

                /* Increment transition frame number for next frame */
                psLP->transition_frame_no++;

            } else if( psLP->transition_frame_no == TRANSITION_FRAMES_DOWN ) {
                /* End of transition phase */
                SKP_Silk_LP_interpolate_filter_taps( B_Q28, A_Q28, TRANSITION_INT_NUM - 1, 0 );
            }
        } else if( psLP->mode == 1 ) {
            if( psLP->transition_frame_no < TRANSITION_FRAMES_UP ) {
                /* Calculate index and interpolation factor for interpolation */
#if( TRANSITION_INT_STEPS_UP == 64 )
                fac_Q16 = SKP_LSHIFT( TRANSITION_FRAMES_UP - psLP->transition_frame_no, 16 - 6 );
#else
                fac_Q16 = SKP_DIV32_16( SKP_LSHIFT( TRANSITION_FRAMES_UP - psLP->transition_frame_no, 16 ), TRANSITION_INT_STEPS_UP );
#endif
                ind      = SKP_RSHIFT( fac_Q16, 16 );
                fac_Q16 -= SKP_LSHIFT( ind, 16 );

                SKP_assert( ind >= 0 );
                SKP_assert( ind < TRANSITION_INT_NUM );

                /* Interpolate filter coefficients */
                SKP_Silk_LP_interpolate_filter_taps( B_Q28, A_Q28, ind, fac_Q16 );

                /* Increment transition frame number for next frame */
                psLP->transition_frame_no++;
            
            } else if( psLP->transition_frame_no == TRANSITION_FRAMES_UP ) {
                /* End of transition phase */
                SKP_Silk_LP_interpolate_filter_taps( B_Q28, A_Q28, 0, 0 );
            }
        }
    } 
    
    if( psLP->transition_frame_no > 0 ) {
        /* ARMA low-pass filtering */
        SKP_assert( TRANSITION_NB == 3 && TRANSITION_NA == 2 );
        SKP_Silk_biquad_alt( in, B_Q28, A_Q28, psLP->In_LP_State, out, frame_length );
    } else {
        /* Instead of using the filter, copy input directly to output */
        SKP_memcpy( out, in, frame_length * sizeof( SKP_int16 ) );
    }
}