/**********************************************************************
MPEG-4 Audio VM
Encoder core (parametric)



This software module was originally developed by

Heiko Purnhagen (University of Hannover / Deutsche Telekom Berkom)
Bernd Edler (University of Hannover / Deutsche Telekom Berkom)
Masayuki Nishiguchi, Kazuyuki Iijima, Jun Matsumoto (Sony Corporation)

and edited by

Akira Inoue (Sony Corporation)

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard. ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. 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-2
NBC/MPEG-4 Audio 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-2 NBC/MPEG-4 Audio conforming products. This
copyright notice must be included in all copies or derivative works.

Copyright (c) 1997.



Source file: enc_par.c

$Id: enc_par.c,v 1.24 1998/05/06 19:11:57 purnhage Exp $

Required libraries:
HVXC.a			HVXC library

Required modules:
common.o		common module
cmdline.o		command line module
bitstream.o		bit stream module
indilinextr.o		indiline extraction module
indilineenc.o		indiline bitstream encoder module
( indilinesyn.o		indiline synthesiser module )

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>
BE    Bernd Edler, Uni Hannover <edler@tnt.uni-hannover.de>
MN    Masayuki Nishiguchi, Sony IPC <nishi@pcrd.sony.co.jp>

Changes:
14-jun-96   HP    first version (dummy)
18-jun-96   HP    added bit reservoir handling
24-jun-96   HP    implemented "individual spectral lines"
25-jun-96   HP    using CommonFreeAlloc(), small bug fixes
28-jun-96   HP    joined with HVXC code by Sony IPC
01-jul-96   HP    added HVXC header file, included modifications by IPC
02-jul-96   HP    included modifications by IPC
14-aug-96   HP    added EncParInfo(), EncParFree()
15-aug-96   HP    adapted to new enc.h
26-aug-96   HP    CVS
10-sep-96   BE
12-sep-96   HP    incorporated indiline modules as source code
26-sep-96   HP    adapted to new indiline module interfaces
26-sep-96   HP    incorporated changes by Sony IPC
04-dec-96   HP    included ISO copyright
10-apr-97   HP    harmonic stuff ...
22-apr-97   HP    noisy stuff ...
15-may-97   HP    clean up
06-jul-97   HP    switch/mix
17-jul-97   HP    "clas" option
23-oct-97   HP    merged IL/HVXC switching
                  swit mode: 2 HVXC frames / 40 ms
07-nov-97   MN    bug-fixes
11-nov-97   HP    added HVXC variable rate switches
12-nov-97   HP    fixed mixed HVXC/IL mode

VMIL stuff
24-jun-97   HP    adapted env/harm/noise interface
16-nov-97   HP    adapted HILN decoder config header
09-apr-98   HP    ILX/ILE config

**********************************************************************/

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


#include "common.h"		/* common module */
#include "cmdline.h"		/* command line module */
#include "bitstream.h"		/* bit stream module */

#include "indilinextr.h"	/* indiline extraction module */
#include "indilineenc.h"	/* indiline bitstream encoder module */

#ifdef IL_ENC_SYNTHESIS_NOT_YET_IMPLEMENTED
#include "indilinesyn.h"	/* indiline synthesiser module */
#endif


#include "enc.h"		/* encoder cores */

#include "mp4_par.h"		/* parametric core declarations */

#include "hvxc.h"		/* hvxc module */
#include "hvxcEnc.h"		/* hvxcEnc module */

#include "pan_par_const.h"
#include "phi_priv.h"		  /* Akira 980506 */

/* ---------- declarations ---------- */

#define PROGVER "parametric encoder core V5.0 12-nov-97"

#define SEPACHAR " ,="

#define MAXSAMPLE 32768		/* full scale sample value */

#define MAXNUMBITFACTOR 1.2	/* for indiline bit reservoir control */

#define STRLEN 255


/* ---------- variables ---------- */

/* init parameters (global) */
static float EPfSample;
static float EPbitRate;
static int EPmode;		/* enum MP4ModePar */
static int EPframeNumSample;
static int EPdelayNumSample;

/* sample buffer (frame-to-frame memory) */
static float *EPsampleBuf = NULL;
static float *EPsampleBuf2 = NULL;
static int EPsampleBufSize;

/* indiline handles */
static ILXstatus *ILX;
static ILEstatus *ILE;


/* command line module */

static char *EPparaMode;
static int EPdelayMode;
static int EPvrMode;
static int EPmixMode;
static int EPdebugLevel;
static int EPdebugLevelIl;
static float EPframeDur;
static int EPmaxNumLine;
static int EPmaxNumLineUsed;
static float EPmaxLineFreq;
static int EPmaxLineFreqUsed;
static int EPmaxNumEnv;
static int EPmaxNumHarm;
static int EPmaxNumHarmLine;
static int EPmaxNumNoisePara;

static int EPtest;
static int EPnewEncDelay;

int HVXCclassifierMode;

