/**********************************************************************
MPEG-4 Audio VM
Encoder frame work



This software module was originally developed by

Heiko Purnhagen (University of Hannover / ACTS-MoMuSys)

and edited by

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: mp4enc.c

$Id: mp4enc.c,v 1.18 1998/05/08 13:37:48 purnhage Exp $

Required modules:
common.o		common module
cmdline.o		command line module
bitstream.o		bit stream module
audio.o			audio i/o module
enc_par.o		encoder core (parametric)
enc_lpc.o		encoder core (CELP)
enc_tf.o		encoder core (T/F)
enc_g729.o		encoder core (G729)
enc_g723.o		encoder core (G723)

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>
BG    Bernhard Grill, Uni Erlangen <grl@titan.lte.e-technik.uni-erlangen.de>
NI    Naoki Iwakami, NTT <iwakami@splab.hil.ntt.jp>
SE    Sebastien ETienne, Jean Bernard Rault CCETT <jbrault@ccett.fr>

Changes:
13-jun-96   HP    first version
14-jun-96   HP    added debug code
17-jun-96   HP    added bit reservoir control / switches -r -b
18-jun-96   HP    added delay compensation
19-jun-96   HP    using new ComposeFileName()
25-jun-96   HP    changed frameNumBit
26-jun-96   HP    improved handling of switch -o
04-jul-96   HP    joined with t/f code by BG (check "DISABLE_TF")
14-aug-96   HP    adapted to new cmdline module, added mp4.h
16-aug-96   HP    adapted to new enc.h
		  added multichannel signal handling
21-aug-96   HP    added NBC_RM4 switches by BG
26-aug-96   HP    CVS
28-aug-96   NI    moved "static CmdLineSwitch switchList[]" into main routine.
29-aug-96   HP    moved "switchlist[]" back again
		  NI stated, that it just was a locally required bug-fix:
		  This table is moved into the main routine,
		  because there was a run-time problem on a
		  SGI (IRIX-5.3) system (using g++).
30-oct-96   HP    additional frame work options
15-nov-96   HP    adapted to new bitstream module
18-nov-96   HP    changed int to long where required
		  added bit stream header options
		  removed NBC_RM4 switches (new -nh, -ms, -bi options)
10-dec-96   HP    new -ni, -vr options, added variable bit rate
23-jan-97   HP    added audio i/o module
07-apr-97   HP    i/o filename handling improved / "-" supported
08-apr-97   SE    added G729-based coder
15-may-97   HP    clean up
30-jun-97   HP    fixed totNumSample bug / calc encNumFrame
05-nov-97   HP    update by FhG/UER
12-nov-97   HP    added options -n & -s
**********************************************************************/

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

#include "common.h"	/* common module */
#include "cmdline.h"	/* command line module */
#include "bitstream.h"	/* bit stream module */
#include "audio.h"	/* audio i/o module */
#include "mp4.h"	/* frame work declarations */
#include "enc.h"	/* encoder cores */
#include "fir_filt.h"	/* fir lowpass filter */


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

#define PROGVER "MPEG-4 Audio VM Encoder FCD V0.1 08-may-98"
#define CVSID "$Id: mp4enc.c,v 1.18 1998/05/08 13:37:48 purnhage Exp $"

/* the framework should be independent from files in the codec */
/* core directories    HP 970515*/
/* #include "tf_main.h" */
#ifndef MONO_CHAN
#define MONO_CHAN 0
#endif


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

/* command line module */

static char *codecMode;
static float bitRate;
static int varBitRate;
static int bitReservSize;
static int bitReservInit;
static int bitReservInitUsed;
static float regionStart;
static float regionDurat;
static int regionDuratUsed;
static char *encPara;
static char *outFileName;
static int outFileNameUsed;
static int numChannelOut;
static float fSampleOut;
static char *oriPath,*bitPath;
static int oriPathUsed,bitPathUsed;
static char *oriExt,*bitExt;
static int oriExtUsed;
static int noInfo;
static int noHeader;
static char *magicString;
static int mainDebugLevel;
static int audioDebugLevel,bitDebugLevel,cmdDebugLevel,quantDebugLevel;
static int *varArgIdx;
static ENC_FRAME_DATA encFrameData;
static OBJECT_DESCRIPTOR objDescr;

