mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			657 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
			
		
		
	
	
			657 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
| /*---------------------------------------------------------------------------*\
 | |
| Original copyright
 | |
| 	FILE........: lsp.c
 | |
| 	AUTHOR......: David Rowe
 | |
| 	DATE CREATED: 24/2/93
 | |
| 
 | |
| Heavily modified by Jean-Marc Valin (c) 2002-2006 (fixed-point, 
 | |
|                        optimizations, additional functions, ...)
 | |
| 
 | |
|    This file contains functions for converting Linear Prediction
 | |
|    Coefficients (LPC) to Line Spectral Pair (LSP) and back. Note that the
 | |
|    LSP coefficients are not in radians format but in the x domain of the
 | |
|    unit circle.
 | |
| 
 | |
|    Speex License:
 | |
| 
 | |
|    Redistribution and use in source and binary forms, with or without
 | |
|    modification, are permitted provided that the following conditions
 | |
|    are met:
 | |
|    
 | |
|    - Redistributions of source code must retain the above copyright
 | |
|    notice, this list of conditions and the following disclaimer.
 | |
|    
 | |
|    - Redistributions in binary form must reproduce the above copyright
 | |
|    notice, this list of conditions and the following disclaimer in the
 | |
|    documentation and/or other materials provided with the distribution.
 | |
|    
 | |
|    - Neither the name of the Xiph.org Foundation nor the names of its
 | |
|    contributors may be used to endorse or promote products derived from
 | |
|    this software without specific prior written permission.
 | |
|    
 | |
|    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
|    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
|    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
|    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 | |
|    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | |
|    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | |
|    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | |
|    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | |
|    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | |
|    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| */
 | |
| 
 | |
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|   Introduction to Line Spectrum Pairs (LSPs)
 | |
|   ------------------------------------------
 | |
| 
 | |
|   LSPs are used to encode the LPC filter coefficients {ak} for
 | |
|   transmission over the channel.  LSPs have several properties (like
 | |
|   less sensitivity to quantisation noise) that make them superior to
 | |
|   direct quantisation of {ak}.
 | |
| 
 | |
|   A(z) is a polynomial of order lpcrdr with {ak} as the coefficients.
 | |
| 
 | |
|   A(z) is transformed to P(z) and Q(z) (using a substitution and some
 | |
|   algebra), to obtain something like:
 | |
| 
 | |
|     A(z) = 0.5[P(z)(z+z^-1) + Q(z)(z-z^-1)]  (1)
 | |
| 
 | |
|   As you can imagine A(z) has complex zeros all over the z-plane. P(z)
 | |
|   and Q(z) have the very neat property of only having zeros _on_ the
 | |
|   unit circle.  So to find them we take a test point z=exp(jw) and
 | |
|   evaluate P (exp(jw)) and Q(exp(jw)) using a grid of points between 0
 | |
|   and pi.
 | |
| 
 | |
|   The zeros (roots) of P(z) also happen to alternate, which is why we
 | |
|   swap coefficients as we find roots.  So the process of finding the
 | |
|   LSP frequencies is basically finding the roots of 5th order
 | |
|   polynomials.
 | |
| 
 | |
|   The root so P(z) and Q(z) occur in symmetrical pairs at +/-w, hence
 | |
|   the name Line Spectrum Pairs (LSPs).
 | |
| 
 | |
|   To convert back to ak we just evaluate (1), "clocking" an impulse
 | |
|   thru it lpcrdr times gives us the impulse response of A(z) which is
 | |
|   {ak}.
 | |
| 
 | |
| \*---------------------------------------------------------------------------*/
 | |
| 
 | |
| #ifdef HAVE_CONFIG_H
 | |
| #include "config.h"
 | |
| #endif
 | |
| 
 | |
| #include <math.h>
 | |
| #include "lsp.h"
 | |
| #include "stack_alloc.h"
 | |
| #include "math_approx.h"
 | |
| 
 | |
| #ifndef M_PI
 | |
| #define M_PI           3.14159265358979323846  /* pi */
 | |
| #endif
 | |
| 
 | |
| #ifndef NULL
 | |
| #define NULL 0
 | |
| #endif
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
| 
 | |
| #define FREQ_SCALE 16384
 | |
| 
 | |
| /*#define ANGLE2X(a) (32768*cos(((a)/8192.)))*/
 | |