/* HILN encoder control HP971116 */
static int EPenhaFlag;
static float EPbaseRate;
static int EPbaseRateUsed;

static ILXconfig ILXcfg;
static ILEconfig ILEcfg;



static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"m",&EPparaMode,"%s",MODEPARNAME_IL,NULL,
   "parametric codec mode (" MODEPARNAME_LIST ")"},
  {"dm",&EPdelayMode,"%i","0",NULL,"HVXC delay mode (0=short 1=long)"},
  {"vr",&EPvrMode,"%i","0",NULL,"HVXC variable rate mode (0=fixed 1=var)\n"
   "(set frame work option -vr)"},
  {"mm",&EPmixMode,"%i","1",NULL,
   "HVXC/IL mix mode (2ch: L=HVXC R=IL, use with -n 1)\n"
   "(0=HVXC 1=HVXC2+IL 2=HVXC4+IL 3=IL  -=cycle periode)\n\b"},
  {"dl",&EPdebugLevel,"%i","0",NULL,"debug level"},
  {"dli",&EPdebugLevelIl,"%i","0",NULL,"debug level individual lines"},
  {"fd",&EPframeDur,"%f","0.032",NULL,"frame duration [sec]"},
  {"mnl",&EPmaxNumLine,"%d",NULL,&EPmaxNumLineUsed,"max num individual lines"},
  {"mlf",&EPmaxLineFreq,"%f",NULL,&EPmaxLineFreqUsed,"max line frequency"},
  {"me",&EPmaxNumEnv,"%d","1",NULL,"max num envelopes (1=il/harm 2=+noise)"},
  {"mh",&EPmaxNumHarm,"%d","1",NULL,"max num harmonic tones"},
  {"mhl",&EPmaxNumHarmLine,"%d","64",NULL,"max num lines of harmonic tone"},
  {"mn",&EPmaxNumNoisePara,"%d","16",NULL,"max num noise paramters"},
  {"hie",&EPenhaFlag,"%d","0",NULL,
   "harm/indi enha flag (0=basic 1=enha)"},
  {"hibr",&EPbaseRate,"%f",NULL,&EPbaseRateUsed,
   "harm/indi basic bit rate (dflt: full rate)"},
  {"hiq",&ILEcfg.quantMode,"%d","-1",NULL,
   "harm/indi quant mode (-1=auto 0..3)"},
  {"hieq",&ILEcfg.enhaQuantMode,"%d","-1",NULL,
   "harm/indi enha quant mode (-1=auto 0..3)"},
  {"hicm",&ILEcfg.contMode,"%d","0",NULL,
   "harm/indi continue mode (0=hi, 1=hi&ii)"},
  {"iecm",&ILEcfg.encContMode,"%d","0",NULL,
   "indi encoder continue mode (0=cont 1=no-cont)"},
  {"bsf",&ILEcfg.bsFormat,"%d","2",NULL,
   "HILN bitstream format (0=VM 1=9705 2=CD)"},
  {"clas",&HVXCclassifierMode,"%i","0",NULL,
   "for test only\n"
   "(classifier mode 0=pause req. 1=no pause req.)"},
  {"test",&EPtest,"%i","0",NULL,
   "for test only\n"
   "(hvxc/il alternate periode +=IL/HVXC/.. -=HVXC/IL/..)\n\b"},
  {"ed",&EPnewEncDelay,"%i","0",NULL,  /* MN 971114 */
   "HVXC only mode encoder delay comp.: 0=0/20ms 1=6/26ms\n\b"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};

/* variables for HVXC */
int ipc_encMode = ENC4K;
int ipc_bitstreamMode = BM_CONSTANT;
int ipc_rateMode = 2;

/* switch/mix stuff */
extern int judge;

#define JUDGE_DLY 124	/* (124+1)*40ms=5s*/
static int numBsBuf;
static int actBsBuf;
static BsBitBuffer *bsBuf[JUDGE_DLY+1];
static BsBitBuffer *bsTmp;
static BsBitStream *bsStream;
static int ILdelayNumSample = 0;
static int HVXdelayNumSample = 0;
static int ILsampleBufSize = 0;
static int HVXsampleBufSize = 0;
static int ILdelayOff = 0;
static int HVXdelayOff = 0;
static BsBitBuffer *dmyBuf;
static int testFrame;

int ipc_encDelayMode = DM_SHORT;


/* HILN */
static float maxNoiseFreq;


/* ---------- internal functions ---------- */


/* EncParInitIl() */
/* Init: individual lines */

