/*************************************************************************
 *
 *  FILE NAME:   D_GAINS.C
 *
 *  FUNCTIONS DEFINED IN THIS FILE:
 *
 *        d_gain_pitch(), d_gain_code()
 *
 * MA prediction is performed on the innovation energy
 * ( in dB/(20*log10(2)) ) with mean removed.
 *************************************************************************/

#include "typedef.h"
#include "sig_proc.h"

#include "gains_tb.h"

#include "cnst.h"


/* past quantized energies.      */
/* initialized to -14.0/constant, constant = 20*Log10(2) */

Word16 past_qua_en[4];

/* MA prediction coeff   */
Word16 pred[4];


/*************************************************************************
 *
 *  FUNCTION:   d_gain_pitch
 *
 *  PURPOSE:  decodes the pitch gain using the received index.
 *
 *  DESCRIPTION:
 *       In case of no frame erasure, the gain is obtained from the
 *       quantization table at the given index; otherwise, a downscaled
 *       past gain is used.
 *
 *************************************************************************/

Word16 d_gain_pitch ( /* out      : quantized pitch gain           */
    Word16 index      /* in       : index of quantization          */
)
{
    Word16 gain;

    gain = qua_gain_pitch[index] >> 2;

    return gain;
}

/*************************************************************************
 *
 *  FUNCTION:  d_gain_code
 *
 *  PURPOSE:  decode the fixed codebook gain using the received index.
 *
 *  DESCRIPTION:
 *      The received index gives the gain correction factor gamma.
 *      The quantized gain is given by   g_q = g0 * gamma
 *      where g0 is the predicted gain.
 *      To find g0, 4th order MA prediction is applied to the mean-removed
 *      innovation energy in dB.
 *      In case of frame erasure, downscaled past gain is used.
 *
 *************************************************************************/

/* average innovation energy.                             */
/* MEAN_ENER = 36.0/constant, constant = 20*Log10(2)      */
#define MEAN_ENER  783741L      /* 36/(20*log10(2))       */

void d_gain_code (
    Word16 index,      /* input : received quantization index */
    Word16 code[],     /* input : innovation codevector       */
    Word16 *gain_code, /* output: decoded innovation gain     */
    Word16 i_subfr
)
{
    Word16 i;
    Word16 gcode0, exp, frac;
    Word32 ener, ener_code;

    /*-------------- Decode codebook gain ---------------*/

    /*-------------------------------------------------------------------*
     *  energy of code:                                                   *
     *  ~~~~~~~~~~~~~~~                                                   *
     *  ener_code = 10 * Log10(energy/L_SUBFR) / constant                   *
     *            = 1/2 * Log2(energy/L_SUBFR)                              *
     *                                           constant = 20*Log10(2)   *
     *-------------------------------------------------------------------*/

    /* ener_code = log10(ener_code/L_SUBFR) / (20*log10(2)) */
    ener_code = 0;
    for (i = 0; i < L_SUBFR; i++)
    {
        ener_code += (Word32)code[i] * (Word32)code[i];
    }
    /* ener_code = ener_code / L_SUBFR */
    ener_code = ((ener_code + (1<<14)) >> 15) * 26214 * 2;

    /* ener_code = 1/2 * Log2(ener_code) */
    Log2 (ener_code, &exp, &frac);
    ener_code = ((Word32)(exp-30) << 16) + ((Word32)frac * 2);

    /* predicted energy */

    ener = MEAN_ENER;
    for (i = 0; i < 4; i++)
    {
        ener += (Word32)past_qua_en[i] * (Word32)pred[i] * 2;
    }

    /*-------------------------------------------------------------------*
     *  predicted codebook gain                                           *
     *  ~~~~~~~~~~~~~~~~~~~~~~~                                           *
     *  gcode0     = Pow10( (ener*constant - ener_code*constant) / 20 )   *
     *             = Pow2(ener-ener_code)                                 *
     *                                           constant = 20*Log10(2)   *
     *-------------------------------------------------------------------*/

    ener = (ener - ener_code) >> 1;
		exp = (Word16)(ener >> 16);
		frac = (Word16)((ener >> 1) - ((Word32)exp << 15));

    gcode0 = (Word16)Pow2 (exp, frac);  /* predicted gain */

    *gain_code = (Word16)((Word32)(qua_gain_code[index] * gcode0) >> 11);

    /*-------------------------------------------------------------------*
     *  update table of past quantized energies                           *
     *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           *
     *  past_qua_en      = 20 * Log10(qua_gain_code) / constant           *
     *                   = Log2(qua_gain_code)                            *
     *                                           constant = 20*Log10(2)   *
     *-------------------------------------------------------------------*/

    for (i = 3; i > 0; i--)
    {
        past_qua_en[i] = past_qua_en[i - 1];
    }
    Log2 (qua_gain_code[index], &exp, &frac);

    past_qua_en[0] =(frac >> 5);
    past_qua_en[0] += (exp-11) << 10;
}
