view sp_sfrm.c @ 2:aa7cc4333d95

Makefile: suppress some of the noise
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 14 Jun 2024 23:57:53 +0000
parents 9008dbc8ca74
children
line wrap: on
line source

/***************************************************************************
 *
 *   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);
}