static void EncParInitIl (
  BsBitStream *hdrStream)	/* out: header for bit stream */
{
  int maxNumLine;
  int frameMaxNumBit;
  int hdrFSample,hdrFrameNumSample;


  /* calc variable default values */
  if (!EPmaxNumLineUsed)
    EPmaxNumLine = -1;
  if (!EPmaxLineFreqUsed)
    EPmaxLineFreq = EPfSample/2;
  if (!EPbaseRateUsed)
    EPbaseRate = EPbitRate;
  else
    if (EPbaseRate > EPbitRate)
      CommonWarning("EncParInitIl: EPbaseRate > EPbitRate");

  /* calc encoder parameters */
  EPframeNumSample = (int)(EPfSample*EPframeDur+0.5);
  EPdelayNumSample = EPframeNumSample/2;
  frameMaxNumBit = (int)(EPbaseRate*EPframeDur*MAXNUMBITFACTOR+0.5);
  hdrFSample = (int)(2*EPmaxLineFreq+.5);
  hdrFrameNumSample = (int)(2*EPmaxLineFreq*EPframeDur+.5);

  /* init modules */
  ILE = IndiLineEncodeInit(hdrFrameNumSample,hdrFSample,MAXSAMPLE,
			   EPenhaFlag,frameMaxNumBit,
			   EPmaxNumLine,EPmaxNumEnv,EPmaxNumHarm,
			   EPmaxNumHarmLine,EPmaxNumNoisePara,
			   &ILEcfg,EPdebugLevelIl,hdrStream,&maxNumLine,
			   &maxNoiseFreq);
  if (!EPmaxNumLineUsed)
    EPmaxNumLine = maxNumLine;

  ILX = IndiLineExtractInit(EPframeNumSample,EPfSample,EPmaxLineFreq,MAXSAMPLE,
			    EPmaxNumLine,EPmaxNumEnv,EPmaxNumHarm,
			    EPmaxNumHarmLine,EPmaxNumNoisePara,maxNoiseFreq,
			    &ILXcfg,EPdebugLevelIl);

  /* set sample buffer size */
  EPsampleBufSize = (EPframeNumSample*5+1)/2;

  if (EPdebugLevel >= 1) {
    printf("EncParInitIl: frmDur=%.6f  dbgLvlIl=%d\n",
	   EPframeDur,EPdebugLevelIl);
    printf("EncParInitIl: frmNumSmp=%d  frmMaxNumBit=%d  smpBufSize=%d\n",
	   EPframeNumSample,frameMaxNumBit,EPsampleBufSize);
    printf("EncParInitIl: maxNumLine=%d  maxLineFreq=%.3f\n",
	   EPmaxNumLine,EPmaxLineFreq);
    printf("EncParInitIl: hdrFSample=%d  hdrFrameNumSample=%d\n",
	   hdrFSample,hdrFrameNumSample);
  }
  dmyBuf=BsAllocBuffer(200);
}

/* Akira 980506 */
#ifdef CELP_LPC_TOOL
typedef struct
{
  PHI_PRIV_TYPE *PHI_Priv;
  /* add private data pointers here for other coding varieties */
}
INST_CONTEXT_LPC_ENC_TYPE;

static INST_CONTEXT_LPC_ENC_TYPE *InstCtxt;
#endif


/* EncParInitHvx() */
/* Init: harmonic vector exitation */

static void EncParInitHvx (
  BsBitStream *hdrStream)	/* out: header for bit stream */
{
#ifdef CELP_LPC_TOOL
  int i;
  long window_sizes[PAN_NUM_ANA_PAR];
  
  for(i=0;i<PAN_NUM_ANA_PAR;i++)
    window_sizes[i] = PAN_WIN_LEN_PAR;
#endif

  EPframeNumSample = 160;	/* 20 ms */
  EPsampleBufSize = 160;	/* 20 ms in buffer EPsampleBuf[] */

  if ( EPmode == MODEPAR_HVX && !EPnewEncDelay){ /* MN 971114 */
    if (ipc_encDelayMode == DM_LONG)
      EPdelayNumSample = 160;
    else
      EPdelayNumSample = 0;
  }
  else {			/* these are the correct values! */
    if (ipc_encDelayMode == DM_LONG)	/* HP 971023 */
      EPdelayNumSample = 208; 	/* 46 ms - 20 ms = 26 ms   HP 971111 */
    else
      EPdelayNumSample = 48; 	/* 26 ms - 20 ms = 6 ms */
  }

  if (BsPutBit(hdrStream,ipc_bitstreamMode,1))
    CommonExit(1,"EncParInitHvx: error generating bit stream header");
  if (BsPutBit(hdrStream,ipc_rateMode,2))
    CommonExit(1,"EncParInitHvx: error generating bit stream header");

  IPC_HVXCInit();

#ifdef CELP_LPC_TOOL
  /*
  PAN_InitLpcAnalysisEncoder(window_sizes, PAN_NUM_ANA_PAR, 
			     PAN_LPC_ORDER_PAR, PAN_GAMMA_BE_PAR,
			     PAN_BITRATE_PAR);
			     */

  /* Akira 980506 */

  /* -----------------------------------------------------------------*/
  /* Create & initialise private storage for instance context         */
  /* -----------------------------------------------------------------*/
  if (( InstCtxt = malloc(sizeof(INST_CONTEXT_LPC_ENC_TYPE))) == NULL) {
    fprintf(stderr, "MALLOC FAILURE in celp_initialisation_encoder\n");
    exit(1);
  }

  if (( InstCtxt->PHI_Priv = malloc(sizeof(PHI_PRIV_TYPE))) == NULL ) {
    fprintf(stderr, "MALLOC FAILURE in celp_initialisation_encoder\n");
    exit(1);
  }
    
  PHI_Init_Private_Data(InstCtxt->PHI_Priv);

  PAN_InitLpcAnalysisEncoder(window_sizes, PAN_NUM_ANA_PAR, 
			     PAN_LPC_ORDER_PAR, PAN_GAMMA_BE_PAR,
			     PAN_BITRATE_PAR, InstCtxt->PHI_Priv);
  /* Akira 980506 */
#endif
}


