
/*

This software module was originally developed by

    Masayuki Nishiguchi and Kazuyuki Iijima (Sony Corporation)

    in the course of development of the MPEG-4 Audio standard (ISO/IEC 14496-3).
    This software module is an implementation of a part of one or more
    MPEG-4 Audio (ISO/IEC 14496-3) tools as specified by the MPEG-4 Audio
    standard (ISO/IEC 14496-3).
    ISO/IEC gives users of the MPEG-4 Audio standards (ISO/IEC 14496-3)
    free license to this software module or modifications thereof for use
    in hardware or software products claiming conformance to the MPEG-4
    Audio standards (ISO/IEC 14496-3).
    Those intending to use this software module in hardware or software
    products are advised that this use may infringe existing patents.
    The original developer of this software module and his/her company,
    the subsequent editors and their companies, and ISO/IEC have no
    liability for use of this software module or modifications thereof in
    an implementation.
    Copyright is not released for non MPEG-4 Audio (ISO/IEC 14496-3)
    conforming products. The original developer retains full right to use
    the code for his/her own purpose, assign or donate the code to a third
    party and to inhibit third party from using the code for non MPEG-4
    Audio (ISO/IEC 14496-3) conforming products.
    This copyright notice must be included in all copies or derivative works.

    Copyright (c)1996.

*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include "hvxc.h"
#include "hvxcDec.h"
#include "hvxcCommon.h"

#define UV_AMP          (1.0 / 1.5)

extern int	ipc_encMode;
extern int	ipc_decMode;

extern int	ipc_encDelayMode;
extern int	ipc_decDelayMode;

static void calc_syn_cont2v(
float	*res,
float	(*alphaip)[11],
float	*syn)
{
    int i,ii,j;
    
    float out2,out2old;
    static float mem2[P+1]={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
    float mem2old[P+1];
    
    float pos[160];
    float nokori[20];
    static float alphaipold[P+1];
    float window[20];
    
    for(i=0;i<10;i++)
	window[i]=(float)(i)/10.;
    for(i=10;i<20;i++)
	window[i]=1.;
    
    for(ii=0;ii<IP;ii++){
	
	for(i=0;i<=P;i++)
	    mem2old[i]=mem2[i];
	
	for(i=0;i<20;i++){  
	    out2 = 0.;
	    out2old = 0.;
	    for(j=P;j>0;j--){
		out2 += mem2[j]*alphaip[ii][j];
		mem2[j]=mem2[j-1];
		out2old += mem2old[j]*alphaipold[j];
		mem2old[j]=mem2old[j-1];
	    }
	    out2 = res[ii*20+i] - out2;
	    mem2[1]= out2;
	    pos[ii*20+i]= out2;
	    out2old = res[ii*20+i] - out2old;
	    mem2old[1]= out2old;
	    nokori[i]= out2old;
	}
	
	for(i=0;i<20;i++)
	    syn[ii*20+i] =  pos[ii*20+i] * window[i] +  nokori[i] * (1.0-window[i]) ;  
	
	for(i=0;i<=P;i++)
	    alphaipold[i]=alphaip[ii][i];
	
    }
    
    
}




static void posfil_v(
float	*syn,
float	(*alphaip)[11])
{
    int i,j,ii;
    float out,outold;
    static float mem1[P+1]={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
    float mem1old[P+1];
    
    float out2,out2old;
    static float mem2[P+1]={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.};
    float mem2old[P+1];
    
    float alphaipFIR[IP][P+1];
    float alphaipIIR[IP][P+1];
    
    float resi[160];
    float pos[160];
    float nokori[160];
    float synhp[20];
    static float hpm=0.;
    float gain;
    static float oldgain;
    static float alphaipFIRold[P+1];
    static float alphaipIIRold[P+1];
    float window[160];
    float sp,op;
    float spn,opn;
    float fac05[11],fac08[11];

    float emph;

    fac05[0]=1.0;
    fac05[1]=0.5;
    fac05[2]=0.25;
    fac05[3]=0.125;
    fac05[4]=0.0625;
    fac05[5]=0.03125;
    fac05[6]=0.015625;
    fac05[7]=0.0078125;
    fac05[8]=0.00390625;
    fac05[9]=0.001953125;
   fac05[10]=0.0009765625;

    fac08[0]=1.0;
    fac08[1]=0.8;
    fac08[2]=0.64;
    fac08[3]=0.512;
    fac08[4]=0.4096;
    fac08[5]=0.32768;
    fac08[6]=0.262144;
    fac08[7]=0.2097152;
    fac08[8]=0.16777216;
    fac08[9]=0.134217728;
   fac08[10]=0.107374182;


    for(i=0;i<20;i++)
	window[i]=(float)(i)/20.;
    for(i=20;i<160;i++)
	window[i]=1.;
    

    for(j=0;j<IP;j++){
	for(i=0;i<=P;i++){
	    alphaipFIR[j][i]=alphaip[j][i]*fac05[i];
	    alphaipIIR[j][i]=alphaip[j][i]*fac08[i];
	}
    }


    for(ii=0;ii<IP;ii++)
    {
	emph = 0.15 * alphaip[ii][1];
	if(-0.5 > emph)
	{
	    emph = -0.5;
	}
	else if(emph > 0)
	{
	    emph = 0.0;
	}
	
	for(i=0;i<20;i++)
	{
	    synhp[i] = syn[ii*20+i] + emph * hpm;
	    hpm=syn[ii*20+i];
	}

	for(i=0;i<=P;i++)
	    mem1old[i]=mem1[i];
	
	for(i=0;i<20;i++){  
	    mem1[0]=synhp[i];
	    mem1old[0]=synhp[i];
	    out = 0.;
	    outold = 0.;
	    for(j=P;j>0;j--){
		out += mem1[j]*alphaipFIR[ii][j];
		mem1[j]=mem1[j-1];
		outold += mem1old[j]*alphaipFIRold[j];
		mem1old[j]=mem1old[j-1];
	    }
	    out += mem1[0];
	    outold += mem1old[0];
	    resi[ii*20+i]= out;
	    nokori[ii*20+i] = outold;
	}
	
	for(i=0;i<=P;i++)
	    mem2old[i]=mem2[i];
	
	for(i=0;i<20;i++){  
	    out2 = 0.;
	    out2old = 0.;
	    for(j=P;j>0;j--){
		out2 += mem2[j]*alphaipIIR[ii][j];
		out2old += mem2old[j]*alphaipIIRold[j];
		mem2[j]=mem2[j-1];
		mem2old[j]=mem2old[j-1];
	    }
	    out2 = resi[ii*20+i] - out2;
	    out2old = nokori[ii*20+i] - out2old;
	    mem2[1]= out2;
	    mem2old[1]= out2old;
	    pos[ii*20+i]= out2;
	    nokori[ii*20+i]= out2old;
	}
	
	for(i=0;i<=P;i++){
	    alphaipFIRold[i]=alphaipFIR[ii][i];
	    alphaipIIRold[i]=alphaipIIR[ii][i];
	}
	
    }
    
    sp = op = spn = opn = 0.;
    for(i=0;i<160;i++){
	sp += syn[i]*syn[i] ;
	op += pos[i]*pos[i] ;
	
    }
    
    if(op >= 2560)
	gain = sqrt(sp/op);
    else
	gain =0.;
    
    for(i=0;i<160;i++)
	syn[i] = gain * pos[i] * window[i] + oldgain * nokori[i] * (1.0-window[i]) ;  
    
    oldgain=gain;
}


static void ip_lsp2(float (*lsp)[11], float (*lspip)[11])
{
    int i, j;
    float fip;
    
    fip=(float)IP * 2;
    
    for(j=0;j<IP;j++)
    {
	for(i=0;i<11;i++)
	    lspip[j][i]=((j * 2.0 + 1.0) / fip) * lsp[1][i] +
		((fip - (j * 2.0 + 1.0)) / fip) * lsp[0][i];
    }
}

void IPC_ip_lsp_LD(float (*lsp)[11], float (*lspip)[11])
{
    int i, j;
    float fj;
	    
if(LD_LEN == 40 ){
	for(j=0;j<6;j++)
	{
	fj=(float)j;
	for(i=0;i<11;i++)
		lspip[j][i]=((fj * 2.0 + 1.0) / 12.0) * lsp[1][i] + ((12.0 - (fj * 2.0 + 1.0)) / 12.0) * lsp[0][i];
	}
	for(j=6;j<8;j++)
		for(i=0;i<11;i++)
		    lspip[j][i]= lsp[1][i] ;
	}

if(LD_LEN == 20 ){
	for(j=0;j<7;j++)
	{
	fj=(float)j;
	for(i=0;i<11;i++)
		lspip[j][i]=((fj * 2.0 + 1.0) / 14.0) * lsp[1][i] + ((14.0 - (fj * 2.0 + 1.0)) / 14.0) * lsp[0][i];
	}
	for(j=7;j<8;j++)
		for(i=0;i<11;i++)
		    lspip[j][i]= lsp[1][i] ;
	}




}

static void lp_synV(
int	*vuv,
float	*synoutv,
float	*resinv,
float	(*qLsp)[10])
{
    int i, j;
    float lsp[2][P+1];
    float lspip[IP][P+1];
    float alphaip[IP][P+1];
    float res[FRM];
    float syn[FRM];
    
    if(vuv[0] != 0 && vuv[1] != 0){ 
	for(i=0;i<10;i++){
	    lsp[0][i+1]=qLsp[0][i];
	    lsp[1][i+1]=qLsp[1][i];
	}
    }
    if(vuv[0] != 0 && vuv[1] ==0){ 
	for(i=0;i<10;i++){
	    lsp[0][i+1]=qLsp[0][i];
	    lsp[1][i+1]=qLsp[1][i];
	}
    }
    if(vuv[0] == 0 && vuv[1] != 0){ 
	for(i=0;i<10;i++){
	    lsp[0][i+1]=qLsp[0][i];
	    lsp[1][i+1]=qLsp[1][i];
	}
    }
    if(vuv[0] == 0 && vuv[1] ==0 ){ 
	for(i=0;i<10;i++){
	    lsp[0][i+1]=(i+1)*0.5/11.;
	    lsp[1][i+1]=(i+1)*0.5/11.;
	}
    }
    
    lsp[0][0]=lsp[1][0]=0.;

    if(ipc_decDelayMode == DM_SHORT)
    {
	IPC_ip_lsp_LD(lsp, lspip);
    }
    else
    {
	ip_lsp2(lsp, lspip);
    }
  
    for(j = 0; j < IP; j++)
    {
	IPC_lsp_lpc(lspip[j], alphaip[j]);
	
	alphaip[j][0]=1.0;
	for(i=1;i<=10;i++)
	    alphaip[j][i]= -alphaip[j][i];
    }

    for(i = 0; i < 160; i++)
	res[i] = resinv[i];

    calc_syn_cont2v(res,alphaip,syn);
    
    posfil_v(syn,alphaip);
    
    for(i = 0; i < 160; i++)
	synoutv[i]=syn[i];
}



void harm_srew_synt(
float	(*am)[3],
float	*PCHs,
int	*VUVs,
float	(*qLsp)[10],
int	lsUn,
float	*synoutv)
{
	int i;
	float sv[FRM];
	float add_uv[FRM];

	if(lsUn != 8){
		printf("Invalid LSF update number !!");
		exit(10);
	}

	IPC_vExt_fft(PCHs,am,VUVs,sv);

	IPC_UvAdd(PCHs,am,VUVs,add_uv);

        for(i=0;i<FRM;i++)
	{
	    sv[i]=2.0 * sv[i] + add_uv[i] * UV_AMP * 1.5 ;
	}

	lp_synV(VUVs, synoutv, sv, qLsp);
}