| #define ANGLE2X(a) (SHL16(spx_cos(a),2))
 | |
| 
 | |
| /*#define X2ANGLE(x) (acos(.00006103515625*(x))*LSP_SCALING)*/
 | |
| #define X2ANGLE(x) (spx_acos(x))
 | |
| 
 | |
| #ifdef BFIN_ASM
 | |
| #include "lsp_bfin.h"
 | |
| #endif
 | |
| 
 | |
| #else
 | |
| 
 | |
| /*#define C1 0.99940307
 | |
| #define C2 -0.49558072
 | |
| #define C3 0.03679168*/
 | |
| 
 | |
| #define FREQ_SCALE 1.
 | |
| #define ANGLE2X(a) (spx_cos(a))
 | |
| #define X2ANGLE(x) (acos(x))
 | |
| 
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|    FUNCTION....: cheb_poly_eva()
 | |
| 
 | |
|    AUTHOR......: David Rowe
 | |
|    DATE CREATED: 24/2/93
 | |
| 
 | |
|    This function evaluates a series of Chebyshev polynomials
 | |
| 
 | |
| \*---------------------------------------------------------------------------*/
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
| 
 | |
| #ifndef OVERRIDE_CHEB_POLY_EVA
 | |
| static inline spx_word32_t cheb_poly_eva(
 | |
|   spx_word16_t *coef, /* P or Q coefs in Q13 format               */
 | |
|   spx_word16_t     x, /* cos of freq (-1.0 to 1.0) in Q14 format  */
 | |
|   int              m, /* LPC order/2                              */
 | |
|   char         *stack
 | |
| )
 | |