/* EncParFrameIl() */
/* Encode frame: individual lines */

void EncParFrameIl (
  float *sampleBuf,
  BsBitStream *stream,		/* out: bit stream */
  int frameAvailNumBit,		/* in: total num bits available for */
				/*     this frame (incl. bit reservoir) */
  int frameNumBit,		/* in: average num bits per frame */
  int frameMaxNumBit)		/* in: max num bits per frame */
{
  int numEnv;
  float **envPara;
  int numLine;
  float *lineFreq;
  float *lineAmpl;
  float *linePhase;
  int *lineEnv;
  int *linePred;
  int numHarm;
  int *numHarmLine;
  float *harmFreq;
  float *harmFreqStretch;
  int *harmLineIdx;
  int *harmEnv;
  int *harmPred;
  float *harmRate;
  int numNoisePara;
  float noiseFreq;
  float *noisePara;
  int noiseEnv;
  float noiseRate;
  int i,il;
  BsBitStream *dmyStream;
  

  /* extract line parameters */
  IndiLineExtract(ILX,EPsampleBuf,NULL,NULL,NULL,NULL,
		  (stream)?EPmaxNumLine:0,
		  &numEnv,&envPara,
		  &numLine,&lineFreq,&lineAmpl,&linePhase,&lineEnv,&linePred,
		  &numHarm,&numHarmLine,&harmFreq,&harmFreqStretch,
		  &harmLineIdx,&harmEnv,&harmPred,&harmRate,
		  &numNoisePara,&noiseFreq,&noisePara,&noiseEnv,&noiseRate);



  if (EPdebugLevel >= 2)
    printf("EncParFrameIl: numLineExtract=%2d\n",numLine);

  if (EPdebugLevel >= 3) {
    for (i=0; i<numEnv; i++)
      printf("env %d: tm=%7.5f atk=%7.5f dec=%7.5f\n",
	     i,envPara[i][0],envPara[i][1],envPara[i][2]);
    for(il=0; il<numLine; il++)
      printf("%2d: f=%7.1f a=%7.1f p=%5.2f e=%1d c=%2d\n",
	     il,lineFreq[il],lineAmpl[il],linePhase[il],
	     lineEnv[il],linePred[il]);
    for (i=0; i<numHarm; i++) {
      printf("harm %d: n=%2d f=%7.1f fs=%10e e=%1d c=%1d r=%5.3f\n",
	     i,numHarmLine[i],harmFreq[i],harmFreqStretch[i],
	     harmEnv[i],harmPred[i],harmRate[i]);
      for(il=harmLineIdx[i]; il<harmLineIdx[i]+numHarmLine[i]; il++)
	printf("h %2d: a=%7.1f\n",il-harmLineIdx[i]+1,lineAmpl[il]);
    }
    if (numNoisePara) {
      printf("noise: n=%d f=%7.1f e=%1d r=%5.3f\n",
	     numNoisePara,noiseFreq,noiseEnv,noiseRate);
      for (i=0; i<numNoisePara; i++)
	printf("noise %d: p=%10e\n",i,noisePara[i]);
    }
  }

  if (EPbaseRateUsed)
    frameAvailNumBit = min(frameAvailNumBit,
			   (int)(EPbaseRate*EPframeDur+.5));
  /* call IndiLineEncodeFrame() to update internal frame-to-frame memory */
  dmyStream = BsOpenBufferWrite(dmyBuf);
  IndiLineEncodeFrame(ILE,numEnv,envPara,
		      numLine,lineFreq,lineAmpl,linePhase,lineEnv,linePred,
		      numHarm,numHarmLine,harmFreq,harmFreqStretch,
		      harmLineIdx,harmEnv,harmPred,harmRate,
		      numNoisePara,noiseFreq,noisePara,noiseEnv,noiseRate,
		      frameAvailNumBit,frameNumBit,frameMaxNumBit,
		      (stream)?stream:dmyStream,
		      (stream && EPenhaFlag)?stream:NULL);
  BsClose(dmyStream);
}


/* EncParFrameHvx() */
/* Encode frame: harmonic vector exitation */

