diff sp_sfrm.c @ 0:9008dbc8ca74

import original C code from GSM 06.06
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 14 Jun 2024 23:27:16 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sp_sfrm.c	Fri Jun 14 23:27:16 2024 +0000
@@ -0,0 +1,2360 @@
+/***************************************************************************
+ *
+ *   File Name:  sp_sfrm.c
+ *
+ *   Purpose:  Contains all functions for subframe based processing in the
+ *      speech encoder.  Subframe based processing determines the synthetic
+ *      LPC excitation signal, which is composed of the adaptive codebook
+ *      (long-term predictor) vector (in voiced modes), the vector-sum
+ *      codebook vector (two of these in unvoiced mode), and the vector-
+ *      quantized gains applied to these vectors.
+ *
+ *      Below is a listing of all the functions appearing in the file.
+ *      The functions are arranged according to their purpose.  Under
+ *      each heading, the ordering is hierarchical.
+ *
+ *      sfrmAnalysis
+ *        decorr
+ *        closedLoopLagSearch
+ *        hnwFilt
+ *        g_quant_vl
+ *          g_corr2
+ *          gainTweak
+ *        v_srch
+ *
+ *
+ ***************************************************************************/
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Include Files                              |
+ |_________________________________________________________________________|
+*/
+
+#include <stdio.h>
+#include "mathhalf.h"
+#include "sp_rom.h"
+#include "sp_dec.h"
+#include "sp_frm.h"
+#include "sp_sfrm.h"
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Local Defines                              |
+ |_________________________________________________________________________|
+*/
+
+#define CG_INT_MACS     6
+#define C_BITS_UV       7
+#define C_BITS_UV_1     C_BITS_UV-1
+#define C_BITS_V        9
+#define C_BITS_V_1      C_BITS_V-1
+#define DELTA_LEVELS    16
+#define GSP0_NUM        32
+#define GSP0_VECTOR_SIZE      5
+#define GTWEAKMAX       0x5A82         /* sqrt(2)/2 */
+#define LMAX            142
+#define LSMAX           (LMAX + CG_INT_MACS/2)
+#define HNW_BUFF_LEN    LSMAX
+#define LSP_MASK        0xffff
+#define LTP_LEN         147            /* maximum ltp lag */
+#define MAX_CANDIDATE   6              /* maximum number of lag candidates */
+
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                     State variables (globals)                           |
+ |_________________________________________________________________________|
+*/
+
+Shortword pswLtpStateBase[LTP_LEN + S_LEN];
+Shortword pswHState[NP];
+Shortword pswHNWState[HNW_BUFF_LEN];
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: closedLoopLagSearch
+ *
+ *   PURPOSE:
+ *
+ *     Performs the closed loop search of a list of candidate lags to
+ *     determine the best fractional lag.
+ *
+ *   INPUTS:
+ *
+ *     pswLagList[0:iNumLags] - list of candidate lags
+ *     iNumLags - number of candidate lags in LagList
+ *     pswLtpState[0:5] - array of past excitations (LTP state)
+ *     pswHCoefs[0:9] - coefficient array of spectral weighting filter
+ *     pswPVect[0:39] - speech sub frame data
+ *
+ *   OUTPUTS:
+ *
+ *     pswLag - pointer to put best lag from list of candidates
+ *     *pswLtpShift - Number of shifts applied to weighted LTP vector.
+ *
+ *   RETURN VALUE:
+ *
+ *     siLagCode - code corresponding to the best lag
+ *
+ *   IMPLEMENTATION:
+ *
+ *     Generate excitation vectors for all candidate lags. Find the candidate
+ *     lag that maximizes C**2/G using the calculated excitation.
+ *
+ *   DESCRIPTION:
+ *
+ *     The function closedLoopLagSearch() searches a very small subset of the
+ *     available LTP lags.  The lags to be searched are defined by the open
+ *     loop lag search.  This information is passed in as a list of
+ *     oversampled lag values.  These values are translated into LTP
+ *     vectors extracted from the LTP history.
+ *
+ *     GSM document 06.20's b sub L prime variable is called
+ *     ppswTVect[L][n] in the C code.  The document's variable p(n) is
+ *     named pswPVect[] in the C code.
+ *
+ *     The function performs a simple maximization of the cross correlation
+ *     of the weighted LTP vector and the weighted speech vector divided
+ *     by the autocorrelation of the weighted LTP vector.  The function is
+ *     encumbered slightly by the necessity of scaling.
+ *
+ *   REFERENCE:  Sub-clause 4.1.8.5 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS:  closed loop, LTP lag search, adaptive codebook search
+ *
+ **************************************************************************/
+
+int    closedLoopLagSearch(Shortword pswLagList[], int iNumLags,
+                           Shortword pswLtpState[], Shortword pswHCoefs[],
+                                  Shortword pswPVect[],
+                                Shortword *pswLag, Shortword *pswLtpShift)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Longword L_Energy,
+         L_ccNorm,
+         L_cgNorm,
+         L_CrossCorr;
+  Longword pL_CCBuf[MAX_CANDIDATE],
+         pL_CGBuf[MAX_CANDIDATE];
+  Shortword swCCMax,
+         swCCShiftCnt,
+         swCGShiftCnt,
+         swGMax,
+         swLTPEnergy,
+         swSampleA,
+         pswCCBuf[MAX_CANDIDATE],
+         pswCGBuf[MAX_CANDIDATE],
+         ppswTVect[N_SUB][S_LEN];
+  Shortword i,
+         j,
+         siLagOffset,
+         siLagCode;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+
+  *pswLtpShift = 0;                    /* Energy in weighted ltp vector =
+                                        * [0..0x7ff] */
+  for (i = 0; i < iNumLags; i++)
+  {
+
+    /* Construct the excitation vector for lag i */
+    /* ----------------------------------------- */
+
+    fp_ex(pswLagList[i], &pswLtpState[LTP_LEN]);
+
+    /* Perform all pole filtering */
+    /* -------------------------- */
+
+    lpcZsIir(&pswLtpState[LTP_LEN], pswHCoefs, ppswTVect[i]);
+  }
+
+  /* scale the pitch vector s.t. its energy is strictly */
+  /* less than 1.0                                      */
+  /*----------------------------------------------------*/
+
+  swSampleA = shr(ppswTVect[0][0], 2);
+  L_Energy = L_mac(0x001dff4cL, swSampleA, swSampleA);
+  for (j = 1; j < S_LEN; j++)
+  {
+    swSampleA = shr(ppswTVect[0][j], 2);
+    L_Energy = L_mac(L_Energy, swSampleA, swSampleA);
+  }
+
+  /* add in the energy of the first sample of the subsequent lags */
+  /*--------------------------------------------------------------*/
+
+  for (i = 1; i < iNumLags; i++)
+  {
+    swSampleA = shr(ppswTVect[i][0], 2);
+    L_Energy = L_mac(L_Energy, swSampleA, swSampleA);
+  }
+
+  /* An upper bound on the weighted pitch vectors energy */
+  /*-----------------------------------------------------*/
+
+  swLTPEnergy = round(L_Energy);
+
+  if (sub(swLTPEnergy, 0x07ff) > 0)
+  {                                    /* E = (0x7ff.. 0x7fff] */
+    if (sub(swLTPEnergy, 0x1fff) > 0)
+    {
+      *pswLtpShift = 2;                /* E = (0x1fff..0x7fff] */
+    }
+    else
+    {
+      *pswLtpShift = 1;                /* E = (0x7ff.. 0x1fff] */
+    }
+
+  }
+
+  for (i = 0; i < iNumLags; i++)
+  {
+
+    /* shift all vectors down s.t. the largest is has energy < 1.0 */
+    /*-------------------------------------------------------------*/
+
+    for (j = 0; j < S_LEN; j++)
+      ppswTVect[i][j] = shr(ppswTVect[i][j], *pswLtpShift);
+
+    /* Calculate the energy of the subframe */
+    /* ------------------------------------ */
+
+    L_Energy = L_mult(ppswTVect[i][0], ppswTVect[i][0]);
+    for (j = 1; j < S_LEN; j++)
+      L_Energy = L_mac(L_Energy, ppswTVect[i][j], ppswTVect[i][j]);
+
+    pL_CGBuf[i] = L_Energy;
+
+    /* Cross correlate the normalized speech frame and the filtered
+     * subframe */
+    /* ---------------------------------------------------------------------
+     * */
+
+    L_CrossCorr = L_mult(ppswTVect[i][0], pswPVect[0]);
+    for (j = 1; j < S_LEN; j++)
+      L_CrossCorr = L_mac(L_CrossCorr, ppswTVect[i][j], pswPVect[j]);
+
+    pL_CCBuf[i] = L_CrossCorr;
+
+  }
+
+  /* find the shift count associated with the largest CC and G */
+  /* ---------------------------------------------------------- */
+  L_ccNorm = L_abs(pL_CCBuf[0]);
+  L_cgNorm = pL_CGBuf[0];
+
+  for (i = 1; i < iNumLags; i++)
+  {
+    L_ccNorm |= L_abs(pL_CCBuf[i]);
+    L_cgNorm |= pL_CGBuf[i];
+  }
+
+  swCCShiftCnt = norm_l(L_ccNorm);
+  swCGShiftCnt = norm_l(L_cgNorm);
+
+  for (i = 0; i < iNumLags; i++)
+  {
+    pswCCBuf[i] = round(L_shl(pL_CCBuf[i], swCCShiftCnt));
+    pswCGBuf[i] = round(L_shl(pL_CGBuf[i], swCGShiftCnt));
+  }
+
+  /* Maximize C**2/G */
+  /* --------------- */
+  siLagOffset = maxCCOverGWithSign(pswCCBuf, pswCGBuf,
+                                   &swCCMax, &swGMax,
+                                   iNumLags);
+
+  /* Determine the offset of the max value into CC buffer */
+  /* ---------------------------------------------------- */
+  *pswLag = pswLagList[siLagOffset];
+
+  /* Store Lag Code for best lag result */
+  /* ---------------------------------- */
+  quantLag(*pswLag, &siLagCode);
+
+  return (siLagCode);
+}
+
+/*****************************************************************************
+ *
+ *   FUNCTION NAME: decorr
+ *
+ *   PURPOSE: Decorrelates(orthogonalizes) a set of vectors from a given
+ *            vector.
+ *
+ *
+ *   INPUTS: iNumVects - number of vectors to decorrelate
+ *           pswGivenVect[0..39] - array of given vectors
+ *           pswVects[0..359] (voice) [0..279] (unvoiced) - array of
+ *             contiguous vectors to be decorrelated
+ *   OUTPUTS: pswVects[0..359] (voice) [0..279] (unvoiced) - output vectors
+ *            are written back over input vectors
+ *
+ *   RETURN VALUE: none
+ *
+ *   IMPLEMENTATION:
+ *
+ *   REFERENCE:  Sub-clause 4.1.10.1 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS: decorrelate, codewords, codevectors, orthogonalize, encoder
+ *
+ ****************************************************************************/
+
+void   decorr(int iNumVects, Shortword pswGivenVect[],
+                     Shortword pswVects[])
+{
+
+/*___________________________________________________________________________
+ |                                                                           |
+ |                            Automatic Variables                            |
+ |___________________________________________________________________________|
+*/
+  int    i,
+         iLoopCnt;
+  Shortword swNorm_energy,
+         swTemp;
+  Shortword swEShift,
+         swCShift,
+         swShiftSum,
+         swQShift;
+  Longword L_Energy,
+         L_Temp1,
+         L_Temp2,
+         L_Accum;
+
+/*___________________________________________________________________________
+ |                                                                           |
+ |                              Executable Code                              |
+ |___________________________________________________________________________|
+ */
+
+  /* Compute normalized energy in given vector */
+  /*-------------------------------------------*/
+  swEShift = g_corr1(pswGivenVect, &L_Energy);
+  swNorm_energy = extract_h(L_Energy);
+
+  if (swNorm_energy == 0)
+  {
+    return;
+  }
+
+  /* Decorrelate vectors */
+  /*---------------------*/
+
+  for (iLoopCnt = 0; iLoopCnt < iNumVects; iLoopCnt++)
+  {
+
+    swCShift = g_corr2(pswGivenVect, &pswVects[iLoopCnt * S_LEN],
+                       &L_Temp1);
+    L_Temp2 = L_Temp1;
+    L_Temp1 = L_abs(L_Temp1);
+    swCShift = sub(swCShift, 1);
+    swShiftSum = sub(swCShift, swEShift);
+    L_Temp1 = L_shr(L_Temp1, 1);
+    swTemp = divide_s(round(L_Temp1), swNorm_energy);
+
+    if (L_Temp2 > 0)
+      swTemp = negate(swTemp);
+
+    swQShift = norm_s(swTemp);
+    swTemp = shl(swTemp, swQShift);
+    swQShift = add(swShiftSum, swQShift);
+
+    if (swQShift > 0)
+    {
+      swTemp = shift_r(swTemp, negate(swQShift));
+      swQShift = 0;
+    }
+    else
+      swQShift = negate(swQShift);
+
+    for (i = 0; i < S_LEN; i++)
+    {
+      L_Accum = L_msu(0x00008000L, pswVects[i + iLoopCnt * S_LEN], SW_MIN);
+      pswVects[iLoopCnt * S_LEN + i] = extract_h(L_mac(L_Accum, swTemp,
+                                         shl(pswGivenVect[i], swQShift)));
+    }
+  }
+}
+
+/***************************************************************************
+ *
+ *    FUNCTION NAME: g_corr2
+ *
+ *    PURPOSE: Calculates correlation between subframe vectors.
+ *
+ *
+ *    INPUT:
+ *
+ *       pswIn[0:39]
+ *                     A subframe vector.
+ *
+ *       pswIn2[0:39]
+ *                     A subframe vector.
+ *
+ *
+ *    OUTPUT:
+ *
+ *       *pL_out
+ *                     A Longword containing the normalized correlation
+ *                     between the input vectors.
+ *
+ *    RETURN:
+ *
+ *       swOut
+ *                     Number of right shifts which the accumulator was
+ *                     shifted to normalize it.  Negative number implies
+ *                     a left shift, and therefore an energy larger than
+ *                     1.0.
+ *
+ *    REFERENCE:  Sub-clauses 4.1.10.1 and 4.1.11.1 of GSM
+ *       Recommendation 06.20
+ *
+ *    keywords: energy, autocorrelation, correlation, g_corr2
+ *
+ *
+ **************************************************************************/
+
+Shortword g_corr2(Shortword *pswIn, Shortword *pswIn2,
+                         Longword *pL_out)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Longword L_sum;
+  Shortword swEngyLShft;
+  int    i;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* Calculate energy in subframe vector (40 samples) */
+  /*--------------------------------------------------*/
+
+  L_sum = L_mult(pswIn[0], pswIn2[0]);
+  for (i = 1; i < S_LEN; i++)
+  {
+    L_sum = L_mac(L_sum, pswIn[i], pswIn2[i]);
+  }
+
+
+
+  if (L_sum != 0)
+  {
+
+    /* Normalize the energy in the output Longword */
+    /*---------------------------------------------*/
+
+    swEngyLShft = norm_l(L_sum);
+    *pL_out = L_shl(L_sum, swEngyLShft);        /* normalize output
+                                                 * Longword */
+  }
+  else
+  {
+
+    /* Special case: energy is zero */
+    /*------------------------------*/
+
+    *pL_out = L_sum;
+    swEngyLShft = 0;
+  }
+
+  return (swEngyLShft);
+}
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: g_quant_vl
+ *
+ *   PURPOSE:
+ *
+ *     Joint quantization of excitation gains.
+ *     GS represents the subframe energy relative to the frame energy.
+ *     P0 represents the relative contribution of the first exctitation
+ *     source to the total excitation.
+ *
+ *   INPUTS:
+ *
+ *     swUVCode - voicing level (Mode 0-3)
+ *     pswWInput[0:39] - weighted input p(n) (used in mode 0-3)
+ *     swWIShift - weighted input shift factor (right shift, 0,1, or 2)
+ *     pswWLTPVec[0:39] - weighted pitch excitation vector (used in mode 1-3)
+ *     pswWVSVec1[0:39] - weighted 1st v-s codevector (used in mode 0-3)
+ *     pswWVSVec2[0:39] - weighted 2nd v-s codevector (used in mode 0)
+ *     snsRs00 - square root of RS/pitch excitation energy (used in mode 1-3)
+ *     snsRs11 - square root of RS/1st v-s codevector energy
+ *               (used in mode 0-3)
+ *     snsRs22 - square root of RS/2nd v-s codevector energy (used in mode 0)
+ *
+ *     pppsrGsp0[0:3][0:31][0:4] - lookup table
+ *
+ *   OUTPUTS:
+ *
+ *     None
+ *
+ *   RETURN VALUE:
+ *
+ *     siCode - output quantized gain code (5 bits)
+ *
+ *   IMPLEMENTATION:
+ *
+ *     Calculates first the parameters required for error equation 7.21:
+ *
+ *     Rcc(k,j)        k = 0,1, j=k,1
+ *     Rx(k)           k = 0,1
+ *     RS
+ *     Rpc(k)          k = 0,1
+ *     a,b,c,d,e
+ *
+ *     The constant terms in equation 7.21 are stored in ROM instead of GS
+ *     and P0. There is one vector quantizer for each voicing state.
+ *
+ *   REFERENCE:  Sub-clause 4.1.11 and 4.1.11.1 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS: gain quantization, energy domain transforms, p0, gs
+ *
+ **************************************************************************/
+
+Shortword g_quant_vl(Shortword swUVCode,
+                            Shortword pswWInput[], Shortword swWIShift,
+                            Shortword pswWLTPVec[],
+                            Shortword pswWVSVec1[], Shortword pswWVSVec2[],
+                            struct NormSw snsRs00, struct NormSw snsRs11,
+                            struct NormSw snsRs22)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Longword L_Temp,
+         L_Temp2;
+  Shortword swShift;
+  struct NormSw ErrorTerm[6];
+  Shortword i,
+         siCode,
+         siNormShift,
+         siNormMin;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* Test voicing level, mode 0-3 */
+  /* ---------------------------- */
+
+  if (swUVCode == 0)
+  {
+
+    /* Unvoiced */
+    /* -------- */
+
+    /* Compute cross correlation Rpc(0) */
+    /* -------------------------------- */
+
+
+    ErrorTerm[0].sh = g_corr2(pswWInput, pswWVSVec1, &L_Temp);
+    ErrorTerm[0].man = round(L_Temp);
+
+    /* Compute cross correlation Rpc(1) */
+    /* -------------------------------- */
+
+    ErrorTerm[1].sh = g_corr2(pswWInput, pswWVSVec2, &L_Temp);
+    ErrorTerm[1].man = round(L_Temp);
+
+    /* Compute cross correlation Rcc(0,1) */
+    /* ---------------------------------- */
+
+
+    ErrorTerm[2].sh = g_corr2(pswWVSVec1, pswWVSVec2, &L_Temp);
+    ErrorTerm[2].man = round(L_Temp);
+
+    /* Compute correlation Rcc(0,0) */
+    /* ---------------------------- */
+
+    ErrorTerm[3].sh = g_corr1(pswWVSVec1, &L_Temp);
+    ErrorTerm[3].man = round(L_Temp);
+
+    /* Compute correlation Rcc(1,1) */
+    /* ---------------------------- */
+
+    ErrorTerm[4].sh = g_corr1(pswWVSVec2, &L_Temp);
+    ErrorTerm[4].man = round(L_Temp);
+
+    /* Compute correlation Rpp */
+    /* ----------------------- */
+
+    ErrorTerm[5].sh = g_corr1(pswWInput, &L_Temp);
+    ErrorTerm[5].man = round(L_Temp);
+
+    /* Compute gain tweak factor, adjusts A and B error coefs */
+    /* ------------------------------------------------------ */
+    gainTweak(&ErrorTerm[0]);
+
+    /* Compute error coefficient A, equation 5.22 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[0].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[0].sh = add(ErrorTerm[0].sh, swShift);
+    ErrorTerm[0].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[0].sh = add(ErrorTerm[0].sh, snsRs11.sh);
+    siNormMin = ErrorTerm[0].sh;
+
+    /* Compute error coefficient B, equation 5.23 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[1].man, snsRs22.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[1].sh = add(ErrorTerm[1].sh, swShift);
+    ErrorTerm[1].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[1].sh = add(ErrorTerm[1].sh, snsRs22.sh);
+    if (sub(ErrorTerm[1].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[1].sh;
+
+    /* Compute error coefficient C, equation 5.24 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[2].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swShift);
+    ErrorTerm[2].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[2].man, snsRs22.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swShift);
+    ErrorTerm[2].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, snsRs11.sh);
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, snsRs22.sh);
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swWIShift);
+    if (sub(ErrorTerm[2].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[2].sh;
+
+    /* Compute error coefficient D, equation 5.25 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[3].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swShift);
+    ErrorTerm[3].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[3].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swShift);
+    ErrorTerm[3].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, snsRs11.sh);
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, snsRs11.sh);
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swWIShift);
+
+    if (sub(ErrorTerm[3].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[3].sh;
+
+    /* Compute error coefficient E, equation 5.26 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[4].man, snsRs22.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swShift);
+    ErrorTerm[4].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[4].man, snsRs22.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swShift);
+    ErrorTerm[4].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, snsRs22.sh);
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, snsRs22.sh);
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swWIShift);
+
+    if (sub(ErrorTerm[4].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[4].sh;
+
+  }
+
+  else
+  {                                    /* Voicing level */
+
+    /* Voiced */
+    /* ------ */
+
+    /* Compute cross correlation Rpc(0) */
+    /* -------------------------------- */
+
+
+    ErrorTerm[0].sh = g_corr2(pswWInput, pswWLTPVec, &L_Temp);
+    ErrorTerm[0].man = round(L_Temp);
+
+    /* Compute cross correlation Rpc(1) */
+    /* -------------------------------- */
+
+
+    ErrorTerm[1].sh = g_corr2(pswWInput, pswWVSVec1, &L_Temp);
+    ErrorTerm[1].man = round(L_Temp);
+
+    /* Compute cross correlation Rcc(0,1) */
+    /* ---------------------------------- */
+
+
+    ErrorTerm[2].sh = g_corr2(pswWLTPVec, pswWVSVec1, &L_Temp);
+    ErrorTerm[2].man = round(L_Temp);
+
+    /* Compute correlation Rcc(0,0) */
+    /* ---------------------------- */
+
+    ErrorTerm[3].sh = g_corr1(pswWLTPVec, &L_Temp);
+    ErrorTerm[3].man = round(L_Temp);
+
+    /* Compute correlation Rcc(1,1) */
+    /* ---------------------------- */
+
+    ErrorTerm[4].sh = g_corr1(pswWVSVec1, &L_Temp);
+    ErrorTerm[4].man = round(L_Temp);
+
+    /* Compute correlation Rpp */
+    /* ----------------------- */
+
+    ErrorTerm[5].sh = g_corr1(pswWInput, &L_Temp);
+    ErrorTerm[5].man = round(L_Temp);
+
+    /* Compute gain tweak factor, adjusts A and B error coefs */
+    /* ------------------------------------------------------ */
+
+    gainTweak(&ErrorTerm[0]);
+
+    /* Compute error coefficient A, equation 5.22 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[0].man, snsRs00.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[0].sh = add(ErrorTerm[0].sh, swShift);
+    ErrorTerm[0].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[0].sh = add(ErrorTerm[0].sh, snsRs00.sh);
+    siNormMin = ErrorTerm[0].sh;
+
+    /* Compute error coefficient B, equation 5.23 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[1].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[1].sh = add(ErrorTerm[1].sh, swShift);
+    ErrorTerm[1].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[1].sh = add(ErrorTerm[1].sh, snsRs11.sh);
+    if (sub(ErrorTerm[1].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[1].sh;
+
+    /* Compute error coefficient C, equation 5.24 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[2].man, snsRs00.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swShift);
+    ErrorTerm[2].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[2].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swShift);
+    ErrorTerm[2].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, snsRs00.sh);
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, snsRs11.sh);
+    ErrorTerm[2].sh = add(ErrorTerm[2].sh, swWIShift);
+    if (sub(ErrorTerm[2].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[2].sh;
+
+    /* Compute error coefficient D, equation 5.25 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[3].man, snsRs00.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swShift);
+    ErrorTerm[3].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[3].man, snsRs00.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swShift);
+    ErrorTerm[3].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, snsRs00.sh);
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, snsRs00.sh);
+    ErrorTerm[3].sh = add(ErrorTerm[3].sh, swWIShift);
+    if (sub(ErrorTerm[3].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[3].sh;
+
+    /* Compute error coefficient E, equation 5.26 */
+    /* ------------------------------------------ */
+
+    L_Temp = L_mult(ErrorTerm[4].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swShift);
+    ErrorTerm[4].man = round(L_shl(L_Temp, swShift));
+    L_Temp = L_mult(ErrorTerm[4].man, snsRs11.man);
+    swShift = norm_s(extract_h(L_Temp));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swShift);
+    ErrorTerm[4].man = round(L_shl(L_Temp, swShift));
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, snsRs11.sh);
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, snsRs11.sh);
+    ErrorTerm[4].sh = add(ErrorTerm[4].sh, swWIShift);
+    if (sub(ErrorTerm[4].sh, siNormMin) < 0)
+      siNormMin = ErrorTerm[4].sh;
+
+  }                                    /* Voicing level */
+
+
+
+  /* Normalize all error coefficients to same shift count */
+  /* ---------------------------------------------------- */
+
+  for (i = 0; i < GSP0_VECTOR_SIZE; i++)
+  {
+    L_Temp = L_deposit_h(ErrorTerm[i].man);
+    siNormShift = sub(ErrorTerm[i].sh, siNormMin);
+    if (siNormShift > 0)
+      L_Temp = L_shr(L_Temp, siNormShift);
+    ErrorTerm[i].man = round(L_Temp);
+  }
+
+
+  /* Codebook search, find max of error equation 5.21 */
+  /* ------------------------------------------------ */
+
+  L_Temp2 = 0x80000000;
+
+  for (i = 0; i < GSP0_NUM; i++)
+  {
+
+    L_Temp = L_mult(pppsrGsp0[swUVCode][i][0], ErrorTerm[0].man);
+    L_Temp = L_mac(L_Temp, pppsrGsp0[swUVCode][i][1], ErrorTerm[1].man);
+    L_Temp = L_mac(L_Temp, pppsrGsp0[swUVCode][i][2], ErrorTerm[2].man);
+    L_Temp = L_mac(L_Temp, pppsrGsp0[swUVCode][i][3], ErrorTerm[3].man);
+    L_Temp = L_mac(L_Temp, pppsrGsp0[swUVCode][i][4], ErrorTerm[4].man);
+    if (L_sub(L_Temp2, L_Temp) < 0)
+    {
+      L_Temp2 = L_Temp;
+      siCode = i;                      /* Save best code */
+    }
+  }
+  return (siCode);
+}
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: gainTweak
+ *
+ *   PURPOSE:
+ *
+ *     Calculates gain bias factor, limits it, and
+ *     applies it to A and B error coefficients.
+ *
+ *   INPUTS:
+ *
+ *     psErrorTerm[0:5] - array (6) of error coefficients in floating
+ *                        point format
+ *
+ *   OUTPUTS:
+ *
+ *     psErrorTerm[0:5] - array of gain adjusted A and B error coefficients
+ *
+ *   RETURN VALUE:
+ *
+ *     None
+ *
+ *   IMPLEMENTATION:
+ *
+ *     The gain tweak is:
+ *
+ *                 Rpp*Rcc(0,0)*Rcc(1,1) - Rpp*Rcc(0,1)*Rcc(0,1)
+ *sqrt(---------------------------------------------------------------------)
+ *     Rcc(0,0)*Rpc(1)*Rpc(1)-2*Rcc(0,1)*Rpc(0)*Rpc(1)+Rcc(1,1)*Rpc(0)*Rpc(0)
+ *
+ *   REFERENCE:  Sub-clause 4.1.11.1 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS: gain tweak, g_quant_vl
+ *
+ **************************************************************************/
+
+void   gainTweak(struct NormSw *psErrorTerm)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Longword L_Temp;
+  Shortword swTemp,
+         swNum,
+         swDenom,
+         swGainTweak,
+         swShift;
+  struct NormSw terms[5];
+  Shortword i,
+         siNormShift,
+         siNorm;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* Calculate third order terms in the gain tweak factor, while
+   * maintaining the largest exponent */
+  /* ---------------------------------------------------- */
+
+  /* Compute Rpp*Rcc(0,0)*Rcc(1,1) */
+  /* ----------------------------- */
+
+  L_Temp = L_mult(psErrorTerm[3].man, psErrorTerm[5].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[0].sh = add(psErrorTerm[3].sh, swShift);
+  terms[0].man = round(L_shl(L_Temp, swShift));
+  L_Temp = L_mult(terms[0].man, psErrorTerm[4].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[0].sh = add(terms[0].sh, swShift);
+  terms[0].man = round(L_shl(L_Temp, swShift));
+  terms[0].sh = add(terms[0].sh, psErrorTerm[4].sh);
+  terms[0].sh = add(terms[0].sh, psErrorTerm[5].sh);
+  /* Init. siNorm */
+  siNorm = terms[0].sh;
+
+  /* Compute Rpp*Rcc(0,1)*Rcc(0,1) */
+  /* ----------------------------- */
+
+  L_Temp = L_mult(psErrorTerm[2].man, psErrorTerm[2].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[1].sh = add(psErrorTerm[2].sh, swShift);
+  terms[1].man = round(L_shl(L_Temp, swShift));
+  L_Temp = L_mult(terms[1].man, psErrorTerm[5].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[1].sh = add(terms[1].sh, swShift);
+  terms[1].man = round(L_shl(L_Temp, swShift));
+  terms[1].sh = add(terms[1].sh, psErrorTerm[2].sh);
+  terms[1].sh = add(terms[1].sh, psErrorTerm[5].sh);
+  if (sub(terms[1].sh, siNorm) < 0)
+    siNorm = terms[1].sh;
+
+  /* Compute Rcc(0,0)*Rpc(1)*Rpc(1) */
+  /* ------------------------------ */
+
+  L_Temp = L_mult(psErrorTerm[1].man, psErrorTerm[1].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[2].sh = add(psErrorTerm[1].sh, swShift);
+  terms[2].man = round(L_shl(L_Temp, swShift));
+  L_Temp = L_mult(terms[2].man, psErrorTerm[3].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[2].sh = add(terms[2].sh, swShift);
+  terms[2].man = round(L_shl(L_Temp, swShift));
+  terms[2].sh = add(terms[2].sh, psErrorTerm[1].sh);
+  terms[2].sh = add(terms[2].sh, psErrorTerm[3].sh);
+  if (sub(terms[2].sh, siNorm) < 0)
+    siNorm = terms[2].sh;
+
+  /* Compute 2*Rcc(0,1)*Rpc(0)*Rpc(1) */
+  /* -------------------------------- */
+
+  L_Temp = L_mult(psErrorTerm[0].man, psErrorTerm[1].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[3].sh = add(psErrorTerm[0].sh, swShift);
+  terms[3].man = round(L_shl(L_Temp, swShift));
+  L_Temp = L_mult(terms[3].man, psErrorTerm[2].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[3].sh = add(terms[3].sh, swShift);
+  terms[3].man = round(L_shl(L_Temp, swShift));
+  terms[3].sh = add(terms[3].sh, psErrorTerm[1].sh);
+  terms[3].sh = add(terms[3].sh, psErrorTerm[2].sh);
+  terms[3].sh = sub(terms[3].sh, 1);   /* Multiply by 2 */
+  if (sub(terms[3].sh, siNorm) < 0)
+    siNorm = terms[3].sh;
+
+  /* Compute Rcc(1,1)*Rpc(0)*Rpc(0) */
+  /* ------------------------------ */
+
+  L_Temp = L_mult(psErrorTerm[0].man, psErrorTerm[4].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[4].sh = add(psErrorTerm[0].sh, swShift);
+  terms[4].man = round(L_shl(L_Temp, swShift));
+  L_Temp = L_mult(terms[4].man, psErrorTerm[0].man);
+  swShift = norm_s(extract_h(L_Temp));
+  terms[4].sh = add(terms[4].sh, swShift);
+  terms[4].man = round(L_shl(L_Temp, swShift));
+  terms[4].sh = add(terms[4].sh, psErrorTerm[0].sh);
+  terms[4].sh = add(terms[4].sh, psErrorTerm[4].sh);
+  if (sub(terms[4].sh, siNorm) < 0)
+    siNorm = terms[4].sh;
+
+  /* Normalize all terms to same shift count */
+  /* --------------------------------------- */
+
+  for (i = 0; i < 5; i++)
+  {
+    L_Temp = L_deposit_h(terms[i].man);
+    siNormShift = sub(terms[i].sh, siNorm);
+    if (siNormShift > 0)
+    {
+      L_Temp = L_shr(L_Temp, siNormShift);
+    }
+    terms[i].man = round(L_Temp);
+  }
+
+  /* Calculate numerator */
+  /* ------------------- */
+
+  /* Rpp*Rcc(0,0)*Rcc(1,1) - Rpp*Rcc(0,1)*Rcc(0,1) */
+  /* --------------------------------------------- */
+
+  swNum = sub(terms[0].man, terms[1].man);
+
+  /* Skip gain tweak if numerator =< 0 */
+  /* --------------------------------- */
+
+  if (swNum <= 0)
+    return;
+
+  /* Calculate denominator */
+  /* --------------------- */
+
+  /* Rcc(0,0)*Rpc(1)*Rpc(1)-2*Rcc(0,1)*Rpc(0)*Rpc(1)+Rcc(1,1)*Rpc(0)*Rpc(0) */
+  /*----------------------------------------------------------------------*/
+
+  swDenom = sub(terms[2].man, terms[3].man);
+  swDenom = add(swDenom, terms[4].man);
+
+  /* Skip gain tweak if denominator =< 0 */
+  /* ----------------------------------- */
+
+  if (swDenom <= 0)
+    return;
+
+  /* Compare numerator to denominator, skip if tweak =< 1 */
+  /* ---------------------------------------------------- */
+
+  swTemp = sub(swNum, swDenom);
+  if (swTemp <= 0)
+    return;
+
+  /* Normalize and do divide */
+  /* ----------------------- */
+
+  swShift = norm_s(swNum);
+  siNormShift = sub(swShift, 1);       /* Multiply by 2 */
+  swNum = shl(swNum, swShift);
+  swNum = shr(swNum, 1);
+  swShift = norm_s(swDenom);
+  siNormShift = sub(siNormShift, swShift);
+  swDenom = shl(swDenom, swShift);
+  swTemp = divide_s(swNum, swDenom);
+  swShift = norm_s(swTemp);
+  siNormShift = add(siNormShift, swShift);
+  L_Temp = L_shl(L_deposit_h(swTemp), swShift);
+
+  /* Calculate square root */
+  /* --------------------- */
+
+  swTemp = sqroot(L_Temp);
+
+  /* If odd no. of shifts compensate by sqrt(0.5) */
+  /* -------------------------------------------- */
+
+  if (siNormShift & 1)
+  {
+    L_Temp = L_mult(0x5a82, swTemp);
+    siNormShift = sub(siNormShift, 1);
+  }
+  else
+    L_Temp = L_deposit_h(swTemp);
+  siNormShift = shr(siNormShift, 1);
+  swShift = norm_s(extract_h(L_Temp));
+  siNormShift = add(siNormShift, swShift);
+  swGainTweak = round(L_shl(L_Temp, swShift));
+
+  /* If exponent > -1, skip gain tweak */
+  /* --------------------------------- */
+
+  if (add(1, siNormShift) > 0)
+    return;
+
+  /* If exponent < -1, limit gain tweak to GTWEAKMAX */
+  /* ----------------------------------------------- */
+
+  if (add(1, siNormShift) < 0)
+    swGainTweak = GTWEAKMAX;
+  else
+  {
+
+    /* If exponent = -1, compare to GTWEAKMAX */
+    /* -------------------------------------- */
+
+    if (sub(GTWEAKMAX, swGainTweak) < 0)
+      swGainTweak = GTWEAKMAX;
+  }
+
+  /* Multiply gain tweak factor on A and B error terms */
+  /* ------------------------------------------------- */
+
+  L_Temp = L_mult(swGainTweak, psErrorTerm[0].man);
+  swShift = norm_s(extract_h(L_Temp));
+  psErrorTerm[0].sh = add(psErrorTerm[0].sh, swShift);
+  psErrorTerm[0].sh = sub(psErrorTerm[0].sh, 1);
+  psErrorTerm[0].man = round(L_shl(L_Temp, swShift));
+
+  L_Temp = L_mult(swGainTweak, psErrorTerm[1].man);
+  swShift = norm_s(extract_h(L_Temp));
+  psErrorTerm[1].sh = add(psErrorTerm[1].sh, swShift);
+  psErrorTerm[1].sh = sub(psErrorTerm[1].sh, 1);
+  psErrorTerm[1].man = round(L_shl(L_Temp, swShift));
+
+}
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: hnwFilt
+ *
+ *   PURPOSE:
+ *     Performs the filtering operation for harmonic noise weighting.
+ *
+ *   INPUTS:
+ *         pswInSample[0:39] - array of input speech signal,
+ *            pswInSample points to the "oldest" sample of the
+ *            current subframe to be hnw filtered, S_LEN samples
+ *            will be stored in this array, this data is not
+ *            explicitly modified.
+ *
+ *         pswState[0:183] - array of state of samples, the most
+ *            recent sample is the tail of the state buffer,
+ *            used only for full- state filtering, this data is
+ *            not modified
+ *         pswInCoef[0:5] - array of unmodified filter coefficients
+ *         iStateOffset - address offset from a sample in the subframe back
+ *            to the oldest element of the state used in the interpolating
+ *            filter for that sample. Although the subframe samples and
+ *            state information can come from different buffers, this
+ *            offset represents the case in which the state and sample
+ *            information are in the same buffer
+ *         swZeroState - indicate if the interpolating filter should be
+ *            "zero-state" filtering or "full-state" filtering:
+ *                       0 ==> zero-state filtering
+ *                      !0 ==> full-state filtering
+ *         iNumSamples - the number of samples that are to be filtered,
+ *            required to be less than or equal to S_LEN in order to
+ *            correctly match speech samples with sample states for the
+ *            filtering procedure
+ *
+ *   OUTPUTS:
+ *         pswOutSample[0:39] - array of output filtered speech signal,
+ *            pswOutSample points to the "oldest" sample location, S_LEN
+ *            filtered samples will be stored at the buffer associated with
+ *            this array, can implicitly overwrite input samples with
+ *            with filtered samples by setting pswOutSample = pswInSample
+ *
+ *   RETURN VALUE:
+ *         none
+ *
+ *   IMPLEMENTATION:
+ *         The harmonic noise weighting filter is implemented in reverse
+ *         temporal order, from most recent input sample backwards through
+ *         the input sample array. The procedure follows the equation:
+ *                   x(n) = x(n) - PW_COEF*x(n - lag)
+ *         where the PW_COEF is the pitch weighting for the current
+ *         subframe and lag is the full-resolution lag for the current
+ *         subframe. x(n - lag) is found by implementing a CG_INT_MACS-
+ *         order FIR interpolating filter
+ *
+ *         Harmonic noise weighting is discussed in secion 5.5.
+ *
+ *   REFERENCE:  Sub-clause 4.1.9 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS: T_SUB, LAG, HNW_FILT, PW_COEF, CG_INT_MACS, S_LEN, LSMAX
+ *
+ **************************************************************************/
+
+void   hnwFilt(Shortword pswInSample[],
+                      Shortword pswOutSample[],
+                      Shortword pswState[],
+                      Shortword pswInCoef[],
+                      int iStateOffset,
+                      Shortword swZeroState,
+                      int iNumSamples)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+  Longword L_temp;
+  int    i,
+         j;
+
+  int    iStatIndx = S_LEN - 1 + iStateOffset;
+  int    iStatIndx1 = S_LEN + iStateOffset;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  if (swZeroState == 0)
+  {
+
+    /* zero state response assumes input and output arrays are the same */
+    /*------------------------------------------------------------------*/
+
+    for (i = 0; i < iNumSamples; i++)
+    {
+
+      /* get input with rounding */
+      /*-------------------------*/
+      L_temp = L_mac((long) 16384, pswInSample[S_LEN - i - 1], 0x4000);
+
+      for (j = 5; (j >= 0) && (iStatIndx - i + j >= 0); j--)
+        /* evaluate taps 1 - 6 that point to input */
+        /*-----------------------------------------*/
+        L_temp = L_mac(L_temp, pswInSample[iStatIndx - i + j], pswInCoef[j]);
+
+      pswOutSample[S_LEN - 1 - i] = extract_h(L_shl(L_temp, 1));
+    }
+  }
+  else
+  {
+    for (i = 0; i < iNumSamples; i++)
+    {
+
+      /* get input with rounding */
+      /*-------------------------*/
+      L_temp = L_mac((long) 16384, pswInSample[S_LEN - i - 1], 0x4000);
+
+      for (j = 5; (j >= 0) && (iStatIndx - i + j >= 0); j--)
+        /* evaluate taps 1 - 6 that point to input */
+        /*-----------------------------------------*/
+        L_temp = L_mac(L_temp, pswInSample[iStatIndx - i + j], pswInCoef[j]);
+
+      for (; (j >= 0); j--)
+        /* evaluate taps 1 - 6 that point to state */
+        /*----------------------------------------*/
+        L_temp = L_mac(L_temp, pswState[iStatIndx1 - i + j], pswInCoef[j]);
+
+      pswOutSample[S_LEN - 1 - i] = extract_h(L_shl(L_temp, 1));
+    }
+  }
+
+}
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: sfrmAnalysis
+ *
+ *   PURPOSE:
+ *
+ *     Determines the synthetic excitation for a subframe.
+ *
+ *   INPUTS:
+ *
+ *     pswWSpeech
+ *                     Input weighted speech vector to be matched.
+ *
+ *     swVoicingMode
+ *
+ *                     Voicing mode 0,1,2 or 3.  0 is unvoiced.  A
+ *                     frame parameter.
+ *
+ *     snsSqrtRs
+ *
+ *                     Normalized estimate of the excitation energy
+ *
+ *     pswHCoefs
+ *
+ *                     Coefficientss used in weighted synthesis filter,
+ *                     H(z), (a negated version is used).  pswHCoefs[0]
+ *                     is  t=-1 tap, pswHCoefs[9] is t=-10 tap.
+ *
+ *     pswLagList
+ *
+ *                     List of lags to be searched in the long-term
+ *                     predictor, determined by the open-loop lag search.
+ *
+ *     siNumLags
+ *
+ *                     Number of lags in pswLagList.
+ *
+ *     swPitch
+ *
+ *                     Fundamental pitch value to be used in harmonic-
+ *                     noise-weighting, actualPitch*OS_FCTR.
+ *
+ *     swHNWCoef
+ *                     Coefficient of the harmonic-noise-weighting filter.
+ *
+ *     ppsrCGIntFilt[0:5][0:5]
+ *
+ *                     polyphase interpolation filter,
+ *                     ppsrCGIntFilt[iTap][iPhase], OS_FCTR phases,
+ *                     CG_INT_MACS taps per phase.  Used to construct
+ *                     sequences delayed by fractional lags for Harmonic-
+ *                     Noise-Weighting.
+ *
+ *     pppsrUvCodeVec[0:1][0:6][0:39]
+ *
+ *                     unvoiced codebooks:
+ *                     pppsrUvCodeVec[codeBook][vectorNumber][time]
+ *
+ *     pppsrVcdCodeVec[0][0:8][0:39]
+ *
+ *                     voiced codebook:
+ *                     pppsrVcdCodeVect[codebook(=0)][vectorNumber][time]
+ *
+ *     swSP
+ *                     speech flag (DTX mode)
+ *
+ *   OUTPUTS:
+ *
+ *     psiLagCode
+ *
+ *                     Lag code: frame- or delta-, or zero if unvoiced.
+ *
+ *     psiVSCode1
+ *
+ *                     First vector-sum codebook code.
+ *
+ *     psiVSCode2
+ *
+ *                     Second vector-sum codebook code, or zero if voiced.
+ *
+ *     psiGsp0Code
+ *
+ *                     Gain quantizer code.
+ *
+ *     DESCRIPTION:
+ *
+ *     sfrmAnalysis() is the calling function for the subframe analysis
+ *     functions.  All subframe based processing is done by it and its
+ *     daughter functions.  All functions in this file are called by
+ *     sfrmAnalysis() or one of its daughter functions.  As can be seen
+ *     above, this routine will select the LTP lag, the VSELP
+ *     codevector(s) and the GSP0 codeword.  It is called by
+ *     speechEncoder().
+ *
+ *     The subframe processing can follow one of two paths depending on
+ *     whether the frame is voiced or unvoiced.  These two paths are now
+ *     described.
+ *
+ *     First the zero input response of H(z) is calculated (lpcZiIir());
+ *     then subtracted from the weighted speech (W(z)).  The outcome, p(n)
+ *     or pswWSVec[], will be the vector matched by the first excitation
+ *     vector (either adaptive or first VSELP codevector).  The p(n)
+ *     vector is scaled to prevent overflow.
+ *
+ *     If the frame is voiced, the closed loop lag search (closedLoop())
+ *     is performed.  An adaptive codevector lag is selected.  Using the
+ *     open loop "pitch" value, the harmonic noise weighting
+ *     coefficients are obtained.  The adaptive codevector is
+ *     reconstructed (fp_ex()), and weighted through the (zero state)
+ *     spectral (lpcZsIir()) and harmonic noise weighting filters
+ *     (hnwFilt()).
+ *
+ *     The basis vectors are also filtered through the weighting
+ *     filters.  If the frame is unvoiced, there is no spectral noise
+ *     weighting.
+ *
+ *     If voiced the VSELP basis vectors are decorrelated (decorr())
+ *     from the selected adaptive (LTP) codevector, and the VSELP
+ *     codevector search is initiated (v_srch()).
+ *
+ *     If unvoiced, the first VSELP codevector search is performed
+ *     (without any decorrelation). After a vector from the first VSELP
+ *     codebook has been selected, the second set of basis vectors are
+ *     decorrelated from the selected vector.
+ *
+ *     Once all the excitation vectors have been selected, the gain
+ *     quantizer is called, g_quant_vl().
+ *
+ *     Finally, once all subframe parameters have been found, the
+ *     selected excitation is scaled according to GSP0 (scaleExcite()),
+ *     and the composite excitation is entered into the long term
+ *     predictor history.  The final excitation is also used to update
+ *     H(z) and C(z).
+ *
+ *   REFERENCE:  Sub-clauses 4.1.8.5, 4.1.9 - 4.1.12 of GSM
+ *     Recommendation 06.20
+ *
+ *   Keywords: codewords, lag, codevectors, gsp0, decoding, analysis, t_sub
+ *
+ **************************************************************************/
+
+void   sfrmAnalysis(Shortword *pswWSpeech,
+                           Shortword swVoicingMode,
+                           struct NormSw snsSqrtRs,
+                           Shortword *pswHCoefs,
+                           Shortword *pswLagList,
+                           short siNumLags,
+                           Shortword swPitch,
+                           Shortword swHNWCoef,
+                           short *psiLagCode,
+                           short *psiVSCode1,
+                           short *psiVSCode2,
+                           short *psiGsp0Code,
+                           Shortword swSP)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Static Variables                             |
+ |_________________________________________________________________________|
+*/
+
+  static short siPrevLagCode;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  short  i,
+         j,
+         siCode,
+         siIntPitch,
+         siRemainder;
+  short  siHnwOffset,
+         siHnwNum,
+         siNumBasisVecs;
+
+  Shortword swLag,
+         swPnEnergy,
+         swPnShift,
+         swSampleA;
+  Shortword swLtpShift;
+
+  Longword L_PnEnergy;
+
+  struct NormSw snsRs00,
+         snsRs11,
+         snsRs22;
+
+  Shortword pswWSVec[S_LEN],
+         pswTempVec[S_LEN];
+  Shortword pswPVec[S_LEN],
+         pswWPVec[S_LEN];
+  Shortword ppswVselpEx[2][S_LEN],
+         ppswWVselpEx[2][S_LEN];
+  Shortword pswWBasisVecs[9 * S_LEN],
+         pswBitArray[9];
+  Shortword pswHNWCoefs[CG_INT_MACS];
+
+  Shortword *pswLtpStateOut;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Executable Code                              |
+ |_________________________________________________________________________|
+*/
+
+  pswLtpStateOut = pswLtpStateBase + LTP_LEN;
+
+  if (swSP == 1)                                             /* DTX mode */ 
+  {                                                          /* DTX mode */   
+
+    /* if not in CNI mode */
+    /*--------------------*/
+
+    /* Get the zero-input response of H(z) */
+    /*-------------------------------------*/
+
+    lpcZiIir(pswHCoefs, pswHState, pswTempVec);
+
+    /* Subtract the zero-input response of H(z) from W(z)-weighted speech. */
+    /* The result is the vector to match for the adaptive codebook (long-  */
+    /* term-predictor) search in voiced modes, or the vector to match for  */
+    /* all synthetic excitation searches in unvoiced mode.                 */
+    /*---------------------------------------------------------------------*/
+
+    for (i = 0; i < S_LEN; i++)
+    {
+
+      pswWSVec[i] = sub(pswWSpeech[i], pswTempVec[i]);  
+    }
+
+    /* scale the weighted speech vector (p[n]) s.t. its energy is strictly */
+    /* less than 1.0                                                       */
+    /*---------------------------------------------------------------------*/
+
+    swSampleA = shr(pswWSVec[0], 2);
+    L_PnEnergy = L_mac(0x001dff4cL, swSampleA, swSampleA);
+    for (i = 1; i < S_LEN; i++)
+    {
+      swSampleA = shr(pswWSVec[i], 2);   /* reduces energy by 16 */
+      L_PnEnergy = L_mac(L_PnEnergy, swSampleA, swSampleA);
+    }
+
+    swPnEnergy = round(L_PnEnergy);
+
+    if (sub(swPnEnergy, 0x7ff) <= 0)
+    {                                    /* E = [0..0x7ff] */
+
+      swPnShift = 0;
+
+    }
+    else
+    {
+
+      if (sub(swPnEnergy, 0x1fff) <= 0)
+      {                                  /* E = (0x7ff.. 0x1fff] */
+        swPnShift = 1;
+
+      }
+      else
+      {
+
+        swPnShift = 2;                   /* E = (0x1fff..0x7fff] */
+
+      }
+    }
+
+    /* shift pswWSVect down by the shift factor */
+    /*------------------------------------------*/
+
+    for (i = 0; i < S_LEN; i++)
+      pswWSVec[i] = shr(pswWSVec[i], swPnShift);
+
+
+    if (swVoicingMode > 0)
+    {
+
+      /* Do restricted adaptive codebook (long-term-predictor) search: */
+      /* the search is restricted to the lags passed in from the       */
+      /* open-loop lag search                                          */
+      /*---------------------------------------------------------------*/
+
+      siCode = closedLoopLagSearch(pswLagList, siNumLags,
+                                   pswLtpStateBase, pswHCoefs, pswWSVec,
+                                   &swLag, &swLtpShift);
+
+      /* Construct frame-lag code if this is the first subframe, */
+      /* or delta-lag code if it is not the first subframe       */
+      /*---------------------------------------------------------*/
+
+      if (swVoicingMode > 0)
+      {
+
+        if (giSfrmCnt == 0)
+        {
+          siPrevLagCode = siCode;
+          *psiLagCode = siCode;
+        }
+        else
+        {
+
+          *psiLagCode = add(sub(siCode, siPrevLagCode), DELTA_LEVELS / 2);
+          siPrevLagCode = siCode;
+        }
+      }
+
+      /* From the value of the fundamental pitch obtained in the open-loop */
+      /* lag search, get the correct phase of the interpolating filter,    */
+      /* and scale the coefficients by the Harmonic-Noise-Weighting        */
+      /* coefficient.  The result is the interpolating coefficients scaled */
+      /* by the HNW coefficient.  These will be used in all C(z) filtering */
+      /*-------------------------------------------------------------------*/
+
+      get_ipjj(swPitch, &siIntPitch, &siRemainder);
+
+      for (i = 0; i < CG_INT_MACS; i++)
+      {
+
+        pswHNWCoefs[i] = mult_r(negate(ppsrCGIntFilt[i][siRemainder]),
+                                swHNWCoef);
+      }
+
+      /* Calculate a few values which will speed up C(z) filtering:      */
+      /* "HnwOffset" is the distance in samples from the input sample of */
+      /* the C(z) filter to the first sample tapped by the interpolating */
+      /* filter.  "HnwNum" is the number of samples which need to be     */
+      /* filtered by C(z) in the zero-state case.                        */
+      /*-----------------------------------------------------------------*/
+
+      siHnwOffset = sub(-CG_INT_MACS / 2, siIntPitch);
+      siHnwNum = sub(S_LEN + CG_INT_MACS / 2 - 1, siIntPitch);
+
+      /* Perform C(z) filter on W(z)-weighted speech, get zero-input */
+      /* response of H(z)C(z) combo, subtract zero-input response    */
+      /* of H(z)C(z) from W(z)C(z)-weighted speech.  The result is   */
+      /* the vector to match for the rest of the synthetic           */
+      /* excitation searches in the voiced modes                     */
+      /*-------------------------------------------------------------*/
+
+      hnwFilt(pswWSpeech, pswWSVec, &pswWSpeech[-1], pswHNWCoefs,
+              siHnwOffset, 1, S_LEN);
+
+      hnwFilt(pswTempVec, pswTempVec, &pswHNWState[HNW_BUFF_LEN - 1],
+              pswHNWCoefs, siHnwOffset, 1, S_LEN);
+
+      for (i = 0; i < S_LEN; i++)
+      {
+
+        pswWSVec[i] = shr(sub(pswWSVec[i], pswTempVec[i]), swPnShift);
+
+      }
+
+      /* Recontruct adaptive codebook (long-term-predictor) vector, */
+      /* weight it through H(z) and C(z), each with zero state      */
+      /*------------------------------------------------------------*/
+
+      fp_ex(swLag, pswLtpStateOut);
+
+      for (i = 0; i < S_LEN; i++)
+        pswPVec[i] = pswLtpStateOut[i];
+
+      lpcZsIir(pswPVec, pswHCoefs, pswWPVec);
+
+      if (siHnwNum > 0)
+      {
+        hnwFilt(pswWPVec, pswWPVec, NULL, pswHNWCoefs, siHnwOffset,
+                0, siHnwNum);
+      }
+      for (i = 0; i < S_LEN; i++)
+      {
+        pswPVec[i] = shr(pswPVec[i], swLtpShift);
+        pswWPVec[i] = shr(pswWPVec[i], swLtpShift);
+      }
+
+    }
+    else
+    {
+
+      /* Unvoiced mode: clear all voiced variables */
+      /*-------------------------------------------*/
+
+      swLag = 0;
+      *psiLagCode = 0;
+      siHnwNum = 0;
+    }
+
+    /* "NumBasisVecs" will be the number of basis vectors in */
+    /* the vector-sum codebook(s)                            */
+    /*-------------------------------------------------------*/
+
+    if (swVoicingMode > 0)
+      siNumBasisVecs = C_BITS_V;
+
+    else
+      siNumBasisVecs = C_BITS_UV;
+
+    /* Filter the basis vectors through H(z) with zero state, and if  */
+    /* voiced, through C(z) with zero state                           */
+    /*----------------------------------------------------------------*/
+
+    for (i = 0; i < siNumBasisVecs; i++)
+    {
+
+      if (swVoicingMode > 0)
+      {
+
+        lpcZsIir((Shortword *) pppsrVcdCodeVec[0][i], pswHCoefs,
+                 &pswWBasisVecs[i * S_LEN]);
+      }
+      else
+      {
+
+        lpcZsIir((Shortword *) pppsrUvCodeVec[0][i], pswHCoefs,
+                 &pswWBasisVecs[i * S_LEN]);
+      }
+
+      if (siHnwNum > 0)
+      {
+
+        hnwFilt(&pswWBasisVecs[i * S_LEN], &pswWBasisVecs[i * S_LEN], 
+                NULL, pswHNWCoefs, siHnwOffset, 0, siHnwNum);
+      }
+    }
+
+    /* If voiced, make the H(z)C(z)-weighted basis vectors orthogonal to */
+    /* the H(z)C(z)-weighted adaptive codebook vector                    */
+    /*-------------------------------------------------------------------*/
+
+    if (swVoicingMode > 0)
+      decorr(siNumBasisVecs, pswWPVec, pswWBasisVecs);
+
+    /* Do the vector-sum codebook search on the H(z)C(z)-weighted, */
+    /* orthogonalized basis vectors                                */
+    /*-------------------------------------------------------------*/
+
+    *psiVSCode1 = v_srch(pswWSVec, pswWBasisVecs, siNumBasisVecs);
+
+    /* Construct the chosen vector-sum codebook vector from basis vectors */
+    /*--------------------------------------------------------------------*/
+
+    b_con(*psiVSCode1, siNumBasisVecs, pswBitArray);
+
+    if (swVoicingMode > 0)
+      v_con((Shortword *) pppsrVcdCodeVec[0][0], ppswVselpEx[0], pswBitArray,
+            siNumBasisVecs);
+
+    else
+      v_con((Shortword *) pppsrUvCodeVec[0][0], ppswVselpEx[0], pswBitArray,
+            siNumBasisVecs);
+
+    if (swVoicingMode == 0)
+    {
+
+      /* Construct the H(z)-weighted 1st-codebook vector */
+      /*-------------------------------------------------*/
+
+      v_con(pswWBasisVecs, ppswWVselpEx[0], pswBitArray, siNumBasisVecs);
+
+      /* Filter the 2nd basis vector set through H(z) with zero state */
+      /*--------------------------------------------------------------*/
+
+      for (i = 0; i < siNumBasisVecs; i++)
+      {
+
+        lpcZsIir((Shortword *) pppsrUvCodeVec[1][i], pswHCoefs,
+                 &pswWBasisVecs[i * S_LEN]);
+      }
+
+      /* Make the 2nd set of H(z)-weighted basis vectors orthogonal to the */
+      /* H(z)-weighted 1st-codebook vector                                 */
+      /*-------------------------------------------------------------------*/
+
+      decorr(siNumBasisVecs, ppswWVselpEx[0], pswWBasisVecs);
+
+      /* Do the vector-sum codebook search on the H(z)-weighted, */
+      /* orthogonalized, 2nd basis vector set                    */
+      /*---------------------------------------------------------*/
+
+      *psiVSCode2 = v_srch(pswWSVec, pswWBasisVecs, siNumBasisVecs);
+
+      /* Construct the chosen vector-sum codebook vector from the 2nd set */
+      /* of basis vectors                                                 */
+      /*------------------------------------------------------------------*/
+
+      b_con(*psiVSCode2, siNumBasisVecs, pswBitArray);
+
+      v_con((Shortword *) pppsrUvCodeVec[1][0], ppswVselpEx[1], pswBitArray,
+            siNumBasisVecs);
+    }
+
+    else
+      *psiVSCode2 = 0;
+
+    /* Filter the 1st-codebook vector through H(z) (also through C(z) */
+    /* if appropriate)                                                */
+    /*----------------------------------------------------------------*/
+
+    lpcZsIir(ppswVselpEx[0], pswHCoefs, ppswWVselpEx[0]);
+
+    if (siHnwNum > 0)
+    {
+      hnwFilt(ppswWVselpEx[0], ppswWVselpEx[0], NULL, pswHNWCoefs,
+              siHnwOffset, 0, siHnwNum);
+    }
+
+    if (swVoicingMode == 0)
+    {
+
+      /* Filter the 2nd-codebook vector through H(z) */  
+      /*---------------------------------------------*/
+
+      lpcZsIir(ppswVselpEx[1], pswHCoefs, ppswWVselpEx[1]);
+    }
+
+    /* Get the square-root of the ratio of residual energy to      */
+    /* excitation vector energy for each of the excitation sources */
+    /*-------------------------------------------------------------*/
+
+    if (swVoicingMode > 0)
+    {
+
+      rs_rrNs(pswPVec, snsSqrtRs, &snsRs00);
+    }
+
+    rs_rrNs(ppswVselpEx[0], snsSqrtRs, &snsRs11);
+
+    if (swVoicingMode == 0)
+    {
+
+      rs_rrNs(ppswVselpEx[1], snsSqrtRs, &snsRs22);
+    }
+
+    /* Determine the vector-quantized gains for each of the excitations */
+    /*------------------------------------------------------------------*/
+
+    *psiGsp0Code = g_quant_vl(swVoicingMode, pswWSVec, swPnShift,
+                              pswWPVec,
+                              ppswWVselpEx[0], ppswWVselpEx[1], snsRs00,
+                              snsRs11, snsRs22);
+
+  }                                                            /* DTX mode */
+  else                                                         /* DTX mode */
+  {                                                            /* DTX mode */
+    
+    /* swSP == 0, currently in comfort noise insertion mode */ /* DTX mode */
+    /*------------------------------------------------------*/ /* DTX mode */
+    
+    /* generate the random codevector */                       /* DTX mode */ 
+    siNumBasisVecs = C_BITS_UV;                                /* DTX mode */ 
+
+    /* build codevector 1 */                                   /* DTX mode */ 
+
+    b_con(*psiVSCode1, siNumBasisVecs, pswBitArray);           /* DTX mode */ 
+    v_con((Shortword *) pppsrUvCodeVec[0][0], ppswVselpEx[0],  /* DTX mode */ 
+          pswBitArray, siNumBasisVecs);                        /* DTX mode */ 
+
+    /* build codevector 2 */                                   /* DTX mode */ 
+
+    b_con(*psiVSCode2, siNumBasisVecs, pswBitArray);           /* DTX mode */ 
+    v_con((Shortword *) pppsrUvCodeVec[1][0], ppswVselpEx[1],  /* DTX mode */ 
+          pswBitArray, siNumBasisVecs);                        /* DTX mode */ 
+
+
+    /* get rs_rr for the two vectors */                        /* DTX mode */ 
+    rs_rrNs(ppswVselpEx[0], snsSqrtRs, &snsRs11);              /* DTX mode */ 
+    rs_rrNs(ppswVselpEx[1], snsSqrtRs, &snsRs22);              /* DTX mode */ 
+
+  }                                                            /* DTX mode */
+
+
+  /* Scale the excitations, each by its gain, and add them. Put the  */
+  /* result at the end of the adaptive codebook (long-term-predictor */
+  /* state)                                                          */
+  /*-----------------------------------------------------------------*/
+
+  if (swVoicingMode == 0)
+  {
+
+    /* unvoiced */
+    /* -------- */
+
+    scaleExcite(ppswVselpEx[0],
+                pppsrGsp0[swVoicingMode][*psiGsp0Code][0],
+                snsRs11, ppswVselpEx[0]);
+    scaleExcite(ppswVselpEx[1],
+                pppsrGsp0[swVoicingMode][*psiGsp0Code][1],
+                snsRs22, ppswVselpEx[1]);
+
+    /* now combine the two scaled excitations */
+    /* -------------------------------------- */
+    for (i = 0; i < S_LEN; i++)
+      pswTempVec[i] = add(ppswVselpEx[0][i], ppswVselpEx[1][i]);
+  }
+
+  else
+  {
+
+    /* voiced */
+    /* ------ */
+
+    scaleExcite(pswPVec,
+                pppsrGsp0[swVoicingMode][*psiGsp0Code][0],
+                snsRs00, pswPVec);
+    scaleExcite(ppswVselpEx[0],
+                pppsrGsp0[swVoicingMode][*psiGsp0Code][1],
+                snsRs11, ppswVselpEx[0]);
+
+    /* now combine the two scaled excitations */
+    /* -------------------------------------- */
+    for (i = 0; i < S_LEN; i++)
+      pswTempVec[i] = add(pswPVec[i], ppswVselpEx[0][i]);
+  }
+
+  /* Update the long-term-predictor state using the synthetic excitation */
+  /*---------------------------------------------------------------------*/
+
+  for (i = -LTP_LEN; i < -S_LEN; i++)
+    pswLtpStateOut[i] = pswLtpStateOut[i + S_LEN];
+
+  for (i = -S_LEN, j = 0; j < S_LEN; i++, j++)
+    pswLtpStateOut[i] = pswTempVec[j];
+
+  /* Filter the synthetic excitation through the weighting filters, */
+  /* H(z) and C(z), only to update filter states (Note that C(z)    */
+  /* state may be updated without filtering, since it is an FIR)    */
+  /* */
+  /* First, perform one subframe's worth of delay on C(z) state     */
+  /*----------------------------------------------------------------*/
+
+  for (i = 0; i < HNW_BUFF_LEN - S_LEN; i++)
+    pswHNWState[i] = pswHNWState[i + S_LEN];
+
+  /* Second, perform H(z) filter on excitation, output goes into */
+  /* C(z) state                                                  */
+  /*-------------------------------------------------------------*/
+
+  lpcIir(pswTempVec, pswHCoefs, pswHState,
+         &pswHNWState[HNW_BUFF_LEN - S_LEN]);
+
+}                                      /* end of sfrmAnalysis() */
+
+/***************************************************************************
+ *
+ *   FUNCTION NAME: v_srch
+ *
+ *   PURPOSE:
+ *     The purpose of this function is search a vector-sum codebook for the
+ *     optimal vector
+ *
+ *   INPUTS:
+ *
+ *     pswWInput[0:S_LEN]
+ *
+ *                     the weighted input speech frame, with the zero-input
+ *                     response of H(z) subtracted
+ *
+ *     pswWBasisVecs[0:S_LEN*siNumBasis]
+ *
+ *                     weighted, decorrelated vector-sum codebook basis
+ *                     vectors
+ *
+ *     siNumBasis
+ *
+ *                     number of basis vectors
+ *
+ *   OUTPUTS:
+ *
+ *     none
+ *
+ *   RETURN VALUE:
+ *
+ *     sw1
+ *
+ *                     output code
+ *
+ *   REFERENCE:  Sub-clause 4.1.10.2 of GSM Recommendation 06.20
+ *
+ *   KEYWORDS: v_srch, codebook, search
+ *
+ **************************************************************************/
+
+Shortword v_srch(Shortword pswWInput[], Shortword pswWBasisVecs[],
+                        short int siNumBasis)
+{
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Local Constants                            |
+ |_________________________________________________________________________|
+*/
+
+#define  V_ARRAY_SIZE   ( 1 << (C_BITS_V-1) )  - 1
+#define  UN_ARRAY_SIZE  ( 1 << (C_BITS_UV-1) ) - 1
+#define  MINUS_HALF     -0x4000
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                           Local Static Variables                        |
+ |_________________________________________________________________________|
+*/
+  static Shortword
+         pswUpdateIndexV[V_ARRAY_SIZE] =
+  {
+    0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x1b, 0x00, 0x09,
+    0x48, 0x5a, 0x00, 0x51, 0x48, 0x24, 0x00, 0x09, 0x48, 0x12,
+    0x00, 0x51, 0x48, 0x63, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51,
+    0x48, 0x2d, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x1b,
+    0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x6c, 0x00, 0x09,
+    0x48, 0x12, 0x00, 0x51, 0x48, 0x63, 0x00, 0x09, 0x48, 0x5a,
+    0x00, 0x51, 0x48, 0x36, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51,
+    0x48, 0x1b, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x24,
+    0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x63, 0x00, 0x09,
+    0x48, 0x5a, 0x00, 0x51, 0x48, 0x75, 0x00, 0x09, 0x48, 0x12,
+    0x00, 0x51, 0x48, 0x1b, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51,
+    0x48, 0x6c, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x63,
+    0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x3f, 0x00, 0x09,
+    0x48, 0x12, 0x00, 0x51, 0x48, 0x1b, 0x00, 0x09, 0x48, 0x5a,
+    0x00, 0x51, 0x48, 0x24, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51,
+    0x48, 0x63, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x2d,
+    0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x1b, 0x00, 0x09,
+    0x48, 0x5a, 0x00, 0x51, 0x48, 0x6c, 0x00, 0x09, 0x48, 0x12,
+    0x00, 0x51, 0x48, 0x63, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51,
+    0x48, 0x7e, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x1b,
+    0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x24, 0x00, 0x09,
+    0x48, 0x12, 0x00, 0x51, 0x48, 0x63, 0x00, 0x09, 0x48, 0x5a,
+    0x00, 0x51, 0x48, 0x75, 0x00, 0x09, 0x48, 0x12, 0x00, 0x51,
+    0x48, 0x1b, 0x00, 0x09, 0x48, 0x5a, 0x00, 0x51, 0x48, 0x6c,
+    0x00, 0x09, 0x48, 0x12, 0x00, 0x51, 0x48, 0x63, 0x00, 0x09,
+    0x48, 0x5a, 0x00, 0x51, 0x48,
+  },
+         pswBitIndexV[V_ARRAY_SIZE] =
+  {
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x06, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x07, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x07, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01,
+  },
+         pswModNextBitV[C_BITS_V] =
+  {
+    1, 2, 3, 4, 5, 6, 7, 0, 1,
+  },
+         pswUpdateIndexUn[UN_ARRAY_SIZE] =
+  {
+    0x00, 0x07, 0x2a, 0x0e, 0x00, 0x31, 0x2a, 0x15, 0x00, 0x07,
+    0x2a, 0x38, 0x00, 0x31, 0x2a, 0x1c, 0x00, 0x07, 0x2a, 0x0e,
+    0x00, 0x31, 0x2a, 0x3f, 0x00, 0x07, 0x2a, 0x38, 0x00, 0x31,
+    0x2a, 0x23, 0x00, 0x07, 0x2a, 0x0e, 0x00, 0x31, 0x2a, 0x15,
+    0x00, 0x07, 0x2a, 0x38, 0x00, 0x31, 0x2a, 0x46, 0x00, 0x07,
+    0x2a, 0x0e, 0x00, 0x31, 0x2a, 0x3f, 0x00, 0x07, 0x2a, 0x38,
+    0x00, 0x31, 0x2a,
+  },
+         pswBitIndexUn[UN_ARRAY_SIZE] =
+  {
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02,
+    0x01, 0x00, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x04,
+    0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02,
+    0x01, 0x03, 0x01, 0x02, 0x01, 0x04, 0x01, 0x02, 0x01, 0x03,
+    0x01, 0x02, 0x01,
+  },
+         pswModNextBitUV[C_BITS_UV] =
+  {
+    1, 2, 3, 4, 5, 0, 1,
+  };
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                            Automatic Variables                          |
+ |_________________________________________________________________________|
+*/
+
+  Shortword **pppswDubD[C_BITS_V - 1],
+        *ppswD[C_BITS_V - 1],
+         pswCGUpdates[2 * C_BITS_V * (C_BITS_V - 1)],
+         pswDSpace[2 * C_BITS_V * (C_BITS_V - 1)],
+        *ppswDPSpace[C_BITS_V * (C_BITS_V - 1)],
+         pswBits[C_BITS_V - 1],
+        *pswUpdatePtr,
+        *pswUIndex,
+        *pswBIndex,
+        *pswModNextBit,
+        *psw0,
+        *psw1,
+        *psw2,
+         swC0,
+         swG0,
+         swCC,
+         swG,
+         swCCMax,
+         swGMax,
+         sw1;
+  Longword pL_R[C_BITS_V],
+         L_R,
+         L_MaxC,
+         L_C0,
+         L_D,
+         L_G0,
+         L_C,
+         L_G,
+         L_1;
+  short int siI,
+         siJ,
+         siK,
+         siEBits,
+         siShiftCnt,
+         siBitIndex,
+         siBest,
+         siMask;
+
+/*_________________________________________________________________________
+ |                                                                         |
+ |                              Executable Code                            |
+ |_________________________________________________________________________|
+*/
+
+  /* initialize variables based on voicing mode */
+  /* ------------------------------------------ */
+
+  if (sub(siNumBasis, C_BITS_V) == 0)
+  {
+    siEBits = C_BITS_V_1;
+    pswUIndex = pswUpdateIndexV;
+    pswBIndex = pswBitIndexV;
+    pswModNextBit = pswModNextBitV;
+  }
+  else
+  {
+    siEBits = C_BITS_UV_1;
+    pswUIndex = pswUpdateIndexUn;
+    pswBIndex = pswBitIndexUn;
+    pswModNextBit = pswModNextBitUV;
+  }
+
+  /* initialize pointers */
+  /* ------------------- */
+
+  for (siI = 0; siI < siNumBasis - 1; siI++)
+  {
+
+    pppswDubD[siI] = &ppswDPSpace[siI * siNumBasis];
+
+    for (siJ = 0; siJ < siNumBasis; siJ++)
+    {
+      ppswDPSpace[(siI * siNumBasis) + siJ] =
+              &pswDSpace[(siI * siNumBasis * 2) + (siJ * 2)];
+    }
+    ppswD[siI] = &pswDSpace[siI * siNumBasis];
+  }
+
+  /* compute correlations (Rm) between given vector and basis vectors,   */
+  /* store in double precision; maintain max C for later scaling of Rm's */
+  /* ------------------------------------------------------------------- */
+
+  L_MaxC = 0L;
+
+  for (siI = 0; siI < siNumBasis; siI++)
+  {
+
+    L_R = L_mult(pswWBasisVecs[siI * S_LEN], pswWInput[0]);
+
+    for (siJ = 1; siJ < S_LEN; siJ++)
+    {
+      L_R = L_mac(L_R, pswWBasisVecs[siJ + (siI * S_LEN)], pswWInput[siJ]);
+    }
+    pL_R[siI] = L_R;
+    L_R = L_abs(L_R);
+    L_MaxC = L_add(L_R, L_MaxC);
+  }
+
+  /* normalize max C to get scaling shift count */
+  /* scale Rm's and calculate C(0)              */
+  /* ------------------------------------------ */
+
+  /* max abs(C) after scale is <= 0.5 */
+  siShiftCnt = add(-1, norm_l(L_MaxC));
+
+  L_C0 = 0L;
+
+  for (siI = 0; siI < siNumBasis; siI++)
+  {
+
+    L_R = L_shl(pL_R[siI], siShiftCnt);
+
+    L_C0 = L_sub(L_C0, L_R);
+
+    pL_R[siI] = L_shl(L_R, 1);
+
+  }
+  swC0 = extract_h(L_C0);
+
+  /* compute correlations (Dmj, for m != j) between the basis vectors */
+  /* store in double precision                                        */
+  /* ---------------------------------------------------------------- */
+
+  for (siI = 0; siI < siNumBasis - 1; siI++)
+  {
+
+    for (siJ = siI + 1; siJ < siNumBasis; siJ++)
+    {
+
+      L_D = L_mult(pswWBasisVecs[siI * S_LEN], pswWBasisVecs[siJ * S_LEN]);
+
+      for (siK = 1; siK < S_LEN; siK++)
+      {
+        L_D = L_mac(L_D, pswWBasisVecs[siK + (siI * S_LEN)],
+                    pswWBasisVecs[siK + (siJ * S_LEN)]);
+      }
+      pppswDubD[siI][siJ][0] = extract_h(L_D);
+      pppswDubD[siI][siJ][1] = extract_l(L_D);
+    }
+  }
+
+  /* compute the sum of the Djj's (to be used for scaling the Dmj's and   */
+  /* for computing G(0)); normalize it, get shift count for scaling Dmj's */
+  /* -------------------------------------------------------------------- */
+
+  psw1 = pswWBasisVecs;
+
+  L_G0 = L_mult(psw1[0], psw1[0]);
+
+  for (siI = 1; siI < siNumBasis * S_LEN; siI++)
+  {
+    L_G0 = L_mac(L_G0, psw1[siI], psw1[siI]);
+  }
+
+  siShiftCnt = add(-4, norm_l(L_G0));
+
+  L_G0 = L_shl(L_G0, siShiftCnt);
+
+  /* scale Dmj's and compute G(0) */
+  /* ---------------------------- */
+
+  for (siI = 0; siI < siNumBasis - 1; siI++)
+  {
+
+    for (siJ = siI + 1; siJ < siNumBasis; siJ++)
+    {
+
+      L_D = L_deposit_h(pppswDubD[siI][siJ][0]);
+      L_D = L_add(L_D, (LSP_MASK & L_deposit_l(pppswDubD[siI][siJ][1])));
+
+      L_D = L_shl(L_D, siShiftCnt);
+
+      L_D = L_shl(L_D, 1);
+
+      L_G0 = L_add(L_D, L_G0);
+
+      L_D = L_shl(L_D, 1);
+
+      ppswD[siI][siJ] = round(L_D);
+    }
+  }
+
+  swG0 = extract_h(L_G0);
+
+  /* build array of update values for codebook search */
+  /* ------------------------------------------------ */
+
+  for (siI = 0; siI < siEBits; siI++)
+  {
+    pswCGUpdates[siI * (siEBits + 1)] = round(pL_R[siI]);
+  }
+  psw0 = &pswCGUpdates[siEBits];
+  psw1 = &pswCGUpdates[1];
+  psw2 = &pswCGUpdates[2 * siEBits];
+
+  for (siI = 0; siI < siEBits - 1; siI++)
+  {
+
+    for (siJ = siI + 1; siJ < siEBits; siJ++)
+    {
+
+      L_1 = L_deposit_h(ppswD[siI][siJ]);
+      L_1 = L_shl(L_1, 1);
+      psw1[siJ - 1 + (siI * siEBits)] = extract_h(L_1);
+      psw2[siI + (siEBits * (siJ - 1))] = extract_h(L_1);
+    }
+    psw0[siI * (siEBits + 1)] = negate(ppswD[siI][siEBits]);
+  }
+
+  psw0[siI * (siEBits + 1)] = negate(ppswD[siEBits - 1][siEBits]);
+
+  /* copy to negative side of array */
+  /* ------------------------------ */
+
+  psw0 = &pswCGUpdates[(siEBits + 1) * siEBits];
+
+  for (siI = 0; siI < (siEBits + 1) * siEBits; siI++)
+  {
+    psw0[siI] = negate(pswCGUpdates[siI]);
+  }
+
+  /* initialize array of bits (magnitude = 0.5) */
+  /* ------------------------------------------ */
+
+  for (siI = 0; siI < siEBits; siI++)
+  {
+    pswBits[siI] = MINUS_HALF;
+  }
+
+  /* initialize and do codebook search */
+  /* --------------------------------- */
+
+  swGMax = swG0;
+  swCCMax = mult_r(swC0, swC0);
+  L_C = L_deposit_h(swC0);
+  L_G = L_deposit_h(swG0);
+  siBest = 0;
+
+  for (siI = 0; siI < (1 << siEBits) - 1; siI++)
+  {
+
+    pswUpdatePtr = &pswCGUpdates[pswUIndex[siI]];
+
+    siBitIndex = pswBIndex[siI];
+
+    L_C = L_msu(L_C, pswUpdatePtr[0], 0x8000);
+
+    for (siJ = 0; siJ < siEBits - 1; siJ++)
+    {
+      L_G = L_mac(L_G, pswUpdatePtr[siJ + 1], pswBits[siBitIndex]);
+      siBitIndex = pswModNextBit[siBitIndex];
+    }
+    L_G = L_msu(L_G, pswUpdatePtr[siJ + 1], 0x8000);
+
+    pswBits[siBitIndex] = negate(pswBits[siBitIndex]);
+
+    sw1 = extract_h(L_C);
+    swCC = mult_r(sw1, sw1);
+
+    swG = extract_h(L_G);
+    L_1 = L_mult(swG, swCCMax);
+
+    L_1 = L_msu(L_1, swGMax, swCC);
+
+    if (L_1 < 0)
+    {
+      swCCMax = swCC;
+      swGMax = swG;
+      siBest = add(siI, 1);
+    }
+  }
+
+  /* generate code for positive correlation;       */
+  /* compute correlation, if negative, invert code */
+  /* --------------------------------------------- */
+
+  sw1 = siBest ^ (shr(siBest, 1));
+
+  siMask = 0x1;
+  L_1 = 0L;
+
+  for (siI = 0; siI < siNumBasis; siI++)
+  {
+
+    if ((sw1 & siMask) == 0)
+    {
+      L_1 = L_sub(L_1, pL_R[siI]);
+    }
+    else
+    {
+      L_1 = L_add(L_1, pL_R[siI]);
+    }
+
+    siMask = shl(siMask, 1);
+  }
+
+  if (L_1 < 0)
+  {
+    sw1 = sw1 ^ (sub(shl(1, siNumBasis), 1));
+  }
+
+  /* return code */
+  /* ----------- */
+
+  return (sw1);
+}