| {
 | |
|     int i;
 | |
|     spx_word16_t b0, b1;
 | |
|     spx_word32_t sum;
 | |
| 
 | |
|     /*Prevents overflows*/
 | |
|     if (x>16383)
 | |
|        x = 16383;
 | |
|     if (x<-16383)
 | |
|        x = -16383;
 | |
| 
 | |
|     /* Initialise values */
 | |
|     b1=16384;
 | |
|     b0=x;
 | |
| 
 | |
|     /* Evaluate Chebyshev series formulation usin g iterative approach  */
 | |
|     sum = ADD32(EXTEND32(coef[m]), EXTEND32(MULT16_16_P14(coef[m-1],x)));
 | |
|     for(i=2;i<=m;i++)
 | |
|     {
 | |
|        spx_word16_t tmp=b0;
 | |
|        b0 = SUB16(MULT16_16_Q13(x,b0), b1);
 | |
|        b1 = tmp;
 | |
|        sum = ADD32(sum, EXTEND32(MULT16_16_P14(coef[m-i],b0)));
 | |
|     }
 | |
|     
 | |
|     return sum;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #else
 | |
| 
 | |
| static float cheb_poly_eva(spx_word32_t *coef, spx_word16_t x, int m, char *stack)
 | |
| {
 | |
|    int k;
 | |
|    float b0, b1, tmp;
 | |
| 
 | |
|    /* Initial conditions */
 | |
|    b0=0; /* b_(m+1) */
 | |
|    b1=0; /* b_(m+2) */
 | |
| 
 | |
|    x*=2;
 | |
| 
 | |
|    /* Calculate the b_(k) */
 | |
|    for(k=m;k>0;k--)
 | |
|    {
 | |
|       tmp=b0;                           /* tmp holds the previous value of b0 */
 | |
|       b0=x*b0-b1+coef[m-k];    /* b0 holds its new value based on b0 and b1 */
 | |
|       b1=tmp;                           /* b1 holds the previous value of b0 */
 | |
|    }
 | |
| 
 | |
|    return(-b1+.5*x*b0+coef[m]);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
|     FUNCTION....: lpc_to_lsp()
 | |
| 
 | |
|     AUTHOR......: David Rowe
 | |
|     DATE CREATED: 24/2/93
 | |
| 
 | |
|     This function converts LPC coefficients to LSP
 | |
|     coefficients.
 | |
| 
 | |
| \*---------------------------------------------------------------------------*/
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
| #define SIGN_CHANGE(a,b) (((a)&0x70000000)^((b)&0x70000000)||(b==0))
 | |
| #else
 | |
| #define SIGN_CHANGE(a,b) (((a)*(b))<0.0)
 | |
| #endif
 | |
| 
 | |
| 
 | |
| int lpc_to_lsp (spx_coef_t *a,int lpcrdr,spx_lsp_t *freq,int nb,spx_word16_t delta, char *stack)
 | |
| /*  float *a 		     	lpc coefficients			*/
 | |
| /*  int lpcrdr			order of LPC coefficients (10) 		*/
 | |
| /*  float *freq 	      	LSP frequencies in the x domain       	*/
 | |
| /*  int nb			number of sub-intervals (4) 		*/
 | |
| /*  float delta			grid spacing interval (0.02) 		*/
 | |
| 
 | |
| 
 | |
| {
 | |
|     spx_word16_t temp_xr,xl,xr,xm=0;
 | |
|     spx_word32_t psuml,psumr,psumm,temp_psumr/*,temp_qsumr*/;
 | |
|     int i,j,m,flag,k;
 | |
|     VARDECL(spx_word32_t *Q);                 	/* ptrs for memory allocation 		*/
 | |
|     VARDECL(spx_word32_t *P);
 | |
|     VARDECL(spx_word16_t *Q16);         /* ptrs for memory allocation 		*/
 | |
|     VARDECL(spx_word16_t *P16);
 | |
|     spx_word32_t *px;                	/* ptrs of respective P'(z) & Q'(z)	*/
 | |
|     spx_word32_t *qx;
 | |
|     spx_word32_t *p;
 | |
|     spx_word32_t *q;
 | |
|     spx_word16_t *pt;                	/* ptr used for cheb_poly_eval()
 | |
| 				whether P' or Q' 			*/
 | |
|     int roots=0;              	/* DR 8/2/94: number of roots found 	*/
 | |
|     flag = 1;                	/*  program is searching for a root when,
 | |
| 				1 else has found one 			*/
 | |
|     m = lpcrdr/2;            	/* order of P'(z) & Q'(z) polynomials 	*/
 | |
| 
 | |
|     /* Allocate memory space for polynomials */
 | |
|     ALLOC(Q, (m+1), spx_word32_t);
 | |
|     ALLOC(P, (m+1), spx_word32_t);
 | |
| 
 | |
|     /* determine P'(z)'s and Q'(z)'s coefficients where
 | |
|       P'(z) = P(z)/(1 + z^(-1)) and Q'(z) = Q(z)/(1-z^(-1)) */
 | |
| 
 | |
|     px = P;                      /* initialise ptrs 			*/
 | |
|     qx = Q;
 | |
|     p = px;
 | |
|     q = qx;
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
|     *px++ = LPC_SCALING;
 | |
|     *qx++ = LPC_SCALING;
 | |
|     for(i=0;i<m;i++){
 | |
|        *px++ = SUB32(ADD32(EXTEND32(a[i]),EXTEND32(a[lpcrdr-i-1])), *p++);
 | |
|        *qx++ = ADD32(SUB32(EXTEND32(a[i]),EXTEND32(a[lpcrdr-i-1])), *q++);
 | |
|     }
 | |
|     px = P;
 | |
|     qx = Q;
 | |
|     for(i=0;i<m;i++)
 | |
|     {
 | |
|        /*if (fabs(*px)>=32768)
 | |
|           speex_warning_int("px", *px);
 | |
|        if (fabs(*qx)>=32768)
 | |
|        speex_warning_int("qx", *qx);*/
 | |
|        *px = PSHR32(*px,2);
 | |
|        *qx = PSHR32(*qx,2);
 | |
|        px++;
 | |
|        qx++;
 | |
|     }
 | |
|     /* The reason for this lies in the way cheb_poly_eva() is implemented for fixed-point */
 | |
|     P[m] = PSHR32(P[m],3);
 | |
|     Q[m] = PSHR32(Q[m],3);
 | |
| #else
 | |
|     *px++ = LPC_SCALING;
 | |
|     *qx++ = LPC_SCALING;
 | |
|     for(i=0;i<m;i++){
 | |
|        *px++ = (a[i]+a[lpcrdr-1-i]) - *p++;
 | |
|        *qx++ = (a[i]-a[lpcrdr-1-i]) + *q++;
 | |
|     }
 | |
|     px = P;
 | |
|     qx = Q;
 | |
|     for(i=0;i<m;i++){
 | |
|        *px = 2**px;
 | |
|        *qx = 2**qx;
 | |
|        px++;
 | |
|        qx++;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     px = P;             	/* re-initialise ptrs 			*/
 | |
|     qx = Q;
 | |
| 
 | |
|     /* now that we have computed P and Q convert to 16 bits to
 | |
|        speed up cheb_poly_eval */
 | |
| 
 | |
|     ALLOC(P16, m+1, spx_word16_t);
 | |
|     ALLOC(Q16, m+1, spx_word16_t);
 | |
| 
 | |
|     for (i=0;i<m+1;i++)
 | |
|     {
 | |
|        P16[i] = P[i];
 | |
|        Q16[i] = Q[i];
 | |
|     }
 | |
| 
 | |
|     /* Search for a zero in P'(z) polynomial first and then alternate to Q'(z).
 | |
|     Keep alternating between the two polynomials as each zero is found 	*/
 | |
| 
 | |
|     xr = 0;             	/* initialise xr to zero 		*/
 | |
|     xl = FREQ_SCALE;               	/* start at point xl = 1 		*/
 | |
| 
 | |
|     for(j=0;j<lpcrdr;j++){
 | |
| 	if(j&1)            	/* determines whether P' or Q' is eval. */
 | |
| 	    pt = Q16;
 | |
| 	else
 | |
| 	    pt = P16;
 | |
| 
 | |
| 	psuml = cheb_poly_eva(pt,xl,m,stack);	/* evals poly. at xl 	*/
 | |
| 	flag = 1;
 | |
| 	while(flag && (xr >= -FREQ_SCALE)){
 | |
|            spx_word16_t dd;
 | |
|            /* Modified by JMV to provide smaller steps around x=+-1 */
 | |
| #ifdef FIXED_POINT
 | |
|            dd = MULT16_16_Q15(delta,SUB16(FREQ_SCALE, MULT16_16_Q14(MULT16_16_Q14(xl,xl),14000)));
 | |
|            if (psuml<512 && psuml>-512)
 | |
|               dd = PSHR16(dd,1);
 | |
| #else
 | |
|            dd=delta*(1-.9*xl*xl);
 | |
|            if (fabs(psuml)<.2)
 | |
|               dd *= .5;
 | |
| #endif
 | |
|            xr = SUB16(xl, dd);                        	/* interval spacing 	*/
 | |
| 	    psumr = cheb_poly_eva(pt,xr,m,stack);/* poly(xl-delta_x) 	*/
 | |
| 	    temp_psumr = psumr;
 | |
| 	    temp_xr = xr;
 | |
| 
 | |
|     /* if no sign change increment xr and re-evaluate poly(xr). Repeat til
 | |
|     sign change.
 | |
|     if a sign change has occurred the interval is bisected and then
 | |
|     checked again for a sign change which determines in which
 | |
|     interval the zero lies in.
 | |
|     If there is no sign change between poly(xm) and poly(xl) set interval
 | |
|     between xm and xr else set interval between xl and xr and repeat till
 | |
|     root is located within the specified limits 			*/
 | |
| 
 | |
| 	    if(SIGN_CHANGE(psumr,psuml))
 | |
|             {
 | |
| 		roots++;
 | |
| 
 | |
| 		psumm=psuml;
 | |
| 		for(k=0;k<=nb;k++){
 | |
| #ifdef FIXED_POINT
 | |
| 		    xm = ADD16(PSHR16(xl,1),PSHR16(xr,1));        	/* bisect the interval 	*/
 | |
| #else
 | |
|                     xm = .5*(xl+xr);        	/* bisect the interval 	*/
 | |
| #endif
 | |
| 		    psumm=cheb_poly_eva(pt,xm,m,stack);
 | |
| 		    /*if(psumm*psuml>0.)*/
 | |
| 		    if(!SIGN_CHANGE(psumm,psuml))
 | |
|                     {
 | |
| 			psuml=psumm;
 | |
| 			xl=xm;
 | |
| 		    } else {
 | |
| 			psumr=psumm;
 | |
| 			xr=xm;
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
| 	       /* once zero is found, reset initial interval to xr 	*/
 | |
| 	       freq[j] = X2ANGLE(xm);
 | |
| 	       xl = xm;
 | |
| 	       flag = 0;       		/* reset flag for next search 	*/
 | |
| 	    }
 | |
| 	    else{
 | |
| 		psuml=temp_psumr;
 | |
| 		xl=temp_xr;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return(roots);
 | |
| }
 | |
| 
 | |
| /*---------------------------------------------------------------------------*\
 | |
| 
 | |
| 	FUNCTION....: lsp_to_lpc()
 | |
| 
 | |
| 	AUTHOR......: David Rowe
 | |
| 	DATE CREATED: 24/2/93
 | |
| 
 | |
|         Converts LSP coefficients to LPC coefficients.
 | |
| 
 | |
| \*---------------------------------------------------------------------------*/
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
| 
 | |
| void lsp_to_lpc(spx_lsp_t *freq,spx_coef_t *ak,int lpcrdr, char *stack)
 | |
| /*  float *freq 	array of LSP frequencies in the x domain	*/
 | |
| /*  float *ak 		array of LPC coefficients 			*/
 | |
| /*  int lpcrdr  	order of LPC coefficients 			*/
 | |
| {
 | |
|     int i,j;
 | |
|     spx_word32_t xout1,xout2,xin;
 | |
|     spx_word32_t mult, a;
 | |
|     VARDECL(spx_word16_t *freqn);
 | |
|     VARDECL(spx_word32_t **xp);
 | |
|     VARDECL(spx_word32_t *xpmem);
 | |
|     VARDECL(spx_word32_t **xq);
 | |
|     VARDECL(spx_word32_t *xqmem);
 | |
|     int m = lpcrdr>>1;
 | |
| 
 | |
|     /* 
 | |
|     
 | |
|        Reconstruct P(z) and Q(z) by cascading second order polynomials
 | |
|        in form 1 - 2cos(w)z(-1) + z(-2), where w is the LSP frequency.
 | |
|        In the time domain this is:
 | |
| 
 | |
|        y(n) = x(n) - 2cos(w)x(n-1) + x(n-2)
 | |
|     
 | |
|        This is what the ALLOCS below are trying to do:
 | |
| 
 | |
|          int xp[m+1][lpcrdr+1+2]; // P matrix in QIMP
 | |
|          int xq[m+1][lpcrdr+1+2]; // Q matrix in QIMP
 | |
| 
 | |
|        These matrices store the output of each stage on each row.  The
 | |
|        final (m-th) row has the output of the final (m-th) cascaded
 | |
|        2nd order filter.  The first row is the impulse input to the
 | |
|        system (not written as it is known).
 | |
| 
 | |
|        The version below takes advantage of the fact that a lot of the
 | |
|        outputs are zero or known, for example if we put an inpulse
 | |
|        into the first section the "clock" it 10 times only the first 3
 | |
|        outputs samples are non-zero (it's an FIR filter).
 | |
|     */
 | |
| 
 | |
|     ALLOC(xp, (m+1), spx_word32_t*);
 | |
|     ALLOC(xpmem, (m+1)*(lpcrdr+1+2), spx_word32_t);
 | |
| 
 | |
|     ALLOC(xq, (m+1), spx_word32_t*);
 | |
|     ALLOC(xqmem, (m+1)*(lpcrdr+1+2), spx_word32_t);
 | |
|     
 | |
|     for(i=0; i<=m; i++) {
 | |
|       xp[i] = xpmem + i*(lpcrdr+1+2);
 | |
|       xq[i] = xqmem + i*(lpcrdr+1+2);
 | |
|     }
 | |
| 
 | |
|     /* work out 2cos terms in Q14 */
 | |
| 
 | |
|     ALLOC(freqn, lpcrdr, spx_word16_t);
 | |
|     for (i=0;i<lpcrdr;i++) 
 | |
|        freqn[i] = ANGLE2X(freq[i]);
 | |
| 
 | |
|     #define QIMP  21   /* scaling for impulse */
 | |
| 
 | |
|     xin = SHL32(EXTEND32(1), (QIMP-1)); /* 0.5 in QIMP format */
 | |
|    
 | |
|     /* first col and last non-zero values of each row are trivial */
 | |
|     
 | |
|     for(i=0;i<=m;i++) {
 | |
|      xp[i][1] = 0;
 | |
|      xp[i][2] = xin;
 | |
|      xp[i][2+2*i] = xin;
 | |
|      xq[i][1] = 0;
 | |
|      xq[i][2] = xin;
 | |
|      xq[i][2+2*i] = xin;
 | |
|     }
 | |
| 
 | |
|     /* 2nd row (first output row) is trivial */
 | |
| 
 | |
|     xp[1][3] = -MULT16_32_Q14(freqn[0],xp[0][2]);
 | |
|     xq[1][3] = -MULT16_32_Q14(freqn[1],xq[0][2]);
 | |
| 
 | |
|     xout1 = xout2 = 0;
 | |
| 
 | |
|     /* now generate remaining rows */
 | |
| 
 | |
|     for(i=1;i<m;i++) {
 | |
| 
 | |
|       for(j=1;j<2*(i+1)-1;j++) {
 | |
| 	mult = MULT16_32_Q14(freqn[2*i],xp[i][j+1]);
 | |
| 	xp[i+1][j+2] = ADD32(SUB32(xp[i][j+2], mult), xp[i][j]);
 | |
| 	mult = MULT16_32_Q14(freqn[2*i+1],xq[i][j+1]);
 | |
| 	xq[i+1][j+2] = ADD32(SUB32(xq[i][j+2], mult), xq[i][j]);
 | |
|       }
 | |
| 
 | |
|       /* for last col xp[i][j+2] = xq[i][j+2] = 0 */
 | |
| 
 | |
|       mult = MULT16_32_Q14(freqn[2*i],xp[i][j+1]);
 | |
|       xp[i+1][j+2] = SUB32(xp[i][j], mult);
 | |
|       mult = MULT16_32_Q14(freqn[2*i+1],xq[i][j+1]);
 | |
|       xq[i+1][j+2] = SUB32(xq[i][j], mult);
 | |
|     }
 | |
| 
 | |
|     /* process last row to extra a{k} */
 | |
| 
 | |
|     for(j=1;j<=lpcrdr;j++) {
 | |
|       int shift = QIMP-13;
 | |
| 
 | |
|       /* final filter sections */
 | |
|       a = PSHR32(xp[m][j+2] + xout1 + xq[m][j+2] - xout2, shift); 
 | |
|       xout1 = xp[m][j+2];
 | |
|       xout2 = xq[m][j+2];
 | |
|       
 | |
|       /* hard limit ak's to +/- 32767 */
 | |
| 
 | |
|       if (a < -32767) a = -32767;
 | |
|       if (a > 32767) a = 32767;
 | |
|       ak[j-1] = (short)a;
 | |
|      
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| void lsp_to_lpc(spx_lsp_t *freq,spx_coef_t *ak,int lpcrdr, char *stack)
 | |
| /*  float *freq 	array of LSP frequencies in the x domain	*/
 | |
| /*  float *ak 		array of LPC coefficients 			*/
 | |
| /*  int lpcrdr  	order of LPC coefficients 			*/
 | |
| 
 | |
| 
 | |
| {
 | |
|     int i,j;
 | |
|     float xout1,xout2,xin1,xin2;
 | |
|     VARDECL(float *Wp);
 | |
|     float *pw,*n1,*n2,*n3,*n4=NULL;
 | |
|     VARDECL(float *x_freq);
 | |
|     int m = lpcrdr>>1;
 | |
| 
 | |
|     ALLOC(Wp, 4*m+2, float);
 | |
|     pw = Wp;
 | |
| 
 | |
|     /* initialise contents of array */
 | |
| 
 | |
|     for(i=0;i<=4*m+1;i++){       	/* set contents of buffer to 0 */
 | |
| 	*pw++ = 0.0;
 | |
|     }
 | |
| 
 | |
|     /* Set pointers up */
 | |
| 
 | |
|     pw = Wp;
 | |
|     xin1 = 1.0;
 | |
|     xin2 = 1.0;
 | |
| 
 | |
|     ALLOC(x_freq, lpcrdr, float);
 | |
|     for (i=0;i<lpcrdr;i++)
 | |
|        x_freq[i] = ANGLE2X(freq[i]);
 | |
| 
 | |
|     /* reconstruct P(z) and Q(z) by  cascading second order
 | |
|       polynomials in form 1 - 2xz(-1) +z(-2), where x is the
 | |
|       LSP coefficient */
 | |
| 
 | |
|     for(j=0;j<=lpcrdr;j++){
 | |
|        int i2=0;
 | |
| 	for(i=0;i<m;i++,i2+=2){
 | |
| 	    n1 = pw+(i*4);
 | |
| 	    n2 = n1 + 1;
 | |
| 	    n3 = n2 + 1;
 | |
| 	    n4 = n3 + 1;
 | |
| 	    xout1 = xin1 - 2.f*x_freq[i2] * *n1 + *n2;
 | |
| 	    xout2 = xin2 - 2.f*x_freq[i2+1] * *n3 + *n4;
 | |
| 	    *n2 = *n1;
 | |
| 	    *n4 = *n3;
 | |
| 	    *n1 = xin1;
 | |
| 	    *n3 = xin2;
 | |
| 	    xin1 = xout1;
 | |
| 	    xin2 = xout2;
 | |
| 	}
 | |
| 	xout1 = xin1 + *(n4+1);
 | |
| 	xout2 = xin2 - *(n4+2);
 | |
| 	if (j>0)
 | |
| 	   ak[j-1] = (xout1 + xout2)*0.5f;
 | |
| 	*(n4+1) = xin1;
 | |
| 	*(n4+2) = xin2;
 | |
| 
 | |
| 	xin1 = 0.0;
 | |
| 	xin2 = 0.0;
 | |
|     }
 | |
| 
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #ifdef FIXED_POINT
 | |
| 
 | |
| /*Makes sure the LSPs are stable*/
 | |
| void lsp_enforce_margin(spx_lsp_t *lsp, int len, spx_word16_t margin)
 | |
| {
 | |
|    int i;
 | |
|    spx_word16_t m = margin;
 | |
|    spx_word16_t m2 = 25736-margin;
 | |
|   
 | |
|    if (lsp[0]<m)
 | |
|       lsp[0]=m;
 | |
|    if (lsp[len-1]>m2)
 | |
|       lsp[len-1]=m2;
 | |
|    for (i=1;i<len-1;i++)
 | |
|    {
 | |
|       if (lsp[i]<lsp[i-1]+m)
 | |
|          lsp[i]=lsp[i-1]+m;
 | |
| 
 | |
|       if (lsp[i]>lsp[i+1]-m)
 | |
|          lsp[i]= SHR16(lsp[i],1) + SHR16(lsp[i+1]-m,1);
 | |
|    }
 | |
| }
 | |
| 
 | |
| 
 | |
| void lsp_interpolate(spx_lsp_t *old_lsp, spx_lsp_t *new_lsp, spx_lsp_t *interp_lsp, int len, int subframe, int nb_subframes)
 | |
| {
 | |
|    int i;
 | |
|    spx_word16_t tmp = DIV32_16(SHL32(EXTEND32(1 + subframe),14),nb_subframes);
 | |
|    spx_word16_t tmp2 = 16384-tmp;
 | |
|    for (i=0;i<len;i++)
 | |
|    {
 | |
|       interp_lsp[i] = MULT16_16_P14(tmp2,old_lsp[i]) + MULT16_16_P14(tmp,new_lsp[i]);
 | |
|    }
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| /*Makes sure the LSPs are stable*/
 | |
| void lsp_enforce_margin(spx_lsp_t *lsp, int len, spx_word16_t margin)
 | |
| {
 | |
|    int i;
 | |
|    if (lsp[0]<LSP_SCALING*margin)
 | |
|       lsp[0]=LSP_SCALING*margin;
 | |
|    if (lsp[len-1]>LSP_SCALING*(M_PI-margin))
 | |
|       lsp[len-1]=LSP_SCALING*(M_PI-margin);
 | |
|    for (i=1;i<len-1;i++)
 | |
|    {
 | |
|       if (lsp[i]<lsp[i-1]+LSP_SCALING*margin)
 | |
|          lsp[i]=lsp[i-1]+LSP_SCALING*margin;
 | |
| 
 | |
|       if (lsp[i]>lsp[i+1]-LSP_SCALING*margin)
 | |
|          lsp[i]= .5f* (lsp[i] + lsp[i+1]-LSP_SCALING*margin);
 | |
|    }
 | |
| }
 | |
| 
 | |
| 
 | |
| void lsp_interpolate(spx_lsp_t *old_lsp, spx_lsp_t *new_lsp, spx_lsp_t *interp_lsp, int len, int subframe, int nb_subframes)
 | |
| {
 | |
|    int i;
 | |
|    float tmp = (1.0f + subframe)/nb_subframes;
 | |
|    for (i=0;i<len;i++)
 | |
|    {
 | |
|       interp_lsp[i] = (1-tmp)*old_lsp[i] + tmp*new_lsp[i];
 | |
|    }
 | |
| }
 | |
| 
 | |
| #endif
 |