view libtwamr/vad1.c @ 553:ebcf414b7d99

doc/TFO-transform: describe details for FRv1, both modes
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 07 Oct 2024 08:24:24 +0000
parents 8847c1740e78
children
line wrap: on
line source

/*
*****************************************************************************
**-------------------------------------------------------------------------**
**                                                                         **
**     GSM AMR-NB speech codec   R98   Version 7.6.0   December 12, 2001       **
**                               R99   Version 3.3.0                       **
**                               REL-4 Version 4.1.0                       **
**                                                                         **
**-------------------------------------------------------------------------**
*****************************************************************************
*
*      File             : vad1.c
*      Purpose          : Voice Activity Detection (VAD) for AMR (option 1)
*
*****************************************************************************
*/

/*
*****************************************************************************
*                         MODULE INCLUDE FILE AND VERSION ID
*****************************************************************************
*/
#include "namespace.h"
#include "vad1.h"
 
/*
*****************************************************************************
*                         INCLUDE FILES
*****************************************************************************
*/
#include "typedef.h"
#include "basic_op.h"
#include "no_count.h"
#include "oper_32b.h"
#include "cnst_vad.h"

/*
*****************************************************************************
*                         LOCAL VARIABLES AND TABLES
*****************************************************************************
*/

/*
********************************************************************************
*                         PRIVATE PROGRAM CODE
********************************************************************************
*/
/****************************************************************************
 *
 *     Function     : first_filter_stage
 *     Purpose      : Scale input down by one bit. Calculate 5th order
 *                    half-band lowpass/highpass filter pair with
 *                    decimation.
 *
 ***************************************************************************/
static void first_filter_stage(Word16 in[],  /* i   : input signal                  */
                               Word16 out[], /* o   : output values, every other    */
                                             /*       output is low-pass part and   */
                                             /*       other is high-pass part every */
                               Word16 data[] /* i/o : filter memory                 */
                               )
{
  Word16 temp0, temp1, temp2, temp3, i;
  Word16 data0, data1;

  data0 = data[0];                                          move16 ();
  data1 = data[1];                                          move16 ();
 
  for (i = 0; i < FRAME_LEN/4; i++)
  {
     temp0 = sub(shr(in[4*i+0], 2), mult(COEFF5_1, data0));
     temp1 = add(data0, mult(COEFF5_1, temp0));
     
     temp3 = sub(shr(in[4*i+1], 2), mult(COEFF5_2, data1));
     temp2 = add(data1, mult(COEFF5_2, temp3));
     
     out[4*i+0] = add(temp1, temp2);                        move16 ();
     out[4*i+1] = sub(temp1, temp2);                        move16 ();
     
     data0 = sub(shr(in[4*i+2], 2), mult(COEFF5_1, temp0));
     temp1 = add(temp0, mult(COEFF5_1, data0));
     
     data1 = sub(shr(in[4*i+3], 2), mult(COEFF5_2, temp3));
     temp2 = add(temp3, mult(COEFF5_2, data1));
     
     out[4*i+2] = add(temp1, temp2);                       move16 ();
     out[4*i+3] = sub(temp1, temp2);                       move16 ();
  } 
  
  data[0] = data0;                                         move16 ();
  data[1] = data1;                                         move16 ();
}

/****************************************************************************
 *
 *     Function     : filter5
 *     Purpose      : Fifth-order half-band lowpass/highpass filter pair with
 *                    decimation.
 *
 ***************************************************************************/
static void filter5(Word16 *in0,    /* i/o : input values; output low-pass part  */
                    Word16 *in1,    /* i/o : input values; output high-pass part */
                    Word16 data[]   /* i/o : updated filter memory               */
                    )
{
  Word16 temp0, temp1, temp2;

  temp0 = sub(*in0, mult(COEFF5_1, data[0]));
  temp1 = add(data[0], mult(COEFF5_1, temp0));
  data[0] = temp0;                                move16 ();

  temp0 = sub(*in1, mult(COEFF5_2, data[1]));
  temp2 = add(data[1], mult(COEFF5_2, temp0));
  data[1] = temp0;                                move16 ();

  *in0 = shr(add(temp1, temp2), 1);               move16 ();
  *in1 = shr(sub(temp1, temp2), 1);               move16 ();
}