#ifdef DEBUGPLOT
int frame; /* global for debugging */
#else 
static  int frame;
#endif 


static CmdLinePara paraList[] = {
  {&varArgIdx,NULL,"<audio file(s)>"},
  {NULL,NULL,NULL}
};

static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"m",&codecMode,"%s",MODENAME_PAR,NULL,"codec mode (" MODENAME_LIST ")"},
  {"r",&bitRate,"%f","6000",NULL,"bit rate [bit/s]"},
  {"vr",&varBitRate,NULL,NULL,NULL,"enable variable bit rate"},
  {"b",&bitReservSize,"%d","0",NULL,"bit reservoir size [bit]"},
  {"bi",&bitReservInit,"%d",NULL,&bitReservInitUsed,
   "initial bit reservoir bits\n"
  "(dflt: bit reservoir size)"},
  {"c",&encPara,"%s","",NULL,
   "encoder parameter string"},
  {"ts",&regionStart,"%f","0",NULL,"start time of encoded region [sec]"},
  {"td",&regionDurat,"%f",NULL,&regionDuratUsed,
   "duration of encoded region [sec]\n"
   "(dflt: until end of file)"},
  {"o",&outFileName,"%s",NULL,&outFileNameUsed,"output file name"},
  {"n",&numChannelOut,"%d","0",NULL,
   "number of audio channels for bit stream header\n"
   "(0=as input file)"},
  {"s",&fSampleOut,"%f","0",NULL,
   "sampling frequency [Hz] for bit stream header\n"
   "(0=as input file)"},
  {"po",&oriPath,"%s",NULL,&oriPathUsed,
   "default path original audio files (dflt: $" MP4_ORI_PATH_ENV ")"},
  {"pb",&bitPath,"%s",NULL,&bitPathUsed,
   "default path bit stream files (dflt: $" MP4_BIT_PATH_ENV ")"},
  {"eo",&oriExt,"%s",NULL,&oriExtUsed,
   "original audio file extension\n"
   "supported file formats: .au, .snd, .wav, .aif, .raw\n"
   "(dflt: $" MP4_ORI_FMT_ENV " / " MP4_ORI_EXT ")\n"
   "(raw format: $" MP4_ORI_RAW_ENV ")"},
  {"eb",&bitExt,"%s",MP4_BIT_EXT,NULL,"bit stream file extension"},
  {"ni",&noInfo,NULL,NULL,NULL,"disable bit info string"},
  {"nh",&noHeader,NULL,NULL,NULL,"disable mp4 bit stream header"},
  {"ms",&magicString,"%s",MP4_MAGIC,NULL,"bit stream magic string"},
  {"d",&mainDebugLevel,"%i","0",NULL,"main debug level"},
  {"da",&audioDebugLevel,"%i","0",NULL,"audio file debug level"},
  {"db",&bitDebugLevel,"%i","0",NULL,"bit stream debug level"},
  {"dc",&cmdDebugLevel,"%i","0",NULL,"cmd line debug level"},
  {"dq",&quantDebugLevel,"%i","0",NULL,"quantizer  debug level"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};


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

/* Encode() */
/* Encode audio file and generate bit stream. */
/* (This function evaluates the global xxxDebugLevel variables !!!) */

