/*
***********************************************************************
*
* Copyright (C) 1999 Ericsson Radio Systems AB
* 
* The Association of Radio Industries and Businesses (ARIB) has the
* licence to reproduce, distribute and publish the Bit Exact C Code
* programs in this CD-ROM together with the other parts of the RCR
* STD-27 (Personal Digital Cellular Telecommunication System)
* specification.
* 
* Ericsson has declared Option 1 (royalty-free licence) to ARIB for
* its Essential Industrial Property Rights related to the ACELP
* specification and the Bit Exact C Code programs. The royalty-free
* licence is limited to the development and manufacturing of
* products complying with the RCR STD-27H specification (or
* subsequent versions) for sale and use in Japan. Copying of the
* program(s) for any other purpose requires the prior written
* permission from Ericsson.
* 
* The user of the Bit Exact C Code hereby acknowledges the
* following:
* 
* 1.  Ericsson does not provide the user with any warranties,
*     whether express or implied, including but not limited to
*     warranties of merchantability and fitness for a particular
*     purpose relating to the Bit Exact C Code.
* 
* 2.  Ericsson shall have no responsibility whatsoever to indemnify
*     the user against actions or claims for infringement of any
*     intellectual property rights, e.g. patents, copyrights,
*     registered designs or any other rights, by reason of the
*     user's use of the Bit Exact C Code.
* 
***********************************************************************
*/

#include <stdlib.h>
#include <stdio.h>
#include "typedef.h"
#include "basic_op.h"
#include "cnst.h"

#define L_CBGAINHIST 7


/*
**************************************************************************
*
*  Function    : Cb_gain_average
*  Purpose     : 
*  Returns     : The mix cb gain 
*
**************************************************************************
*/
Word16 Cb_gain_average (    
   Word16 gain_code,         /* i   : CB gain                          Q1  */ 
   Word16 lsp[],             /* i   : The LSP for the current frame    Q15 */ 
   Word16 lspAver[]          /* i   : The average of LSP for 8 frames  Q15 */     
   )
{
   /*---------------------------------------------------------*
    * Compute mixed cb gain, used to make cb gain more        *
    * smooth in background noise.                             *
    *---------------------------------------------------------*/
   Word16 i;
   Word16 cbGainMix, diff, tmp_diff, bgMix, cbGainMean;
   Word32 L_sum;
   Word16 tmp[M], tmp1, tmp2, shift1, shift2, shift;

   /* history vector of past synthesis speech energy */
   static Word16 cbGainHistory[L_CBGAINHIST] = {0, 0, 0, 0, 0, 0, 0};
   static Word16 hangVar = 0;       /* counter; */
   static Word16 hangCount = 0;     /* counter; */

   /* set correct cbGainMix */
   cbGainMix = gain_code;              
   
   /*-------------------------------------------------------*
    *   Store list of CB gain needed in the CB gain         *
    *   averaging                                           *
    *-------------------------------------------------------*/
   for (i = 0; i < (L_CBGAINHIST-1); i++)
   {
      cbGainHistory[i] = cbGainHistory[i+1];    
   }
   cbGainHistory[L_CBGAINHIST-1] = gain_code;       
   
   /* compute lsp difference */
   for (i = 0; i < M; i++) {
      tmp1 = abs_s(sub(lspAver[i], lsp[i]));  /* Q15       */
      shift1 = norm_s(tmp1) - 1;            /* Qn        */
      tmp1 = shl(tmp1, shift1);             /* Q15+Qn    */
      shift2 = norm_s(lspAver[i]);          /* Qm        */
      tmp2 = shl(lspAver[i], shift2);       /* Q15+Qm    */
      tmp[i] = div_s(tmp1, tmp2);           /* Q15+(Q15+Qn)-(Q15+Qm) */
      shift = sub(add(2, shift1), shift2);
      
      if (shift >= 0)
      {
         tmp[i] = shr(tmp[i], shift);          /* Q15+Qn-Qm-Qx=Q13 */
      }
      else
      {
         tmp[i] = shl(tmp[i], negate(shift));  /* Q15+Qn-Qm-Qx=Q13 */
      }
   }
   
   diff = tmp[0];                                   
   for (i = 1; i < M; i++) { 
      diff = add(diff, tmp[i]);       /* Q13 */
   }   

   /* Compute hangover */
   
   if (sub(diff, 5325) > 0)  /* 0.65 in Q11 */
   {
      hangVar = add(hangVar, 1);
   }
   else
   {
      hangVar = 0;     
   }

    
   if (sub(hangVar, 10) > 0)
   {
      hangCount = 0;  /* Speech period, reset hangover variable */ 
   }

   /* Compute mix constant (bgMix) */   
   bgMix = 8192;    /* 1 in Q13 */     
   
   /* bgMix = min(0.25, max(0.0, diff-0.40)) / 0.25; */        
   tmp_diff = sub(diff, 3277); /* 0.4 in Q13 */

   /* max(0.0, diff-0.40) */
   
   if (tmp_diff > 0)
   {
      tmp1 = tmp_diff;        
   }
   else
   {
      tmp1 = 0;         
   }

   /* min(0.25, tmp1) */
   
   if (sub(2048, tmp1) < 0)
   {
      bgMix = 8192;     
   }
   else
   {
      bgMix = shl(tmp1, 2);
   }

    
   if ((sub(hangCount, 40) < 0) || (sub(diff, 5325) > 0)) /* 0.65 in Q13 */
   {
      bgMix = 8192;  /* disable mix if too short time since */ 
   }

   /* Smoothen the cb gain trajectory  */
   /* smoothing depends on mix constant bgMix */
   L_sum = L_mult(6554, cbGainHistory[2]);     /* 0.2 in Q15; L_sum in Q17 */   
   for (i = 3; i < L_CBGAINHIST; i++)
   {
      L_sum = L_mac(L_sum, 6554, cbGainHistory[i]);
   }
   cbGainMean = round(L_sum);                      /* Q1 */
   
   /* cbGainMix = bgMix*cbGainMix + (1-bgMix)*cbGainMean; */
   L_sum = L_mult(bgMix, cbGainMix);               /* L_sum in Q15 */
   L_sum = L_mac(L_sum, 8192, cbGainMean);         
   L_sum = L_msu(L_sum, bgMix, cbGainMean);
   cbGainMix = round(L_shl(L_sum, 2));             /* Q1 */

   hangCount = add(hangCount, 1);
   return cbGainMix;
}