/****************************************************************************
 *
 *     Function     : filter3
 *     Purpose      : Third-order half-band lowpass/highpass filter pair with
 *                    decimation.
 *     Return value : 
 *
 ***************************************************************************/
static void filter3(Word16 *in0,   /* i/o : input values; output low-pass part  */ 
                    Word16 *in1,   /* i/o : input values; output high-pass part */
                    Word16 *data   /* i/o : updated filter memory               */
                    )
{
  Word16 temp1, temp2;

  temp1 = sub(*in1, mult(COEFF3, *data));
  temp2 = add(*data, mult(COEFF3, temp1));
  *data = temp1;                              move16 ();

  *in1 = shr(sub(*in0, temp2), 1);            move16 ();
  *in0 = shr(add(*in0, temp2), 1);            move16 ();
}

/****************************************************************************
 *
 *     Function     : level_calculation
 *     Purpose      : Calculate signal level in a sub-band. Level is calculated
 *                    by summing absolute values of the input data.
 *     Return value : signal level
 *
 ***************************************************************************/
static Word16 level_calculation(
    Word16 data[],     /* i   : signal buffer                                    */
    Word16 *sub_level, /* i   : level calculate at the end of the previous frame */
                       /* o   : level of signal calculated from the last         */
                       /*       (count2 - count1) samples                        */
    Word16 count1,     /* i   : number of samples to be counted                  */
    Word16 count2,     /* i   : number of samples to be counted                  */
    Word16 ind_m,      /* i   : step size for the index of the data buffer       */
    Word16 ind_a,      /* i   : starting index of the data buffer                */
    Word16 scale       /* i   : scaling for the level calculation                */
    )
{
  Word32 l_temp1, l_temp2;
  Word16 level, i;

  l_temp1 = 0L;                                           move32 ();
  for (i = count1; i < count2; i++)
  {
     l_temp1 = L_mac(l_temp1, 1, abs_s(data[ind_m*i+ind_a]));
  }
  
  l_temp2 = L_add(l_temp1, L_shl(*sub_level, sub(16, scale)));
  *sub_level = extract_h(L_shl(l_temp1, scale));
  
  for (i = 0; i < count1; i++)
  {
     l_temp2 = L_mac(l_temp2, 1, abs_s(data[ind_m*i+ind_a]));
  }
  level = extract_h(L_shl(l_temp2, scale));
  
  return level;
}

/****************************************************************************
 *
 *     Function     : filter_bank
 *     Purpose      : Divides input signal into 9-bands and calculas level of
 *                    the signal in each band 
 *
 ***************************************************************************/
static void filter_bank(vadState1 *st,  /* i/o : State struct               */
                        Word16 in[],   /* i   : input frame                */
                        Word16 level[] /* 0   : signal levels at each band */
                        )
{
  Word16 i;
  Word16 tmp_buf[FRAME_LEN];

  /* calculate the filter bank */

  first_filter_stage(in, tmp_buf, st->a_data5[0]);
  
  for (i = 0; i < FRAME_LEN/4; i++)
  {
     filter5(&tmp_buf[4*i], &tmp_buf[4*i+2], st->a_data5[1]);
     filter5(&tmp_buf[4*i+1], &tmp_buf[4*i+3], st->a_data5[2]);
  }
  for (i = 0; i < FRAME_LEN/8; i++)
  {
     filter3(&tmp_buf[8*i+0], &tmp_buf[8*i+4], &st->a_data3[0]);
     filter3(&tmp_buf[8*i+2], &tmp_buf[8*i+6], &st->a_data3[1]);
     filter3(&tmp_buf[8*i+3], &tmp_buf[8*i+7], &st->a_data3[4]);
  }
  
  for (i = 0; i < FRAME_LEN/16; i++)
  {
     filter3(&tmp_buf[16*i+0], &tmp_buf[16*i+8], &st->a_data3[2]);
     filter3(&tmp_buf[16*i+4], &tmp_buf[16*i+12], &st->a_data3[3]);
  }
  
  /* calculate levels in each frequency band */
  
  /* 3000 - 4000 Hz*/
  level[8] = level_calculation(tmp_buf, &st->sub_level[8], FRAME_LEN/4-8,
                               FRAME_LEN/4, 4, 1, 15);
  move16 ();
  /* 2500 - 3000 Hz*/  
  level[7] = level_calculation(tmp_buf, &st->sub_level[7], FRAME_LEN/8-4,
                               FRAME_LEN/8, 8, 7, 16);
  move16 ();
  /* 2000 - 2500 Hz*/
  level[6] = level_calculation(tmp_buf, &st->sub_level[6], FRAME_LEN/8-4,
                               FRAME_LEN/8, 8, 3, 16);
  move16 ();
  /* 1500 - 2000 Hz*/
  level[5] = level_calculation(tmp_buf, &st->sub_level[5], FRAME_LEN/8-4,
                               FRAME_LEN/8, 8, 2, 16);
  move16 ();
  /* 1000 - 1500 Hz*/
  level[4] = level_calculation(tmp_buf, &st->sub_level[4], FRAME_LEN/8-4,
                               FRAME_LEN/8, 8, 6, 16);
  move16 ();
  /* 750 - 1000 Hz*/
  level[3] = level_calculation(tmp_buf, &st->sub_level[3], FRAME_LEN/16-2,
                               FRAME_LEN/16, 16, 4, 16);
  move16 ();
  /* 500 - 750 Hz*/
  level[2] = level_calculation(tmp_buf, &st->sub_level[2], FRAME_LEN/16-2,
                               FRAME_LEN/16, 16, 12, 16);
  move16 ();
  /* 250 - 500 Hz*/
  level[1] = level_calculation(tmp_buf, &st->sub_level[1], FRAME_LEN/16-2,
                               FRAME_LEN/16, 16, 8, 16);
  move16 ();
  /* 0 - 250 Hz*/
  level[0] = level_calculation(tmp_buf, &st->sub_level[0], FRAME_LEN/16-2,
                               FRAME_LEN/16, 16, 0, 16);
  move16 ();
}