static int Encode (
  char *audioFileName,		/* in: audio file name */
  char *bitFileName,		/* in: bit stream file name */
  char *codecMode,		/* in: codec mode string */
  float bitRate,		/* in: bit rate [bit/sec] */
  int varBitRate,		/* in: variable bit rate */
  int bitReservSize,		/* in: bit reservoir size [bit] */
  int bitReservInit,		/* in: initial bit reservoir bits */
  char *encPara,		/* in: encoder parameter string */
  char *info,			/* in: info string for bit stream */
  int noHeader,			/* in: disable bit stream header */
  char *magicString,		/* in: bit stream magic string */
  float regionStart,		/* in: start time of region */
  float regionDurat,		/* in: duration of region */
  int numChannelOut,		/* in: number of channels (0 = as input) */
  float fSampleOut)		/* in: sampling frequency (0 = as input) */
				/* returns: 0=OK  1=error */
{
  int mode;			/* enum MP4Mode */
  float fSample;
  long fileNumSample;
  long totNumSample;
  int numChannel;
  int frameNumSample,delayNumSample;
  int frameMinNumBit,frameMaxNumBit;
  long fSampleLong,bitRateLong;
  BsBitBuffer *bitBuf;
  float **sampleBuf;
  BsBitStream *bitStream;
  AudioFile *audioFile;
  BsBitBuffer *bitHeader;
  FILE *tmpFile;
  int startupNumFrame,startupFrame;
  int numSample;
  int frameNumBit,frameAvailNumBit,usedNumBit;
  int headerNumBit;
  int padNumBit;
  long totDataNumBit,totPadNumBit;
  int minUsedNumBit,maxUsedNumBit;
  int minPadNumBit,maxPadNumBit;
  int minReservNumBit,maxReservNumBit;
  long totReservNumBit;
  int ch,i;
  long startSample;
  long encNumSample;
  int numChannelBS;
  long fSampleLongBS;
  ENC_FRAME_DATA* frameData=NULL;
                       
  FIR_FILT *lowpassFilt = NULL;
  int downsamplFac = 1;
  float *tmpBuff;

  /* init */
  if (mainDebugLevel >= 3) {
    printf("Encode:\n");
    printf("audioFileName=\"%s\"\n",audioFileName);
    printf("bitFileName=\"%s\"\n",bitFileName);
    printf("codecMode=\"%s\"\n",codecMode);
    printf("bitRate=%.3f\n",bitRate);
    printf("varBitRate=%d\n",varBitRate);
    printf("bitReservSize=%d\n",bitReservSize);
    printf("bitReservInit=%d\n",bitReservInit);
    printf("encPara=\"%s\"\n",encPara?encPara:"(NULL)");
    printf("info=\"%s\"\n",info);
    printf("noHeader=%d\n",noHeader);
    printf("magicString=\"%s\"\n",magicString);
    printf("regionStart=%.6f\n",regionStart);
    printf("regionDurat=%.6f\n",regionDurat);
  }

  BsInit(0,bitDebugLevel);
  AudioInit(getenv(MP4_ORI_RAW_ENV),	   /* headerless file format */
	    audioDebugLevel);

  mode = 0;
  do
    mode++;
  while (mode < MODE_NUM && strcmp(codecMode,MP4ModeName[mode]) != 0);
  if (mode >= MODE_NUM)
    CommonExit(1,"Encode: unknown codec mode %s",codecMode);

  /* check audio file */
  if ((tmpFile=fopen(audioFileName,"rb"))==NULL) {
    CommonWarning("Encode: error opening audio file %s",audioFileName);
    return 1;
  }
  fclose(tmpFile);

  /* open audio file */
  audioFile = AudioOpenRead(audioFileName,&numChannel,&fSample,
			    &fileNumSample);
  if (audioFile==NULL)
    CommonExit(1,"Encode: error opening audio file %s "
	       "(maybe unknown format)",
	       audioFileName);

  startSample = (long)(regionStart*fSample+0.5);
  if (regionDurat >= 0)
    encNumSample = (long)(regionDurat*fSample+0.5);
  else
    if (fileNumSample == 0)
      encNumSample = -1;
    else
      encNumSample = max(0,fileNumSample-startSample);

  /* init encoder */
  bitHeader = BsAllocBuffer(BITHEADERBUFSIZE);
  bitRateLong = (long)(bitRate+0.5);
  fSampleLong = (long)(fSample+0.5);
  numChannelBS = (numChannelOut==0) ? numChannel : numChannelOut;
  fSampleLongBS = (fSampleOut==0) ? fSampleLong : (long)(fSampleOut+0.5);

  if ((strstr(encPara, "-aac_sys") != NULL) || (strstr(encPara, "-aac_sys_bsac") != NULL)) {
    frameData= &encFrameData;
    frameData->od= &objDescr;
  }

  switch (mode) {
  case MODE_PAR:
    EncParInit(numChannel,fSample,bitRate,encPara,
	       &frameNumSample,&delayNumSample,bitHeader);
    break;
  case MODE_LPC:
    EncLpcInit(numChannel,fSample,bitRate,encPara,
	       &frameNumSample,&delayNumSample,bitHeader);
    break;
  case MODE_G729:
    bitRate=8000;
    bitRateLong = (long)bitRate;
    if (fSample == 48000){
      lowpassFilt=initFirLowPass(48000/4000,120)  ;
      downsamplFac=6;      
    }
    EncG729Init(numChannel,fSample/downsamplFac,bitRate,encPara,
                &frameNumSample,&delayNumSample,bitHeader);
    frameNumSample=frameNumSample*downsamplFac;
    break;
  case MODE_G723:
    bitRate=6400;    
    bitRateLong =   (long) bitRate;    
    if (fSample == 48000){
      lowpassFilt=initFirLowPass(48000/4000,120)  ;
      downsamplFac=6;      
    }
    EncG723Init(numChannel,fSample/downsamplFac,bitRate,encPara,encNumSample,
	      &frameNumSample,&delayNumSample,bitHeader);
    frameNumSample=frameNumSample*downsamplFac;
    break;
  case MODE_TF:
    EncTfInit(numChannel,fSample,bitRate,encPara,quantDebugLevel,
	      &frameNumSample,&delayNumSample,bitHeader,frameData);

    break;
  }


  frameNumBit = (int)(bitRate*frameNumSample/fSample+0.5);
  /* variable bit rate: minimum 8 bit/frame (1 byte) */
  /*                    to allow end_of_bitstream detection in decoder */
  frameMinNumBit = varBitRate ? 8 : frameNumBit;
  frameMaxNumBit = frameNumBit+bitReservSize;

  if (mainDebugLevel >= 3) {
    printf("mode=%d\n",mode);
    printf("fSample=%.3f Hz  (int=%ld)\n",fSample,fSampleLong);
    printf("bitRate=%.3f bit/sec  (int=%ld)\n",bitRate,bitRateLong);
    printf("bitReservSize=%d bit  (%.6f sec)\n",
	   bitReservSize,bitReservSize/bitRate);
    printf("bitReservInit=%d bit\n",bitReservInit);
    printf("frameNumSample=%d  (%.6f sec/frame)\n",
	   frameNumSample,frameNumSample/fSample);
    printf("delayNumSample=%d  (%.6f sec)\n",
	   delayNumSample,delayNumSample/fSample);
    printf("frameNumBit=%d\n",frameNumBit);
    printf("frameMinNumBit=%d\n",frameMinNumBit);
    printf("frameMaxNumBit=%d\n",frameMaxNumBit);
    printf("bitHeaderNumBit=%ld\n",BsBufferNumBit(bitHeader));
    printf("fileNumSample=%ld  (%.3f sec  %.3f frames)\n",
	   fileNumSample,fileNumSample/fSample,
	   fileNumSample/(float)frameNumSample);
    printf("startSample=%ld\n",startSample);
    printf("encNumSample=%ld  (%.3f sec  %.3f frames)\n",
	   encNumSample,encNumSample/fSample,
	   encNumSample/(float)frameNumSample);
  }

  /* allocate buffers */
  bitBuf = BsAllocBuffer(frameMaxNumBit);
  if ((sampleBuf=(float**)malloc(numChannel*sizeof(float*)))==NULL)
    CommonExit(1,"Encode: memory allocation error");
  for (ch=0; ch<numChannel; ch++)
    if ((sampleBuf[ch]=(float*)malloc(frameNumSample*sizeof(float)))==NULL)
      CommonExit(1,"Encode: memory allocation error");
  if ((tmpBuff=(float*)malloc(frameNumSample*sizeof(float)))==NULL)
    CommonExit(1,"Encode: memory allocation error");

  /* if we have aac_raw , we should treat it, as with no header , 
     if we have scaleable aac the header is will be written in aacScaleableEncode*/
  if ((strstr(encPara, "-aac_raw") != NULL)|| (strstr(encPara, "-aac_sca") != NULL))
    noHeader=1;
  if ((strstr(encPara, "-aac_sys") != NULL) || (strstr(encPara, "-aac_sys_bsac") != NULL))
    noHeader=0;


  /* open bit stream file */
  if (!noHeader)
    bitStream = BsOpenFileWrite(bitFileName,magicString,info);
  else
    bitStream = BsOpenFileWrite(bitFileName,NULL, NULL);

  if (bitStream==NULL)
    CommonExit(1,"Encode: error opening bit stream file %s",bitFileName);

  /* write bit stream header */
  if ((strstr(encPara, "-aac_sys") == NULL) && (strstr(encPara, "-aac_sys_bsac") == NULL)) {
    if (!noHeader)
      if (BsPutBit(bitStream,MP4_BS_VERSION,16) ||
          BsPutBit(bitStream,numChannelBS,8) ||
          BsPutBit(bitStream,fSampleLongBS,32) ||
          BsPutBit(bitStream,bitRateLong,32) ||
          BsPutBit(bitStream,frameNumBit,16) ||
          BsPutBit(bitStream,frameMinNumBit,16) ||
          BsPutBit(bitStream,bitReservSize,16) ||
          BsPutBit(bitStream,bitReservInit,16) ||
          BsPutBit(bitStream,mode,8) ||
          BsPutBit(bitStream,BsBufferNumBit(bitHeader),16))
        CommonExit(1,"Encode: error writing bit stream header (frame)");
    if (BsPutBuffer(bitStream,bitHeader))
      CommonExit(1,"Encode: error writing bit stream header (core)");
    BsFreeBuffer(bitHeader);
    
    headerNumBit = BsCurrentBit(bitStream);
  } else {
    /* write object descriptor  length */
    int length= BsBufferNumBit(bitHeader)/8;
    int align = 8 - BsBufferNumBit(bitHeader) % 8;
     if (align == 8) align = 0;
     if (align != 0) {
       length += 1;
     }
    BsPutBit(bitStream,length,32);
    if (BsPutBuffer(bitStream,bitHeader))
      CommonExit(1,"Encode: error writing bit stream header (core)");
    BsPutBit(bitStream,0,align);

    BsFreeBuffer(bitHeader);
    headerNumBit = BsCurrentBit(bitStream);
  }
  if (mainDebugLevel >= 3)
    printf("headerNumBit=%d\n",headerNumBit);

  /* num frames to start up encoder due to delay compensation */
  startupNumFrame = (delayNumSample+frameNumSample-1)/frameNumSample;

  /* seek to beginning of first (startup) frame (with delay compensation) */
  AudioSeek(audioFile,
	    startSample+delayNumSample-startupNumFrame*frameNumSample);

  if (mainDebugLevel >= 3)
    printf("startupNumFrame=%d\n",startupNumFrame);

  /* process audio file frame by frame */
  frame = -startupNumFrame;
  totNumSample = 0;
  totPadNumBit = 0;
  frameAvailNumBit = bitReservInit;
  minUsedNumBit = minPadNumBit = minReservNumBit = frameMaxNumBit;
  maxUsedNumBit = maxPadNumBit = maxReservNumBit = 0;
  totReservNumBit = 0;

  do {
    if (mainDebugLevel >= 1 && mainDebugLevel <= 3) {
      printf("\rframe %4d ",frame);
      fflush(stdout);
    }
    if (mainDebugLevel > 3)
      printf("frame %4d\n",frame);

    /* check for startup frame */
    startupFrame = frame < 0;

    /* read audio file */
    numSample = AudioReadData(audioFile,sampleBuf,frameNumSample);
    totNumSample += numSample;
    if (numSample != frameNumSample && encNumSample == -1)
      encNumSample = totNumSample;
      

    /* encode one frame */
    if (!startupFrame) {
      /* variable bit rate: don't exceed bit reservoir size */
      if (frameAvailNumBit > bitReservSize)
	frameAvailNumBit = bitReservSize;

      if (frameData!=NULL){
        frameAvailNumBit += frameNumBit-(18* (frameNumBit/(250*8)+1) + 7) ; /* minus flexmux overhead: 18 bits per flexmux packet plus max 7 align bits*/
      }else {
        frameAvailNumBit += frameNumBit;
      }
      if (mainDebugLevel >= 5)
	printf("frameAvailNumBit=%d\n",frameAvailNumBit);
    }

    switch (mode) {
    case MODE_PAR:
      EncParFrame(sampleBuf,
		  startupFrame ? (BsBitBuffer*)NULL : bitBuf,
		  startupFrame ? 0 : frameAvailNumBit,
		  frameNumBit,frameMaxNumBit);
      break;
    case MODE_LPC:
      EncLpcFrame(sampleBuf,
		  startupFrame ? (BsBitBuffer*)NULL : bitBuf,
		  startupFrame ? 0 : frameAvailNumBit,
		  frameNumBit,frameMaxNumBit);
      break;
    case MODE_G729:
      /* I'd suggest just to use frameNumSample! */
      /* numSample is for internal purposes only ...   HP 970630 */
      if (numSample !=frameNumSample)
	break;
      if (downsamplFac!=0 && lowpassFilt!=NULL){
	firLowPass(sampleBuf[MONO_CHAN],tmpBuff, numSample, lowpassFilt );
	subSampl(tmpBuff,sampleBuf[MONO_CHAN],downsamplFac,&numSample);
      }
      EncG729Frame(sampleBuf,
		  startupFrame ? (BsBitBuffer*)NULL : bitBuf,
		  startupFrame ? 0 : frameAvailNumBit,
		  frameNumBit,frameMaxNumBit,numSample);
      break;
    case MODE_G723:
      /* I'd suggest just to use frameNumSample! */
      /* numSample is for internal purposes only ...   HP 970630 */
      if (numSample !=frameNumSample)
	break;
      if (downsamplFac!=0 && lowpassFilt!=NULL){
	firLowPass(sampleBuf[MONO_CHAN],tmpBuff, numSample, lowpassFilt );
	subSampl(tmpBuff,sampleBuf[MONO_CHAN],downsamplFac,&numSample);
      }
      EncG723Frame(sampleBuf,
		  startupFrame ? (BsBitBuffer*)NULL : bitBuf,
		  startupFrame ? 0 : frameAvailNumBit,
		  frameNumBit,frameMaxNumBit,numSample);
      break;
    case MODE_TF:
      EncTfFrame(sampleBuf,
		  startupFrame ? (BsBitBuffer*)NULL : bitBuf,
		  startupFrame ? 0 : frameAvailNumBit,
		  frameNumBit,frameMaxNumBit,bitRateLong,fSampleLong,frameData);
      break;
    }

    if (!startupFrame) {
      usedNumBit = BsBufferNumBit(bitBuf);

      if (mainDebugLevel >= 5)
	printf("usedNumBit=%d\n",usedNumBit);

      /* write bit stream */
      if (usedNumBit > frameAvailNumBit)
	CommonExit(1,"Encode: more bits used than available in frame+buffer");
      if (BsPutBuffer(bitStream,bitBuf))
	CommonExit(1,"Encode: error writing bit stream data");
      frameAvailNumBit -= usedNumBit;

      if (frameData==NULL){
        /* write padding bits */
        padNumBit = 0;
        if (frameAvailNumBit-frameNumBit+frameMinNumBit > bitReservSize) {
          padNumBit = frameAvailNumBit-frameNumBit+frameMinNumBit-bitReservSize;
          
          if (mainDebugLevel >= 5)
            printf("padNumBit=%d\n",padNumBit);
          
          for (i=0; i<padNumBit; i++)
            if (BsPutBit(bitStream,0,1))
              CommonExit(1,"Encode: error writing bit stream padding bits");
          frameAvailNumBit -= padNumBit;
          totPadNumBit += padNumBit;
        }
      }
      if (minUsedNumBit > usedNumBit)
	minUsedNumBit = usedNumBit;
      if (maxUsedNumBit < usedNumBit)
	maxUsedNumBit = usedNumBit;
      if (minPadNumBit > padNumBit)
	minPadNumBit = padNumBit;
      if (maxPadNumBit < padNumBit)
	maxPadNumBit = padNumBit;
      if (minReservNumBit > frameAvailNumBit)
	minReservNumBit = frameAvailNumBit;
      if (maxReservNumBit < frameAvailNumBit)
	maxReservNumBit = frameAvailNumBit;
      totReservNumBit += frameAvailNumBit;
    
    }
    frame++;
  } while (encNumSample < 0 || frame*(long)frameNumSample < encNumSample);

  if (mainDebugLevel >= 1 && mainDebugLevel <= 3)
    printf(" \n");

  totDataNumBit = BsCurrentBit(bitStream)-headerNumBit-totPadNumBit;

  if (frameData==NULL){
    /* write last frame and bit reservoir padding bits */
    /* required also in case of variable bit rate */
    /* to allow end_of_bitstream detection in decoder */    
    for (i=0; i<frameAvailNumBit; i++)
      if (BsPutBit(bitStream,0,1))
        CommonExit(1,"Encode: error writing bit reservoir padding bits");
  }

  if (mainDebugLevel >= 3) {
    printf("totNumFrame=%d\n",frame);
    printf("encNumSample=%ld  (%.3f sec  %.3f frames)\n",
	   encNumSample,encNumSample/fSample,
	   encNumSample/(float)frameNumSample);
    printf("totNumSample=%ld\n",totNumSample);
    printf("totNumBit=%ld\n",BsCurrentBit(bitStream));
    printf("totDataNumBit=%ld  (%.3f bit/frame  %.3f bit/sec)\n",
	   totDataNumBit,totDataNumBit/(float)frame,
	   totDataNumBit/(float)frame*fSample/frameNumSample);
    printf("totPadNumBit=%ld\n",totPadNumBit);
    printf("lastPadNumBit=%d\n",frameAvailNumBit);
    printf("minUsedNumBit=%d\n",minUsedNumBit);
    printf("maxUsedNumBit=%d\n",maxUsedNumBit);
    printf("minPadNumBit=%d\n",minPadNumBit);
    printf("maxPadNumBit=%d\n",maxPadNumBit);
    printf("minReservNumBit=%d\n",minReservNumBit);
    printf("maxReservNumBit=%d\n",maxReservNumBit);
    printf("avgReservNumBit=%.1f\n",totReservNumBit/(float)frame);
  }

  /* free encoder memory */
  switch (mode) {
  case MODE_PAR:
    EncParFree();
    break;
  case MODE_LPC:
    EncLpcFree();
    break;
  case MODE_TF:
    EncTfFree();
    break;
  }

  /* close audio file */
  AudioClose(audioFile);
  
  /* close bit stream file */
  if (BsClose(bitStream))
    CommonExit(1,"Encode: error closing bit stream file");

  /* free buffers */
  if (numChannel>1)
    for (ch=0; ch<numChannel; ch++)
      free(sampleBuf[ch]);
  free(sampleBuf);
  free(tmpBuff);
  BsFreeBuffer(bitBuf);

  return 0;
}



