5472 lines
169 KiB
C
5472 lines
169 KiB
C
/***************************************************************************
|
|
*
|
|
* File Name: sp_dec.c
|
|
*
|
|
* Purpose:
|
|
* Contains all functions for decoding speech. It does not
|
|
* include those routines needed to decode channel information.
|
|
*
|
|
* Since the GSM half-rate speech coder is an analysis-by-synthesis
|
|
* coder, many of the routines in this file are also called by the
|
|
* encoder. Functions are included for coded-parameter lookup,
|
|
* LPC filter coefficient interpolation, excitation vector lookup
|
|
* and construction, vector quantized gain lookup, and LPC synthesis
|
|
* filtering. In addition, some post-processing functions are
|
|
* included.
|
|
*
|
|
* 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.
|
|
*
|
|
* The entire speech decoder, under which all these routines fall,
|
|
* except were noted:
|
|
* speechDecoder()
|
|
*
|
|
* Spectral Smoothing of LPC:
|
|
* a_sst()
|
|
* aFlatRcDp()
|
|
* rcToCorrDpL()
|
|
* aToRc()
|
|
* rcToADp()
|
|
* VSELP codevector construction:
|
|
* b_con()
|
|
* v_con()
|
|
* LTP vector contruction:
|
|
* fp_ex()
|
|
* get_ipjj()
|
|
* lagDecode()
|
|
* LPC contruction
|
|
* getSfrmLpc()
|
|
* interpolateCheck()
|
|
* res_eng()
|
|
* lookupVq()
|
|
* Excitation scaling:
|
|
* rs_rr()
|
|
* g_corr1() (no scaling)
|
|
* rs_rrNs()
|
|
* g_corr1s() (g_corr1 with scaling)
|
|
* scaleExcite()
|
|
* Post filtering:
|
|
* pitchPreFilt()
|
|
* agcGain()
|
|
* lpcIir()
|
|
* r0BasedEnergyShft()
|
|
* spectralPostFilter()
|
|
* lpcFir()
|
|
*
|
|
*
|
|
* Routines not referenced by speechDecoder()
|
|
* Filtering routines:
|
|
* lpcIrZsIir()
|
|
* lpcZiIir()
|
|
* lpcZsFir()
|
|
* lpcZsIir()
|
|
* lpcZsIirP()
|
|
* Square root:
|
|
* sqroot()
|
|
*
|
|
**************************************************************************/
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Include Files |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#include "typedefs.h"
|
|
#include "mathhalf.h"
|
|
#include "sp_rom.h"
|
|
#include "sp_dec.h"
|
|
#include "err_conc.h"
|
|
#include "dtx.h"
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Functions (scope is limited to this file) |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
static void a_sst(int16_t swAshift, int16_t swAscale,
|
|
int16_t pswDirectFormCoefIn[],
|
|
int16_t pswDirectFormCoefOut[]);
|
|
|
|
static short aToRc(int16_t swAshift, int16_t pswAin[],
|
|
int16_t pswRc[]);
|
|
|
|
static int16_t agcGain(int16_t pswStateCurr[],
|
|
struct NormSw snsInSigEnergy,
|
|
int16_t swEngyRShft);
|
|
|
|
static int16_t lagDecode(int16_t swDeltaLag);
|
|
|
|
static void lookupVq(int16_t pswVqCodeWds[], int16_t pswRCOut[]);
|
|
|
|
static void pitchPreFilt(int16_t pswExcite[],
|
|
int16_t swRxGsp0,
|
|
int16_t swRxLag,
|
|
int16_t swUvCode,
|
|
int16_t swSemiBeta,
|
|
struct NormSw snsSqrtRs,
|
|
int16_t pswExciteOut[],
|
|
int16_t pswPPreState[]);
|
|
|
|
static void spectralPostFilter(int16_t pswSPFIn[],
|
|
int16_t pswNumCoef[], int16_t pswDenomCoef[],
|
|
int16_t pswSPFOut[]);
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Defines |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define P_INT_MACS 10
|
|
#define ASCALE 0x0800
|
|
#define ASHIFT 4
|
|
#define DELTA_LEVELS 16
|
|
#define GSP0_SCALE 1
|
|
#define C_BITS_V 9 /* number of bits in any voiced VSELP
|
|
* codeword */
|
|
#define C_BITS_UV 7 /* number of bits in a unvoiced VSELP
|
|
* codeword */
|
|
#define MAXBITS C_BITS_V /* max number of bits in any VSELP
|
|
* codeword */
|
|
#define LTP_LEN 147 /* 147==0x93 length of LTP history */
|
|
#define SQRT_ONEHALF 0x5a82 /* the 0.5 ** 0.5 */
|
|
#define LPC_ROUND 0x00000800L /* 0x8000 >> ASHIFT */
|
|
#define AFSHIFT 2 /* number of right shifts to be
|
|
* applied to the autocorrelation
|
|
* sequence in aFlatRcDp */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| State variables (globals) |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t gswPostFiltAgcGain,
|
|
gpswPostFiltStateNum[NP],
|
|
gpswPostFiltStateDenom[NP],
|
|
swPostEmphasisState,
|
|
pswSynthFiltState[NP],
|
|
pswOldFrmKsDec[NP],
|
|
pswOldFrmAsDec[NP],
|
|
pswOldFrmPFNum[NP],
|
|
pswOldFrmPFDenom[NP],
|
|
swOldR0Dec,
|
|
pswLtpStateBaseDec[LTP_LEN + S_LEN],
|
|
pswPPreState[LTP_LEN + S_LEN];
|
|
|
|
|
|
int16_t swMuteFlagOld; /* error concealment */
|
|
|
|
|
|
/* DTX state variables */
|
|
/* ------------------- */
|
|
|
|
int16_t swRxDTXState = CNINTPER - 1; /* DTX State at the rx.
|
|
* Modulo */
|
|
|
|
/* counter [0,11]. */
|
|
|
|
int16_t swDecoMode = SPEECH;
|
|
int16_t swDtxMuting = 0;
|
|
int16_t swDtxBfiCnt = 0;
|
|
|
|
int16_t swOldR0IndexDec = 0;
|
|
|
|
int16_t swRxGsHistPtr = 0;
|
|
int32_t pL_RxGsHist[(OVERHANG - 1) * N_SUB];
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Global Data |
|
|
| (scope is global to this file) |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t swR0Dec;
|
|
|
|
int16_t swVoicingMode, /* MODE */
|
|
pswVq[3], /* LPC1, LPC2, LPC3 */
|
|
swSi, /* INT_LPC */
|
|
swEngyRShift; /* for use by spectral postfilter */
|
|
|
|
|
|
int16_t swR0NewCN; /* DTX mode */
|
|
|
|
extern int32_tRom ppLr_gsTable[4][32]; /* DTX mode */
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: aFlatRcDp
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Given a int32_t autocorrelation sequence, representing LPC
|
|
* information, aFlatRcDp converts the vector to one of NP
|
|
* int16_t reflection coefficients.
|
|
*
|
|
* INPUT:
|
|
*
|
|
*
|
|
* pL_R[0:NP] - An input int32_t autocorrelation sequence, (pL_R[0] =
|
|
* not necessarily 0x7fffffffL). pL_R is altered in the
|
|
* call, by being right shifted by global constant
|
|
* AFSHIFT bits.
|
|
*
|
|
* The input array pL_R[] should be shifted left as much
|
|
* as possible to improve precision.
|
|
*
|
|
* AFSHIFT - The number of right shifts to be applied to the
|
|
* normalized autocorrelation sequence pL_R.
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* pswRc[0:NP-1] - A int16_t output vector of NP reflection
|
|
* coefficients.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This routine transforms LPC information from one set of
|
|
* parameters to another. It is better suited for fixed point
|
|
* implementations than the Levinson-Dubin recursion.
|
|
*
|
|
* The function is called by a_sst(), and getNWCoefs(). In a_sst()
|
|
* direct form coefficients are converted to autocorrelations,
|
|
* and smoothed in that domain. Conversion back to direct form
|
|
* coefficients is done by calling aFlatRc(), followed by rcToADp().
|
|
*
|
|
* In getNwCoefs() again the conversion back to direct form
|
|
* coefficients is done by calling aFlatRc(), followed by rcToADp().
|
|
* In getNwCoefs() an autocorrelation sequence is generated from the
|
|
* impulse response of the weighting filters.
|
|
*
|
|
* The fundamental recursion is derived from AFLAT, which is
|
|
* described in section 4.1.4.1.
|
|
*
|
|
* Unlike in AFLAT where the reflection coefficients are known, here
|
|
* they are the unknowns. PBar and VBar for j==0 are initially
|
|
* known, as is rSub1. From this information the next set of P's
|
|
* and V's are generated. At the end of the recursion the next,
|
|
* reflection coefficient rSubj (pswRc[j]) can be calcluated by
|
|
* dividing Vsubj by Psubj.
|
|
*
|
|
* Precision is crucial in this routine. At each stage, a
|
|
* normalization is performed prior to the reflection coefficient
|
|
* calculation. In addition, to prevent overflow, the
|
|
* autocorrelation sequence is scaled down by ASHIFT (4) right
|
|
* shifts.
|
|
*
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.9 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: reflection coefficients, AFLAT, aflat, recursion, LPC
|
|
* KEYWORDS: autocorrelation
|
|
*
|
|
*************************************************************************/
|
|
|
|
void aFlatRcDp(int32_t *pL_R, int16_t *pswRc)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t pL_pjNewSpace[NP];
|
|
int32_t pL_pjOldSpace[NP];
|
|
int32_t pL_vjNewSpace[2 * NP - 1];
|
|
int32_t pL_vjOldSpace[2 * NP - 1];
|
|
|
|
int32_t *pL_pjOld;
|
|
int32_t *pL_pjNew;
|
|
int32_t *pL_vjOld;
|
|
int32_t *pL_vjNew;
|
|
int32_t *pL_swap;
|
|
|
|
int32_t L_temp;
|
|
int32_t L_sum;
|
|
int16_t swRc,
|
|
swRcSq,
|
|
swTemp,
|
|
swTemp1,
|
|
swAbsTemp1,
|
|
swTemp2;
|
|
int i,
|
|
j;
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
pL_pjOld = pL_pjOldSpace;
|
|
pL_pjNew = pL_pjNewSpace;
|
|
pL_vjOld = pL_vjOldSpace + NP - 1;
|
|
pL_vjNew = pL_vjNewSpace + NP - 1;
|
|
|
|
|
|
/* Extract the 0-th reflection coefficient */
|
|
/*-----------------------------------------*/
|
|
|
|
swTemp1 = round(pL_R[1]);
|
|
swTemp2 = round(pL_R[0]);
|
|
swAbsTemp1 = abs_s(swTemp1);
|
|
if (swTemp2 <= 0 || sub(swAbsTemp1, swTemp2) >= 0)
|
|
{
|
|
j = 0;
|
|
for (i = j; i < NP; i++)
|
|
{
|
|
pswRc[i] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
swRc = divide_s(swAbsTemp1, swTemp2);/* return division result */
|
|
|
|
if (sub(swTemp1, swAbsTemp1) == 0)
|
|
swRc = negate(swRc); /* negate reflection Rc[j] */
|
|
|
|
pswRc[0] = swRc; /* copy into the output Rc array */
|
|
|
|
for (i = 0; i <= NP; i++)
|
|
{
|
|
pL_R[i] = L_shr(pL_R[i], AFSHIFT);
|
|
}
|
|
|
|
/* Initialize the pjOld and vjOld recursion arrays */
|
|
/*-------------------------------------------------*/
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pL_pjOld[i] = pL_R[i];
|
|
pL_vjOld[i] = pL_R[i + 1];
|
|
}
|
|
for (i = -1; i > -NP; i--)
|
|
pL_vjOld[i] = pL_R[-(i + 1)];
|
|
|
|
|
|
/* Compute the square of the j=0 reflection coefficient */
|
|
/*------------------------------------------------------*/
|
|
|
|
swRcSq = mult_r(swRc, swRc);
|
|
|
|
/* Update pjNew and vjNew arrays for lattice stage j=1 */
|
|
/*-----------------------------------------------------*/
|
|
|
|
/* Updating pjNew: */
|
|
/*-------------------*/
|
|
|
|
for (i = 0; i <= NP - 2; i++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_vjOld[i], swRc);
|
|
L_sum = L_add(L_temp, pL_pjOld[i]);
|
|
L_temp = L_mpy_ls(pL_pjOld[i], swRcSq);
|
|
L_sum = L_add(L_temp, L_sum);
|
|
L_temp = L_mpy_ls(pL_vjOld[-i], swRc);
|
|
pL_pjNew[i] = L_add(L_sum, L_temp);
|
|
}
|
|
|
|
/* Updating vjNew: */
|
|
/*-------------------*/
|
|
|
|
for (i = -NP + 2; i <= NP - 2; i++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_vjOld[-i - 1], swRcSq);
|
|
L_sum = L_add(L_temp, pL_vjOld[i + 1]);
|
|
L_temp = L_mpy_ls(pL_pjOld[(((i + 1) >= 0) ? i + 1 : -(i + 1))], swRc);
|
|
L_temp = L_shl(L_temp, 1);
|
|
pL_vjNew[i] = L_add(L_temp, L_sum);
|
|
}
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
/* Compute reflection coefficients Rc[1],...,Rc[9] */
|
|
/*-------------------------------------------------*/
|
|
|
|
for (j = 1; j < NP; j++)
|
|
{
|
|
|
|
/* Swap pjNew and pjOld buffers */
|
|
/*------------------------------*/
|
|
|
|
pL_swap = pL_pjNew;
|
|
pL_pjNew = pL_pjOld;
|
|
pL_pjOld = pL_swap;
|
|
|
|
/* Swap vjNew and vjOld buffers */
|
|
/*------------------------------*/
|
|
|
|
pL_swap = pL_vjNew;
|
|
pL_vjNew = pL_vjOld;
|
|
pL_vjOld = pL_swap;
|
|
|
|
/* Compute the j-th reflection coefficient */
|
|
/*-----------------------------------------*/
|
|
|
|
swTemp = norm_l(pL_pjOld[0]); /* get shift count */
|
|
swTemp1 = round(L_shl(pL_vjOld[0], swTemp)); /* normalize num. */
|
|
swTemp2 = round(L_shl(pL_pjOld[0], swTemp)); /* normalize den. */
|
|
|
|
/* Test for invalid divide conditions: a) devisor < 0 b) abs(divident) >
|
|
* abs(devisor) If either of these conditions is true, zero out
|
|
* reflection coefficients for i=j,...,NP-1 and return. */
|
|
|
|
swAbsTemp1 = abs_s(swTemp1);
|
|
if (swTemp2 <= 0 || sub(swAbsTemp1, swTemp2) >= 0)
|
|
{
|
|
i = j;
|
|
for (i = j; i < NP; i++)
|
|
{
|
|
pswRc[i] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
swRc = divide_s(swAbsTemp1, swTemp2); /* return division result */
|
|
if (sub(swTemp1, swAbsTemp1) == 0)
|
|
swRc = negate(swRc); /* negate reflection Rc[j] */
|
|
swRcSq = mult_r(swRc, swRc); /* compute Rc^2 */
|
|
pswRc[j] = swRc; /* copy Rc[j] to output array */
|
|
|
|
/* Update pjNew and vjNew arrays for the next lattice stage if j < NP-1 */
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
/* Updating pjNew: */
|
|
/*-----------------*/
|
|
|
|
for (i = 0; i <= NP - j - 2; i++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_vjOld[i], swRc);
|
|
L_sum = L_add(L_temp, pL_pjOld[i]);
|
|
L_temp = L_mpy_ls(pL_pjOld[i], swRcSq);
|
|
L_sum = L_add(L_temp, L_sum);
|
|
L_temp = L_mpy_ls(pL_vjOld[-i], swRc);
|
|
pL_pjNew[i] = L_add(L_sum, L_temp);
|
|
}
|
|
|
|
/* Updating vjNew: */
|
|
/*-----------------*/
|
|
|
|
for (i = -NP + j + 2; i <= NP - j - 2; i++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_vjOld[-i - 1], swRcSq);
|
|
L_sum = L_add(L_temp, pL_vjOld[i + 1]);
|
|
L_temp = L_mpy_ls(pL_pjOld[(((i + 1) >= 0) ? i + 1 : -(i + 1))], swRc);
|
|
L_temp = L_shl(L_temp, 1);
|
|
pL_vjNew[i] = L_add(L_temp, L_sum);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: aToRc
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* This subroutine computes a vector of reflection coefficients, given
|
|
* an input vector of direct form LPC filter coefficients.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the LPC filter (global constant)
|
|
*
|
|
* swAshift
|
|
* The number of right shifts applied externally
|
|
* to the direct form filter coefficients.
|
|
*
|
|
* pswAin[0:NP-1]
|
|
* The input vector of direct form LPC filter
|
|
* coefficients.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswRc[0:NP-1]
|
|
* Array containing the reflection coefficients.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* siUnstableFlt
|
|
* If stable reflection coefficients 0, 1 if unstable.
|
|
*
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function performs the conversion from direct form
|
|
* coefficients to reflection coefficients. It is used in a_sst()
|
|
* and interpolateCheck(). In a_sst() reflection coefficients used
|
|
* as a transitional data format. aToRc() is used for this
|
|
* conversion.
|
|
*
|
|
* When performing interpolation, a stability check must be
|
|
* performed. interpolateCheck() does this by calling aToRc().
|
|
*
|
|
* First coefficients are shifted down by iAshift. NP, the filter
|
|
* order is 10. The a's and rc's each have NP elements in them. An
|
|
* elaborate algorithm description can be found on page 443, of
|
|
* "Digital Processing of Speech Signals" by L.R. Rabiner and R.W.
|
|
* Schafer; Prentice-Hall; Englewood Cliffs, NJ (USA). 1978.
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.6, and 4.2.3 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: reflectioncoefficients, parcors, conversion, atorc, ks, as
|
|
* KEYWORDS: parcorcoefficients, lpc, flat, vectorquantization
|
|
*
|
|
*************************************************************************/
|
|
|
|
static short aToRc(int16_t swAshift, int16_t pswAin[],
|
|
int16_t pswRc[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t pswTmpSpace[NP],
|
|
pswASpace[NP],
|
|
swNormShift,
|
|
swActShift,
|
|
swNormProd,
|
|
swRcOverE,
|
|
swDiv,
|
|
*pswSwap,
|
|
*pswTmp,
|
|
*pswA;
|
|
|
|
int32_t L_temp;
|
|
|
|
short int siUnstableFlt,
|
|
i,
|
|
j; /* Loop control variables */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Initialize starting addresses for temporary buffers */
|
|
/*-----------------------------------------------------*/
|
|
|
|
pswA = pswASpace;
|
|
pswTmp = pswTmpSpace;
|
|
|
|
/* Copy the direct form filter coefficients to a temporary array */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswA[i] = pswAin[i];
|
|
}
|
|
|
|
/* Initialize the flag for filter stability check */
|
|
/*------------------------------------------------*/
|
|
|
|
siUnstableFlt = 0;
|
|
|
|
/* Start computation of the reflection coefficients, Rc[9],...,Rc[1] */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
for (i = NP - 1; i >= 1; i--)
|
|
{
|
|
|
|
pswRc[i] = shl(pswA[i], swAshift); /* write Rc[i] to output array */
|
|
|
|
/* Check the stability of i-th reflection coefficient */
|
|
/*----------------------------------------------------*/
|
|
|
|
siUnstableFlt = siUnstableFlt | isSwLimit(pswRc[i]);
|
|
|
|
/* Precompute intermediate variables for needed for the computation */
|
|
/* of direct form filter of order i-1 */
|
|
/*------------------------------------------------------------------*/
|
|
|
|
if (sub(pswRc[i], SW_MIN) == 0)
|
|
{
|
|
siUnstableFlt = 1;
|
|
swRcOverE = 0;
|
|
swDiv = 0;
|
|
swActShift = 2;
|
|
}
|
|
else
|
|
{
|
|
L_temp = LW_MAX; /* Load ~1.0 into accum */
|
|
L_temp = L_msu(L_temp, pswRc[i], pswRc[i]); /* 1.-Rc[i]*Rc[i] */
|
|
swNormShift = norm_l(L_temp);
|
|
L_temp = L_shl(L_temp, swNormShift);
|
|
swNormProd = extract_h(L_temp);
|
|
swActShift = add(2, swNormShift);
|
|
swDiv = divide_s(0x2000, swNormProd);
|
|
swRcOverE = mult_r(pswRc[i], swDiv);
|
|
}
|
|
/* Check stability */
|
|
/*---------------------*/
|
|
siUnstableFlt = siUnstableFlt | isSwLimit(swRcOverE);
|
|
|
|
/* Compute direct form filter coefficients corresponding to */
|
|
/* a direct form filter of order i-1 */
|
|
/*----------------------------------------------------------*/
|
|
|
|
for (j = 0; j <= i - 1; j++)
|
|
{
|
|
L_temp = L_mult(pswA[j], swDiv);
|
|
L_temp = L_msu(L_temp, pswA[i - j - 1], swRcOverE);
|
|
L_temp = L_shl(L_temp, swActShift);
|
|
pswTmp[j] = round(L_temp);
|
|
siUnstableFlt = siUnstableFlt | isSwLimit(pswTmp[j]);
|
|
}
|
|
|
|
/* Swap swA and swTmp buffers */
|
|
/*----------------------------*/
|
|
|
|
pswSwap = pswA;
|
|
pswA = pswTmp;
|
|
pswTmp = pswSwap;
|
|
}
|
|
|
|
/* Compute reflection coefficient Rc[0] */
|
|
/*--------------------------------------*/
|
|
|
|
pswRc[0] = shl(pswA[0], swAshift); /* write Rc[0] to output array */
|
|
|
|
/* Check the stability of 0-th reflection coefficient */
|
|
/*----------------------------------------------------*/
|
|
|
|
siUnstableFlt = siUnstableFlt | isSwLimit(pswRc[0]);
|
|
|
|
return (siUnstableFlt);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: a_sst
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform spectral smoothing of the
|
|
* direct form filter coefficients
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* swAshift
|
|
* number of shift for coefficients
|
|
*
|
|
* swAscale
|
|
* scaling factor for coefficients
|
|
*
|
|
* pswDirectFormCoefIn[0:NP-1]
|
|
*
|
|
* array of input direct form coefficients
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswDirectFormCoefOut[0:NP-1]
|
|
*
|
|
* array of output direct form coefficients
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* In a_sst() direct form coefficients are converted to
|
|
* autocorrelations, and smoothed in that domain. The function is
|
|
* used in the spectral postfilter. A description can be found in
|
|
* section 3.2.4 as well as in the reference by Y. Tohkura et al.
|
|
* "Spectral Smoothing Technique in PARCOR Speech
|
|
* Analysis-Synthesis", IEEE Trans. ASSP, vol. ASSP-26, pp. 591-596,
|
|
* Dec. 1978.
|
|
*
|
|
* After smoothing is performed conversion back to direct form
|
|
* coefficients is done by calling aFlatRc(), followed by rcToADp().
|
|
*
|
|
* The spectral smoothing filter coefficients with bandwidth set to 300
|
|
* and a sampling rate of 8000 be :
|
|
* static int16_tRom psrSST[NP+1] = { 0x7FFF,
|
|
* 0x7F5C, 0x7D76, 0x7A5B, 0x7622, 0x70EC,
|
|
* 0x6ADD, 0x641F, 0x5CDD, 0x5546, 0x4D86
|
|
* }
|
|
*
|
|
* REFERENCES: Sub_Clause 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: spectral smoothing, direct form coef, sst, atorc, atocor
|
|
* KEYWORDS: levinson
|
|
*
|
|
*************************************************************************/
|
|
|
|
static void a_sst(int16_t swAshift, int16_t swAscale,
|
|
int16_t pswDirectFormCoefIn[],
|
|
int16_t pswDirectFormCoefOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Static Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
static int16_tRom psrSST[NP + 1] = {0x7FFF,
|
|
0x7F5C, 0x7D76, 0x7A5B, 0x7622, 0x70EC,
|
|
0x6ADD, 0x641F, 0x5CDD, 0x5546, 0x4D86,
|
|
};
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t pL_CorrTemp[NP + 1];
|
|
|
|
int16_t pswRCNum[NP],
|
|
pswRCDenom[NP];
|
|
|
|
short int siLoopCnt;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* convert direct form coefs to reflection coefs */
|
|
/* --------------------------------------------- */
|
|
|
|
aToRc(swAshift, pswDirectFormCoefIn, pswRCDenom);
|
|
|
|
/* convert to autocorrelation coefficients */
|
|
/* --------------------------------------- */
|
|
|
|
rcToCorrDpL(swAshift, swAscale, pswRCDenom, pL_CorrTemp);
|
|
|
|
/* do spectral smoothing technique */
|
|
/* ------------------------------- */
|
|
|
|
for (siLoopCnt = 1; siLoopCnt <= NP; siLoopCnt++)
|
|
{
|
|
pL_CorrTemp[siLoopCnt] = L_mpy_ls(pL_CorrTemp[siLoopCnt],
|
|
psrSST[siLoopCnt]);
|
|
}
|
|
|
|
/* Compute the reflection coefficients via AFLAT */
|
|
/*-----------------------------------------------*/
|
|
|
|
aFlatRcDp(pL_CorrTemp, pswRCNum);
|
|
|
|
|
|
/* Convert reflection coefficients to direct form filter coefficients */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
rcToADp(swAscale, pswRCNum, pswDirectFormCoefOut);
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* FUNCTION NAME: agcGain
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Figure out what the agc gain should be to make the energy in the
|
|
* output signal match that of the input signal. Used in the post
|
|
* filters.
|
|
*
|
|
* INPUT:
|
|
*
|
|
* pswStateCurr[0:39]
|
|
* Input signal into agc block whose energy is
|
|
* to be modified using the gain returned. Signal is not
|
|
* modified in this routine.
|
|
*
|
|
* snsInSigEnergy
|
|
* Normalized number with shift count - the energy in
|
|
* the input signal.
|
|
*
|
|
* swEngyRShft
|
|
* Number of right shifts to apply to the vectors energy
|
|
* to ensure that it remains less than 1.0
|
|
* (swEngyRShft is always positive or zero)
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* none
|
|
*
|
|
* RETURN:
|
|
*
|
|
* the agc's gain/2 note DIVIDED by 2
|
|
*
|
|
*
|
|
* REFERENCES: Sub_Clause 4.2.2 and 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: postfilter, agc, automaticgaincontrol, leveladjust
|
|
*
|
|
*************************************************************************/
|
|
|
|
static int16_t agcGain(int16_t pswStateCurr[],
|
|
struct NormSw snsInSigEnergy, int16_t swEngyRShft)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_OutEnergy,
|
|
L_AgcGain;
|
|
|
|
struct NormSw snsOutEnergy,
|
|
snsAgc;
|
|
|
|
int16_t swAgcOut,
|
|
swAgcShftCnt;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Calculate the energy in the output vector divided by 2 */
|
|
/*--------------------------------------------------------*/
|
|
|
|
snsOutEnergy.sh = g_corr1s(pswStateCurr, swEngyRShft, &L_OutEnergy);
|
|
|
|
/* reduce energy by a factor of 2 */
|
|
snsOutEnergy.sh = add(snsOutEnergy.sh, 1);
|
|
|
|
/* if waveform has nonzero energy, find AGC gain */
|
|
/*-----------------------------------------------*/
|
|
|
|
if (L_OutEnergy == 0)
|
|
{
|
|
swAgcOut = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
snsOutEnergy.man = round(L_OutEnergy);
|
|
|
|
/* divide input energy by 2 */
|
|
snsInSigEnergy.man = shr(snsInSigEnergy.man, 1);
|
|
|
|
|
|
/* Calculate AGC gain squared */
|
|
/*----------------------------*/
|
|
|
|
snsAgc.man = divide_s(snsInSigEnergy.man, snsOutEnergy.man);
|
|
swAgcShftCnt = norm_s(snsAgc.man);
|
|
snsAgc.man = shl(snsAgc.man, swAgcShftCnt);
|
|
|
|
/* find shift count for G^2 */
|
|
/*--------------------------*/
|
|
|
|
snsAgc.sh = add(sub(snsInSigEnergy.sh, snsOutEnergy.sh),
|
|
swAgcShftCnt);
|
|
L_AgcGain = L_deposit_h(snsAgc.man);
|
|
|
|
|
|
/* Calculate AGC gain */
|
|
/*--------------------*/
|
|
|
|
snsAgc.man = sqroot(L_AgcGain);
|
|
|
|
|
|
/* check if 1/2 sqrt(G^2) >= 1.0 */
|
|
/* This is equivalent to checking if shiftCnt/2+1 < 0 */
|
|
/*----------------------------------------------------*/
|
|
|
|
if (add(snsAgc.sh, 2) < 0)
|
|
{
|
|
swAgcOut = SW_MAX;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (0x1 & snsAgc.sh)
|
|
{
|
|
snsAgc.man = mult(snsAgc.man, SQRT_ONEHALF);
|
|
}
|
|
|
|
snsAgc.sh = shr(snsAgc.sh, 1); /* shiftCnt/2 */
|
|
snsAgc.sh = add(snsAgc.sh, 1); /* shiftCnt/2 + 1 */
|
|
|
|
if (snsAgc.sh > 0)
|
|
{
|
|
snsAgc.man = shr(snsAgc.man, snsAgc.sh);
|
|
}
|
|
swAgcOut = snsAgc.man;
|
|
}
|
|
}
|
|
|
|
return (swAgcOut);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: b_con
|
|
*
|
|
* PURPOSE:
|
|
* Expands codeword into an one dimensional array. The 0/1 input is
|
|
* changed to an element with magnitude +/- 0.5.
|
|
*
|
|
* input output
|
|
*
|
|
* 0 -0.5
|
|
* 1 +0.5
|
|
*
|
|
* INPUT:
|
|
*
|
|
* swCodeWord
|
|
* Input codeword, information in the LSB's
|
|
*
|
|
* siNumBits
|
|
* number of bits in the input codeword and number
|
|
* of elements in output vector
|
|
*
|
|
* pswVectOut[0:siNumBits]
|
|
*
|
|
* pointer to bit array
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* pswVectOut[0:siNumBits]
|
|
*
|
|
* signed bit array
|
|
*
|
|
* RETURN:
|
|
*
|
|
* none
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.10 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: b_con, codeword, expansion
|
|
*
|
|
*************************************************************************/
|
|
|
|
void b_con(int16_t swCodeWord, short siNumBits,
|
|
int16_t pswVectOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
short int siLoopCnt;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
for (siLoopCnt = 0; siLoopCnt < siNumBits; siLoopCnt++)
|
|
{
|
|
|
|
if (swCodeWord & 1) /* temp accumulator get 0.5 */
|
|
pswVectOut[siLoopCnt] = (int16_t) 0x4000;
|
|
else /* temp accumulator gets -0.5 */
|
|
pswVectOut[siLoopCnt] = (int16_t) 0xc000;
|
|
|
|
swCodeWord = shr(swCodeWord, 1);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: fp_ex
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Looks up a vector in the adaptive excitation codebook (long-term
|
|
* predictor).
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* swOrigLagIn
|
|
*
|
|
* Extended resolution lag (lag * oversampling factor)
|
|
*
|
|
* pswLTPState[-147:39]
|
|
*
|
|
* Adaptive codebook (with space at end for looked up
|
|
* vector). Indicies [-147:-1] are the history, [0:39]
|
|
* are for the looked up vector.
|
|
*
|
|
* psrPitchIntrpFirBase[0:59]
|
|
* ppsrPVecIntFilt[0:9][0:5] ([tap][phase])
|
|
*
|
|
* Interpolating FIR filter coefficients.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswLTPState[0:39]
|
|
*
|
|
* Array containing the contructed output vector
|
|
*
|
|
* RETURN VALUE:
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* The adaptive codebook consists of the history of the excitation.
|
|
* The vector is looked up by going back into this history
|
|
* by the amount of the input lag. If the input lag is fractional,
|
|
* then the samples to be looked up are interpolated from the existing
|
|
* samples in the history.
|
|
*
|
|
* If the lag is less than the length of the vector to be generated
|
|
* (i.e. less than the subframe length), then the lag is doubled
|
|
* after the first n samples have been looked up (n = input lag).
|
|
* In this way, the samples being generated are not part of the
|
|
* codebook. This is described in section 4.1.8.
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.8.5 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* Keywords: pitch, excitation vector, long term filter, history,
|
|
* Keywords: fractional lag, get_ipjj
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
|
|
void fp_ex(int16_t swOrigLagIn,
|
|
int16_t pswLTPState[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Temp;
|
|
int16_t swIntLag,
|
|
swRemain,
|
|
swRunningLag;
|
|
short int siSampsSoFar,
|
|
siSampsThisPass,
|
|
i,
|
|
j;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Loop: execute until all samples in the vector have been looked up */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
swRunningLag = swOrigLagIn;
|
|
siSampsSoFar = 0;
|
|
while (siSampsSoFar < S_LEN)
|
|
{
|
|
|
|
/* Get integer lag and remainder. These are used in addressing */
|
|
/* the LTP state and the interpolating filter, respectively */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
get_ipjj(swRunningLag, &swIntLag, &swRemain);
|
|
|
|
|
|
/* Get the number of samples to look up in this pass */
|
|
/*---------------------------------------------------*/
|
|
|
|
if (sub(swIntLag, S_LEN) < 0)
|
|
siSampsThisPass = swIntLag - siSampsSoFar;
|
|
else
|
|
siSampsThisPass = S_LEN - siSampsSoFar;
|
|
|
|
/* Look up samples by interpolating (fractional lag), or copying */
|
|
/* (integer lag). */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
if (swRemain == 0)
|
|
{
|
|
|
|
/* Integer lag: copy samples from history */
|
|
/*----------------------------------------*/
|
|
|
|
for (i = siSampsSoFar; i < siSampsSoFar + siSampsThisPass; i++)
|
|
pswLTPState[i] = pswLTPState[i - swIntLag];
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Fractional lag: interpolate to get samples */
|
|
/*--------------------------------------------*/
|
|
|
|
for (i = siSampsSoFar; i < siSampsSoFar + siSampsThisPass; i++)
|
|
{
|
|
|
|
/* first tap with rounding offset */
|
|
/*--------------------------------*/
|
|
L_Temp = L_mac((long) 32768,
|
|
pswLTPState[i - swIntLag - P_INT_MACS / 2],
|
|
ppsrPVecIntFilt[0][swRemain]);
|
|
|
|
for (j = 1; j < P_INT_MACS - 1; j++)
|
|
{
|
|
|
|
L_Temp = L_mac(L_Temp,
|
|
pswLTPState[i - swIntLag - P_INT_MACS / 2 + j],
|
|
ppsrPVecIntFilt[j][swRemain]);
|
|
|
|
}
|
|
|
|
pswLTPState[i] = extract_h(L_mac(L_Temp,
|
|
pswLTPState[i - swIntLag + P_INT_MACS / 2 - 1],
|
|
ppsrPVecIntFilt[P_INT_MACS - 1][swRemain]));
|
|
}
|
|
}
|
|
|
|
/* Done with this pass: update loop controls */
|
|
/*-------------------------------------------*/
|
|
|
|
siSampsSoFar += siSampsThisPass;
|
|
swRunningLag = add(swRunningLag, swOrigLagIn);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: g_corr1 (no scaling)
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Calculates energy in subframe vector. Differs from g_corr1s,
|
|
* in that there the estimate of the maximum possible
|
|
* energy is < 1.0
|
|
*
|
|
*
|
|
* INPUT:
|
|
*
|
|
* pswIn[0:39]
|
|
* A subframe vector.
|
|
*
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* *pL_out
|
|
* A int32_t containing the normalized energy
|
|
* in the input vector.
|
|
*
|
|
* 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.
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.10.2 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: energy, autocorrelation, correlation, g_corr1
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
int16_t g_corr1(int16_t *pswIn, int32_t *pL_out)
|
|
{
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_sum;
|
|
int16_t swEngyLShft;
|
|
int i;
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
|
|
/* Calculate energy in subframe vector (40 samples) */
|
|
/*--------------------------------------------------*/
|
|
|
|
L_sum = L_mult(pswIn[0], pswIn[0]);
|
|
for (i = 1; i < S_LEN; i++)
|
|
{
|
|
L_sum = L_mac(L_sum, pswIn[i], pswIn[i]);
|
|
}
|
|
|
|
|
|
|
|
if (L_sum != 0)
|
|
{
|
|
|
|
/* Normalize the energy in the output int32_t */
|
|
/*---------------------------------------------*/
|
|
|
|
swEngyLShft = norm_l(L_sum);
|
|
*pL_out = L_shl(L_sum, swEngyLShft); /* normalize output
|
|
* int32_t */
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Special case: energy is zero */
|
|
/*------------------------------*/
|
|
|
|
*pL_out = L_sum;
|
|
swEngyLShft = 0;
|
|
}
|
|
|
|
return (swEngyLShft);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: g_corr1s (g_corr1 with scaling)
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Calculates energy in subframe vector. Differs from g_corr1,
|
|
* in that there is an estimate of the maximum possible energy in the
|
|
* vector.
|
|
*
|
|
* INPUT:
|
|
*
|
|
* pswIn[0:39]
|
|
* A subframe vector.
|
|
*
|
|
* swEngyRShft
|
|
*
|
|
* Number of right shifts to apply to the vectors energy
|
|
* to ensure that it remains less than 1.0
|
|
* (swEngyRShft is always positive or zero)
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* *pL_out
|
|
* A int32_t containing the normalized energy
|
|
* in the input vector.
|
|
*
|
|
* 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.
|
|
*
|
|
* REFERENCES: Sub-Clause 4.1.8, 4.2.1, 4.2.2, and 4.2.4
|
|
* of GSM Recomendation 06.20
|
|
*
|
|
* keywords: energy, autocorrelation, correlation, g_corr1
|
|
*
|
|
*
|
|
*************************************************************************/
|
|
|
|
int16_t g_corr1s(int16_t pswIn[], int16_t swEngyRShft,
|
|
int32_t *pL_out)
|
|
{
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_sum;
|
|
int16_t swTemp,
|
|
swEngyLShft;
|
|
int16_t swInputRShft;
|
|
|
|
int i;
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
|
|
/* Calculate energy in subframe vector (40 samples) */
|
|
/*--------------------------------------------------*/
|
|
|
|
if (sub(swEngyRShft, 1) <= 0)
|
|
{
|
|
|
|
/* use the energy shift factor, although it is an odd shift count */
|
|
/*----------------------------------------------------------------*/
|
|
|
|
swTemp = shr(pswIn[0], swEngyRShft);
|
|
L_sum = L_mult(pswIn[0], swTemp);
|
|
for (i = 1; i < S_LEN; i++)
|
|
{
|
|
swTemp = shr(pswIn[i], swEngyRShft);
|
|
L_sum = L_mac(L_sum, pswIn[i], swTemp);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/* convert energy shift factor to an input shift factor */
|
|
/*------------------------------------------------------*/
|
|
|
|
swInputRShft = shift_r(swEngyRShft, -1);
|
|
swEngyRShft = shl(swInputRShft, 1);
|
|
|
|
swTemp = shr(pswIn[0], swInputRShft);
|
|
L_sum = L_mult(swTemp, swTemp);
|
|
for (i = 1; i < S_LEN; i++)
|
|
{
|
|
swTemp = shr(pswIn[i], swInputRShft);
|
|
L_sum = L_mac(L_sum, swTemp, swTemp);
|
|
}
|
|
}
|
|
|
|
if (L_sum != 0)
|
|
{
|
|
|
|
/* Normalize the energy in the output int32_t */
|
|
/*---------------------------------------------*/
|
|
|
|
swTemp = norm_l(L_sum);
|
|
*pL_out = L_shl(L_sum, swTemp); /* normalize output int32_t */
|
|
swEngyLShft = sub(swTemp, swEngyRShft);
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Special case: energy is zero */
|
|
/*------------------------------*/
|
|
|
|
*pL_out = L_sum;
|
|
swEngyLShft = 0;
|
|
}
|
|
|
|
return (swEngyLShft);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: getSfrmLpc
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Given frame information from past and present frame, interpolate
|
|
* (or copy) the frame based LPC coefficients into subframe
|
|
* lpc coeffs, i.e. the ones which will be used by the subframe
|
|
* as opposed to those coded and transmitted.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* siSoftInterpolation
|
|
*
|
|
* interpolate 1/0, a coded parameter.
|
|
*
|
|
* swPrevR0,swNewR0
|
|
*
|
|
* Rq0 for the last frame and for this frame.
|
|
* These are the decoded values, not the codewords.
|
|
*
|
|
* Previous lpc coefficients from the previous frame:
|
|
* in all filters below array[0] is the t=-1 element array[9]
|
|
* t=-10 element.
|
|
*
|
|
* pswPrevFrmKs[0:9]
|
|
*
|
|
* decoded version of the rc's tx'd last frame
|
|
*
|
|
* pswPrevFrmAs[0:9]
|
|
*
|
|
* the above K's converted to A's. i.e. direct
|
|
* form coefficients.
|
|
*
|
|
* pswPrevFrmPFNum[0:9], pswPrevFrmPFDenom[0:9]
|
|
*
|
|
* numerator and denominator coefficients used in the
|
|
* postfilter
|
|
*
|
|
* Current lpc coefficients from the current frame:
|
|
*
|
|
* pswNewFrmKs[0:9], pswNewFrmAs[0:9],
|
|
* pswNewFrmPFNum[0:9], pswNewFrmPFDenom[0:9] same as above.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* psnsSqrtRs[0:3]
|
|
*
|
|
* a normalized number (struct NormSw)
|
|
* containing an estimate of RS for each subframe.
|
|
* (number and a shift)
|
|
*
|
|
* ppswSynthAs[0:3][0:9]
|
|
*
|
|
* filter coefficients used by the synthesis filter.
|
|
*
|
|
* ppswPFNumAs[0:3][0:9]
|
|
*
|
|
* filter coefficients used by the postfilters
|
|
* numerator.
|
|
*
|
|
* ppswPFDenomAs[0:3][0:9]
|
|
*
|
|
* filter coefficients used by postfilters denominator.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* For interpolated subframes, the direct form coefficients
|
|
* are converted to reflection coeffiecients to check for
|
|
* filter stability. If unstable, the uninterpolated coef.
|
|
* are used for that subframe.
|
|
*
|
|
* Interpolation is described in section 4.1.6, "Soft Interpolation
|
|
* of the Spectral Parameters"
|
|
*
|
|
* REFERENCES: Sub_clause 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: soft interpolation, int_lpc, interpolate, atorc,res_eng,i_mov
|
|
*
|
|
*************************************************************************/
|
|
|
|
void getSfrmLpc(short int siSoftInterpolation,
|
|
int16_t swPrevR0, int16_t swNewR0,
|
|
/* last frm */ int16_t pswPrevFrmKs[], int16_t pswPrevFrmAs[],
|
|
int16_t pswPrevFrmPFNum[],
|
|
int16_t pswPrevFrmPFDenom[],
|
|
|
|
/* this frm */ int16_t pswNewFrmKs[], int16_t pswNewFrmAs[],
|
|
int16_t pswNewFrmPFNum[],
|
|
int16_t pswNewFrmPFDenom[],
|
|
|
|
/* output */ struct NormSw *psnsSqrtRs,
|
|
int16_t *ppswSynthAs[], int16_t *ppswPFNumAs[],
|
|
int16_t *ppswPFDenomAs[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Static Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
short int siSfrm,
|
|
siStable,
|
|
i;
|
|
|
|
int32_t L_Temp1,
|
|
L_Temp2;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
if (siSoftInterpolation)
|
|
{
|
|
/* yes, interpolating */
|
|
/* ------------------ */
|
|
|
|
siSfrm = 0;
|
|
|
|
siStable = interpolateCheck(pswPrevFrmKs, pswPrevFrmAs,
|
|
pswPrevFrmAs, pswNewFrmAs,
|
|
psrOldCont[siSfrm], psrNewCont[siSfrm],
|
|
swPrevR0,
|
|
&psnsSqrtRs[siSfrm],
|
|
ppswSynthAs[siSfrm]);
|
|
if (siStable)
|
|
{
|
|
|
|
/* interpolate between direct form coefficient sets */
|
|
/* for both numerator and denominator coefficients */
|
|
/* assume output will be stable */
|
|
/* ------------------------------------------------ */
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
L_Temp1 = L_mult(pswNewFrmPFNum[i], psrNewCont[siSfrm]);
|
|
ppswPFNumAs[siSfrm][i] = mac_r(L_Temp1, pswPrevFrmPFNum[i],
|
|
psrOldCont[siSfrm]);
|
|
L_Temp2 = L_mult(pswNewFrmPFDenom[i], psrNewCont[siSfrm]);
|
|
ppswPFDenomAs[siSfrm][i] = mac_r(L_Temp2, pswPrevFrmPFDenom[i],
|
|
psrOldCont[siSfrm]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* this subframe is unstable */
|
|
/* ------------------------- */
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
ppswPFNumAs[siSfrm][i] = pswPrevFrmPFNum[i];
|
|
ppswPFDenomAs[siSfrm][i] = pswPrevFrmPFDenom[i];
|
|
}
|
|
}
|
|
for (siSfrm = 1; siSfrm < N_SUB - 1; siSfrm++)
|
|
{
|
|
|
|
siStable = interpolateCheck(pswNewFrmKs, pswNewFrmAs,
|
|
pswPrevFrmAs, pswNewFrmAs,
|
|
psrOldCont[siSfrm], psrNewCont[siSfrm],
|
|
swNewR0,
|
|
&psnsSqrtRs[siSfrm],
|
|
ppswSynthAs[siSfrm]);
|
|
if (siStable)
|
|
{
|
|
|
|
/* interpolate between direct form coefficient sets */
|
|
/* for both numerator and denominator coefficients */
|
|
/* assume output will be stable */
|
|
/* ------------------------------------------------ */
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
L_Temp1 = L_mult(pswNewFrmPFNum[i], psrNewCont[siSfrm]);
|
|
ppswPFNumAs[siSfrm][i] = mac_r(L_Temp1, pswPrevFrmPFNum[i],
|
|
psrOldCont[siSfrm]);
|
|
L_Temp2 = L_mult(pswNewFrmPFDenom[i], psrNewCont[siSfrm]);
|
|
ppswPFDenomAs[siSfrm][i] = mac_r(L_Temp2, pswPrevFrmPFDenom[i],
|
|
psrOldCont[siSfrm]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* this subframe has unstable filter coeffs, would like to
|
|
* interpolate but can not */
|
|
/* -------------------------------------- */
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
ppswPFNumAs[siSfrm][i] = pswNewFrmPFNum[i];
|
|
ppswPFDenomAs[siSfrm][i] = pswNewFrmPFDenom[i];
|
|
}
|
|
}
|
|
}
|
|
/* the last subframe never interpolate */
|
|
/* ----------------------------------- */
|
|
siSfrm = 3;
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
ppswPFNumAs[siSfrm][i] = pswNewFrmPFNum[i];
|
|
ppswPFDenomAs[siSfrm][i] = pswNewFrmPFDenom[i];
|
|
ppswSynthAs[siSfrm][i] = pswNewFrmAs[i];
|
|
}
|
|
|
|
res_eng(pswNewFrmKs, swNewR0, &psnsSqrtRs[siSfrm]);
|
|
|
|
}
|
|
/* SoftInterpolation == 0 - no interpolation */
|
|
/* ------------------------------------------ */
|
|
else
|
|
{
|
|
siSfrm = 0;
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
ppswPFNumAs[siSfrm][i] = pswPrevFrmPFNum[i];
|
|
ppswPFDenomAs[siSfrm][i] = pswPrevFrmPFDenom[i];
|
|
ppswSynthAs[siSfrm][i] = pswPrevFrmAs[i];
|
|
}
|
|
|
|
res_eng(pswPrevFrmKs, swPrevR0, &psnsSqrtRs[siSfrm]);
|
|
|
|
/* for subframe 1 and all subsequent sfrms, use result from new frm */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
|
|
res_eng(pswNewFrmKs, swNewR0, &psnsSqrtRs[1]);
|
|
|
|
for (siSfrm = 1; siSfrm < N_SUB; siSfrm++)
|
|
{
|
|
|
|
|
|
psnsSqrtRs[siSfrm].man = psnsSqrtRs[1].man;
|
|
psnsSqrtRs[siSfrm].sh = psnsSqrtRs[1].sh;
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
ppswPFNumAs[siSfrm][i] = pswNewFrmPFNum[i];
|
|
ppswPFDenomAs[siSfrm][i] = pswNewFrmPFDenom[i];
|
|
ppswSynthAs[siSfrm][i] = pswNewFrmAs[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: get_ipjj
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* This subroutine calculates IP, the single-resolution lag rounded
|
|
* down to the nearest integer, and JJ, the remainder when the
|
|
* extended resolution lag is divided by the oversampling factor
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* swLagIn
|
|
* extended resolution lag as an integer, i.e.
|
|
* fractional lag x oversampling factor
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* *pswIp
|
|
* fractional lag rounded down to nearest integer, IP
|
|
*
|
|
* *pswJj
|
|
* the remainder JJ
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* ip = integer[lag/OS_FCTR]
|
|
* jj = integer_round[((lag/OS_FCTR)-ip)*(OS_FCTR)]
|
|
* if the rounding caused an 'overflow'
|
|
* set remainder jj to 0 and add 'carry' to ip
|
|
*
|
|
* This routine is involved in the mechanics of fractional and
|
|
* integer LTP searchs. The LTP is described in section 5.
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.8 and 4.2.2 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lag, fractional, remainder, ip, jj, get_ipjj
|
|
*
|
|
*************************************************************************/
|
|
|
|
void get_ipjj(int16_t swLagIn,
|
|
int16_t *pswIp, int16_t *pswJj)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define OS_FCTR_INV (int16_t)0x1555/* SW_MAX/OS_FCTR */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Temp;
|
|
|
|
int16_t swTemp,
|
|
swTempIp,
|
|
swTempJj;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* calculate ip */
|
|
/* ------------ */
|
|
|
|
L_Temp = L_mult(OS_FCTR_INV, swLagIn); /* lag/OS_FCTR */
|
|
swTempIp = extract_h(L_Temp);
|
|
|
|
/* calculate jj */
|
|
/* ------------ */
|
|
|
|
swTemp = extract_l(L_Temp); /* loose ip */
|
|
swTemp = shr(swTemp, 1); /* isolate jj fraction */
|
|
swTemp = swTemp & SW_MAX;
|
|
L_Temp = L_mult(swTemp, OS_FCTR); /* ((lag/OS_FCTR)-ip))*(OS_FCTR) */
|
|
swTemp = round(L_Temp); /* round and pick-off jj */
|
|
if (sub(swTemp, OS_FCTR) == 0)
|
|
{ /* if 'overflow ' */
|
|
swTempJj = 0; /* set remainder,jj to 0 */
|
|
swTempIp = add(swTempIp, 1); /* 'carry' overflow into ip */
|
|
}
|
|
else
|
|
{
|
|
swTempJj = swTemp; /* read-off remainder,jj */
|
|
}
|
|
|
|
/* return ip and jj */
|
|
/* ---------------- */
|
|
|
|
*pswIp = swTempIp;
|
|
*pswJj = swTempJj;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: interpolateCheck
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Interpolates between direct form coefficient sets.
|
|
* Before releasing the interpolated coefficients, they are checked.
|
|
* If unstable, the "old" parameters are used.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswRefKs[0:9]
|
|
* decoded version of the rc's tx'd last frame
|
|
*
|
|
* pswRefCoefsA[0:9]
|
|
* above K's converted to direct form coefficients
|
|
*
|
|
* pswOldCoefsA[0:9]
|
|
* array of old Coefseters
|
|
*
|
|
* pswNewCoefsA[0:9]
|
|
* array of new Coefseters
|
|
*
|
|
* swOldPer
|
|
* amount old coefs supply to the output
|
|
*
|
|
* swNewPer
|
|
* amount new coefs supply to the output
|
|
*
|
|
* ASHIFT
|
|
* shift for reflection coef. conversion
|
|
*
|
|
* swRq
|
|
* quantized energy to use for subframe
|
|
* *
|
|
* OUTPUTS:
|
|
*
|
|
* psnsSqrtRsOut
|
|
* output pointer to sqrt(RS) normalized
|
|
*
|
|
* pswCoefOutA[0:9]
|
|
* output coefficients
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* siInterp_flg
|
|
* temporary subframe interpolation flag
|
|
* 0 - coef. interpolated, 1 -coef. not interpolated
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* For interpolated subframes, the direct form coefficients
|
|
* are converted to reflection coefficients to check for
|
|
* filter stability. If unstable, the uninterpolated coef.
|
|
* are used for that subframe. Section 4.1.6 describes
|
|
* interpolation.
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.6 and 4.2.3 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: soft interpolation, int_lpc, interpolate, atorc,res_eng,i_mov
|
|
*
|
|
*************************************************************************/
|
|
|
|
short int interpolateCheck(int16_t pswRefKs[],
|
|
int16_t pswRefCoefsA[],
|
|
int16_t pswOldCoefsA[], int16_t pswNewCoefsA[],
|
|
int16_t swOldPer, int16_t swNewPer,
|
|
int16_t swRq,
|
|
struct NormSw *psnsSqrtRsOut,
|
|
int16_t pswCoefOutA[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t pswRcTemp[NP];
|
|
|
|
int32_t L_Temp;
|
|
|
|
short int siInterp_flg,
|
|
i;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Interpolation loop, NP is order of LPC filter */
|
|
/* --------------------------------------------- */
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
L_Temp = L_mult(pswNewCoefsA[i], swNewPer);
|
|
pswCoefOutA[i] = mac_r(L_Temp, pswOldCoefsA[i], swOldPer);
|
|
}
|
|
|
|
/* Convert to reflection coefficients and check stability */
|
|
/* ------------------------------------------------------ */
|
|
|
|
if (aToRc(ASHIFT, pswCoefOutA, pswRcTemp) != 0)
|
|
{
|
|
|
|
/* Unstable, use uninterpolated parameters and compute RS update the
|
|
* state with the frame data closest to this subfrm */
|
|
/* --------------------------------------------------------- */
|
|
|
|
res_eng(pswRefKs, swRq, psnsSqrtRsOut);
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswCoefOutA[i] = pswRefCoefsA[i];
|
|
}
|
|
siInterp_flg = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Stable, compute RS */
|
|
/* ------------------ */
|
|
res_eng(pswRcTemp, swRq, psnsSqrtRsOut);
|
|
|
|
/* Set temporary subframe interpolation flag */
|
|
/* ----------------------------------------- */
|
|
siInterp_flg = 1;
|
|
}
|
|
|
|
/* Return subframe interpolation flag */
|
|
/* ---------------------------------- */
|
|
return (siInterp_flg);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lagDecode
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to decode the lag received from the
|
|
* speech encoder into a full resolution lag for the speech decoder
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* swDeltaLag
|
|
*
|
|
* lag received from channel decoder
|
|
*
|
|
* giSfrmCnt
|
|
*
|
|
* current sub-frame count
|
|
*
|
|
* swLastLag
|
|
*
|
|
* previous lag to un-delta this sub-frame's lag
|
|
*
|
|
* psrLagTbl[0:255]
|
|
*
|
|
* table used to look up full resolution lag
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* swLastLag
|
|
*
|
|
* new previous lag for next sub-frame
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* swLag
|
|
*
|
|
* decoded full resolution lag
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* If first subframe, use lag as index to look up table directly.
|
|
*
|
|
* If it is one of the other subframes, the codeword represents a
|
|
* delta offset. The previously decoded lag is used as a starting
|
|
* point for decoding the current lag.
|
|
*
|
|
* REFERENCES: Sub-clause 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: deltalags, lookup lag
|
|
*
|
|
*************************************************************************/
|
|
|
|
static int16_t lagDecode(int16_t swDeltaLag)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define DELTA_LEVELS_D2 DELTA_LEVELS/2
|
|
#define MAX_LAG 0x00ff
|
|
#define MIN_LAG 0x0000
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Static Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
static int16_t swLastLag;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t swLag;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* first sub-frame */
|
|
/* --------------- */
|
|
|
|
if (giSfrmCnt == 0)
|
|
{
|
|
swLastLag = swDeltaLag;
|
|
}
|
|
|
|
/* remaining sub-frames */
|
|
/* -------------------- */
|
|
|
|
else
|
|
{
|
|
|
|
/* get lag biased around 0 */
|
|
/* ----------------------- */
|
|
|
|
swLag = sub(swDeltaLag, DELTA_LEVELS_D2);
|
|
|
|
/* get real lag relative to last */
|
|
/* ----------------------------- */
|
|
|
|
swLag = add(swLag, swLastLag);
|
|
|
|
/* clip to max or min */
|
|
/* ------------------ */
|
|
|
|
if (sub(swLag, MAX_LAG) > 0)
|
|
{
|
|
swLastLag = MAX_LAG;
|
|
}
|
|
else if (sub(swLag, MIN_LAG) < 0)
|
|
{
|
|
swLastLag = MIN_LAG;
|
|
}
|
|
else
|
|
{
|
|
swLastLag = swLag;
|
|
}
|
|
}
|
|
|
|
/* return lag after look up */
|
|
/* ------------------------ */
|
|
|
|
swLag = psrLagTbl[swLastLag];
|
|
return (swLag);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lookupVq
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to recover the reflection coeffs from
|
|
* the received LPC codewords.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswVqCodeWds[0:2]
|
|
*
|
|
* the codewords for each of the segments
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswRCOut[0:NP-1]
|
|
*
|
|
* the decoded reflection coefficients
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none.
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* For each segment do the following:
|
|
* setup the retrieval pointers to the correct vector
|
|
* get that vector
|
|
*
|
|
* REFERENCES: Sub-clause 4.2.3 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: vq, vectorquantizer, lpc
|
|
*
|
|
*************************************************************************/
|
|
|
|
static void lookupVq(int16_t pswVqCodeWds[], int16_t pswRCOut[])
|
|
{
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define LSP_MASK 0x00ff
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
short int siSeg,
|
|
siIndex,
|
|
siVector,
|
|
siVector1,
|
|
siVector2,
|
|
siWordPtr;
|
|
|
|
int16_tRom *psrQTable;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* for each segment */
|
|
/* ---------------- */
|
|
|
|
for (siSeg = 0; siSeg < QUANT_NUM_OF_TABLES; siSeg++)
|
|
{
|
|
|
|
siVector = pswVqCodeWds[siSeg];
|
|
siIndex = psvqIndex[siSeg].l;
|
|
|
|
if (sub(siSeg, 2) == 0)
|
|
{ /* segment 3 */
|
|
|
|
/* set table */
|
|
/* --------- */
|
|
|
|
psrQTable = psrQuant3;
|
|
|
|
/* set offset into table */
|
|
/* ---------------------- */
|
|
|
|
siWordPtr = add(siVector, siVector);
|
|
|
|
/* look up coeffs */
|
|
/* -------------- */
|
|
|
|
siVector1 = psrQTable[siWordPtr];
|
|
siVector2 = psrQTable[siWordPtr + 1];
|
|
|
|
pswRCOut[siIndex - 1] = psrSQuant[shr(siVector1, 8) & LSP_MASK];
|
|
pswRCOut[siIndex] = psrSQuant[siVector1 & LSP_MASK];
|
|
pswRCOut[siIndex + 1] = psrSQuant[shr(siVector2, 8) & LSP_MASK];
|
|
pswRCOut[siIndex + 2] = psrSQuant[siVector2 & LSP_MASK];
|
|
}
|
|
else
|
|
{ /* segments 1 and 2 */
|
|
|
|
/* set tables */
|
|
/* ---------- */
|
|
|
|
if (siSeg == 0)
|
|
{
|
|
psrQTable = psrQuant1;
|
|
}
|
|
else
|
|
{
|
|
psrQTable = psrQuant2;
|
|
|
|
}
|
|
|
|
/* set offset into table */
|
|
/* --------------------- */
|
|
|
|
siWordPtr = add(siVector, siVector);
|
|
siWordPtr = add(siWordPtr, siVector);
|
|
siWordPtr = shr(siWordPtr, 1);
|
|
|
|
/* look up coeffs */
|
|
/* -------------- */
|
|
|
|
siVector1 = psrQTable[siWordPtr];
|
|
siVector2 = psrQTable[siWordPtr + 1];
|
|
|
|
if ((siVector & 0x0001) == 0)
|
|
{
|
|
pswRCOut[siIndex - 1] = psrSQuant[shr(siVector1, 8) & LSP_MASK];
|
|
pswRCOut[siIndex] = psrSQuant[siVector1 & LSP_MASK];
|
|
pswRCOut[siIndex + 1] = psrSQuant[shr(siVector2, 8) & LSP_MASK];
|
|
}
|
|
else
|
|
{
|
|
pswRCOut[siIndex - 1] = psrSQuant[siVector1 & LSP_MASK];
|
|
pswRCOut[siIndex] = psrSQuant[shr(siVector2, 8) & LSP_MASK];
|
|
pswRCOut[siIndex + 1] = psrSQuant[siVector2 & LSP_MASK];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcFir
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform direct form fir filtering
|
|
* assuming a NP order filter and given state, coefficients, and input.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswInput[0:S_LEN-1]
|
|
*
|
|
* input array of points to be filtered.
|
|
* pswInput[0] is the oldest point (first to be filtered)
|
|
* pswInput[siLen-1] is the last point filtered (newest)
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
*
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* pswState[0:NP-1]
|
|
*
|
|
* array of the filter state following form of pswCoef
|
|
* pswState[0] = state of filter for delay n = -1
|
|
* pswState[NP-1] = state of filter for delay n = -NP
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswState[0:NP-1]
|
|
*
|
|
* updated filter state, ready to filter
|
|
* pswInput[siLen], i.e. the next point
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswInput, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] += state[j]*coef[j] (state is taken from either input
|
|
* state[] or input in[] arrays)
|
|
* rescale(out[i])
|
|
* out[i] += in[i]
|
|
* update final state array using in[]
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 and 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, fir, lpcFir, inversefilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set, i_dir_mod
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcFir(int16_t pswInput[], int16_t pswCoef[],
|
|
int16_t pswState[], int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* filter 1st sample */
|
|
/* ----------------- */
|
|
|
|
/* sum past state outputs */
|
|
/* ---------------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_mac(LPC_ROUND, pswState[0], pswCoef[0]);
|
|
|
|
for (siStage = 1; siStage < NP; siStage++)
|
|
{ /* remaining coefs */
|
|
L_Sum = L_mac(L_Sum, pswState[siStage], pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[0], 0x8000);
|
|
|
|
/* save 1st output sample */
|
|
/* ---------------------- */
|
|
|
|
pswFiltOut[0] = extract_h(L_Sum);
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_mac(LPC_ROUND, pswInput[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_mac(L_Sum, pswInput[siSmp - siStage - 1], pswCoef[siStage]);
|
|
}
|
|
|
|
/* sum past states, if any */
|
|
/* ----------------------- */
|
|
|
|
for (siStage = siSmp; siStage < NP; siStage++)
|
|
{
|
|
L_Sum = L_mac(L_Sum, pswState[siStage - siSmp], pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[siSmp], 0x8000);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
|
|
/* save final state */
|
|
/* ---------------- */
|
|
|
|
for (siStage = 0; siStage < NP; siStage++)
|
|
{
|
|
pswState[siStage] = pswInput[S_LEN - siStage - 1];
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcIir
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform direct form IIR filtering
|
|
* assuming a NP order filter and given state, coefficients, and input
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswInput[0:S_LEN-1]
|
|
*
|
|
* input array of points to be filtered
|
|
* pswInput[0] is the oldest point (first to be filtered)
|
|
* pswInput[siLen-1] is the last point filtered (newest)
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* pswState[0:NP-1]
|
|
*
|
|
* array of the filter state following form of pswCoef
|
|
* pswState[0] = state of filter for delay n = -1
|
|
* pswState[NP-1] = state of filter for delay n = -NP
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswState[0:NP-1]
|
|
*
|
|
* updated filter state, ready to filter
|
|
* pswInput[siLen], i.e. the next point
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswInput, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] -= state[j]*coef[j] (state is taken from either input
|
|
* state[] or prior out[] arrays)
|
|
* rescale(out[i])
|
|
* out[i] += in[i]
|
|
* update final state array using out[]
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 and 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, iir, synthesisfilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcIir(int16_t pswInput[], int16_t pswCoef[],
|
|
int16_t pswState[], int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* filter 1st sample */
|
|
/* ----------------- */
|
|
|
|
/* sum past state outputs */
|
|
/* ---------------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswState[0], pswCoef[0]);
|
|
|
|
for (siStage = 1; siStage < NP; siStage++)
|
|
{ /* remaining coefs */
|
|
L_Sum = L_msu(L_Sum, pswState[siStage], pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[0], 0x8000);
|
|
|
|
/* save 1st output sample */
|
|
/* ---------------------- */
|
|
|
|
pswFiltOut[0] = extract_h(L_Sum);
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswFiltOut[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswFiltOut[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* sum past states, if any */
|
|
/* ----------------------- */
|
|
|
|
for (siStage = siSmp; siStage < NP; siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswState[siStage - siSmp], pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[siSmp], 0x8000);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
|
|
/* save final state */
|
|
/* ---------------- */
|
|
|
|
for (siStage = 0; siStage < NP; siStage++)
|
|
{
|
|
pswState[siStage] = pswFiltOut[S_LEN - siStage - 1];
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcIrZsIir
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to calculate the impulse response
|
|
* via direct form IIR filtering with zero state assuming a NP order
|
|
* filter and given coefficients
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswInput, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This routine is called by getNWCoefs().
|
|
*
|
|
* Because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] -= state[j]*coef[j] (state taken from prior output[])
|
|
* rescale(out[i])
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.8 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, iir, synthesisfilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcIrZsIir(int16_t pswCoef[], int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* output 1st sample */
|
|
/* ----------------- */
|
|
|
|
pswFiltOut[0] = 0x0400;
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswFiltOut[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswFiltOut[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* scale output */
|
|
/* ------------ */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcZiIir
|
|
*
|
|
* PURPOSE:
|
|
* The purpose of this function is to perform direct form iir filtering
|
|
* with zero input assuming a NP order filter, and given state and
|
|
* coefficients
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter MUST be <= MAX_ZIS
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
*
|
|
* array of direct form coefficients.
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* pswState[0:NP-1]
|
|
*
|
|
* array of the filter state following form of pswCoef
|
|
* pswState[0] = state of filter for delay n = -1
|
|
* pswState[NP-1] = state of filter for delay n = -NP
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswIn, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* The routine is called from sfrmAnalysis, and is used to let the
|
|
* LPC filters ring out.
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] -= state[j]*coef[j] (state is taken from either input
|
|
* state[] or prior output[] arrays)
|
|
* rescale(out[i])
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, iir, synthesisfilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcZiIir(int16_t pswCoef[], int16_t pswState[],
|
|
int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* filter 1st sample */
|
|
/* ----------------- */
|
|
|
|
/* sum past state outputs */
|
|
/* ---------------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswState[0], pswCoef[0]);
|
|
|
|
for (siStage = 1; siStage < NP; siStage++)
|
|
{ /* remaining coefs */
|
|
L_Sum = L_msu(L_Sum, pswState[siStage], pswCoef[siStage]);
|
|
}
|
|
|
|
/* scale output */
|
|
/* ------------ */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
|
|
/* save 1st output sample */
|
|
/* ---------------------- */
|
|
|
|
pswFiltOut[0] = extract_h(L_Sum);
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswFiltOut[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswFiltOut[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* sum past states, if any */
|
|
/* ----------------------- */
|
|
|
|
for (siStage = siSmp; siStage < NP; siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswState[siStage - siSmp], pswCoef[siStage]);
|
|
}
|
|
|
|
/* scale output */
|
|
/* ------------ */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcZsFir
|
|
*
|
|
* PURPOSE:
|
|
* The purpose of this function is to perform direct form fir filtering
|
|
* with zero state, assuming a NP order filter and given coefficients
|
|
* and non-zero input.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswInput[0:S_LEN-1]
|
|
*
|
|
* input array of points to be filtered.
|
|
* pswInput[0] is the oldest point (first to be filtered)
|
|
* pswInput[siLen-1] is the last point filtered (newest)
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
*
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswInput, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This routine is used in getNWCoefs(). See section 4.1.7.
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] += state[j]*coef[j] (state taken from in[])
|
|
* rescale(out[i])
|
|
* out[i] += in[i]
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, fir, lpcFir, inversefilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set, i_dir_mod
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcZsFir(int16_t pswInput[], int16_t pswCoef[],
|
|
int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* output 1st sample */
|
|
/* ----------------- */
|
|
|
|
pswFiltOut[0] = pswInput[0];
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_mac(LPC_ROUND, pswInput[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_mac(L_Sum, pswInput[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[siSmp], 0x8000);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcZsIir
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform direct form IIR filtering
|
|
* with zero state, assuming a NP order filter and given coefficients
|
|
* and non-zero input.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswInput[0:S_LEN-1]
|
|
*
|
|
* input array of points to be filtered
|
|
* pswInput[0] is the oldest point (first to be filtered)
|
|
* pswInput[siLen-1] is the last point filtered (newest)
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswFiltOut[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* same format as pswInput, pswFiltOut[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This routine is used in the subframe analysis process. It is
|
|
* called by sfrmAnalysis() and fnClosedLoop(). It is this function
|
|
* which performs the weighting of the excitation vectors.
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] -= state[j]*coef[j] (state taken from prior out[])
|
|
* rescale(out[i])
|
|
* out[i] += in[i]
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.8.5 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, iir, synthesisfilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcZsIir(int16_t pswInput[], int16_t pswCoef[],
|
|
int16_t pswFiltOut[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* output 1st sample */
|
|
/* ----------------- */
|
|
|
|
pswFiltOut[0] = pswInput[0];
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_msu(LPC_ROUND, pswFiltOut[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_msu(L_Sum, pswFiltOut[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswInput[siSmp], 0x8000);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswFiltOut[siSmp] = extract_h(L_Sum);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: lpcZsIirP
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform direct form iir filtering
|
|
* with zero state, assuming a NP order filter and given coefficients
|
|
* and input
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the lpc filter
|
|
*
|
|
* S_LEN
|
|
* number of samples to filter
|
|
*
|
|
* pswCommonIO[0:S_LEN-1]
|
|
*
|
|
* input array of points to be filtered
|
|
* pswCommonIO[0] is oldest point (first to be filtered)
|
|
* pswCommonIO[siLen-1] is last point filtered (newest)
|
|
*
|
|
* pswCoef[0:NP-1]
|
|
* array of direct form coefficients
|
|
* pswCoef[0] = coeff for delay n = -1
|
|
* pswCoef[NP-1] = coeff for delay n = -NP
|
|
*
|
|
* ASHIFT
|
|
* number of shifts input A's have been shifted down by
|
|
*
|
|
* LPC_ROUND
|
|
* rounding constant
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswCommonIO[0:S_LEN-1]
|
|
*
|
|
* the filtered output
|
|
* pswCommonIO[0] is oldest point
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is called by geNWCoefs(). See section 4.1.7.
|
|
*
|
|
* because of the default sign of the coefficients the
|
|
* formula for the filter is :
|
|
* i=0, i < S_LEN
|
|
* out[i] = rounded(state[i]*coef[0])
|
|
* j=1, j < NP
|
|
* out[i] += state[j]*coef[j] (state taken from prior out[])
|
|
* rescale(out[i])
|
|
* out[i] += in[i]
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: lpc, directform, iir, synthesisfilter, lpcFilt
|
|
* KEYWORDS: dirForm, dir_mod, dir_clr, dir_neg, dir_set
|
|
*
|
|
*************************************************************************/
|
|
|
|
void lpcZsIirP(int16_t pswCommonIO[], int16_t pswCoef[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Sum;
|
|
short int siStage,
|
|
siSmp;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* filter remaining samples */
|
|
/* ------------------------ */
|
|
|
|
for (siSmp = 1; siSmp < S_LEN; siSmp++)
|
|
{
|
|
|
|
/* sum past outputs */
|
|
/* ---------------- */
|
|
/* 0th coef, with rounding */
|
|
L_Sum = L_mac(LPC_ROUND, pswCommonIO[siSmp - 1], pswCoef[0]);
|
|
/* remaining coefs */
|
|
for (siStage = 1; ((0 < (siSmp - siStage)) && siStage < NP); siStage++)
|
|
{
|
|
L_Sum = L_mac(L_Sum, pswCommonIO[siSmp - siStage - 1],
|
|
pswCoef[siStage]);
|
|
}
|
|
|
|
/* add input to partial output */
|
|
/* --------------------------- */
|
|
|
|
L_Sum = L_shl(L_Sum, ASHIFT);
|
|
L_Sum = L_msu(L_Sum, pswCommonIO[siSmp], 0x8000);
|
|
|
|
/* save current output sample */
|
|
/* -------------------------- */
|
|
|
|
pswCommonIO[siSmp] = extract_h(L_Sum);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* FUNCTION NAME: pitchPreFilt
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Performs pitch pre-filter on excitation in speech decoder.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswExcite[0:39]
|
|
*
|
|
* Synthetic residual signal to be filtered, a subframe-
|
|
* length vector.
|
|
*
|
|
* ppsrPVecIntFilt[0:9][0:5] ([tap][phase])
|
|
*
|
|
* Interpolation filter coefficients.
|
|
*
|
|
* ppsrSqtrP0[0:2][0:31] ([voicing level-1][gain code])
|
|
*
|
|
* Sqrt(P0) look-up table, used to determine pitch
|
|
* pre-filtering coefficient.
|
|
*
|
|
* swRxGsp0
|
|
*
|
|
* Coded value from gain quantizer, used to look up
|
|
* sqrt(P0).
|
|
*
|
|
* swRxLag
|
|
*
|
|
* Full-resolution lag value (fractional lag *
|
|
* oversampling factor), used to index pitch pre-filter
|
|
* state.
|
|
*
|
|
* swUvCode
|
|
*
|
|
* Coded voicing level, used to distinguish between
|
|
* voiced and unvoiced conditions, and to look up
|
|
* sqrt(P0).
|
|
*
|
|
* swSemiBeta
|
|
*
|
|
* The gain applied to the adaptive codebook excitation
|
|
* (long-term predictor excitation) limited to a maximum
|
|
* of 1.0, used to determine the pitch pre-filter
|
|
* coefficient.
|
|
*
|
|
* snsSqrtRs
|
|
*
|
|
* The estimate of the energy in the residual, used only
|
|
* for scaling.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswExciteOut[0:39]
|
|
*
|
|
* The output pitch pre-filtered excitation.
|
|
*
|
|
* pswPPreState[0:44]
|
|
*
|
|
* Contains the state of the pitch pre-filter
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* If the voicing mode for the frame is unvoiced, then the pitch pre-
|
|
* filter state is updated with the input excitation, and the input
|
|
* excitation is copied to the output.
|
|
*
|
|
* If voiced: first the energy in the input excitation is calculated.
|
|
* Then, the coefficient of the pitch pre-filter is obtained:
|
|
*
|
|
* PpfCoef = POST_EPSILON * min(beta, sqrt(P0)).
|
|
*
|
|
* Then, the pitch pre-filter is performed:
|
|
*
|
|
* ex_p(n) = ex(n) + PpfCoef * ex_p(n-L)
|
|
*
|
|
* The ex_p(n-L) sample is interpolated from the surrounding samples,
|
|
* even for integer values of L.
|
|
*
|
|
* Note: The coefficients of the interpolating filter are multiplied
|
|
* by PpfCoef, rather multiplying ex_p(n_L) after interpolation.
|
|
*
|
|
* Finally, the energy in the output excitation is calculated, and
|
|
* automatic gain control is applied to the output signal so that
|
|
* its energy matches the original.
|
|
*
|
|
* The pitch pre-filter is described in section 4.2.2.
|
|
*
|
|
* REFERENCES: Sub-clause 4.2.2 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: prefilter, pitch, pitchprefilter, excitation, residual
|
|
*
|
|
*************************************************************************/
|
|
|
|
static void pitchPreFilt(int16_t pswExcite[],
|
|
int16_t swRxGsp0,
|
|
int16_t swRxLag, int16_t swUvCode,
|
|
int16_t swSemiBeta, struct NormSw snsSqrtRs,
|
|
int16_t pswExciteOut[],
|
|
int16_t pswPPreState[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define POST_EPSILON 0x2666
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Static Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_1,
|
|
L_OrigEnergy;
|
|
|
|
int16_t swScale,
|
|
swSqrtP0,
|
|
swIntLag,
|
|
swRemain,
|
|
swEnergy,
|
|
pswInterpCoefs[P_INT_MACS];
|
|
|
|
short int i,
|
|
j;
|
|
|
|
struct NormSw snsOrigEnergy;
|
|
|
|
int16_t *pswPPreCurr = &pswPPreState[LTP_LEN];
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Initialization */
|
|
/*----------------*/
|
|
|
|
swEnergy = 0;
|
|
|
|
/* Check voicing level */
|
|
/*---------------------*/
|
|
|
|
if (swUvCode == 0)
|
|
{
|
|
|
|
/* Unvoiced: perform one subframe of delay on state, copy input to */
|
|
/* state, copy input to output (if not same) */
|
|
/*-----------------------------------------------------------------*/
|
|
|
|
for (i = 0; i < LTP_LEN - S_LEN; i++)
|
|
pswPPreState[i] = pswPPreState[i + S_LEN];
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
pswPPreState[i + LTP_LEN - S_LEN] = pswExcite[i];
|
|
|
|
if (pswExciteOut != pswExcite)
|
|
{
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
pswExciteOut[i] = pswExcite[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Voiced: calculate energy in input, filter, calculate energy in */
|
|
/* output, scale */
|
|
/*----------------------------------------------------------------*/
|
|
|
|
/* Get energy in input excitation vector */
|
|
/*---------------------------------------*/
|
|
|
|
swEnergy = add(negate(shl(snsSqrtRs.sh, 1)), 3);
|
|
|
|
if (swEnergy > 0)
|
|
{
|
|
|
|
/* High-energy residual: scale input vector during energy */
|
|
/* calculation. The shift count + 1 of the energy of the */
|
|
/* residual estimate is used as an estimate of the shift */
|
|
/* count needed for the excitation energy */
|
|
/*--------------------------------------------------------*/
|
|
|
|
|
|
snsOrigEnergy.sh = g_corr1s(pswExcite, swEnergy, &L_OrigEnergy);
|
|
snsOrigEnergy.man = round(L_OrigEnergy);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/* set shift count to zero for AGC later */
|
|
/*---------------------------------------*/
|
|
|
|
swEnergy = 0;
|
|
|
|
/* Lower-energy residual: no overflow protection needed */
|
|
/*------------------------------------------------------*/
|
|
|
|
L_OrigEnergy = 0;
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
|
|
L_OrigEnergy = L_mac(L_OrigEnergy, pswExcite[i], pswExcite[i]);
|
|
}
|
|
|
|
snsOrigEnergy.sh = norm_l(L_OrigEnergy);
|
|
snsOrigEnergy.man = round(L_shl(L_OrigEnergy, snsOrigEnergy.sh));
|
|
}
|
|
|
|
/* Determine pitch pre-filter coefficient, and scale the appropriate */
|
|
/* phase of the interpolating filter by it */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
swSqrtP0 = ppsrSqrtP0[swUvCode - 1][swRxGsp0];
|
|
|
|
if (sub(swSqrtP0, swSemiBeta) > 0)
|
|
swScale = swSemiBeta;
|
|
else
|
|
swScale = swSqrtP0;
|
|
|
|
swScale = mult_r(POST_EPSILON, swScale);
|
|
|
|
get_ipjj(swRxLag, &swIntLag, &swRemain);
|
|
|
|
for (i = 0; i < P_INT_MACS; i++)
|
|
pswInterpCoefs[i] = mult_r(ppsrPVecIntFilt[i][swRemain], swScale);
|
|
|
|
/* Perform filter */
|
|
/*----------------*/
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
|
|
L_1 = L_deposit_h(pswExcite[i]);
|
|
|
|
for (j = 0; j < P_INT_MACS - 1; j++)
|
|
{
|
|
|
|
L_1 = L_mac(L_1, pswPPreCurr[i - swIntLag - P_INT_MACS / 2 + j],
|
|
pswInterpCoefs[j]);
|
|
}
|
|
|
|
pswPPreCurr[i] = mac_r(L_1,
|
|
pswPPreCurr[i - swIntLag + P_INT_MACS / 2 - 1],
|
|
pswInterpCoefs[P_INT_MACS - 1]);
|
|
}
|
|
|
|
/* Get energy in filtered vector, determine automatic-gain-control */
|
|
/* scale factor */
|
|
/*-----------------------------------------------------------------*/
|
|
|
|
swScale = agcGain(pswPPreCurr, snsOrigEnergy, swEnergy);
|
|
|
|
/* Scale filtered vector by AGC, put out. NOTE: AGC scale returned */
|
|
/* by routine above is divided by two, hence the shift below */
|
|
/*------------------------------------------------------------------*/
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
|
|
L_1 = L_mult(pswPPreCurr[i], swScale);
|
|
L_1 = L_shl(L_1, 1);
|
|
pswExciteOut[i] = round(L_1);
|
|
}
|
|
|
|
/* Update pitch pre-filter state */
|
|
/*-------------------------------*/
|
|
|
|
for (i = 0; i < LTP_LEN; i++)
|
|
pswPPreState[i] = pswPPreState[i + S_LEN];
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: r0BasedEnergyShft
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Given an R0 voicing level, find the number of shifts to be
|
|
* performed on the energy to ensure that the subframe energy does
|
|
* not overflow. example if energy can maximally take the value
|
|
* 4.0, then 2 shifts are required.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* swR0Index
|
|
* R0 codeword (0-0x1f)
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* none
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* swShiftDownSignal
|
|
*
|
|
* number of right shifts to apply to energy (0..6)
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Based on the R0, the average frame energy, we can get an
|
|
* upper bound on the energy any one subframe can take on.
|
|
* Using this upper bound we can calculate what right shift is
|
|
* needed to ensure an unsaturated output out of a subframe
|
|
* energy calculation (g_corr).
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.9 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: spectral postfilter
|
|
*
|
|
*************************************************************************/
|
|
|
|
int16_t r0BasedEnergyShft(int16_t swR0Index)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int16_t swShiftDownSignal;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
if (sub(swR0Index, 26) <= 0)
|
|
{
|
|
if (sub(swR0Index, 23) <= 0)
|
|
{
|
|
if (sub(swR0Index, 21) <= 0)
|
|
swShiftDownSignal = 0; /* r0 [0, 21] */
|
|
else
|
|
swShiftDownSignal = 1; /* r0 [22, 23] */
|
|
}
|
|
else
|
|
{
|
|
if (sub(swR0Index, 24) <= 0)
|
|
swShiftDownSignal = 2; /* r0 [23, 24] */
|
|
else
|
|
swShiftDownSignal = 3; /* r0 [24, 26] */
|
|
}
|
|
}
|
|
else
|
|
{ /* r0 index > 26 */
|
|
if (sub(swR0Index, 28) <= 0)
|
|
{
|
|
swShiftDownSignal = 4; /* r0 [26, 28] */
|
|
}
|
|
else
|
|
{
|
|
if (sub(swR0Index, 29) <= 0)
|
|
swShiftDownSignal = 5; /* r0 [28, 29] */
|
|
else
|
|
swShiftDownSignal = 6; /* r0 [29, 31] */
|
|
}
|
|
}
|
|
if (sub(swR0Index, 18) > 0)
|
|
swShiftDownSignal = add(swShiftDownSignal, 2);
|
|
|
|
return (swShiftDownSignal);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: rcToADp
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* This subroutine computes a vector of direct form LPC filter
|
|
* coefficients, given an input vector of reflection coefficients.
|
|
* Double precision is used internally, but 16 bit direct form
|
|
* filter coefficients are returned.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* order of the LPC filter (global constant)
|
|
*
|
|
* swAscale
|
|
* The multiplier which scales down the direct form
|
|
* filter coefficients.
|
|
*
|
|
* pswRc[0:NP-1]
|
|
* The input vector of reflection coefficients.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswA[0:NP-1]
|
|
* Array containing the scaled down direct form LPC
|
|
* filter coefficients.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* siLimit
|
|
* 1 if limiting occured in computation, 0 otherwise.
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function performs the conversion from reflection coefficients
|
|
* to direct form LPC filter coefficients. The direct form coefficients
|
|
* are scaled by multiplication by swAscale. NP, the filter order is 10.
|
|
* The a's and rc's each have NP elements in them. Double precision
|
|
* calculations are used internally.
|
|
*
|
|
* The equations are:
|
|
* for i = 0 to NP-1{
|
|
*
|
|
* a(i)(i) = rc(i) (scaling by swAscale occurs here)
|
|
*
|
|
* for j = 0 to i-1
|
|
* a(i)(j) = a(i-1)(j) + rc(i)*a(i-1)(i-j-1)
|
|
* }
|
|
*
|
|
* See page 443, of
|
|
* "Digital Processing of Speech Signals" by L.R. Rabiner and R.W.
|
|
* Schafer; Prentice-Hall; Englewood Cliffs, NJ (USA). 1978.
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 and 4.2.3 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: reflectioncoefficients, parcors, conversion, rctoadp, ks, as
|
|
* KEYWORDS: parcorcoefficients, lpc, flat, vectorquantization
|
|
*
|
|
*************************************************************************/
|
|
|
|
short rcToADp(int16_t swAscale, int16_t pswRc[],
|
|
int16_t pswA[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t pL_ASpace[NP],
|
|
pL_tmpSpace[NP],
|
|
L_temp,
|
|
*pL_A,
|
|
*pL_tmp,
|
|
*pL_swap;
|
|
|
|
short int i,
|
|
j, /* loop counters */
|
|
siLimit;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Initialize starting addresses for temporary buffers */
|
|
/*-----------------------------------------------------*/
|
|
|
|
pL_A = pL_ASpace;
|
|
pL_tmp = pL_tmpSpace;
|
|
|
|
/* Initialize the flag for checking if limiting has occured */
|
|
/*----------------------------------------------------------*/
|
|
|
|
siLimit = 0;
|
|
|
|
/* Compute direct form filter coefficients, pswA[0],...,pswA[9] */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
|
|
pL_tmp[i] = L_mult(swAscale, pswRc[i]);
|
|
for (j = 0; j <= i - 1; j++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_A[i - j - 1], pswRc[i]);
|
|
pL_tmp[j] = L_add(L_temp, pL_A[j]);
|
|
siLimit |= isLwLimit(pL_tmp[j]);
|
|
}
|
|
if (i != NP - 1)
|
|
{
|
|
/* Swap swA and swTmp buffers */
|
|
|
|
pL_swap = pL_tmp;
|
|
pL_tmp = pL_A;
|
|
pL_A = pL_swap;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswA[i] = round(pL_tmp[i]);
|
|
siLimit |= isSwLimit(pswA[i]);
|
|
}
|
|
return (siLimit);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: rcToCorrDpL
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* This subroutine computes an autocorrelation vector, given a vector
|
|
* of reflection coefficients as an input. Double precision calculations
|
|
* are used internally, and a double precision (int32_t)
|
|
* autocorrelation sequence is returned.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* NP
|
|
* LPC filter order passed in as a global constant.
|
|
*
|
|
* swAshift
|
|
* Number of right shifts to be applied to the
|
|
* direct form filter coefficients being computed
|
|
* as an intermediate step to generating the
|
|
* autocorrelation sequence.
|
|
*
|
|
* swAscale
|
|
* A multiplicative scale factor corresponding to
|
|
* swAshift; i.e. swAscale = 2 ^(-swAshift).
|
|
*
|
|
* pswRc[0:NP-1]
|
|
* An input vector of reflection coefficients.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pL_R[0:NP]
|
|
* An output int32_t array containing the
|
|
* autocorrelation vector where
|
|
* pL_R[0] = 0x7fffffff; (i.e., ~1.0).
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* The algorithm used for computing the correlation sequence is
|
|
* described on page 232 of the book "Linear Prediction of Speech",
|
|
* by J.D. Markel and A.H. Gray, Jr.; Springer-Verlag, Berlin,
|
|
* Heidelberg, New York, 1976.
|
|
*
|
|
* REFERENCES: Sub_Clause 4.1.4 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: normalized autocorrelation, reflection coefficients
|
|
* KEYWORDS: conversion
|
|
*
|
|
**************************************************************************/
|
|
|
|
void rcToCorrDpL(int16_t swAshift, int16_t swAscale,
|
|
int16_t pswRc[], int32_t pL_R[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t pL_ASpace[NP],
|
|
pL_tmpSpace[NP],
|
|
L_temp,
|
|
L_sum,
|
|
*pL_A,
|
|
*pL_tmp,
|
|
*pL_swap;
|
|
|
|
short int i,
|
|
j; /* loop control variables */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Set R[0] = 0x7fffffff, (i.e., R[0] = 1.0) */
|
|
/*-------------------------------------------*/
|
|
|
|
pL_R[0] = LW_MAX;
|
|
|
|
/* Assign an address onto each of the two temporary buffers */
|
|
/*----------------------------------------------------------*/
|
|
|
|
pL_A = pL_ASpace;
|
|
pL_tmp = pL_tmpSpace;
|
|
|
|
/* Compute correlations R[1],...,R[10] */
|
|
/*------------------------------------*/
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
|
|
/* Compute, as an intermediate step, the filter coefficients for */
|
|
/* for an i-th order direct form filter (pL_tmp[j],j=0,i) */
|
|
/*---------------------------------------------------------------*/
|
|
|
|
pL_tmp[i] = L_mult(swAscale, pswRc[i]);
|
|
for (j = 0; j <= i - 1; j++)
|
|
{
|
|
L_temp = L_mpy_ls(pL_A[i - j - 1], pswRc[i]);
|
|
pL_tmp[j] = L_add(L_temp, pL_A[j]);
|
|
}
|
|
|
|
/* Swap pL_A and pL_tmp buffers */
|
|
/*------------------------------*/
|
|
|
|
pL_swap = pL_A;
|
|
pL_A = pL_tmp;
|
|
pL_tmp = pL_swap;
|
|
|
|
/* Given the direct form filter coefficients for an i-th order filter */
|
|
/* and the autocorrelation vector computed up to and including stage i */
|
|
/* compute the autocorrelation coefficient R[i+1] */
|
|
/*---------------------------------------------------------------------*/
|
|
|
|
L_temp = L_mpy_ll(pL_A[0], pL_R[i]);
|
|
L_sum = L_negate(L_temp);
|
|
|
|
for (j = 1; j <= i; j++)
|
|
{
|
|
L_temp = L_mpy_ll(pL_A[j], pL_R[i - j]);
|
|
L_sum = L_sub(L_sum, L_temp);
|
|
}
|
|
pL_R[i + 1] = L_shl(L_sum, swAshift);
|
|
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: res_eng
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Calculates square root of subframe residual energy estimate:
|
|
*
|
|
* sqrt( R(0)(1-k1**2)...(1-k10**2) )
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswReflecCoefIn[0:9]
|
|
*
|
|
* Array of reflection coeffcients.
|
|
*
|
|
* swRq
|
|
*
|
|
* Subframe energy = sqrt(frame_energy * S_LEN/2**S_SH)
|
|
* (quantized).
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* psnsSqrtRsOut
|
|
*
|
|
* (Pointer to) the output residual energy estimate.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* The shift count of the normalized residual energy estimate, as int.
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* First, the canonic product of the (1-ki**2) terms is calculated
|
|
* (normalizations are done to maintain precision). Also, a factor of
|
|
* 2**S_SH is applied to the product to offset this same factor in the
|
|
* quantized square root of the subframe energy.
|
|
*
|
|
* Then the product is square-rooted, and multiplied by the quantized
|
|
* square root of the subframe energy. This combined product is put
|
|
* out as a normalized fraction and shift count (mantissa and exponent).
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.7 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: residualenergy, res_eng, rs
|
|
*
|
|
*************************************************************************/
|
|
|
|
void res_eng(int16_t pswReflecCoefIn[], int16_t swRq,
|
|
struct NormSw *psnsSqrtRsOut)
|
|
{
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define S_SH 6 /* ceiling(log2(S_LEN)) */
|
|
#define MINUS_S_SH -S_SH
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Product,
|
|
L_Shift,
|
|
L_SqrtResEng;
|
|
|
|
int16_t swPartialProduct,
|
|
swPartialProductShift,
|
|
swTerm,
|
|
swShift,
|
|
swSqrtPP,
|
|
swSqrtPPShift,
|
|
swSqrtResEng,
|
|
swSqrtResEngShift;
|
|
|
|
short int i;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Form canonic product, maintain precision and shift count */
|
|
/*----------------------------------------------------------*/
|
|
|
|
/* (Start off with unity product (actually -1), and shift offset) */
|
|
/*----------------------------------------------------------------*/
|
|
swPartialProduct = SW_MIN;
|
|
swPartialProductShift = MINUS_S_SH;
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
|
|
/* Get next (-1 + k**2) term, form partial canonic product */
|
|
/*---------------------------------------------------------*/
|
|
|
|
|
|
swTerm = mac_r(LW_MIN, pswReflecCoefIn[i], pswReflecCoefIn[i]);
|
|
|
|
L_Product = L_mult(swTerm, swPartialProduct);
|
|
|
|
/* Normalize partial product, round */
|
|
/*----------------------------------*/
|
|
|
|
swShift = norm_s(extract_h(L_Product));
|
|
swPartialProduct = round(L_shl(L_Product, swShift));
|
|
swPartialProductShift = add(swPartialProductShift, swShift);
|
|
}
|
|
|
|
/* Correct sign of product, take square root */
|
|
/*-------------------------------------------*/
|
|
|
|
swPartialProduct = abs_s(swPartialProduct);
|
|
|
|
swSqrtPP = sqroot(L_deposit_h(swPartialProduct));
|
|
|
|
L_Shift = L_shr(L_deposit_h(swPartialProductShift), 1);
|
|
|
|
swSqrtPPShift = extract_h(L_Shift);
|
|
|
|
if (extract_l(L_Shift) != 0)
|
|
{
|
|
|
|
/* Odd exponent: shr above needs to be compensated by multiplying */
|
|
/* mantissa by sqrt(0.5) */
|
|
/*----------------------------------------------------------------*/
|
|
|
|
swSqrtPP = mult_r(swSqrtPP, SQRT_ONEHALF);
|
|
}
|
|
|
|
/* Form final product, the residual energy estimate, and do final */
|
|
/* normalization */
|
|
/*----------------------------------------------------------------*/
|
|
|
|
L_SqrtResEng = L_mult(swRq, swSqrtPP);
|
|
|
|
swShift = norm_l(L_SqrtResEng);
|
|
swSqrtResEng = round(L_shl(L_SqrtResEng, swShift));
|
|
swSqrtResEngShift = add(swSqrtPPShift, swShift);
|
|
|
|
/* Return */
|
|
/*--------*/
|
|
psnsSqrtRsOut->man = swSqrtResEng;
|
|
psnsSqrtRsOut->sh = swSqrtResEngShift;
|
|
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: rs_rr
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Calculates sqrt(RS/R(x,x)) using floating point format,
|
|
* where RS is the approximate residual energy in a given
|
|
* subframe and R(x,x) is the power in each long term
|
|
* predictor vector or in each codevector.
|
|
* Used in the joint optimization of the gain and the long
|
|
* term predictor coefficient.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswExcitation[0:39] - excitation signal array
|
|
* snsSqrtRs - structure sqrt(RS) normalized with mantissa and shift
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* snsSqrtRsRr - structure sqrt(RS/R(x,x)) with mantissa and shift
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Implemented as sqrt(RS)/sqrt(R(x,x)) where both sqrts
|
|
* are stored normalized (0.5<=x<1.0) and the associated
|
|
* shift. See section 4.1.11.1 for details
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.11.1 and 4.2.1 of GSM
|
|
* Recomendation 06.20
|
|
*
|
|
* KEYWORDS: rs_rr, sqroot
|
|
*
|
|
*************************************************************************/
|
|
|
|
void rs_rr(int16_t pswExcitation[], struct NormSw snsSqrtRs,
|
|
struct NormSw *snsSqrtRsRr)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
int32_t L_Temp;
|
|
int16_t swTemp,
|
|
swTemp2,
|
|
swEnergy,
|
|
swNormShift,
|
|
swShift;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
swEnergy = sub(shl(snsSqrtRs.sh, 1), 3); /* shift*2 + margin ==
|
|
* energy. */
|
|
|
|
|
|
if (swEnergy < 0)
|
|
{
|
|
|
|
/* High-energy residual: scale input vector during energy */
|
|
/* calculation. The shift count of the energy of the */
|
|
/* residual estimate is used as an estimate of the shift */
|
|
/* count needed for the excitation energy */
|
|
/*--------------------------------------------------------*/
|
|
|
|
swNormShift = g_corr1s(pswExcitation, negate(swEnergy), &L_Temp);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Lower-energy residual: no overflow protection needed */
|
|
/*------------------------------------------------------*/
|
|
|
|
swNormShift = g_corr1(pswExcitation, &L_Temp);
|
|
}
|
|
|
|
/* Compute single precision square root of energy sqrt(R(x,x)) */
|
|
/* ----------------------------------------------------------- */
|
|
swTemp = sqroot(L_Temp);
|
|
|
|
/* If odd no. of shifts compensate by sqrt(0.5) */
|
|
/* -------------------------------------------- */
|
|
if (swNormShift & 1)
|
|
{
|
|
|
|
/* Decrement no. of shifts in accordance with sqrt(0.5) */
|
|
/* ---------------------------------------------------- */
|
|
swNormShift = sub(swNormShift, 1);
|
|
|
|
/* sqrt(R(x,x) = sqrt(R(x,x)) * sqrt(0.5) */
|
|
/* -------------------------------------- */
|
|
L_Temp = L_mult(0x5a82, swTemp);
|
|
}
|
|
else
|
|
{
|
|
L_Temp = L_deposit_h(swTemp);
|
|
}
|
|
|
|
/* Normalize again and update shifts */
|
|
/* --------------------------------- */
|
|
swShift = norm_l(L_Temp);
|
|
swNormShift = add(shr(swNormShift, 1), swShift);
|
|
L_Temp = L_shl(L_Temp, swShift);
|
|
|
|
/* Shift sqrt(RS) to make sure less than divisor */
|
|
/* --------------------------------------------- */
|
|
swTemp = shr(snsSqrtRs.man, 1);
|
|
|
|
/* Divide sqrt(RS)/sqrt(R(x,x)) */
|
|
/* ---------------------------- */
|
|
swTemp2 = divide_s(swTemp, round(L_Temp));
|
|
|
|
/* Calculate shift for division, compensate for shift before division */
|
|
/* ------------------------------------------------------------------ */
|
|
swNormShift = sub(snsSqrtRs.sh, swNormShift);
|
|
swNormShift = sub(swNormShift, 1);
|
|
|
|
/* Normalize and get no. of shifts */
|
|
/* ------------------------------- */
|
|
swShift = norm_s(swTemp2);
|
|
snsSqrtRsRr->sh = add(swNormShift, swShift);
|
|
snsSqrtRsRr->man = shl(swTemp2, swShift);
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: rs_rrNs
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Calculates sqrt(RS/R(x,x)) using floating point format,
|
|
* where RS is the approximate residual energy in a given
|
|
* subframe and R(x,x) is the power in each long term
|
|
* predictor vector or in each codevector.
|
|
*
|
|
* Used in the joint optimization of the gain and the long
|
|
* term predictor coefficient.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswExcitation[0:39] - excitation signal array
|
|
* snsSqrtRs - structure sqrt(RS) normalized with mantissa and shift
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* snsSqrtRsRr - structure sqrt(RS/R(x,x)) with mantissa and shift
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* None
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Implemented as sqrt(RS)/sqrt(R(x,x)) where both sqrts
|
|
* are stored normalized (0.5<=x<1.0) and the associated
|
|
* shift.
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.11.1 and 4.2.1 of GSM
|
|
* Recomendation 06.20
|
|
*
|
|
* KEYWORDS: rs_rr, sqroot
|
|
*
|
|
*************************************************************************/
|
|
|
|
void rs_rrNs(int16_t pswExcitation[], struct NormSw snsSqrtRs,
|
|
struct NormSw *snsSqrtRsRr)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
int32_t L_Temp;
|
|
int16_t swTemp,
|
|
swTemp2,
|
|
swNormShift,
|
|
swShift;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Lower-energy residual: no overflow protection needed */
|
|
/*------------------------------------------------------*/
|
|
|
|
swNormShift = g_corr1(pswExcitation, &L_Temp);
|
|
|
|
|
|
/* Compute single precision square root of energy sqrt(R(x,x)) */
|
|
/* ----------------------------------------------------------- */
|
|
swTemp = sqroot(L_Temp);
|
|
|
|
/* If odd no. of shifts compensate by sqrt(0.5) */
|
|
/* -------------------------------------------- */
|
|
if (swNormShift & 1)
|
|
{
|
|
|
|
/* Decrement no. of shifts in accordance with sqrt(0.5) */
|
|
/* ---------------------------------------------------- */
|
|
swNormShift = sub(swNormShift, 1);
|
|
|
|
/* sqrt(R(x,x) = sqrt(R(x,x)) * sqrt(0.5) */
|
|
/* -------------------------------------- */
|
|
L_Temp = L_mult(0x5a82, swTemp);
|
|
}
|
|
else
|
|
{
|
|
L_Temp = L_deposit_h(swTemp);
|
|
}
|
|
|
|
/* Normalize again and update shifts */
|
|
/* --------------------------------- */
|
|
|
|
swShift = norm_l(L_Temp);
|
|
swNormShift = add(shr(swNormShift, 1), swShift);
|
|
L_Temp = L_shl(L_Temp, swShift);
|
|
|
|
/* Shift sqrt(RS) to make sure less than divisor */
|
|
/* --------------------------------------------- */
|
|
swTemp = shr(snsSqrtRs.man, 1);
|
|
|
|
/* Divide sqrt(RS)/sqrt(R(x,x)) */
|
|
/* ---------------------------- */
|
|
swTemp2 = divide_s(swTemp, round(L_Temp));
|
|
|
|
/* Calculate shift for division, compensate for shift before division */
|
|
/* ------------------------------------------------------------------ */
|
|
swNormShift = sub(snsSqrtRs.sh, swNormShift);
|
|
swNormShift = sub(swNormShift, 1);
|
|
|
|
/* Normalize and get no. of shifts */
|
|
/* ------------------------------- */
|
|
swShift = norm_s(swTemp2);
|
|
snsSqrtRsRr->sh = add(swNormShift, swShift);
|
|
snsSqrtRsRr->man = shl(swTemp2, swShift);
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: scaleExcite
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Scale an arbitrary excitation vector (codevector or
|
|
* pitch vector)
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswVect[0:39] - the unscaled vector.
|
|
* iGsp0Scale - an positive offset to compensate for the fact
|
|
* that GSP0 table is scaled down.
|
|
* swErrTerm - rather than a gain being passed in, (beta, gamma)
|
|
* it is calculated from this error term - either
|
|
* Gsp0[][][0] error term A or Gsp0[][][1] error
|
|
* term B. Beta is calculated from error term A,
|
|
* gamma from error term B.
|
|
* snsRS - the RS_xx appropriate to pswVect.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswScldVect[0:39] - the output, scaled excitation vector.
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* swGain - One of two things. Either a clamped value of 0x7fff if the
|
|
* gain's shift was > 0 or the rounded vector gain otherwise.
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* If gain > 1.0 then
|
|
* (do not shift gain up yet)
|
|
* partially scale vector element THEN shift and round save
|
|
* else
|
|
* shift gain correctly
|
|
* scale vector element and round save
|
|
* update state array
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.10.2 and 4.2.1 of GSM
|
|
* Recomendation 06.20
|
|
*
|
|
* KEYWORDS: excite_vl, sc_ex, excitevl, scaleexcite, codevector, p_vec,
|
|
* KEYWORDS: x_vec, pitchvector, gain, gsp0
|
|
*
|
|
*************************************************************************/
|
|
|
|
int16_t scaleExcite(int16_t pswVect[],
|
|
int16_t swErrTerm, struct NormSw snsRS,
|
|
int16_t pswScldVect[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
int32_t L_GainUs,
|
|
L_scaled,
|
|
L_Round_off;
|
|
int16_t swGain,
|
|
swGainUs,
|
|
swGainShift,
|
|
i,
|
|
swGainUsShft;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
|
|
L_GainUs = L_mult(swErrTerm, snsRS.man);
|
|
swGainUsShft = norm_s(extract_h(L_GainUs));
|
|
L_GainUs = L_shl(L_GainUs, swGainUsShft);
|
|
|
|
swGainShift = add(swGainUsShft, snsRS.sh);
|
|
swGainShift = sub(swGainShift, GSP0_SCALE);
|
|
|
|
|
|
/* gain > 1.0 */
|
|
/* ---------- */
|
|
|
|
if (swGainShift < 0)
|
|
{
|
|
swGainUs = round(L_GainUs);
|
|
|
|
L_Round_off = L_shl((long) 32768, swGainShift);
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
L_scaled = L_mac(L_Round_off, swGainUs, pswVect[i]);
|
|
L_scaled = L_shr(L_scaled, swGainShift);
|
|
pswScldVect[i] = extract_h(L_scaled);
|
|
}
|
|
|
|
if (swGainShift == 0)
|
|
swGain = swGainUs;
|
|
else
|
|
swGain = 0x7fff;
|
|
}
|
|
|
|
/* gain < 1.0 */
|
|
/* ---------- */
|
|
|
|
else
|
|
{
|
|
|
|
/* shift down or not at all */
|
|
/* ------------------------ */
|
|
if (swGainShift > 0)
|
|
L_GainUs = L_shr(L_GainUs, swGainShift);
|
|
|
|
/* the rounded actual vector gain */
|
|
/* ------------------------------ */
|
|
swGain = round(L_GainUs);
|
|
|
|
/* now scale the vector (with rounding) */
|
|
/* ------------------------------------ */
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
L_scaled = L_mac((long) 32768, swGain, pswVect[i]);
|
|
pswScldVect[i] = extract_h(L_scaled);
|
|
}
|
|
}
|
|
return (swGain);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: spectralPostFilter
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* Perform spectral post filter on the output of the
|
|
* synthesis filter.
|
|
*
|
|
*
|
|
* INPUT:
|
|
*
|
|
* S_LEN a global constant
|
|
*
|
|
* pswSPFIn[0:S_LEN-1]
|
|
*
|
|
* input to the routine. Unmodified
|
|
* pswSPFIn[0] is the oldest point (first to be filtered),
|
|
* pswSPFIn[iLen-1] is the last pointer filtered,
|
|
* the newest.
|
|
*
|
|
* pswNumCoef[0:NP-1],pswDenomCoef[0:NP-1]
|
|
*
|
|
* numerator and denominator
|
|
* direct form coeffs used by postfilter.
|
|
* Exactly like lpc coefficients in format. Shifted down
|
|
* by iAShift to ensure that they are < 1.0.
|
|
*
|
|
* gpswPostFiltStateNum[0:NP-1], gpswPostFiltStateDenom[0:NP-1]
|
|
*
|
|
* array of the filter state.
|
|
* Same format as coefficients: *praState = state of
|
|
* filter for delay n = -1 praState[NP] = state of
|
|
* filter for delay n = -NP These numbers are not
|
|
* shifted at all. These states are static to this
|
|
* file.
|
|
*
|
|
* OUTPUT:
|
|
*
|
|
* gpswPostFiltStateNum[0:NP-1], gpswPostFiltStateDenom[0:NP-1]
|
|
*
|
|
* See above for description. These are updated.
|
|
*
|
|
* pswSPFOut[0:S_LEN-1]
|
|
*
|
|
* same format as pswSPFIn,
|
|
* *pswSPFOut is oldest point. The filtered output.
|
|
* Note this routine can handle pswSPFOut = pswSPFIn.
|
|
* output can be the same as input. i.e. in place
|
|
* calculation.
|
|
*
|
|
* RETURN:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* find energy in input,
|
|
* perform the numerator fir
|
|
* perform the denominator iir
|
|
* perform the post emphasis
|
|
* find energy in signal,
|
|
* perform the agc using energy in and energy in signam after
|
|
* post emphasis signal
|
|
*
|
|
* The spectral postfilter is described in section 4.2.4.
|
|
*
|
|
* REFERENCES: Sub-clause 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: postfilter, emphasis, postemphasis, brightness,
|
|
* KEYWORDS: numerator, deminator, filtering, lpc,
|
|
*
|
|
*************************************************************************/
|
|
|
|
static void spectralPostFilter(int16_t pswSPFIn[],
|
|
int16_t pswNumCoef[],
|
|
int16_t pswDenomCoef[], int16_t pswSPFOut[])
|
|
{
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define AGC_COEF (int16_t)0x19a /* (1.0 - POST_AGC_COEF)
|
|
* 1.0-.9875 */
|
|
#define POST_EMPHASIS (int16_t)0x199a /* 0.2 */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
short int i;
|
|
|
|
int32_t L_OrigEnergy,
|
|
L_runningGain,
|
|
L_Output;
|
|
|
|
int16_t swAgcGain,
|
|
swRunningGain,
|
|
swTemp;
|
|
|
|
struct NormSw snsOrigEnergy;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* calculate the energy in the input and save it */
|
|
/*-----------------------------------------------*/
|
|
|
|
snsOrigEnergy.sh = g_corr1s(pswSPFIn, swEngyRShift, &L_OrigEnergy);
|
|
snsOrigEnergy.man = round(L_OrigEnergy);
|
|
|
|
/* numerator of the postfilter */
|
|
/*-----------------------------*/
|
|
|
|
lpcFir(pswSPFIn, pswNumCoef, gpswPostFiltStateNum, pswSPFOut);
|
|
|
|
/* denominator of the postfilter */
|
|
/*-------------------------------*/
|
|
|
|
lpcIir(pswSPFOut, pswDenomCoef, gpswPostFiltStateDenom, pswSPFOut);
|
|
|
|
/* postemphasis section of postfilter */
|
|
/*------------------------------------*/
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
swTemp = msu_r(L_deposit_h(pswSPFOut[i]), swPostEmphasisState,
|
|
POST_EMPHASIS);
|
|
swPostEmphasisState = pswSPFOut[i];
|
|
pswSPFOut[i] = swTemp;
|
|
}
|
|
|
|
swAgcGain = agcGain(pswSPFOut, snsOrigEnergy, swEngyRShift);
|
|
|
|
/* scale the speech vector */
|
|
/*-----------------------------*/
|
|
|
|
swRunningGain = gswPostFiltAgcGain;
|
|
L_runningGain = L_deposit_h(gswPostFiltAgcGain);
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
L_runningGain = L_msu(L_runningGain, swRunningGain, AGC_COEF);
|
|
L_runningGain = L_mac(L_runningGain, swAgcGain, AGC_COEF);
|
|
swRunningGain = extract_h(L_runningGain);
|
|
|
|
/* now scale input with gain */
|
|
|
|
L_Output = L_mult(swRunningGain, pswSPFOut[i]);
|
|
pswSPFOut[i] = extract_h(L_shl(L_Output, 2));
|
|
}
|
|
gswPostFiltAgcGain = swRunningGain;
|
|
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: speechDecoder
|
|
*
|
|
* PURPOSE:
|
|
* The purpose of this function is to call all speech decoder
|
|
* subroutines. This is the top level routine for the speech
|
|
* decoder.
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswParameters[0:21]
|
|
*
|
|
* pointer to this frame's parameters. See below for input
|
|
* data format.
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswDecodedSpeechFrame[0:159]
|
|
*
|
|
* this frame's decoded 16 bit linear pcm frame
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* The sequence of events in the decoder, and therefore this routine
|
|
* follow a simple plan. First, the frame based parameters are
|
|
* decoded. Second, on a subframe basis, the subframe based
|
|
* parameters are decoded and the excitation is generated. Third,
|
|
* on a subframe basis, the combined and scaled excitation is
|
|
* passed through the synthesis filter, and then the pitch and
|
|
* spectral postfilters.
|
|
*
|
|
* The in-line comments for the routine speechDecoder, are very
|
|
* detailed. Here in a more consolidated form, are the main
|
|
* points.
|
|
*
|
|
* The R0 parameter is decoded using the lookup table
|
|
* psrR0DecTbl[]. The LPC codewords are looked up using lookupVQ().
|
|
* The decoded parameters are reflection coefficients
|
|
* (pswFrmKs[]).
|
|
*
|
|
* The decoder does not use reflection coefficients directly.
|
|
* Instead it converts them to direct form coeficients. This is
|
|
* done using rcToADp(). If this conversion results in invalid
|
|
* results, the previous frames parameters are used.
|
|
*
|
|
* The direct form coeficients are used to derive the spectal
|
|
* postfilter's numerator and denominator coeficients. The
|
|
* denominators coefficients are widened, and the numerators
|
|
* coefficients are a spectrally smoothed version of the
|
|
* denominator. The smoothing is done with a_sst().
|
|
*
|
|
* The frame based LPC coefficients are either used directly as the
|
|
* subframe coefficients, or are derived through interpolation.
|
|
* The subframe based coeffiecients are calculated in getSfrmLpc().
|
|
*
|
|
* Based on voicing mode, the decoder will construct and scale the
|
|
* excitation in one of two ways. For the voiced mode the lag is
|
|
* decoded using lagDecode(). The fractional pitch LTP lookup is
|
|
* done by the function fp_ex(). In both voiced and unvoiced
|
|
* mode, the VSELP codewords are decoded into excitation vectors
|
|
* using b_con() and v_con().
|
|
*
|
|
* rs_rr(), rs_rrNs(), and scaleExcite() are used to calculate
|
|
* the gamma's, codevector gains, as well as beta, the LTP vector
|
|
* gain. Description of this can be found in section 4.1.11. Once
|
|
* the vectors have been scaled and combined, the excitation is
|
|
* stored in the LTP history.
|
|
*
|
|
* The excitation, pswExcite[], passes through the pitch pre-filter
|
|
* (pitchPreFilt()). Then the harmonically enhanced excitation
|
|
* passes through the synthesis filter, lpcIir(), and finally the
|
|
* reconstructed speech passes through the spectral post-filter
|
|
* (spectalPostFilter()). The final output speech is passed back in
|
|
* the array pswDecodedSpeechFrame[].
|
|
*
|
|
* INPUT DATA FORMAT:
|
|
*
|
|
* The format/content of the input parameters is the so called
|
|
* bit alloc format.
|
|
*
|
|
* voiced mode bit alloc format:
|
|
* -----------------------------
|
|
* index number of bits parameter name
|
|
* 0 5 R0
|
|
* 1 11 k1Tok3
|
|
* 2 9 k4Tok6
|
|
* 3 8 k7Tok10
|
|
* 4 1 softInterpolation
|
|
* 5 2 voicingDecision
|
|
* 6 8 frameLag
|
|
* 7 9 code_1
|
|
* 8 5 gsp0_1
|
|
* 9 4 deltaLag_2
|
|
* 10 9 code_2
|
|
* 11 5 gsp0_2
|
|
* 12 4 deltaLag_3
|
|
* 13 9 code_3
|
|
* 14 5 gsp0_3
|
|
* 15 4 deltaLag_4
|
|
* 16 9 code_4
|
|
* 17 5 gsp0_4
|
|
*
|
|
* 18 1 BFI
|
|
* 19 1 UFI
|
|
* 20 2 SID
|
|
* 21 1 TAF
|
|
*
|
|
*
|
|
* unvoiced mode bit alloc format:
|
|
* -------------------------------
|
|
*
|
|
* index number of bits parameter name
|
|
* 0 5 R0
|
|
* 1 11 k1Tok3
|
|
* 2 9 k4Tok6
|
|
* 3 8 k7Tok10
|
|
* 4 1 softInterpolation
|
|
* 5 2 voicingDecision
|
|
* 6 7 code1_1
|
|
* 7 7 code2_1
|
|
* 8 5 gsp0_1
|
|
* 9 7 code1_2
|
|
* 10 7 code2_2
|
|
* 11 5 gsp0_2
|
|
* 12 7 code1_3
|
|
* 13 7 code2_3
|
|
* 14 5 gsp0_3
|
|
* 15 7 code1_4
|
|
* 16 7 code2_4
|
|
* 17 5 gsp0_4
|
|
*
|
|
* 18 1 BFI
|
|
* 19 1 UFI
|
|
* 20 2 SID
|
|
* 21 1 TAF
|
|
*
|
|
*
|
|
* REFERENCES: Sub_Clause 4.2 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: synthesis, speechdecoder, decoding
|
|
* KEYWORDS: codewords, lag, codevectors, gsp0
|
|
*
|
|
*************************************************************************/
|
|
|
|
void speechDecoder(int16_t pswParameters[],
|
|
int16_t pswDecodedSpeechFrame[])
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Static Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
static int16_t
|
|
*pswLtpStateOut = &pswLtpStateBaseDec[LTP_LEN],
|
|
pswSythAsSpace[NP * N_SUB],
|
|
pswPFNumAsSpace[NP * N_SUB],
|
|
pswPFDenomAsSpace[NP * N_SUB],
|
|
*ppswSynthAs[N_SUB] = {
|
|
&pswSythAsSpace[0],
|
|
&pswSythAsSpace[10],
|
|
&pswSythAsSpace[20],
|
|
&pswSythAsSpace[30],
|
|
},
|
|
|
|
*ppswPFNumAs[N_SUB] = {
|
|
&pswPFNumAsSpace[0],
|
|
&pswPFNumAsSpace[10],
|
|
&pswPFNumAsSpace[20],
|
|
&pswPFNumAsSpace[30],
|
|
},
|
|
*ppswPFDenomAs[N_SUB] = {
|
|
&pswPFDenomAsSpace[0],
|
|
&pswPFDenomAsSpace[10],
|
|
&pswPFDenomAsSpace[20],
|
|
&pswPFDenomAsSpace[30],
|
|
};
|
|
|
|
static int16_tRom
|
|
psrSPFDenomWidenCf[NP] = {
|
|
0x6000, 0x4800, 0x3600, 0x2880, 0x1E60,
|
|
0x16C8, 0x1116, 0x0CD0, 0x099C, 0x0735,
|
|
};
|
|
|
|
|
|
static int32_t L_RxPNSeed; /* DTX mode */
|
|
static int16_t swRxDtxGsIndex; /* DTX mode */
|
|
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
short int i,
|
|
j,
|
|
siLagCode,
|
|
siGsp0Code,
|
|
psiVselpCw[2],
|
|
siVselpCw,
|
|
siNumBits,
|
|
siCodeBook;
|
|
|
|
int16_t pswFrmKs[NP],
|
|
pswFrmAs[NP],
|
|
pswFrmPFNum[NP],
|
|
pswFrmPFDenom[NP],
|
|
pswPVec[S_LEN],
|
|
ppswVselpEx[2][S_LEN],
|
|
pswExcite[S_LEN],
|
|
pswPPFExcit[S_LEN],
|
|
pswSynthFiltOut[S_LEN],
|
|
swR0Index,
|
|
swLag,
|
|
swSemiBeta,
|
|
pswBitArray[MAXBITS];
|
|
|
|
struct NormSw psnsSqrtRs[N_SUB],
|
|
snsRs00,
|
|
snsRs11,
|
|
snsRs22;
|
|
|
|
|
|
int16_t swMutePermit,
|
|
swLevelMean,
|
|
swLevelMax, /* error concealment */
|
|
swMuteFlag; /* error concealment */
|
|
|
|
|
|
int16_t swTAF,
|
|
swSID,
|
|
swBfiDtx; /* DTX mode */
|
|
int16_t swFrameType; /* DTX mode */
|
|
|
|
int32_t L_RxDTXGs; /* DTX mode */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* do bad frame handling (error concealment) and comfort noise */
|
|
/* insertion */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
|
/* This flag indicates whether muting is performed in the actual frame */
|
|
/* ------------------------------------------------------------------- */
|
|
swMuteFlag = 0;
|
|
|
|
|
|
/* This flag indicates whether muting is allowed in the actual frame */
|
|
/* ----------------------------------------------------------------- */
|
|
swMutePermit = 0;
|
|
|
|
|
|
/* frame classification */
|
|
/* -------------------- */
|
|
|
|
swSID = pswParameters[20];
|
|
swTAF = pswParameters[21];
|
|
|
|
swBfiDtx = pswParameters[18] | pswParameters[19]; /* BFI | UFI */
|
|
|
|
if ((swSID == 2) && (swBfiDtx == 0))
|
|
swFrameType = VALIDSID;
|
|
else if ((swSID == 0) && (swBfiDtx == 0))
|
|
swFrameType = GOODSPEECH;
|
|
else if ((swSID == 0) && (swBfiDtx != 0))
|
|
swFrameType = UNUSABLE;
|
|
else
|
|
swFrameType = INVALIDSID;
|
|
|
|
|
|
/* update of decoder state */
|
|
/* ----------------------- */
|
|
|
|
if (swDecoMode == SPEECH)
|
|
{
|
|
/* speech decoding mode */
|
|
/* -------------------- */
|
|
|
|
if (swFrameType == VALIDSID)
|
|
swDecoMode = CNIFIRSTSID;
|
|
else if (swFrameType == INVALIDSID)
|
|
swDecoMode = CNIFIRSTSID;
|
|
else if (swFrameType == UNUSABLE)
|
|
swDecoMode = SPEECH;
|
|
else if (swFrameType == GOODSPEECH)
|
|
swDecoMode = SPEECH;
|
|
}
|
|
else
|
|
{
|
|
/* comfort noise insertion mode */
|
|
/* ---------------------------- */
|
|
|
|
if (swFrameType == VALIDSID)
|
|
swDecoMode = CNICONT;
|
|
else if (swFrameType == INVALIDSID)
|
|
swDecoMode = CNICONT;
|
|
else if (swFrameType == UNUSABLE)
|
|
swDecoMode = CNIBFI;
|
|
else if (swFrameType == GOODSPEECH)
|
|
swDecoMode = SPEECH;
|
|
}
|
|
|
|
|
|
if (swDecoMode == SPEECH)
|
|
{
|
|
/* speech decoding mode */
|
|
/* -------------------- */
|
|
|
|
/* Perform parameter concealment, depending on BFI (pswParameters[18]) */
|
|
/* or UFI (pswParameters[19]) */
|
|
/* ------------------------------------------------------------------- */
|
|
para_conceal_speech_decoder(&pswParameters[18],
|
|
pswParameters, &swMutePermit);
|
|
|
|
|
|
/* copy the frame rate parameters */
|
|
/* ------------------------------ */
|
|
|
|
swR0Index = pswParameters[0]; /* R0 Index */
|
|
pswVq[0] = pswParameters[1]; /* LPC1 */
|
|
pswVq[1] = pswParameters[2]; /* LPC2 */
|
|
pswVq[2] = pswParameters[3]; /* LPC3 */
|
|
swSi = pswParameters[4]; /* INT_LPC */
|
|
swVoicingMode = pswParameters[5]; /* MODE */
|
|
|
|
|
|
/* lookup R0 and VQ parameters */
|
|
/* --------------------------- */
|
|
|
|
swR0Dec = psrR0DecTbl[swR0Index * 2]; /* R0 */
|
|
lookupVq(pswVq, pswFrmKs);
|
|
|
|
|
|
/* save this frames GS values */
|
|
/* -------------------------- */
|
|
|
|
for (i = 0; i < N_SUB; i++)
|
|
{
|
|
pL_RxGsHist[swRxGsHistPtr] =
|
|
ppLr_gsTable[swVoicingMode][pswParameters[(i * 3) + 8]];
|
|
swRxGsHistPtr++;
|
|
if (swRxGsHistPtr > ((OVERHANG - 1) * N_SUB) - 1)
|
|
swRxGsHistPtr = 0;
|
|
}
|
|
|
|
|
|
/* DTX variables */
|
|
/* ------------- */
|
|
|
|
swDtxBfiCnt = 0;
|
|
swDtxMuting = 0;
|
|
swRxDTXState = CNINTPER - 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
/* comfort noise insertion mode */
|
|
/*----------------------------- */
|
|
|
|
/* copy the frame rate parameters */
|
|
/* ------------------------------ */
|
|
|
|
swR0Index = pswParameters[0]; /* R0 Index */
|
|
pswVq[0] = pswParameters[1]; /* LPC1 */
|
|
pswVq[1] = pswParameters[2]; /* LPC2 */
|
|
pswVq[2] = pswParameters[3]; /* LPC3 */
|
|
swSi = 1; /* INT_LPC */
|
|
swVoicingMode = 0; /* MODE */
|
|
|
|
|
|
/* bad frame handling in comfort noise insertion mode */
|
|
/* -------------------------------------------------- */
|
|
|
|
if (swDecoMode == CNIFIRSTSID) /* first SID frame */
|
|
{
|
|
swDtxBfiCnt = 0;
|
|
swDtxMuting = 0;
|
|
swRxDTXState = CNINTPER - 1;
|
|
|
|
if (swFrameType == VALIDSID) /* valid SID frame */
|
|
{
|
|
swR0NewCN = psrR0DecTbl[swR0Index * 2];
|
|
lookupVq(pswVq, pswFrmKs);
|
|
}
|
|
else if (swFrameType == INVALIDSID) /* invalid SID frame */
|
|
{
|
|
swR0NewCN = psrR0DecTbl[swOldR0IndexDec * 2];
|
|
swR0Index = swOldR0IndexDec;
|
|
for (i = 0; i < NP; i++)
|
|
pswFrmKs[i] = pswOldFrmKsDec[i];
|
|
}
|
|
|
|
}
|
|
else if (swDecoMode == CNICONT) /* SID frame detected, but */
|
|
{ /* not the first SID */
|
|
swDtxBfiCnt = 0;
|
|
swDtxMuting = 0;
|
|
|
|
if (swFrameType == VALIDSID) /* valid SID frame */
|
|
{
|
|
swRxDTXState = 0;
|
|
swR0NewCN = psrR0DecTbl[swR0Index * 2];
|
|
lookupVq(pswVq, pswFrmKs);
|
|
}
|
|
else if (swFrameType == INVALIDSID) /* invalid SID frame */
|
|
{
|
|
if (swRxDTXState < (CNINTPER - 1))
|
|
swRxDTXState = add(swRxDTXState, 1);
|
|
swR0Index = swOldR0IndexDec;
|
|
}
|
|
|
|
}
|
|
else if (swDecoMode == CNIBFI) /* bad frame received in */
|
|
{ /* CNI mode */
|
|
if (swRxDTXState < (CNINTPER - 1))
|
|
swRxDTXState = add(swRxDTXState, 1);
|
|
swR0Index = swOldR0IndexDec;
|
|
|
|
if (swDtxMuting == 1)
|
|
{
|
|
swOldR0IndexDec = sub(swOldR0IndexDec, 2); /* attenuate
|
|
* by 4 dB */
|
|
if (swOldR0IndexDec < 0)
|
|
swOldR0IndexDec = 0;
|
|
|
|
swR0Index = swOldR0IndexDec;
|
|
|
|
swR0NewCN = psrR0DecTbl[swOldR0IndexDec * 2]; /* R0 */
|
|
|
|
}
|
|
|
|
swDtxBfiCnt = add(swDtxBfiCnt, 1);
|
|
if ((swTAF == 1) && (swDtxBfiCnt >= (2 * CNINTPER + 1))) /* 25 */
|
|
swDtxMuting = 1;
|
|
|
|
}
|
|
|
|
|
|
if (swDecoMode == CNIFIRSTSID)
|
|
{
|
|
|
|
/* the first SID frame is received */
|
|
/* ------------------------------- */
|
|
|
|
/* initialize the decoders pn-generator */
|
|
/* ------------------------------------ */
|
|
|
|
L_RxPNSeed = PN_INIT_SEED;
|
|
|
|
|
|
/* using the stored rx history, generate averaged GS */
|
|
/* ------------------------------------------------- */
|
|
|
|
avgGsHistQntz(pL_RxGsHist, &L_RxDTXGs);
|
|
swRxDtxGsIndex = gsQuant(L_RxDTXGs, 0);
|
|
|
|
}
|
|
|
|
|
|
/* Replace the "transmitted" subframe parameters with */
|
|
/* synthetic ones */
|
|
/* -------------------------------------------------- */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
/* initialize the GSP0 parameter */
|
|
pswParameters[(i * 3) + 8] = swRxDtxGsIndex;
|
|
|
|
/* CODE1 */
|
|
pswParameters[(i * 3) + 6] = getPnBits(7, &L_RxPNSeed);
|
|
/* CODE2 */
|
|
pswParameters[(i * 3) + 7] = getPnBits(7, &L_RxPNSeed);
|
|
}
|
|
|
|
|
|
/* Interpolation of CN parameters */
|
|
/* ------------------------------ */
|
|
|
|
rxInterpR0Lpc(pswOldFrmKsDec, pswFrmKs, swRxDTXState,
|
|
swDecoMode, swFrameType);
|
|
|
|
}
|
|
|
|
|
|
/* ------------------- */
|
|
/* do frame processing */
|
|
/* ------------------- */
|
|
|
|
/* generate the direct form coefs */
|
|
/* ------------------------------ */
|
|
|
|
if (!rcToADp(ASCALE, pswFrmKs, pswFrmAs))
|
|
{
|
|
|
|
/* widen direct form coefficients using the widening coefs */
|
|
/* ------------------------------------------------------- */
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswFrmPFDenom[i] = mult_r(pswFrmAs[i], psrSPFDenomWidenCf[i]);
|
|
}
|
|
|
|
a_sst(ASHIFT, ASCALE, pswFrmPFDenom, pswFrmPFNum);
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswFrmKs[i] = pswOldFrmKsDec[i];
|
|
pswFrmAs[i] = pswOldFrmAsDec[i];
|
|
pswFrmPFDenom[i] = pswOldFrmPFDenom[i];
|
|
pswFrmPFNum[i] = pswOldFrmPFNum[i];
|
|
}
|
|
}
|
|
|
|
/* interpolate, or otherwise get sfrm reflection coefs */
|
|
/* --------------------------------------------------- */
|
|
|
|
getSfrmLpc(swSi, swOldR0Dec, swR0Dec, pswOldFrmKsDec, pswOldFrmAsDec,
|
|
pswOldFrmPFNum, pswOldFrmPFDenom, pswFrmKs, pswFrmAs,
|
|
pswFrmPFNum, pswFrmPFDenom, psnsSqrtRs, ppswSynthAs,
|
|
ppswPFNumAs, ppswPFDenomAs);
|
|
|
|
/* calculate shift for spectral postfilter */
|
|
/* --------------------------------------- */
|
|
|
|
swEngyRShift = r0BasedEnergyShft(swR0Index);
|
|
|
|
|
|
/* ----------------------- */
|
|
/* do sub-frame processing */
|
|
/* ----------------------- */
|
|
|
|
for (giSfrmCnt = 0; giSfrmCnt < 4; giSfrmCnt++)
|
|
{
|
|
|
|
/* copy this sub-frame's parameters */
|
|
/* -------------------------------- */
|
|
|
|
if (sub(swVoicingMode, 0) == 0)
|
|
{ /* unvoiced */
|
|
psiVselpCw[0] = pswParameters[(giSfrmCnt * 3) + 6]; /* CODE_1 */
|
|
psiVselpCw[1] = pswParameters[(giSfrmCnt * 3) + 7]; /* CODE_2 */
|
|
siGsp0Code = pswParameters[(giSfrmCnt * 3) + 8]; /* GSP0 */
|
|
}
|
|
else
|
|
{ /* voiced */
|
|
siLagCode = pswParameters[(giSfrmCnt * 3) + 6]; /* LAG */
|
|
psiVselpCw[0] = pswParameters[(giSfrmCnt * 3) + 7]; /* CODE */
|
|
siGsp0Code = pswParameters[(giSfrmCnt * 3) + 8]; /* GSP0 */
|
|
}
|
|
|
|
/* for voiced mode, reconstruct the pitch vector */
|
|
/* --------------------------------------------- */
|
|
|
|
if (swVoicingMode)
|
|
{
|
|
|
|
/* convert delta lag to lag and convert to fractional lag */
|
|
/* ------------------------------------------------------ */
|
|
|
|
swLag = lagDecode(siLagCode);
|
|
|
|
/* state followed by out */
|
|
/* --------------------- */
|
|
|
|
fp_ex(swLag, pswLtpStateOut);
|
|
|
|
/* extract a piece of pswLtpStateOut into newly named vector pswPVec */
|
|
/* ----------------------------------------------------------------- */
|
|
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
pswPVec[i] = pswLtpStateOut[i];
|
|
}
|
|
}
|
|
|
|
/* for unvoiced, do not reconstruct a pitch vector */
|
|
/* ----------------------------------------------- */
|
|
|
|
else
|
|
{
|
|
swLag = 0; /* indicates invalid lag
|
|
* and unvoiced */
|
|
}
|
|
|
|
/* now work on the VSELP codebook excitation output */
|
|
/* x_vec, x_a_vec here named ppswVselpEx[0] and [1] */
|
|
/* ------------------------------------------------ */
|
|
|
|
if (swVoicingMode)
|
|
{ /* voiced */
|
|
|
|
siNumBits = C_BITS_V;
|
|
siVselpCw = psiVselpCw[0];
|
|
|
|
b_con(siVselpCw, siNumBits, pswBitArray);
|
|
|
|
v_con(pppsrVcdCodeVec[0][0], ppswVselpEx[0], pswBitArray, siNumBits);
|
|
}
|
|
|
|
else
|
|
{ /* unvoiced */
|
|
|
|
siNumBits = C_BITS_UV;
|
|
|
|
for (siCodeBook = 0; siCodeBook < 2; siCodeBook++)
|
|
{
|
|
|
|
siVselpCw = psiVselpCw[siCodeBook];
|
|
|
|
b_con(siVselpCw, siNumBits, (int16_t *) pswBitArray);
|
|
|
|
v_con(pppsrUvCodeVec[siCodeBook][0], ppswVselpEx[siCodeBook],
|
|
pswBitArray, siNumBits);
|
|
}
|
|
}
|
|
|
|
/* all excitation vectors have been created: ppswVselpEx and pswPVec */
|
|
/* if voiced compute rs00 and rs11; if unvoiced cmpute rs11 and rs22 */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
if (swLag)
|
|
{
|
|
rs_rr(pswPVec, psnsSqrtRs[giSfrmCnt], &snsRs00);
|
|
}
|
|
|
|
rs_rrNs(ppswVselpEx[0], psnsSqrtRs[giSfrmCnt], &snsRs11);
|
|
|
|
if (!swVoicingMode)
|
|
{
|
|
rs_rrNs(ppswVselpEx[1], psnsSqrtRs[giSfrmCnt], &snsRs22);
|
|
}
|
|
|
|
/* now implement synthesis - combine the excitations */
|
|
/* ------------------------------------------------- */
|
|
|
|
if (swVoicingMode)
|
|
{ /* voiced */
|
|
|
|
/* scale pitch and codebook excitations and get beta */
|
|
/* ------------------------------------------------- */
|
|
swSemiBeta = scaleExcite(pswPVec,
|
|
pppsrGsp0[swVoicingMode][siGsp0Code][0],
|
|
snsRs00, pswPVec);
|
|
scaleExcite(ppswVselpEx[0],
|
|
pppsrGsp0[swVoicingMode][siGsp0Code][1],
|
|
snsRs11, ppswVselpEx[0]);
|
|
|
|
/* combine the two scaled excitations */
|
|
/* ---------------------------------- */
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
pswExcite[i] = add(pswPVec[i], ppswVselpEx[0][i]);
|
|
}
|
|
}
|
|
else
|
|
{ /* unvoiced */
|
|
|
|
/* scale codebook excitations and set beta to 0 as not applicable */
|
|
/* -------------------------------------------------------------- */
|
|
swSemiBeta = 0;
|
|
scaleExcite(ppswVselpEx[0],
|
|
pppsrGsp0[swVoicingMode][siGsp0Code][0],
|
|
snsRs11, ppswVselpEx[0]);
|
|
scaleExcite(ppswVselpEx[1],
|
|
pppsrGsp0[swVoicingMode][siGsp0Code][1],
|
|
snsRs22, ppswVselpEx[1]);
|
|
|
|
/* combine the two scaled excitations */
|
|
/* ---------------------------------- */
|
|
for (i = 0; i < S_LEN; i++)
|
|
{
|
|
pswExcite[i] = add(ppswVselpEx[1][i], ppswVselpEx[0][i]);
|
|
}
|
|
}
|
|
|
|
/* now update the pitch state using the combined/scaled excitation */
|
|
/* --------------------------------------------------------------- */
|
|
|
|
for (i = 0; i < LTP_LEN; i++)
|
|
{
|
|
pswLtpStateBaseDec[i] = pswLtpStateBaseDec[i + S_LEN];
|
|
}
|
|
|
|
/* add the current sub-frames data to the state */
|
|
/* -------------------------------------------- */
|
|
|
|
for (i = -S_LEN, j = 0; j < S_LEN; i++, j++)
|
|
{
|
|
pswLtpStateOut[i] = pswExcite[j];/* add new frame at t = -S_LEN */
|
|
}
|
|
|
|
/* given the excitation perform pitch prefiltering */
|
|
/* ----------------------------------------------- */
|
|
|
|
pitchPreFilt(pswExcite, siGsp0Code, swLag,
|
|
swVoicingMode, swSemiBeta, psnsSqrtRs[giSfrmCnt],
|
|
pswPPFExcit, pswPPreState);
|
|
|
|
|
|
/* Concealment on subframe signal level: */
|
|
/* ------------------------------------- */
|
|
level_estimator(0, &swLevelMean, &swLevelMax,
|
|
&pswDecodedSpeechFrame[giSfrmCnt * S_LEN]);
|
|
|
|
signal_conceal_sub(pswPPFExcit, ppswSynthAs[giSfrmCnt], pswSynthFiltState,
|
|
&pswLtpStateOut[-S_LEN], &pswPPreState[LTP_LEN - S_LEN],
|
|
swLevelMean, swLevelMax,
|
|
pswParameters[19], swMuteFlagOld,
|
|
&swMuteFlag, swMutePermit);
|
|
|
|
|
|
/* synthesize the speech through the synthesis filter */
|
|
/* -------------------------------------------------- */
|
|
|
|
lpcIir(pswPPFExcit, ppswSynthAs[giSfrmCnt], pswSynthFiltState,
|
|
pswSynthFiltOut);
|
|
|
|
/* pass reconstructed speech through adaptive spectral postfilter */
|
|
/* -------------------------------------------------------------- */
|
|
|
|
spectralPostFilter(pswSynthFiltOut, ppswPFNumAs[giSfrmCnt],
|
|
ppswPFDenomAs[giSfrmCnt],
|
|
&pswDecodedSpeechFrame[giSfrmCnt * S_LEN]);
|
|
|
|
level_estimator(1, &swLevelMean, &swLevelMax,
|
|
&pswDecodedSpeechFrame[giSfrmCnt * S_LEN]);
|
|
|
|
}
|
|
|
|
/* Save muting information for next frame */
|
|
/* -------------------------------------- */
|
|
swMuteFlagOld = swMuteFlag;
|
|
|
|
/* end of frame processing - save this frame's frame energy, */
|
|
/* reflection coefs, direct form coefs, and post filter coefs */
|
|
/* ---------------------------------------------------------- */
|
|
|
|
swOldR0Dec = swR0Dec;
|
|
swOldR0IndexDec = swR0Index; /* DTX mode */
|
|
|
|
for (i = 0; i < NP; i++)
|
|
{
|
|
pswOldFrmKsDec[i] = pswFrmKs[i];
|
|
pswOldFrmAsDec[i] = pswFrmAs[i];
|
|
pswOldFrmPFNum[i] = pswFrmPFNum[i];
|
|
pswOldFrmPFDenom[i] = pswFrmPFDenom[i];
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: sqroot
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* The purpose of this function is to perform a single precision square
|
|
* root function on a int32_t
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* L_SqrtIn
|
|
* input to square root function
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* none
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* swSqrtOut
|
|
* output to square root function
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Input assumed to be normalized
|
|
*
|
|
* The algorithm is based around a six term Taylor expansion :
|
|
*
|
|
* y^0.5 = (1+x)^0.5
|
|
* ~= 1 + (x/2) - 0.5*((x/2)^2) + 0.5*((x/2)^3)
|
|
* - 0.625*((x/2)^4) + 0.875*((x/2)^5)
|
|
*
|
|
* Max error less than 0.08 % for normalized input ( 0.5 <= x < 1 )
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.4.1, 4.1.7, 4.1.11.1, 4.2.1,
|
|
* 4.2.2, 4.2.3 and 4.2.4 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: sqrt, squareroot, sqrt016
|
|
*
|
|
*************************************************************************/
|
|
|
|
int16_t sqroot(int32_t L_SqrtIn)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Local Constants |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
#define PLUS_HALF 0x40000000L /* 0.5 */
|
|
#define MINUS_ONE 0x80000000L /* -1 */
|
|
#define TERM5_MULTIPLER 0x5000 /* 0.625 */
|
|
#define TERM6_MULTIPLER 0x7000 /* 0.875 */
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Temp0,
|
|
L_Temp1;
|
|
|
|
int16_t swTemp,
|
|
swTemp2,
|
|
swTemp3,
|
|
swTemp4,
|
|
swSqrtOut;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* determine 2nd term x/2 = (y-1)/2 */
|
|
/* -------------------------------- */
|
|
|
|
L_Temp1 = L_shr(L_SqrtIn, 1); /* L_Temp1 = y/2 */
|
|
L_Temp1 = L_sub(L_Temp1, PLUS_HALF); /* L_Temp1 = (y-1)/2 */
|
|
swTemp = extract_h(L_Temp1); /* swTemp = x/2 */
|
|
|
|
/* add contribution of 2nd term */
|
|
/* ---------------------------- */
|
|
|
|
L_Temp1 = L_sub(L_Temp1, MINUS_ONE); /* L_Temp1 = 1 + x/2 */
|
|
|
|
/* determine 3rd term */
|
|
/* ------------------ */
|
|
|
|
L_Temp0 = L_msu(0L, swTemp, swTemp); /* L_Temp0 = -(x/2)^2 */
|
|
swTemp2 = extract_h(L_Temp0); /* swTemp2 = -(x/2)^2 */
|
|
L_Temp0 = L_shr(L_Temp0, 1); /* L_Temp0 = -0.5*(x/2)^2 */
|
|
|
|
/* add contribution of 3rd term */
|
|
/* ---------------------------- */
|
|
|
|
L_Temp0 = L_add(L_Temp1, L_Temp0); /* L_Temp0 = 1 + x/2 - 0.5*(x/2)^2 */
|
|
|
|
/* determine 4rd term */
|
|
/* ------------------ */
|
|
|
|
L_Temp1 = L_msu(0L, swTemp, swTemp2);/* L_Temp1 = (x/2)^3 */
|
|
swTemp3 = extract_h(L_Temp1); /* swTemp3 = (x/2)^3 */
|
|
L_Temp1 = L_shr(L_Temp1, 1); /* L_Temp1 = 0.5*(x/2)^3 */
|
|
|
|
/* add contribution of 4rd term */
|
|
/* ---------------------------- */
|
|
|
|
/* L_Temp1 = 1 + x/2 - 0.5*(x/2)^2 + 0.5*(x/2)^3 */
|
|
|
|
L_Temp1 = L_add(L_Temp0, L_Temp1);
|
|
|
|
/* determine partial 5th term */
|
|
/* -------------------------- */
|
|
|
|
L_Temp0 = L_mult(swTemp, swTemp3); /* L_Temp0 = (x/2)^4 */
|
|
swTemp4 = round(L_Temp0); /* swTemp4 = (x/2)^4 */
|
|
|
|
/* determine partial 6th term */
|
|
/* -------------------------- */
|
|
|
|
L_Temp0 = L_msu(0L, swTemp2, swTemp3); /* L_Temp0 = (x/2)^5 */
|
|
swTemp2 = round(L_Temp0); /* swTemp2 = (x/2)^5 */
|
|
|
|
/* determine 5th term and add its contribution */
|
|
/* ------------------------------------------- */
|
|
|
|
/* L_Temp0 = -0.625*(x/2)^4 */
|
|
|
|
L_Temp0 = L_msu(0L, TERM5_MULTIPLER, swTemp4);
|
|
|
|
/* L_Temp1 = 1 + x/2 - 0.5*(x/2)^2 + 0.5*(x/2)^3 - 0.625*(x/2)^4 */
|
|
|
|
L_Temp1 = L_add(L_Temp0, L_Temp1);
|
|
|
|
/* determine 6th term and add its contribution */
|
|
/* ------------------------------------------- */
|
|
|
|
/* swSqrtOut = 1 + x/2 - 0.5*(x/2)^2 + 0.5*(x/2)^3 */
|
|
/* - 0.625*(x/2)^4 + 0.875*(x/2)^5 */
|
|
|
|
swSqrtOut = mac_r(L_Temp1, TERM6_MULTIPLER, swTemp2);
|
|
|
|
/* return output */
|
|
/* ------------- */
|
|
|
|
return (swSqrtOut);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* FUNCTION NAME: v_con
|
|
*
|
|
* PURPOSE:
|
|
*
|
|
* This subroutine constructs a codebook excitation
|
|
* vector from basis vectors
|
|
*
|
|
* INPUTS:
|
|
*
|
|
* pswBVects[0:siNumBVctrs*S_LEN-1]
|
|
*
|
|
* Array containing a set of basis vectors.
|
|
*
|
|
* pswBitArray[0:siNumBVctrs-1]
|
|
*
|
|
* Bit array dictating the polarity of the
|
|
* basis vectors in the output vector.
|
|
* Each element of the bit array is either -0.5 or +0.5
|
|
*
|
|
* siNumBVctrs
|
|
* Number of bits in codeword
|
|
*
|
|
* OUTPUTS:
|
|
*
|
|
* pswOutVect[0:39]
|
|
*
|
|
* Array containing the contructed output vector
|
|
*
|
|
* RETURN VALUE:
|
|
*
|
|
* none
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* The array pswBitArray is used to multiply each of the siNumBVctrs
|
|
* basis vectors. The input pswBitArray[] is an array whose
|
|
* elements are +/-0.5. These multiply the VSELP basis vectors and
|
|
* when summed produce a VSELP codevector. b_con() is the function
|
|
* used to translate a VSELP codeword into pswBitArray[].
|
|
*
|
|
*
|
|
* REFERENCES: Sub-clause 4.1.10 and 4.2.1 of GSM Recomendation 06.20
|
|
*
|
|
* KEYWORDS: v_con, codeword, reconstruct, basis vector, excitation
|
|
*
|
|
*************************************************************************/
|
|
|
|
void v_con(int16_t pswBVects[], int16_t pswOutVect[],
|
|
int16_t pswBitArray[], short int siNumBVctrs)
|
|
{
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Automatic Variables |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
int32_t L_Temp;
|
|
|
|
short int siSampleCnt,
|
|
siCVectCnt;
|
|
|
|
/*_________________________________________________________________________
|
|
| |
|
|
| Executable Code |
|
|
|_________________________________________________________________________|
|
|
*/
|
|
|
|
/* Sample loop */
|
|
/*--------------*/
|
|
for (siSampleCnt = 0; siSampleCnt < S_LEN; siSampleCnt++)
|
|
{
|
|
|
|
/* First element of output vector */
|
|
L_Temp = L_mult(pswBitArray[0], pswBVects[0 * S_LEN + siSampleCnt]);
|
|
|
|
/* Construct output vector */
|
|
/*-------------------------*/
|
|
for (siCVectCnt = 1; siCVectCnt < siNumBVctrs; siCVectCnt++)
|
|
{
|
|
L_Temp = L_mac(L_Temp, pswBitArray[siCVectCnt],
|
|
pswBVects[siCVectCnt * S_LEN + siSampleCnt]);
|
|
}
|
|
|
|
/* store the output vector sample */
|
|
/*--------------------------------*/
|
|
L_Temp = L_shl(L_Temp, 1);
|
|
pswOutVect[siSampleCnt] = extract_h(L_Temp);
|
|
}
|
|
}
|