/****************************************************************************
 *
 *     Function   : update_cntrl
 *     Purpose    : Control update of the background noise estimate.
 *     Inputs     : pitch:      flags for pitch detection
 *                  stat_count: stationary counter
 *                  tone:       flags indicating presence of a tone
 *                  complex:      flags for complex  detection
 *                  vadreg:     intermediate VAD flags
 *     Output     : stat_count: stationary counter
 *
 ***************************************************************************/
static void update_cntrl(vadState1 *st,  /* i/o : State struct                       */
                         Word16 level[] /* i   : sub-band levels of the input frame */
                         )
{
  Word16 i, temp, stat_rat, exp;
  Word16 num, denom;
  Word16 alpha; 

  /* handle highband complex signal input  separately       */
  /* if ther has been highband correlation for some time    */
  /* make sure that the VAD update speed is low for a while */
  test ();
  if (st->complex_warning != 0)
  {
     test ();
     if (sub(st->stat_count, CAD_MIN_STAT_COUNT) < 0)
     {
        st->stat_count = CAD_MIN_STAT_COUNT;              move16 ();    
     }
  }
  /* NB stat_count is allowed to be decreased by one below again  */
  /* deadlock in speech is not possible unless the signal is very */
  /* complex and need a high rate                                 */

  /* if fullband pitch or tone have been detected for a while, initialize stat_count */
  logic16 (); test (); logic16 (); test ();
  if ((sub((st->pitch & 0x6000), 0x6000) == 0) ||
      (sub((st->tone & 0x7c00), 0x7c00) == 0))
  {
     st->stat_count = STAT_COUNT;                          move16 ();  
  }
  else
  {
     /* if 8 last vad-decisions have been "0", reinitialize stat_count */
     logic16 (); test ();
     if ((st->vadreg & 0x7f80) == 0) 
     { 
        st->stat_count = STAT_COUNT;                       move16 ();
     }
     else
     {
        stat_rat = 0;                                      move16 ();
        for (i = 0; i < COMPLEN; i++)
        {
           test ();
           if (sub(level[i], st->ave_level[i]) > 0)
           {
              num = level[i];                              move16 ();
              denom = st->ave_level[i];                    move16 ();
           }
           else
           {
              num = st->ave_level[i];                      move16 ();
              denom = level[i];                            move16 ();
           }
           /* Limit nimimum value of num and denom to STAT_THR_LEVEL */
           test ();
           if (sub(num, STAT_THR_LEVEL) < 0)
           {
              num = STAT_THR_LEVEL;                        move16 ();
           }
           test ();
           if (sub(denom, STAT_THR_LEVEL) < 0)
           {
              denom = STAT_THR_LEVEL;                      move16 ();
           }
           
           exp = norm_s(denom);
           denom = shl(denom, exp);
           
           /* stat_rat = num/denom * 64 */
           temp = div_s(shr(num, 1), denom);
           stat_rat = add(stat_rat, shr(temp, sub(8, exp)));
        }
        
        /* compare stat_rat with a threshold and update stat_count */
        test ();
        if (sub(stat_rat, STAT_THR) > 0)
        {
           st->stat_count = STAT_COUNT;                    move16 ();
        }
        else
        {
           logic16 ();test ();
           if ((st->vadreg & 0x4000) != 0)
           {
              test ();
              if (st->stat_count != 0)
              {
                 st->stat_count = sub(st->stat_count, 1);  move16 ();
              }
           }
        }
     }
  }
  
  /* Update average amplitude estimate for stationarity estimation */
  alpha = ALPHA4;                                          move16 ();
  test ();
  if (sub(st->stat_count, STAT_COUNT) == 0) 
  {
     alpha = 32767;                                        move16 ();
  }
  else if ((st->vadreg & 0x4000) == 0) 
  {
     logic16 (); test ();
     alpha = ALPHA5;                                       move16 ();
  }
  
  for (i = 0; i < COMPLEN; i++)
  {
     st->ave_level[i] = add(st->ave_level[i],
                            mult_r(alpha, sub(level[i], st->ave_level[i])));
     move16 ();
  }  
}