/* ---------- main ---------- */

int main (int argc, char *argv[])
{
  char *progName = "<no program name>";
  int result;
  char oriFileName[STRLEN],bitFileName[STRLEN];
  char infoDate[STRLEN];
  int i,j,len;
  int fileIdx;
  char *info,*infoTail;
  time_t curTime;

  /* evaluate command line  */
  CmdLineInit(0);
  result = CmdLineEval(argc,argv,paraList,switchList,1,&progName);
  if (result) {
    if (result==1) {
      printf("%s: %s\n",progName,PROGVER);
      CmdLineHelp(progName,paraList,switchList,stdout);
      EncParInfo(stdout);
      EncLpcInfo(stdout);
      EncTfInfo(stdout);
      EncG729Info(stdout);
      EncG723Info(stdout);
      exit (1);
    }
    else
      CommonExit(1,"command line error (\"-h\" for help)");
  }

  if (mainDebugLevel >= 1)
    printf("%s: %s\n",progName,PROGVER);
  if (mainDebugLevel >= 2) {
    printf("CVS Id: %s\n",CVSID);
    printf("%s\n",EncParInfo(NULL));
    printf("%s\n",EncLpcInfo(NULL));
    printf("%s\n",EncTfInfo(NULL));
    printf("%s\n",EncG723Info(NULL));
    printf("%s\n",EncG729Info(NULL));
  }

  CmdLineInit(cmdDebugLevel);

  /* calc variable default values */
  if (!bitReservInitUsed)
    bitReservInit = bitReservSize;
  if (!regionDuratUsed)
    regionDurat = -1;
  if (!oriPathUsed)
    oriPath = getenv(MP4_ORI_PATH_ENV);
  if (!bitPathUsed)
    bitPath = getenv(MP4_BIT_PATH_ENV);
  if (!oriExtUsed)
    oriExt = getenv(MP4_ORI_FMT_ENV);
  if (oriExt==NULL)
    oriExt = MP4_ORI_EXT;

  /* check command line options */
  if (bitRate <= 0)
    CommonExit(1,"bit rate <= 0");
  if (bitReservSize < 0)
    CommonExit(1,"bit reservoir size < 0");
  if (bitReservInit < 0)
    CommonExit(1,"bit reservoir initial bits < 0");
  if (bitReservInit > bitReservSize)
    CommonExit(1,"bit reservoir initial bits > size");
  if (regionDuratUsed && regionDurat < 0)
    CommonExit(1,"duration of region < 0");
  if (outFileNameUsed && varArgIdx[0]>=0 && varArgIdx[1]>=0)
    CommonExit(1,"only one input file allowed when using -o");
  if (varArgIdx[0]<0)
    CommonExit(1,"no input file specified");
  
  if( strstr( encPara, "-aac_sca" ) ) {
    if (bitReservSize<6000)
      bitReservSize=6000 ;
    bitReservInit=0;    
    varBitRate=1;
  } 

  /* generate info string for bit stream */
  len = 1;
  curTime = time((time_t*)NULL);
  len += strftime(infoDate,STRLEN,"%Y/%m/%d %H:%M:%S UTC",gmtime(&curTime));
  len += strlen(PROGVER);
  j = 0;
  for (i=0; i<argc; i++)
    if (varArgIdx[j] == i)
      j++;
    else
      len += strlen(argv[i])+1;
  len += 2*STRLEN;
  len += 5*7+1;
  if ((info=(char*)malloc(len))==NULL)
    CommonExit(1,"memory allocation error");
  strcpy(info,"\n");
  strcat(info,"date: ");
  strcat(info,infoDate);	    
  strcat(info,"\n");
  strcat(info,"prog: ");
  strcat(info,PROGVER);	    
  strcat(info,"\n");
  strcat(info,"para:");
  j = 0;
  for (i=0; i<argc; i++) {
    if (varArgIdx[j] == i)
      j++;
    else {
      strcat(info," ");
      strcat(info,argv[i]);
    }
  }
  strcat(info,"\n");
  infoTail = info+strlen(info);

  /* process all files on command line */
  fileIdx = 0;
  while (varArgIdx[fileIdx] >= 0) {

    /* compose file names */
    if (ComposeFileName(argv[varArgIdx[fileIdx]],0,oriPath,oriExt,oriFileName,
			STRLEN))
      CommonExit(1,"composed file name too long");
    if (outFileNameUsed) {
      if (ComposeFileName(outFileName,0,bitPath,bitExt,bitFileName,
			  STRLEN))
	CommonExit(1,"composed file name too long");
    }
    else
      if (ComposeFileName(argv[varArgIdx[fileIdx]],1,bitPath,bitExt,
			  bitFileName,STRLEN))
	CommonExit(1,"composed file name too long");

    /* complete info string */
    *infoTail = '\0';
    strcat(infoTail,"ori: ");
    strcat(infoTail,oriFileName);
    strcat(infoTail,"\n");
    strcat(infoTail,"bit: ");
    strcat(infoTail,bitFileName);
    strcat(infoTail,"\n");

    /* encode file */
    if (mainDebugLevel >= 1)
      printf("encoding %s -> %s\n",oriFileName,bitFileName);

    if (Encode(oriFileName,bitFileName,codecMode,bitRate,varBitRate,
	       bitReservSize,bitReservInit,encPara,
	       noInfo ? "" : info,noHeader,magicString,
	       regionStart,regionDurat,numChannelOut,fSampleOut))
      CommonWarning("error encoding audio file %s",oriFileName);

    fileIdx++;
  }
  
  CmdLineEvalFree(paraList);

  if (mainDebugLevel >= 1)
    printf("%s: finished\n",progName);

  return 0;
}

/* end of mp4enc.c */