void EncParFrameHvx (
  float *sampleBuf,
  BsBitStream *stream,		/* out: bit stream */
  int frameAvailNumBit,		/* in: total num bits available for */
				/*     this frame (incl. bit reservoir) */
  int frameNumBit,		/* in: average num bits per frame */
  int frameMaxNumBit)		/* in: max num bits per frame */
{
    int		i;

    short	frmBuf[FRM];
    IdLsp	idLsp;
    int		idVUV;
    IdCelp	idCelp;
    float	mfdpch;
    IdAm	idAm;

    unsigned char encBit[10];	/* for 4kbps(80bits) */
    unsigned int	bit;


    static int ENC3Kflag;

    /* HP 970706 */
    if (frameAvailNumBit >= 80-6) {  /* MN 971106 */
      ipc_encMode = ENC4K;
      if (frameAvailNumBit >= 80){
	ENC3Kflag = 0;
      }
      else{
	ENC3Kflag = 1;
      }
    }
    else{
      ipc_encMode = ENC2K;
    }

    if (EPdebugLevel >= 2) {
      printf("EncParFrameHvx: stream=%s\n",stream?"valid":"NULL");
      printf("EncParFrameHvx: availBit=%d\n",frameAvailNumBit);
      printf("EncParFrameHvx: encMode=");
      switch (ipc_encMode) {
      case ENC2K : printf("ENC2K\n"); break;
      case ENC4K : printf("%s\n",ENC3Kflag?"ENC3K":"ENC4K"); break;
      default : printf("ERROR!!!\n");
      }
    }

    for(i = 0; i < FRM; i++)
    {
	frmBuf[i] = (short) sampleBuf[i];
    }
    
    IPC_HVXCEncParFrm(frmBuf, &idLsp, &idVUV, &idCelp, &mfdpch, &idAm);
    
    IPC_PackPrm2Bit(&idLsp, idVUV, &idCelp, mfdpch, &idAm, encBit);

    if(ipc_encMode == ENC4K)
    {
      for(i = 0; i < 9; i++) {
	bit = encBit[i];
	if(BsPutBit(stream, bit, 8))
	  CommonExit(1,"EncParFrameHvx: error generating bit stream");
      }
      if (!ENC3Kflag) {
	bit = encBit[9]; /* MN 971106 */
	if(BsPutBit(stream, bit, 8))
	  CommonExit(1,"EncParFrameHvx: error generating bit stream");
      }
      else {
	/* MN 971107 */
  	bit = (encBit[9] & 0xc0) >> 6;
	if(BsPutBit(stream, bit, 8-6))
	  CommonExit(1,"EncParFrameHvx: error generating bit stream");


      }
    }
    else if(ipc_encMode == ENC2K)
    {
      if(ipc_bitstreamMode == BM_VARIABLE)
      {
	/* HP 971111 */
	if (EPdebugLevel >= 2)
	  printf("EncParFrameHVXC: idVUV=%d  bit=%d\n",
		 idVUV,(encBit[0] & 0xc0) >> 6);

        switch(idVUV)
        {
        case 0:
          for(i = 0; i < 3; i++)
          {
  	      bit = encBit[i];
	      if(BsPutBit(stream, bit, 8))
	        CommonExit(1,"EncParFrameHvx: error generating bit stream");
          }

  	  bit = (encBit[3] & 0xf0) >> 4;
	  if(BsPutBit(stream, bit, 4))
	    CommonExit(1,"EncParFrameHvx: error generating bit stream");
          break;
        case 1:
  	  bit = (encBit[0] & 0xc0) >> 6;
	  if(BsPutBit(stream, bit, 2))
	    CommonExit(1,"EncParFrameHvx: error generating bit stream");
          break;
        case 2:
        case 3:
          for(i = 0; i < 5; i++)
          {
  	      bit = encBit[i];
	      if(BsPutBit(stream, bit, 8))
	        CommonExit(1,"EncParFrameHvx: error generating bit stream");
          }
          break;
        }
      }
      else
      {
        for(i = 0; i < 5; i++)
        {
  	    bit = encBit[i];
	    if(BsPutBit(stream, bit, 8))
	      CommonExit(1,"EncParFrameHvx: error generating bit stream");
        }
      }
    }
}


/* EncParFreeIl() */
/* Free memory: individual lines */

void EncParFreeIl ()
{
  IndiLineExtractFree(ILX);
  IndiLineEncodeFree(ILE);
}


/* EncParFreeHvx() */
/* Free memory: harmonic vector exitation */

void EncParFreeHvx ()
{
  /* Akira 980506 */
  PAN_FreeLpcAnalysisEncoder(1, InstCtxt->PHI_Priv);
  /*
  PAN_FreeLpcAnalysisEncoder(1);
  */
  IPC_HVXCFree();
}


/* ---------- functions ---------- */


char *EncParInfo (
  FILE *helpStream)		/* in: print encPara help text to helpStream */
				/*     if helpStream not NULL */
				/* returns: core version string */
{
  if (helpStream != NULL) {
    fprintf(helpStream,
	    PROGVER "\n"
	    "encoder parameter string format:\n"
	    "  list of tokens (tokens separated by characters in \"%s\")\n",
	    SEPACHAR);
    CmdLineHelp(NULL,NULL,switchList,helpStream);
  }
  return PROGVER;
}