/****************************************************************************
 *
 *     Function     : hangover_addition
 *     Purpose      : Add hangover for complex signal or after speech bursts
 *     Inputs       : burst_count:  counter for the length of speech bursts
 *                    hang_count:   hangover counter
 *                    vadreg:       intermediate VAD decision
 *     Outputs      : burst_count:  counter for the length of speech bursts
 *                    hang_count:   hangover counter
 *     Return value : VAD_flag indicating final VAD decision
 *
 ***************************************************************************/
static Word16 hangover_addition(
              vadState1 *st,       /* i/o : State struct                     */
              Word16 noise_level, /* i   : average level of the noise       */
                                  /*       estimates                        */
              Word16 low_power    /* i   : flag power of the input frame    */
              )
{
   Word16 hang_len, burst_len;
   
   /* 
      Calculate burst_len and hang_len
      burst_len: number of consecutive intermediate vad flags with "1"-decision
                 required for hangover addition
      hang_len:  length of the hangover
      */

   test ();
   if (sub(noise_level, HANG_NOISE_THR) > 0)
   {
      burst_len = BURST_LEN_HIGH_NOISE;                           move16 ();
      hang_len = HANG_LEN_HIGH_NOISE;                             move16 ();
   }
   else
   {
      burst_len = BURST_LEN_LOW_NOISE;                            move16 ();
      hang_len = HANG_LEN_LOW_NOISE;                              move16 ();
   }
   
   /* if the input power (pow_sum) is lower than a threshold, clear
      counters and set VAD_flag to "0"  "fast exit"                 */
   test ();
   if (low_power != 0)
   {
      st->burst_count = 0;                                        move16 ();
      st->hang_count = 0;                                         move16 ();
      st->complex_hang_count = 0;                                 move16 ();
      st->complex_hang_timer = 0;                                 move16 ();
      return 0;
   }
   
   test ();
   if (sub(st->complex_hang_timer, CVAD_HANG_LIMIT) > 0)
   {
      test ();
      if (sub(st->complex_hang_count, CVAD_HANG_LENGTH) < 0)
      {
         st->complex_hang_count = CVAD_HANG_LENGTH;               move16 ();
      }      
   }
   
   /* long time very complex signal override VAD output function */
   test ();
   if (st->complex_hang_count != 0)
   {
      st->burst_count = BURST_LEN_HIGH_NOISE;                     move16 ();
      st->complex_hang_count = sub(st->complex_hang_count, 1);    move16 ();
      return 1; 
   }
   else
   {
      /* let hp_corr work in from a noise_period indicated by the VAD */
      test (); test (); logic16 ();
      if (((st->vadreg & 0x3ff0) == 0) &&
          (sub(st->corr_hp_fast, CVAD_THRESH_IN_NOISE) > 0))
      {
         return 1;
      }  
   }

   /* update the counters (hang_count, burst_count) */
   logic16 (); test ();
   if ((st->vadreg & 0x4000) != 0)
   {
      st->burst_count = add(st->burst_count, 1);                  move16 ();
      test ();
      if (sub(st->burst_count, burst_len) >= 0)
      {
         st->hang_count = hang_len;                               move16 ();
      }
      return 1;
   }
   else
   {
      st->burst_count = 0;                                        move16 ();
      test ();
      if (st->hang_count > 0)
      {
         st->hang_count = sub(st->hang_count, 1);                 move16 ();
         return 1;
      }
   }
   return 0;
}

