/*
 * 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.*;

/**
 * processing of gains.
 *
 * @author Dingxin Xu
 */
public class ProcessGainsFLP
{
    /**
     * Processing of gains.
     * @param psEnc Encoder state FLP
     * @param psEncCtrl Encoder control FLP
     */
    static void SKP_Silk_process_gains_FLP(
        SKP_Silk_encoder_state_FLP      psEnc,             /* I/O  Encoder state FLP                       */
        SKP_Silk_encoder_control_FLP    psEncCtrl          /* I/O  Encoder control FLP                     */
    )
    {
        SKP_Silk_shape_state_FLP psShapeSt = psEnc.sShape;
        int     k;
        int     pGains_Q16[] = new int[ NB_SUBFR ];
        float   s, InvMaxSqrVal, gain;

        /* Gain reduction when LTP coding gain is high */
        if( psEncCtrl.sCmn.sigtype == SIG_TYPE_VOICED ) {
            s = 1.0f - 0.5f * SigProcFLP.SKP_sigmoid( 0.25f * ( psEncCtrl.LTPredCodGain - 12.0f ) );
            for( k = 0; k < NB_SUBFR; k++ ) {
                psEncCtrl.Gains[ k ] *= s;
            }
        }

        /* Limit the quantized signal */
        InvMaxSqrVal = ( float )( Math.pow( 2.0f, 0.33f * ( 21.0f - psEncCtrl.current_SNR_dB ) ) / psEnc.sCmn.subfr_length );

        for( k = 0; k < NB_SUBFR; k++ ) {
            /* Soft limit on ratio residual energy and squared gains */
            gain = psEncCtrl.Gains[ k ];
            gain = ( float )Math.sqrt( gain * gain + psEncCtrl.ResNrg[ k ] * InvMaxSqrVal );
            psEncCtrl.Gains[ k ] = ( gain < 32767.0f ? gain : 32767.0f );
        }

        /* Prepare gains for noise shaping quantization */
        for( k = 0; k < NB_SUBFR; k++ ) {
            pGains_Q16[ k ] = ( int ) ( psEncCtrl.Gains[ k ] * 65536.0f );
        }

        /* Noise shaping quantization */
        int[] LastGainIndex_ptr = new int[1];
        LastGainIndex_ptr[0] = psShapeSt.LastGainIndex;
        GainQuant.SKP_Silk_gains_quant( psEncCtrl.sCmn.GainsIndices, pGains_Q16,
                LastGainIndex_ptr, psEnc.sCmn.nFramesInPayloadBuf );
        psShapeSt.LastGainIndex = LastGainIndex_ptr[0];
        /* Overwrite unquantized gains with quantized gains and convert back to Q0 from Q16 */
        for( k = 0; k < NB_SUBFR; k++ ) {
            psEncCtrl.Gains[ k ] = pGains_Q16[ k ] / 65536.0f;
        }

        /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */
        if( psEncCtrl.sCmn.sigtype == SIG_TYPE_VOICED ) {
            if( psEncCtrl.LTPredCodGain + psEncCtrl.input_tilt > 1.0f ) {
                psEncCtrl.sCmn.QuantOffsetType = 0;
            } else {
                psEncCtrl.sCmn.QuantOffsetType = 1;
            }
        }

        /* Quantizer boundary adjustment */
        if( psEncCtrl.sCmn.sigtype == SIG_TYPE_VOICED ) {
            psEncCtrl.Lambda = 1.2f - 0.4f * psEnc.speech_activity
                                     - 0.3f * psEncCtrl.input_quality
                                     + 0.2f * psEncCtrl.sCmn.QuantOffsetType
                                     - 0.1f * psEncCtrl.coding_quality;
        } else {
            psEncCtrl.Lambda = 1.2f - 0.4f * psEnc.speech_activity
                                     - 0.4f * psEncCtrl.input_quality
                                     + 0.4f * psEncCtrl.sCmn.QuantOffsetType
                                     - 0.1f * psEncCtrl.coding_quality;
        }

        assert( psEncCtrl.Lambda >= 0.0f );
        assert( psEncCtrl.Lambda <  2.0f );
    }
}