/* EncParInit() */
/* Init parametric encoder core. */

void EncParInit (
  int numChannel,		/* in: num audio channels */
  float fSample,		/* in: sampling frequency [Hz] */
  float bitRate,		/* in: total bit rate [bit/sec] */
  char *encPara,		/* in: encoder parameter string */
  int *frameNumSample,		/* out: num samples per frame */
  int *delayNumSample,		/* out: encoder delay (num samples) */
  BsBitBuffer *bitHeader)	/* out: header for bit stream */
{
  int parac;
  char **parav;
  int result;
  BsBitStream *hdrStream;
  int i;
  int maxCh;

  /* evalute encoder parameter string */
  parav = CmdLineParseString(encPara,SEPACHAR,&parac);
  result = CmdLineEval(parac,parav,NULL,switchList,1,NULL);
  if (result) {
    if (result==1) {
      EncParInfo(stdout);
      exit (1);
    }
    else
      CommonExit(1,"encoder parameter string error");
  }


  if (EPdelayMode)
    ipc_encDelayMode = DM_LONG;
  else
    ipc_encDelayMode = DM_SHORT;

  if (EPvrMode)
    ipc_bitstreamMode = BM_VARIABLE;
  else
    ipc_bitstreamMode = BM_CONSTANT;

  ipc_rateMode = 2;	/* 4000 kbps */
  if (bitRate <= 3999.5)
    ipc_rateMode = 1;	/* 3850 kbps */
  if (bitRate <= 3849.5)
    ipc_rateMode = 0;	/* 2000 kbps */

  if (EPdebugLevel >= 1) {
    printf("EncParInit: numChannel=%d  fSample=%f  bitRate=%f  "
	   "encPara=\"%s\"\n",
	   numChannel,fSample,bitRate,encPara);
    printf("EncParInit: debugLevel=%d  mode=\"%s\"\n",
	   EPdebugLevel,EPparaMode);
    printf("EncParInit: encDlyMode=%d  vrMode=%d  rateMode=%d\n",
	   ipc_encDelayMode,ipc_bitstreamMode,ipc_rateMode);
  }

  EPmode = -1;
  do
    EPmode++;
  while (EPmode < MODEPAR_NUM &&
	 strcmp(EPparaMode,MP4ModeParName[EPmode]) != 0);
  if (EPmode >= MODEPAR_NUM)
    CommonExit(1,"EncParInit: unknown parametric codec mode %s",EPparaMode);

  maxCh = (EPmode == MODEPAR_MIX) ? 2 : 1;
  if (numChannel != maxCh)
    CommonExit(1,"EncParInit: audio data must have %d channel(s) (%d)",
	       maxCh,numChannel);

  CmdLineParseFree(parav);

  EPfSample = fSample;
  EPbitRate = bitRate;

  hdrStream = BsOpenBufferWrite(bitHeader);
  if (BsPutBit(hdrStream,EPmode,2))
    CommonExit(1,"EncParInit: error generating bit stream header");
  switch (EPmode) {
  case MODEPAR_HVX:
    EncParInitHvx(hdrStream);
    break;
  case MODEPAR_IL:
    EncParInitIl(hdrStream);
    break;
  case MODEPAR_SWIT:
    EncParInitHvx(hdrStream);
    HVXdelayNumSample = EPdelayNumSample;
    HVXsampleBufSize = EPsampleBufSize;
    EPframeDur = 0.040;
    EncParInitIl(hdrStream);
    ILdelayNumSample = EPdelayNumSample;
    ILsampleBufSize = EPsampleBufSize;
    /* HP 971023   swit mode: 2 HVXC frames / 40 ms */
    EPframeNumSample = 320; /* 40 ms */
    /* HP 971111 */
    EPsampleBufSize =
      max(320+ILdelayNumSample,
	  EPframeNumSample*JUDGE_DLY+320+HVXdelayNumSample)-
      min(320+ILdelayNumSample-ILsampleBufSize,
	  EPframeNumSample*JUDGE_DLY+160+HVXdelayNumSample-HVXsampleBufSize);
    EPdelayNumSample =
      max(ILdelayNumSample,
	  EPframeNumSample*JUDGE_DLY+HVXdelayNumSample);
    HVXdelayOff = ILdelayOff = 
      -min(320+ILdelayNumSample-ILsampleBufSize,
	   EPframeNumSample*JUDGE_DLY+160+HVXdelayNumSample-HVXsampleBufSize);
    HVXdelayOff +=
      EPframeNumSample*JUDGE_DLY+160+HVXdelayNumSample-HVXsampleBufSize;
    ILdelayOff +=
      320+ILdelayNumSample-ILsampleBufSize;
    actBsBuf = 0;
    numBsBuf = JUDGE_DLY+1;
    for (i=0; i<numBsBuf; i++)
      bsBuf[i] = BsAllocBuffer(160);
    bsTmp = BsAllocBuffer(160);
    break;
  case MODEPAR_MIX:
    EncParInitHvx(hdrStream);
    HVXdelayNumSample = EPdelayNumSample;
    HVXsampleBufSize = EPsampleBufSize;
    EPframeDur = 0.040;
    EncParInitIl(hdrStream);
    ILdelayNumSample = EPdelayNumSample;
    ILsampleBufSize = EPsampleBufSize;
    /* HP 971023   swit mode: 2 HVXC frames / 40 ms */
    EPframeNumSample = 320; /* 40 ms */
    EPsampleBufSize =
      max(320+ILdelayNumSample,
	  320+HVXdelayNumSample)-
      min(320+ILdelayNumSample-ILsampleBufSize,
	  160+HVXdelayNumSample-HVXsampleBufSize);
    EPdelayNumSample =
      max(ILdelayNumSample,
	  HVXdelayNumSample);
    HVXdelayOff = ILdelayOff = 
      -min(320+ILdelayNumSample-ILsampleBufSize,
	   160+HVXdelayNumSample-HVXsampleBufSize);
    HVXdelayOff +=
      160+HVXdelayNumSample-HVXsampleBufSize;
    ILdelayOff +=
      320+ILdelayNumSample-ILsampleBufSize;
    break;
  }
  BsClose(hdrStream);

  testFrame = 0;	/* for EPtest!=0 or EPmixMode<0 */

  /* init sample buffer */
  if (CommonFreeAlloc((void**)&EPsampleBuf,
		      EPsampleBufSize*sizeof(float)) == NULL)
    CommonExit(1,"EncParInit: memory allocation error");
  for (i=0; i<EPsampleBufSize; i++)
    EPsampleBuf[i] = 0;
  if (EPmode == MODEPAR_MIX) {
    if (CommonFreeAlloc((void**)&EPsampleBuf2,
			EPsampleBufSize*sizeof(float)) == NULL)
      CommonExit(1,"EncParInit: memory allocation error");
    for (i=0; i<EPsampleBufSize; i++)
      EPsampleBuf2[i] = 0;
  }

  *frameNumSample = EPframeNumSample;
  *delayNumSample = EPdelayNumSample;

  if (EPdebugLevel >= 1) {
    printf("EncParInit: modeInt=%d\n",EPmode);
    printf("EncParInit: frmNumSmp=%d  dlyNumSmp=%d  hdrNumBit=%ld\n",
	   *frameNumSample,*delayNumSample,BsBufferNumBit(bitHeader));
    printf("EncParInit: dly=%d  buf=%d  frm=%d\n",
	   EPdelayNumSample,EPsampleBufSize,EPframeNumSample);
    printf("EncParInit: ILdly=%d  ILbuf=%d  ILoff=%d\n",
	   ILdelayNumSample,ILsampleBufSize,ILdelayOff);
    printf("EncParInit: HVXdly=%d  HVXbuf=%d  HVXoff=%d\n",
	   HVXdelayNumSample,HVXsampleBufSize,HVXdelayOff);
  }
}