/****************************************************************************
 *
 *     Function   : noise_estimate_update
 *     Purpose    : Update of background noise estimate
 *     Inputs     : bckr_est:   background noise estimate
 *                  pitch:      flags for pitch detection
 *                  stat_count: stationary counter
 *     Outputs    : bckr_est:   background noise estimate
 *
 ***************************************************************************/
static void noise_estimate_update(
                  vadState1 *st,    /* i/o : State struct                       */
                  Word16 level[]   /* i   : sub-band levels of the input frame */
                  )
{
   Word16 i, alpha_up, alpha_down, bckr_add;
   
   /* Control update of bckr_est[] */
   update_cntrl(st, level);
   
   /* Choose update speed */
   bckr_add = 2;                                           move16 ();
   
   logic16 (); test (); logic16 (); test (); test ();
   if (((0x7800 & st->vadreg) == 0) && 
       ((st->pitch & 0x7800) == 0) 
       &&  (st->complex_hang_count == 0))
   {
      alpha_up = ALPHA_UP1;                                move16 ();
      alpha_down = ALPHA_DOWN1;                            move16 ();
   }
   else 
   {
      test (); test ();
      if ((st->stat_count == 0) 
          && (st->complex_hang_count == 0))
      {
         alpha_up = ALPHA_UP2;                             move16 ();
         alpha_down = ALPHA_DOWN2;                         move16 ();
      }
      else
      {
         alpha_up = 0;                                     move16 ();
         alpha_down = ALPHA3;                              move16 ();
         bckr_add = 0;                                     move16 ();
      }
   }
   
   /* Update noise estimate (bckr_est) */
   for (i = 0; i < COMPLEN; i++)
   {
      Word16 temp;
      temp = sub(st->old_level[i], st->bckr_est[i]);
      
      test ();
      if (temp < 0)
      { /* update downwards*/
         st->bckr_est[i] = add(-2, add(st->bckr_est[i], mult_r(alpha_down, temp)));
         move16 ();
         
         /* limit minimum value of the noise estimate to NOISE_MIN */
         test ();
         if (sub(st->bckr_est[i], NOISE_MIN) < 0)
         {
            st->bckr_est[i] = NOISE_MIN;                  move16 ();
         }
      }
      else
      { /* update upwards */
         st->bckr_est[i] = add(bckr_add, add(st->bckr_est[i], mult_r(alpha_up, temp)));
         move16 ();
         
         /* limit maximum value of the noise estimate to NOISE_MAX */
         test ();
         if (sub(st->bckr_est[i], NOISE_MAX) > 0)
         {
            st->bckr_est[i] = NOISE_MAX;                  move16 ();
         }
      }
   }
   
   /* Update signal levels of the previous frame (old_level) */
   for(i = 0; i < COMPLEN; i++)
   {
      st->old_level[i] = level[i];                        move16 ();
   }
}

/****************************************************************************
 *
 *     Function   : complex_estimate_adapt
 *     Purpose    : Update/adapt of complex signal estimate
 *     Inputs     : low_power:   low signal power flag 
 *     Outputs    : st->corr_hp_fast:   long term complex signal estimate
 *
 ***************************************************************************/
