/*
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jitsi.impl.neomedia.codec.audio.silk;

import static org.jitsi.impl.neomedia.codec.audio.silk.Define.*;

/**
 * Limit, stabilize, convert and quantize NLSFs.
 *
 * @author Dingxin Xu
 */
public class ProcessNLSFsFLP
{
    /**
     * Limit, stabilize, convert and quantize NLSFs.
     * @param psEnc Encoder state FLP
     * @param psEncCtrl Encoder control FLP
     * @param pNLSF NLSFs (quantized output)
     */
    static void SKP_Silk_process_NLSFs_FLP(
        SKP_Silk_encoder_state_FLP      psEnc,             /* I/O  Encoder state FLP                       */
        SKP_Silk_encoder_control_FLP    psEncCtrl,         /* I/O  Encoder control FLP                     */
        float                           []pNLSF              /* I/O  NLSFs (quantized output)                */
    )
    {
        boolean     doInterpolate;
        float   pNLSFW[] = new float[ MAX_LPC_ORDER ];
        float   NLSF_mu, NLSF_mu_fluc_red, i_sqr, NLSF_interpolation_factor = 0.0f;
        final SKP_Silk_NLSF_CB_FLP psNLSF_CB_FLP;


        /* Used only for NLSF interpolation */
        float   pNLSF0_temp[] = new float[  MAX_LPC_ORDER ];
        float   pNLSFW0_temp[] = new float[ MAX_LPC_ORDER ];
        int     i;

        assert( psEncCtrl.sCmn.sigtype == SIG_TYPE_VOICED || psEncCtrl.sCmn.sigtype == SIG_TYPE_UNVOICED );

        /***********************/
        /* Calculate mu values */
        /***********************/
        if( psEncCtrl.sCmn.sigtype == SIG_TYPE_VOICED ) {
            NLSF_mu          = 0.002f - 0.001f * psEnc.speech_activity;
            NLSF_mu_fluc_red = 0.1f   - 0.05f  * psEnc.speech_activity;
        } else {
            NLSF_mu          = 0.005f - 0.004f * psEnc.speech_activity;
            NLSF_mu_fluc_red = 0.2f   - 0.1f   * ( psEnc.speech_activity + psEncCtrl.sparseness );
        }

        /* Calculate NLSF weights */
        NLSFVQWeightsLaroiaFLP.SKP_Silk_NLSF_VQ_weights_laroia_FLP( pNLSFW, pNLSF, psEnc.sCmn.predictLPCOrder );

        /* Update NLSF weights for interpolated NLSFs */
        doInterpolate = ( psEnc.sCmn.useInterpolatedNLSFs == 1 ) && ( psEncCtrl.sCmn.NLSFInterpCoef_Q2 < ( 1 << 2 ) );
        if( doInterpolate ) {

            /* Calculate the interpolated NLSF vector for the first half */
            NLSF_interpolation_factor = 0.25f * psEncCtrl.sCmn.NLSFInterpCoef_Q2;
            WrappersFLP.SKP_Silk_interpolate_wrapper_FLP( pNLSF0_temp, psEnc.sPred.prev_NLSFq, pNLSF,
                NLSF_interpolation_factor, psEnc.sCmn.predictLPCOrder );

            /* Calculate first half NLSF weights for the interpolated NLSFs */
            NLSFVQWeightsLaroiaFLP.SKP_Silk_NLSF_VQ_weights_laroia_FLP( pNLSFW0_temp, pNLSF0_temp, psEnc.sCmn.predictLPCOrder );

            /* Update NLSF weights with contribution from first half */
            i_sqr = NLSF_interpolation_factor * NLSF_interpolation_factor;
            for( i = 0; i < psEnc.sCmn.predictLPCOrder; i++ ) {
                pNLSFW[ i ] = 0.5f * ( pNLSFW[ i ] + i_sqr * pNLSFW0_temp[ i ] );
            }
        }

        /* Set pointer to the NLSF codebook for the current signal type and LPC order */
        psNLSF_CB_FLP = psEnc.psNLSF_CB_FLP[ psEncCtrl.sCmn.sigtype ];

        /* Quantize NLSF parameters given the trained NLSF codebooks */
        NLSFMSVQEncodeFLP.SKP_Silk_NLSF_MSVQ_encode_FLP( psEncCtrl.sCmn.NLSFIndices, pNLSF, psNLSF_CB_FLP, psEnc.sPred.prev_NLSFq,
                pNLSFW, NLSF_mu, NLSF_mu_fluc_red, psEnc.sCmn.NLSF_MSVQ_Survivors,
                psEnc.sCmn.predictLPCOrder, psEnc.sCmn.first_frame_after_reset );

        /* Convert quantized NLSFs back to LPC coefficients */
        WrappersFLP.SKP_Silk_NLSF2A_stable_FLP( psEncCtrl.PredCoef[ 1 ], pNLSF, psEnc.sCmn.predictLPCOrder );

        if( doInterpolate ) {
            /* Calculate the interpolated, quantized NLSF vector for the first half */
            WrappersFLP.SKP_Silk_interpolate_wrapper_FLP( pNLSF0_temp, psEnc.sPred.prev_NLSFq, pNLSF,
                NLSF_interpolation_factor, psEnc.sCmn.predictLPCOrder );

            /* Convert back to LPC coefficients */
            WrappersFLP.SKP_Silk_NLSF2A_stable_FLP( psEncCtrl.PredCoef[ 0 ], pNLSF0_temp, psEnc.sCmn.predictLPCOrder );

        } else {
            /* Copy LPC coefficients for first half from second half */
            System.arraycopy(psEncCtrl.PredCoef[1], 0, psEncCtrl.PredCoef[0], 0, psEnc.sCmn.predictLPCOrder);
        }
    }
}