/* EncParFrame() */
/* Encode one audio frame into one bit stream frame with */
/* parametric encoder core. */

void EncParFrame (
  float **sampleBuf,		/* in: audio frame samples */
				/*     sampleBuf[numChannel][frameNumSample] */
  BsBitBuffer *bitBuf,		/* out: bit stream frame */
				/*      or NULL during encoder start up */
  int frameAvailNumBit,		/* in: total num bits available for */
				/*     this frame (incl. bit reservoir) */
  int frameNumBit,		/* in: average num bits per frame */
  int frameMaxNumBit)		/* in: max num bits per frame */
{
  BsBitStream *stream;
  int i;
  int hvxcFrame1;
  int mixMode;
  int ilBits;

  if (EPdebugLevel >= 1)
    printf("EncParFrame: %s  availNum=%d  num=%d  maxNum=%d\n",
	   (bitBuf==NULL)?"start up":"normal",
	   frameAvailNumBit,frameNumBit,frameMaxNumBit);

  /* update sample buffer */
  for (i=0; i<EPsampleBufSize-EPframeNumSample; i++)
    EPsampleBuf[i] = EPsampleBuf[i+EPframeNumSample];
  for (i=0; i<EPframeNumSample; i++)
    EPsampleBuf[EPsampleBufSize-EPframeNumSample+i] = sampleBuf[0][i];
  if (EPmode == MODEPAR_MIX) {
    for (i=0; i<EPsampleBufSize-EPframeNumSample; i++)
      EPsampleBuf2[i] = EPsampleBuf2[i+EPframeNumSample];
    for (i=0; i<EPframeNumSample; i++)
      EPsampleBuf2[EPsampleBufSize-EPframeNumSample+i] = sampleBuf[1][i];
  }

  if (bitBuf)
    stream = BsOpenBufferWrite(bitBuf);
  else
    stream = NULL;
  switch (EPmode) {
  case MODEPAR_HVX:
    if (bitBuf)
      EncParFrameHvx(EPsampleBuf,stream,
		     frameAvailNumBit,frameNumBit,frameMaxNumBit);
    break;
  case MODEPAR_IL:
    if (bitBuf)
      EncParFrameIl(EPsampleBuf,stream,
		    frameAvailNumBit,frameNumBit,frameMaxNumBit);
    break;
  case MODEPAR_SWIT:
    bsStream=BsOpenBufferWrite(bsBuf[actBsBuf]);
    /* HP 971023   swit mode: 2 HVXC frames / 40 ms */
    if (frameNumBit-1 >= 40+80)
      hvxcFrame1 = 80;
    else
      hvxcFrame1 = 40;
    EncParFrameHvx(EPsampleBuf+HVXdelayOff,bsStream,
		   hvxcFrame1,hvxcFrame1,hvxcFrame1);
    EncParFrameHvx(EPsampleBuf+HVXdelayOff+160,bsStream,
		   frameNumBit-hvxcFrame1-1,frameNumBit-hvxcFrame1,
		   frameMaxNumBit-hvxcFrame1);
    BsClose(bsStream);
    if (EPdebugLevel >= 1)
      printf("judge = %d\n",judge);
    if (bitBuf) {
      if (judge==0)
	CommonWarning("EncParFrame: judge==0");
      if (EPtest) {
	judge = (testFrame%max(EPtest,-EPtest) < max(EPtest,-EPtest)/2)?1:2;
	if (EPtest<0)
	  judge = 3-judge;
	testFrame++;
	if (EPdebugLevel >= 1)
	  printf("testjudge = %d\n",judge);
      }
      if (EPdebugLevel >= 1)
	printf("switch: %s\n",(judge==1)?"il":"hvxc");
      if (judge==1) {
	/* music */
	BsPutBit(stream,1,1);
	EncParFrameIl(EPsampleBuf+ILdelayOff,stream,
		      frameAvailNumBit-1,frameNumBit,frameMaxNumBit);
      }
      else {
	/* speech & dflt */
	BsPutBit(stream,0,1);
	BsPutBuffer(stream,bsBuf[(actBsBuf+1)%numBsBuf]);
	if (EPdebugLevel >= 1)
	  printf("hvxcbufnumbit=%ld\n",
		 BsBufferNumBit(bsBuf[(actBsBuf+1)%numBsBuf]));
	EncParFrameIl(EPsampleBuf+ILdelayOff,NULL,
		      frameAvailNumBit-1,frameNumBit,frameMaxNumBit);
      }
    }
    actBsBuf=(actBsBuf+1)%numBsBuf;  
    break;
  case MODEPAR_MIX:
    mixMode = EPmixMode;
    if (EPmixMode < 0) {
      mixMode = (testFrame%(-4*EPmixMode))/(-EPmixMode);
      testFrame++;
    }
    if (EPdebugLevel >= 1)
      printf("mix: %d\n",mixMode);
    if (bitBuf) {
      BsPutBit(stream,mixMode,2);
      ilBits = frameAvailNumBit-2;
      switch (mixMode) {
      case 0:
	if (frameNumBit-2 >= 40+80)
	  hvxcFrame1 = 80;
	else
	  hvxcFrame1 = 40;
	EncParFrameHvx(EPsampleBuf+HVXdelayOff,stream,
		       hvxcFrame1,hvxcFrame1,hvxcFrame1);
	EncParFrameHvx(EPsampleBuf+HVXdelayOff+160,stream,
		       frameNumBit-hvxcFrame1-1,frameNumBit-hvxcFrame1,
		       frameMaxNumBit-hvxcFrame1);
	break;
      case 1:
	EncParFrameHvx(EPsampleBuf+HVXdelayOff,stream,
		       40,40,40);
	EncParFrameHvx(EPsampleBuf+HVXdelayOff+160,stream,
		       40,40,40);
	ilBits -= 80;
	break;
      case 2:
	EncParFrameHvx(EPsampleBuf+HVXdelayOff,stream,
		       80,80,80);
	EncParFrameHvx(EPsampleBuf+HVXdelayOff+160,stream,
		       80,80,80);
	ilBits -= 160;
	break;
      }
      EncParFrameIl(EPsampleBuf2+ILdelayOff,(mixMode==0) ? NULL : stream,
		    ilBits,frameNumBit,frameMaxNumBit);
    }
    break;
  }
  if (bitBuf==NULL)
    /* encoder start up */
    return;
  BsClose(stream);

  if (EPdebugLevel >= 1)
    printf("EncParFrame: numBit=%ld\n",BsBufferNumBit(bitBuf));

}


/* EncParFree() */
/* Free memory allocated by parametric encoder core. */

void EncParFree ()
{
  if (EPdebugLevel >= 1)
    printf("EncParFree: ...\n");

  if (EPmode != MODEPAR_IL)
    EncParFreeHvx();
  if (EPmode != MODEPAR_HVX)
    EncParFreeIl();
}


/* end of enc_par.c */