static void complex_estimate_adapt(
         vadState1 *st,       /* i/o : VAD state struct                       */
         Word16 low_power    /* i   : very low level flag of the input frame */
         )
{
   Word16 alpha;            /* Q15 */
   Word32 L_tmp;            /* Q31 */


   /* adapt speed on own state */
   test ();
   if (sub(st->best_corr_hp, st->corr_hp_fast) < 0) /* decrease */
   {
      test ();
      if (sub(st->corr_hp_fast, CVAD_THRESH_ADAPT_HIGH) < 0)
      {  /* low state  */
         alpha = CVAD_ADAPT_FAST;                          move16(); 
      }  
      else 
      {  /* high state */
         alpha = CVAD_ADAPT_REALLY_FAST;                   move16();   
      }      
   }
   else  /* increase */ 
   {
      test ();
      if (sub(st->corr_hp_fast, CVAD_THRESH_ADAPT_HIGH) < 0)
      {  
         alpha = CVAD_ADAPT_FAST;                          move16(); 
      }  
      else 
      {  
         alpha = CVAD_ADAPT_SLOW;                          move16();
      }      
   }

   L_tmp = L_deposit_h(st->corr_hp_fast);
   L_tmp = L_msu(L_tmp, alpha, st->corr_hp_fast);
   L_tmp = L_mac(L_tmp, alpha, st->best_corr_hp);
   st->corr_hp_fast = round(L_tmp);           /* Q15 */    move16();   

   test ();
   if (sub(st->corr_hp_fast, CVAD_MIN_CORR) <  0)
   {
      st->corr_hp_fast = CVAD_MIN_CORR;                    move16();
   }

   test ();
   if (low_power != 0)
   {
      st->corr_hp_fast = CVAD_MIN_CORR;                    move16();
   }   
}

/****************************************************************************
 *
 *     Function     : complex_vad
 *     Purpose      : complex background decision
 *     Return value : the complex background decision
 *
 ***************************************************************************/
static Word16 complex_vad(vadState1 *st,    /* i/o : VAD state struct              */
                          Word16 low_power /* i   : flag power of the input frame */
                          )
{
   st->complex_high = shr(st->complex_high, 1);                      move16 ();
   st->complex_low = shr(st->complex_low, 1);                        move16 ();

   test ();
   if (low_power == 0)
   {
      test ();
      if (sub(st->corr_hp_fast, CVAD_THRESH_ADAPT_HIGH) > 0)
      {
         st->complex_high = st->complex_high | 0x4000;   logic16 (); move16 ();
      }
      
      test ();
      if (sub(st->corr_hp_fast, CVAD_THRESH_ADAPT_LOW) > 0 )
      {
         st->complex_low = st->complex_low | 0x4000;     logic16 (); move16 ();
      }
   }

   test ();
   if (sub(st->corr_hp_fast, CVAD_THRESH_HANG) > 0)
   {
      st->complex_hang_timer = add(st->complex_hang_timer, 1);       move16 ();
   }
   else
   {
      st->complex_hang_timer =  0;                                   move16 ();
   }               
   
   test (); logic16 (); test (); logic16 ();
   return ((sub((st->complex_high & 0x7f80), 0x7f80) == 0) ||
           (sub((st->complex_low & 0x7fff), 0x7fff) == 0));
}

/****************************************************************************
 *
 *     Function     : vad_decision
 *     Purpose      : Calculates VAD_flag
 *     Inputs       : bckr_est:    background noise estimate
 *                    vadreg:      intermediate VAD flags
 *     Outputs      : noise_level: average level of the noise estimates
 *                    vadreg:      intermediate VAD flags
 *     Return value : VAD_flag
 *
 ***************************************************************************/
