diff libtwamr/vad1.c @ 408:8847c1740e78

libtwamr: integrate VAD1
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 07 May 2024 00:56:10 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libtwamr/vad1.c	Tue May 07 00:56:10 2024 +0000
@@ -0,0 +1,1008 @@
+/*
+*****************************************************************************
+**-------------------------------------------------------------------------**
+**                                                                         **
+**     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));
+}