static Word16 vad_decision(
             vadState1 *st,          /* i/o : State struct                       */
             Word16 level[COMPLEN], /* i   : sub-band levels of the input frame */
             Word32 pow_sum         /* i   : power of the input frame           */
             )
{
   Word16 i;
   Word16 snr_sum;
   Word32 L_temp;
   Word16 vad_thr, temp, noise_level;
   Word16 low_power_flag;
   
   /* 
      Calculate squared sum of the input levels (level)
      divided by the background noise components (bckr_est).
      */
   L_temp = 0;                                            move32();
   for (i = 0; i < COMPLEN; i++)
   {
      Word16 exp;
      
      exp = norm_s(st->bckr_est[i]);
      temp = shl(st->bckr_est[i], exp);
      temp = div_s(shr(level[i], 1), temp);
      temp = shl(temp, sub(exp, UNIRSHFT-1));
      L_temp = L_mac(L_temp, temp, temp);
   }
   snr_sum = extract_h(L_shl(L_temp, 6));
   snr_sum = mult(snr_sum, INV_COMPLEN);

   /* Calculate average level of estimated background noise */
   L_temp = 0;                                            move32();
   for (i = 0; i < COMPLEN; i++)
   {
      L_temp = L_add(L_temp, st->bckr_est[i]);
   }
   
   noise_level = extract_h(L_shl(L_temp, 13));
   
   /* Calculate VAD threshold */
   vad_thr = add(mult(VAD_SLOPE, sub(noise_level, VAD_P1)), VAD_THR_HIGH);
   
   test ();
   if (sub(vad_thr, VAD_THR_LOW) < 0)
   {
      vad_thr = VAD_THR_LOW;                              move16 ();
   }
   
   /* Shift VAD decision register */
   st->vadreg = shr(st->vadreg, 1);                       move16 ();
   
   /* Make intermediate VAD decision */
   test ();
   if (sub(snr_sum, vad_thr) > 0)
   {
      st->vadreg = st->vadreg | 0x4000;       logic16 (); move16 ();
   }
   /* primary vad decsion made */
   
   /* check if the input power (pow_sum) is lower than a threshold" */
   test ();
   if (L_sub(pow_sum, VAD_POW_LOW) < 0)
   {
      low_power_flag = 1;                                 move16 ();
   }
   else
   {
      low_power_flag = 0;                                 move16 ();
   }
   
   /* update complex signal estimate st->corr_hp_fast and hangover reset timer using */
   /* low_power_flag and corr_hp_fast  and various adaptation speeds                 */
   complex_estimate_adapt(st, low_power_flag);

   /* check multiple thresholds of the st->corr_hp_fast value */
   st->complex_warning = complex_vad(st, low_power_flag); move16();    

   /* Update speech subband vad background noise estimates */
   noise_estimate_update(st, level);
     
   /*  Add speech and complex hangover and return speech VAD_flag */
   /*  long term complex hangover may be added */
   st->speech_vad_decision = hangover_addition(st, noise_level, low_power_flag);
   move16 ();
   
   return (st->speech_vad_decision);
}

/*
*****************************************************************************
*                         PUBLIC PROGRAM CODE
*****************************************************************************
*/
 
/*************************************************************************
*
*  Function:   vad1_reset
*  Purpose:    Initializes state memory to zero
*
**************************************************************************
*/
void vad1_reset (vadState1 *state)
{
   Word16 i, j;
   
   /* Initialize pitch detection variables */
   state->oldlag_count = 0;
   state->oldlag = 0;         
   state->pitch = 0;
   state->tone = 0;            

   state->complex_high = 0;            
   state->complex_low = 0;            
   state->complex_hang_timer = 0;

   state->vadreg = 0;         

   state->stat_count = 0;    
   state->burst_count = 0;    
   state->hang_count = 0;     
   state->complex_hang_count = 0;     
   
   /* initialize memory used by the filter bank */
   for (i = 0; i < 3; i++)
   {
      for (j = 0; j < 2; j++) 
      {
         state->a_data5[i][j] = 0;  
      }
   }
   
   for (i = 0; i < 5; i++)
   {
      state->a_data3[i] = 0;        
   }
   
   /* initialize the rest of the memory */
   for (i = 0; i < COMPLEN; i++)
   {
      state->bckr_est[i] = NOISE_INIT;  
      state->old_level[i] = NOISE_INIT; 
      state->ave_level[i] = NOISE_INIT; 
      state->sub_level[i] = 0;          
   }
   
   state->best_corr_hp = CVAD_LOWPOW_RESET; 

   state->speech_vad_decision = 0;
   state->complex_warning = 0;
   state->sp_burst_count = 0;        

   state->corr_hp_fast = CVAD_LOWPOW_RESET;
}

/****************************************************************************
 *
 *     Function     : vad_complex_detection_update
 *     Purpose      : update vad->bestCorr_hp  complex signal feature state 
 *
 ***************************************************************************/
void vad_complex_detection_update (vadState1 *st,       /* i/o : State struct */
                                   Word16 best_corr_hp /* i   : best Corr    */
                                   )
{
   st->best_corr_hp = best_corr_hp;         move16();
}

/****************************************************************************
 *
 *     Function     : vad_tone_detection
 *     Purpose      : Set tone flag if pitch gain is high. This is used to detect
 *                    signaling tones and other signals with high pitch gain.
 *     Inputs       : tone: flags indicating presence of a tone
 *     Outputs      : tone: flags indicating presence of a tone
 *
 ***************************************************************************/
void vad_tone_detection (vadState1 *st,  /* i/o : State struct            */
                         Word32 t0,     /* i   : autocorrelation maxima  */
                         Word32 t1      /* i   : energy                  */
                         )
{
   Word16 temp;
   /* 
      if (t0 > TONE_THR * t1)
      set tone flag
      */
   temp = round(t1);
   
   test (); test ();
   if ((temp > 0) && (L_msu(t0, temp, TONE_THR) > 0))
   {
      st->tone = st->tone | 0x4000;              logic16 (); move16 ();
   }
}

/****************************************************************************
 *
 *     Function     : vad_tone_detection_update
 *     Purpose      : Update the tone flag register. Tone flags are shifted right
 *                    by one bit. This function should be called from the speech
 *                    encoder before call to Vad_tone_detection() function.
 *
 ***************************************************************************/
void vad_tone_detection_update (
                vadState1 *st,              /* i/o : State struct              */
                Word16 one_lag_per_frame   /* i   : 1 if one open-loop lag is
                                              calculated per each frame,
                                              otherwise 0                     */
                )
{
   /* Shift tone flags right by one bit */
   st->tone = shr(st->tone, 1);                move16 ();
   
   /* If open-loop lag is calculated only once in each frame, do extra update
      and assume that the other tone flag of the frame is one. */
   if (one_lag_per_frame != 0)
   {
      st->tone = shr(st->tone, 1);            
      st->tone = st->tone | 0x2000;            logic16 (); move16 ();
   }
}

/****************************************************************************
 *
 *     Function     : vad_pitch_detection
 *     Purpose      : Test whether signal contains pitch or other periodic
 *                    component.
 *     Return value : Boolean voiced / unvoiced decision in state variable 
 *
 ***************************************************************************/
void vad_pitch_detection (vadState1 *st,   /* i/o : State struct                  */
                          Word16 T_op[]   /* i   : speech encoder open loop lags */
                          )
{
   Word16 lagcount, i;
   
   lagcount = 0;               move16 ();
   
   for (i = 0; i < 2; i++)
   {
      test ();
      if (sub (abs_s (sub (st->oldlag, T_op[i])), LTHRESH) < 0)
      {
         lagcount = add (lagcount, 1);
      }
      
      /* Save the current LTP lag */
      st->oldlag = T_op[i];       move16 ();
   }
   
   /* Make pitch decision.
      Save flag of the pitch detection to the variable pitch.
      */
   st->pitch = shr(st->pitch, 1); move16();
   
   test ();
   if (sub ( add (st->oldlag_count, lagcount), NTHRESH) >= 0)
   {
      st->pitch = st->pitch | 0x4000; logic16(); move16();
   }
   
   /* Update oldlagcount */
   st->oldlag_count = lagcount;     move16 ();
}

/****************************************************************************
 *
 *     Function     : vad
 *     Purpose      : Main program for Voice Activity Detection (VAD) for AMR 
 *     Return value : VAD Decision, 1 = speech, 0 = noise
 *
 ***************************************************************************/
Word16 vad1(vadState1 *st,      /* i/o : State struct                 */
            Word16 in_buf[]     /* i   : samples of the input frame   */
           )
{
   Word16 level[COMPLEN];
   Word32 pow_sum;
   Word16 i;
   
   /* Calculate power of the input frame. */
   pow_sum = 0L;                                     move32 ();

   for (i = 0; i < FRAME_LEN; i++)
   {  
      pow_sum = L_mac(pow_sum, in_buf[i-LOOKAHEAD], in_buf[i-LOOKAHEAD]);
   }

   /* If input power is very low, clear pitch flag of the current frame */
   test ();
   if (L_sub(pow_sum, POW_PITCH_THR) < 0)
   {
      st->pitch = st->pitch & 0x3fff;                logic16 (); move16 ();
   }

   /* If input power is very low, clear complex flag of the "current" frame */
   test ();
   if (L_sub(pow_sum, POW_COMPLEX_THR) < 0)
   {
      st->complex_low = st->complex_low & 0x3fff;    logic16 (); move16 ();
   }
   
   /* Run the filter bank which calculates signal levels at each band */
   filter_bank(st, in_buf, level);
   
   return (vad_decision(st, level, pow_sum));
}