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



This software module was originally developed by

Heiko Purnhagen (University of Hannover / ACTS-MoMuSys)

and edited by

Naoya Tanaka (Matsushita Communication Industrial Co., Ltd.)
Ralph Sperschneider (Fraunhofer Gesellschaft IIS)

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

$Id: mp4dec.c,v 1.40 1998/05/08 16:10:52 purnhage Exp $

Required modules:
common.o                common module
cmdline.o               command line module
bitstream.o             bit stream module
audio.o                 audio i/o module
dec_par.o               decoder core (parametric)
dec_lpc.o               decoder core (CELP)
dec_tf.o                decoder core (T/F)
dec_g729.o              decoder core (G729)
dec_g723.o              decoder core (G723)

Authors:
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>
NT    Naoya Tanaka, Panasonic <natanaka@telecom.mci.mei.co.jp>
BT    Bodo Teichmann, FhG/IIS <tmn@iis.fhg.de>
CCETT N.N., CCETT <@ccett.fr>

Changes:
18-jun-96   HP    first version
19-jun-96   HP    added .wav format / using new ComposeFileName()
26-jun-96   HP    improved handling of switch -o
04-jul-96   HP    joined with t/f code by BG
09-aug-96   HP    adapted to new cmdline module, added mp4.h
16-aug-96   HP    adapted to new dec.h
                  added multichannel signal handling
                  added cmdline parameters for numChannel, fSample
26-aug-96   HP    CVS
03-sep-96   HP    added speed change & pitch change for parametric core
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
10-dec-96   HP    added variable bit rate
10-jan-97   HP    using BsGetSkip()
23-jan-97   HP    added audio i/o module
03-feb-97   HP    audio module bug fix
07-feb-97   NT    added PICOLA speed control
14-mar-97   HP    merged FhG AAC code
21-mar-97   BT    various changes (t/f, AAC)
04-apr-97   HP    new option -r for scalable decoder
26-mar-97   CCETT added G729 decoder
07-apr-97   HP    i/o filename handling improved / "-" supported
05-nov-97   HP    update by FhG/UER
30-mar-98   HP    added ts option
06-may-98   HP    
**********************************************************************/

#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 "audio.h"      /* audio i/o module */

#include "mp4.h"        /* frame work common declarations */
#include "dec.h"        /* decoder cores */
#include "plotmtv.h"

#include "pan_picola.h" /* for speed control (PICOLA) */

#include "resilience.h" /* for error resilience */
#include "reorderspec.h"


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

#define PROGVER "MPEG-4 Audio VM Decoder FCD V0.1 08-may-98"
#define CVSID "$Id: mp4dec.c,v 1.40 1998/05/08 16:10:52 purnhage Exp $"


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

/* command line module */

static float decBitRate;
static int   decBitRateUsed;
static int   numChannelOut;
static float fSampleOut;
static char* predFileName;
static int   epFlag;
static float speedFact;
static float pitchFact;
static float regionStart;
static float regionDurat;
static int   regionDuratUsed;
static char* decPara;
static char* outFileName;
static int   outFileNameUsed;
static char* bitPath;
static char* decPath;
static int   bitPathUsed;
static int   decPathUsed;
static char* bitExt;
static char* decExt;
static int   decExtUsed;
static int   testBitStream;
static int   mainDebugLevel;
static int   audioDebugLevel;
static int   bitDebugLevel;
static int   cmdDebugLevel;
static char* aacDebugString; /* Debug options for aac */
static int*  varArgIdx;

#ifdef VERSION2

/* NEW for EP mode */

static char infoFileName[100];

#endif /* VERSION2 */

static FRAME_DATA frameData;

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

static int aacEOF = 0;	/* HP 980506   AAC bitstream EOF detection */

/* for speed control (PICOLA) */
static int SpeedControlMode;

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

static CmdLineSwitch switchList[] = {
  {"h",NULL,NULL,NULL,NULL,"print help"},
  {"r",&decBitRate,"%f",NULL,&decBitRateUsed,
   "decoder bit rate for scalability [bit/s]\n"
   "(dflt: encoder bit rate)"},
  {"n",&numChannelOut,"%d","0",NULL,"number of audio channels "
   "(0=as original)"},
  {"s",&fSampleOut,"%f","0",NULL,"sampling frequency [Hz] "
   "(0=as original)"},
#ifdef VERSION2
  {"ep",&predFileName,"%s",NULL,&epFlag,
   "error protection mode (needs error protection\n"
   "predefinition file name as parameter)"},
#endif /*VERSION2*/
  {"sf",&speedFact,"%f","1",NULL,"speed change factor"},
  {"pf",&pitchFact,"%f","1",NULL,"pitch change factor"},
  {"c",&decPara,"%s","",NULL,
   "decoder parameter string (\"-c h\" for help)"},
  {"ts",&regionStart,"%f","0",NULL,"start time of decoded region [sec]"},
  {"td",&regionDurat,"%f",NULL,&regionDuratUsed,
   "duration of decoded region [sec]\n"
   "(dflt: until end of bit stream)"},
  {"o",&outFileName,"%s",NULL,&outFileNameUsed,"output file name"},
  {"pb",&bitPath,"%s",NULL,&bitPathUsed,
   "default path bit stream files (dflt: $" MP4_BIT_PATH_ENV ")"},
  {"pd",&decPath,"%s",NULL,&decPathUsed,
   "default path decoded audio files (dflt: $" MP4_DEC_PATH_ENV ")"},
  {"eb",&bitExt,"%s",MP4_BIT_EXT,NULL,"bit stream file extension"},
  {"ed",&decExt,"%s",NULL,&decExtUsed,
   "decoded audio file extension\n"
   "supported file formats: .au, .snd, .wav, .aif, .raw\n"
   "(dflt: $" MP4_DEC_FMT_ENV " / " MP4_DEC_EXT ")\n"
   "(raw format: integer16, native)"},
  {"t",&testBitStream,NULL,NULL,NULL,"test bit stream (decoder disabled)"},
  {"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"},
  {"daac",&aacDebugString,"%s","",NULL,"[a-Z] Debug Options for aac-Decoder"},
  {"picola", &SpeedControlMode, NULL, NULL, NULL, "speed control by PICOLA"},
  {"aaceof", &aacEOF, NULL, NULL, NULL, "AAC bitstream end detection"},
  {NULL,NULL,NULL,NULL,NULL,NULL}
};

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

/* Decode() */
/* Decode bit stream and generate audio file. */
/* (This function evaluates the global xxxDebugLevel variables !!!) */
extern int  samplFreqIndex[];

static int Decode (
  char *bitFileName,            /* in: bit stream file name */
  char *audioFileName,          /* in: audio file name */
  char *audioFileFormat,        /* in: audio file format */
                                /*     (au, wav, aif, raw) */
  float decBitRate,             /* in: decoder bit rate (0=as encoder) */
  int   testBitStream,          /* in: 0=normal decoding */
                                /*     1=test bit stream (disable decoder) */
  int   numChannelOut,          /* in: num audio channels */
  float fSampleOut,             /* in: sampling frequency [Hz] */
  int   epFlag,			/* in: error protection flag */
  float speedFact,              /* in: speed change factor */
  float pitchFact,              /* in: pitch change factor */
  char *decPara,                /* in: decoder parameter string */
  float regionStart,
  float regionDurat             /* in: duration of region */
#ifdef VERSION2
  ,char  *infoFileName,         /* in: info file name */
  char  *predFileName           /* in: pre definition file name */
#endif
 )
                                /* returns: 0=OK  1=error */
{
  unsigned int      x;
  int               mode;                    /* enum MP4Mode */
  float             fSample,fSampleOri,bitRate;
  long              totNumSample;
  int               numChannel,numChannelOri;
  int               frameMaxNumSample,frameBufNumSample,delayNumSample;
  int               frameMinNumBit,frameMaxNumBit;
  long              fSampleLong,bitRateLong;
  char*             info;
  char*             codecMode;
  int               bitReservSize;
  int               bitReservInit;
  int               bitHeaderNumBit;
  BsBitBuffer*      bitBuf;
  float**           sampleBuf;
  BsBitStream*      bitStream;
  AudioFile*        audioFile;
  BsBitBuffer*      bitHeader;
  int               numSample;
  int               frameNumBit,frameAvailNumBit,usedNumBit;
  int               frameDecNumBit;
  int               headerNumBit;
  int               padNumBit;
  long              totDataNumBit,totPadNumBit;
  int               ch;
  int               bsVersion;
  int               decNumFrame;
  HANDLE_RESILIENCE hResilience;
  HANDLE_BUFFER     hVm;
  HANDLE_BUFFER     hHcrSpecData;
  HANDLE_HCR        hHcrInfo;
  HANDLE_EP_INFO    hEPInfo;
  OBJECT_DESCRIPTOR objDescr;
  int decStartFrame;
  int decodeFrame;
  /* for speed control (PICOLA) */
  float**      PicolaBuffer;
  static int   PicolaDecodeFlag = 1;
  static int   PicolaOutputFlag = 1;
  long         PicolaNumSample;
  int          PicolaNumInputSample;

  /* init */
  if (mainDebugLevel >= 3) {
    printf("Decode:\n");
    printf("bitFileName=\"%s\"\n",bitFileName);
#if VERSION2
    printf("epFlag=%d\n",epFlag);
    if ( epFlag )
      printf("predFileName=\"%s\"\n",predFileName);
#endif /*VERSION2*/
    printf("audioFileName=\"%s\"\n",audioFileName);
    printf("audioFileFormat=\"%s\"\n",audioFileFormat);
    printf("decBitRate=%.3f\n",decBitRate);
    printf("testBitStream=%d\n",testBitStream);
    printf("decPara=\"%s\"\n",decPara?decPara:"(NULL)");
    printf("regionStart=%.6f\n",regionStart);
    printf("regionDurat=%.6f\n",regionDurat);
  }

  BsInit(MAXREADAHEAD,bitDebugLevel);
  initObjDescr( &objDescr);
  AudioInit(NULL,audioDebugLevel);
  info = NULL;
  
  /* open bit stream file */
  if ((strstr(decPara, "-aac_raw") != NULL) ||
      (strstr(decPara, "-aac_sca") != NULL) || 
      (strstr(decPara, "-aac_core_bsac") != NULL) )
    bitStream = BsOpenFileRead(bitFileName,NULL,&info);
  else
    bitStream = BsOpenFileRead(bitFileName,MP4_MAGIC,&info);
  
  if (bitStream==NULL) {
    CommonWarning("Decode: error opening bit stream file %s",bitFileName);
    return 1;
  }

  if ((mainDebugLevel >= 3) && (info != NULL))
    printf("info=\"%s\"\n",info);

  /* read bit stream header */
  bitHeader = BsAllocBuffer(BITHEADERBUFSIZE);


  if (!((strstr(decPara, "-aac_raw") != NULL) ||
        (strstr(decPara, "-aac_sca") != NULL) ||
        (strstr(decPara, "-aac_core_bsac") != NULL))) {
    if (strstr(decPara, "-aac_sys") != NULL || strstr(decPara, "-aac_sys_bsac") != NULL) {
      unsigned long length,align,dummy,odStartByte;
      bitHeaderNumBit=0;
      if (BsGetBit(bitStream,&(objDescr.ODLength.value),objDescr.ODLength.length) )
        CommonExit(1,"Decode: error reading bit stream header (frame)");
      odStartByte=BsCurrentBit(bitStream)/8;

      if (BsCurrentBit(bitStream) % 8 != 0 )CommonExit(-1,"error in reading object descr");

      if ( BsGetBit(bitStream,&(objDescr.ODescrId.value),objDescr.ODescrId.length) ||
           BsGetBit(bitStream,&(objDescr.streamCount.value),objDescr.streamCount.length) ||
           BsGetBit(bitStream,&(objDescr.extensionFlag.value),objDescr.extensionFlag.length))
        CommonExit(1,"Decode: error reading bit stream header (frame)");
      if (mainDebugLevel >= 3) {
        printf("ObjectDescriptor.streamCount: %ld \n",objDescr.streamCount.value);
      }

      frameNumBit=0;
      bitRateLong=0;
      for (x=0;x<objDescr.streamCount.value;x++){
        int frameNumBitLay;
        initESDescr(&(objDescr.ESDescriptor[x]),0 );
        advanceESDescr(bitStream,objDescr.ESDescriptor[x],0);
        fSampleLong=samplFreqIndex[objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.samplingFreqencyIndex.value];
        frameNumBitLay = ( objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.frameLength.value ? 960:1024) 
          *  objDescr.ESDescriptor[x]->DecConfigDescr.avgBitrate.value / fSampleLong ;
        
        frameNumBitLay += 3*8*(1 + (frameNumBitLay/2040)) ; /* overhead for flexMux transport */
        frameNumBit += frameNumBitLay;
        bitRateLong += objDescr.ESDescriptor[x]->DecConfigDescr.avgBitrate.value;
        if (mainDebugLevel >= 3) {
          printf("\nESDescriptor.ESNumber: %ld \n",objDescr.ESDescriptor[x]->ESNumber.value);
          printf("  ESDescriptor.streamDependence: %ld \n",objDescr.ESDescriptor[x]->streamDependence.value);
          printf("  ESDescriptor.dependsOn_Es_number: %ld \n",objDescr.ESDescriptor[x]->dependsOn_Es_number.value);
          printf("  ESDescriptor.DecConfigDescr.streamType: %ld \n",objDescr.ESDescriptor[x]->DecConfigDescr.streamType.value);
          printf("  ESDescriptor.DecConfigDescr.bufferSizeDB: %ld \n",objDescr.ESDescriptor[x]->DecConfigDescr.bufferSizeDB.value);
          printf("  ESDescriptor.DecConfigDescr.avgBitrate: %ld \n",objDescr.ESDescriptor[x]->DecConfigDescr.avgBitrate.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.audioDecoderType: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.audioDecoderType.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.samplingFreqencyIndex: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.samplingFreqencyIndex.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.channelConfiguration: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.channelConfiguration.value);

          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.TFCodingType: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.TFCodingType.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.frameLength: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.frameLength.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.dependsOnCoreCoder: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.dependsOnCoreCoder.value);
          printf("  ESDescriptor.DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.coreCoderDelay: %ld \n",
                 objDescr.ESDescriptor[x]->DecConfigDescr.audioSpecificConfig.specConf.TFSpecificConfig.coreCoderDelay.value);
        }
      }
      frameMinNumBit=0;
      length=  BsCurrentBit(bitStream)/8;
      align = 8 -  BsCurrentBit(bitStream) % 8;
      if (align == 8) align = 0;
      if (align != 0) {
        length += 1;
      }
      BsGetBit(bitStream,&(dummy),align) ;
      if ( (length-odStartByte) != objDescr.ODLength.value) CommonExit(-1,"error in object descriptor read");

      bitReservSize= 6000;        /* objDescr.ESDescriptor[x]->DecConfigDescr.bufferSizeDB.value 6144-1366; */
      bitReservInit= 6000;        /* BT 971105 */  
      bitHeaderNumBit=0;
      mode = MODE_TF;
      numChannelOri=1;
      frameData.od = &objDescr;
    } else {
      frameData.od = NULL;
      /* for qc_aac we read */
      if (BsGetBitInt(bitStream,(unsigned int*)&bsVersion,16) ||
          BsGetBitInt(bitStream,(unsigned int*)&numChannelOri,8) ||
          BsGetBit(bitStream,(unsigned long*)&fSampleLong,32) ||
          BsGetBit(bitStream,(unsigned long*)&bitRateLong,32) ||
          BsGetBitInt(bitStream,(unsigned int*)&frameNumBit,16) ||
          BsGetBitInt(bitStream,(unsigned int*)&frameMinNumBit,16) ||
          BsGetBitInt(bitStream,(unsigned int*)&bitReservSize,16) ||
          BsGetBitInt(bitStream,(unsigned int*)&bitReservInit,16) ||
          BsGetBitInt(bitStream,(unsigned int*)&mode,8) ||
          BsGetBitInt(bitStream,(unsigned int*)&bitHeaderNumBit,16))
        CommonExit(1,"Decode: error reading bit stream header (frame)");
      
      if (bsVersion != MP4_BS_VERSION) {
        CommonWarning("Decode: wrong bitstream version "
                      "(found 0x%4x, need 0x%4x)",bsVersion,MP4_BS_VERSION);
        return 1;
      }
    }

    if (bitHeaderNumBit > BITHEADERBUFSIZE)
      CommonExit(1,"Decode: bit stream header too long");
    
    if (BsGetBuffer(bitStream,bitHeader,bitHeaderNumBit))
      CommonExit(1,"Decode: error reading bit stream header (core)");
  } 
  else{
    /* raw data */
    numChannelOri=1;
    fSampleLong= (long int) fSampleOut;
    if (fSampleLong < 8000 ){
      CommonExit(-1,"\n wrong sample rate %d \n",fSampleLong);
    }
    bitRateLong=128000;
    frameNumBit= 1024 * bitRateLong / fSampleLong; 
    if (strstr(decPara, "-qc_aac_960") != NULL)
      frameNumBit = 960*bitRateLong/fSampleLong ;
    if (strstr(decPara, "-aac_sca") != NULL || 
        strstr(decPara, "-aac_core_bsac") != NULL)
      frameNumBit = 960*bitRateLong/fSampleLong ;
    frameMinNumBit=0;
    bitReservSize= 6000;        /* 6144-1366; */
    bitReservInit= 6000;        /* BT 971105 */  
    bitHeaderNumBit=0;
    mode = MODE_TF;
  }


  /* --- Set debugging options for aac_decoder --- */
  /*
    if (strstr(decPara, "-qc_aac") != NULL){
    extern int     aacSamplingRate;
    aacSamplingRate = (int)fSampleLong;
    } 
  */ 
/*   if (aacDebugString != NULL){ */
/*     aacDebugString    */
/*   } */

  headerNumBit = BsCurrentBit(bitStream);

  frameMaxNumBit = frameNumBit+bitReservSize;
  fSampleOri = fSampleLong;
  bitRate = bitRateLong;
  if (decBitRate == 0) {
    decBitRate = bitRate;
    frameDecNumBit = 0;
  }
  else {
    if (decBitRate > bitRate) {
      CommonWarning("Decode: decBitRate > bitRate, using bitRate");
      decBitRate = bitRate;
    }
    if (frameMinNumBit < frameNumBit)
      CommonExit(1,"Decode: scalability not possible for variable bit rate");
    if (bitReservSize != 0)
      CommonExit(1,"Decode: scalability not possible for bit reservoir");
    frameDecNumBit  = (int)(frameNumBit/bitRate*decBitRate+.5);
  }

  if (regionDurat < 0)
    decNumFrame = -1;
  else
    decNumFrame = max(0,(int)(regionDurat*bitRate/frameNumBit));
  decStartFrame = max(0,(int)(regionStart*bitRate/frameNumBit+.5));

  numChannel = (numChannelOut != 0) ? numChannelOut : numChannelOri;
  fSample = (fSampleOut != 0) ? fSampleOut : fSampleOri;

  if(SpeedControlMode) {
    if(numChannel>1) {
      CommonWarning("Decode: "
                    "numChannel>1 : PICOLA speed control is disabled\n");
      SpeedControlMode = 0;
    }
  }
  PicolaNumSample = 0;

  if (mainDebugLevel >= 3) {
    printf("\nmode=%d (3=MODE_TF) \n",mode);
    printf("numChannelOri=%d\n",numChannelOri);
    printf("fSampleOri=%.3f Hz  (int=%ld)\n",fSampleOri,fSampleLong);
    printf("numChannel=%d\n",numChannel);
    printf("fSample=%.3f Hz\n",fSample);
    printf("speedFact=%.3f",speedFact);
    if(SpeedControlMode) {
      printf(" :picola\n");
    }else {
      printf("\n");
    }
    printf("pitchFact=%.3f\n",pitchFact);
    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("frameNumBit=%d  (%.6f sec)\n",frameNumBit,frameNumBit/bitRate);
    printf("frameDecNumBit=%d\n",frameDecNumBit);
    printf("frameMinNumBit=%d\n",frameMinNumBit);
    printf("frameMaxNumBit=%d\n",frameMaxNumBit);
    printf("bitHeaderNumBit=%d\n",bitHeaderNumBit);
    printf("headerNumBit=%d\n",headerNumBit);
    printf("decStartFrame=%d\n",decStartFrame);
    printf("decNumFrame=%d\n",decNumFrame);
  }

  if (testBitStream && frameNumBit != frameMinNumBit)
    CommonExit(1,"Decode: variable bit rate, bit stream test NOT possible!");

  if (regionStart != 0 && frameNumBit != frameMinNumBit)
    CommonExit(1,"Decode: variable bit rate, start time MUST be 0!");

  if (frameMaxNumBit > MAXREADAHEAD)
    CommonExit(1,"Decode: max bit stream frame size too long");

  if (mode<=0 || mode>=MODE_NUM)
    CommonExit(1,"Decode: unknown codec mode %d",mode);
  codecMode = MP4ModeName[mode];

  if (mainDebugLevel >= 3)
    printf("codecMode=\"%s\"\n",codecMode);

#ifdef VERSION2
  hResilience = CreateErrorResilience ( decPara, epFlag );
#endif /*VERSION2*/

  if (!testBitStream) {
    switch (mode) {
    case MODE_PAR:
      if(SpeedControlMode) {
        DecParInit(numChannel,fSample,decBitRate,1,pitchFact,decPara,
                   bitHeader,&frameMaxNumSample,&delayNumSample);
      }else {
        DecParInit(numChannel,fSample,decBitRate,speedFact,pitchFact,decPara,
                   bitHeader,&frameMaxNumSample,&delayNumSample);
      }
      break;
    case MODE_LPC:
      DecLpcInit(numChannel,fSample,decBitRate,decPara,
                 bitHeader,&frameMaxNumSample,&delayNumSample);
      break;
    case MODE_G729:
      DecG729Init(numChannel,fSample,decBitRate,decPara,
                 bitHeader,&frameMaxNumSample,&delayNumSample);
      break;
    case MODE_G723:
      DecG723Init(numChannel,fSample,decBitRate,decPara,
                 bitHeader,&frameMaxNumSample,&delayNumSample);
      break;
    case MODE_TF:
      if (!aacEOF)
        CommonWarning("Decode: option '-aaceof' is recommended in AAC mode");
      DecTfInit ( numChannel,
                  fSample,
                  decBitRate,
                  decPara,
                  bitHeader,
                  &frameMaxNumSample,
                  &delayNumSample,
                  aacDebugString,
                  hResilience,
                  &hVm,
                  &hHcrSpecData,
                  &hHcrInfo,
#ifdef VERSION2
                  infoFileName,
                  predFileName,
#endif
                  (frameData.od == NULL ? NULL : &frameData),
                  &hEPInfo );
      break;
    }
  }     /* if (!testBitStream) */

  /* speed control initialization */
  if(SpeedControlMode)
    PicolaInit(frameMaxNumSample, fSample, speedFact);

  BsFreeBuffer(bitHeader);

  if (!testBitStream) {
    if (mainDebugLevel >= 3) {
      printf("frameMaxNumSample=%d  (%.6f sec/frame)\n",
             frameMaxNumSample,frameMaxNumSample/fSample);
      printf("delayNumSample=%d  (%.6f sec)\n",
             delayNumSample,delayNumSample/fSample);
    }

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

    /* for speed control (PICOLA) */
    if(NULL==(PicolaBuffer=(float **)calloc(numChannel, sizeof(float)))) {
      CommonExit(1, "Decode: memory allocation error");
    }
    for(ch=0;ch<numChannel;ch++) {
      if(NULL==(*(PicolaBuffer+ch)=(float *)calloc(frameMaxNumSample, 
                                                   sizeof(float)))) {
        CommonExit(1, "Decode: memory allocation error");
      }
    }

    /* open audio file */
    /* (sample format: 16 bit twos complement, uniform quantisation) */
    audioFile = AudioOpenWrite(audioFileName,audioFileFormat,
                               numChannel,fSample);
    if (audioFile==NULL)
      CommonExit(1,"Decode: error opening audio file %s "
                 "(maybe unknown format \"%s\")",
                 audioFileName,audioFileFormat);

    /* seek to beginning of first frame (with delay compensation) */
    AudioSeek(audioFile,-delayNumSample);

  }     /* if (!testBitStream) */

  /* process bit stream frame by frame */
  frame = -decStartFrame;
  totNumSample = 0;
  totPadNumBit = 0;
  frameAvailNumBit = frameNumBit+bitReservInit;

#ifdef FHG_DEBUGPLOT
  plotInit();
#endif

  while (!BsEof(bitStream,(aacEOF)?1:frameAvailNumBit-1) &&
         (decNumFrame < 0 || frame < decNumFrame)) {

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

    decodeFrame = !testBitStream && frame >= 0;
    if (decodeFrame) {

#ifdef VERSION2
      if( epFlag == 1 ) {
        ReadFrameData(hEPInfo);
      }
#endif
#ifdef VERSION2
      if( epFlag == 1 ) {
#if 1 /* there must be a bug somewhere...*/
        if (BsGetBufferAheadEP( bitStream,bitBuf,
                                frameDecNumBit ? frameDecNumBit : frameAvailNumBit, hEPInfo ))
            if (aacEOF)
	      CommonWarning("Decode: Warning reading bit stream data");
	    else
	      CommonExit(1,"Decode: error reading bit stream data");
#else
        BsGetBufferAhead(bitStream,bitBuf, bitBuf->size);/* patch : keep the buffer full */
#endif /* bug search */
      } 
      else  
#endif /* VERSION2  */
        {
#if 1 /* there must be a bug somewhere...*/
          if (BsGetBufferAhead(bitStream,bitBuf,
                               frameDecNumBit ? frameDecNumBit : frameAvailNumBit))
            if (aacEOF)
	      CommonWarning("Decode: Warning reading bit stream data");
	    else
	      CommonExit(1,"Decode: error reading bit stream data");
#else
          BsGetBufferAhead(bitStream,bitBuf, bitBuf->size);/* patch : keep the buffer full */
#endif /* bug search */
        }
      /* decode one frame */
      switch (mode) {
      case MODE_PAR:
        DecParFrame(bitBuf,sampleBuf,&usedNumBit,&frameBufNumSample);
        break;
      case MODE_LPC:
        DecLpcFrame(bitBuf,sampleBuf,&usedNumBit);
        frameBufNumSample = frameMaxNumSample;
        break;
      case MODE_G729:
	DecG729Frame(bitBuf,sampleBuf,&usedNumBit,0);
        frameBufNumSample = frameMaxNumSample;
        break;
      case MODE_G723:
        DecG723Frame(bitBuf,sampleBuf,&usedNumBit);
        frameBufNumSample = frameMaxNumSample;
        break;
      case MODE_TF:
        DecTfFrame ( bitBuf,
                     sampleBuf,
                     &usedNumBit,
                     numChannel, 
                     hResilience,
                     hVm,
                     hHcrSpecData,
                     hHcrInfo ,
                     (frameData.od == NULL ? NULL : &frameData),
                     hEPInfo );
        frameBufNumSample = frameMaxNumSample;
        break;
      }
    }
    else	/* if (decodeFrame) */
      usedNumBit = frameNumBit;

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

    if (usedNumBit > frameAvailNumBit)
      CommonWarning("Decode: more bits used than available in frame+buffer");

    /* check for padding bits */
    if (frameAvailNumBit-usedNumBit-frameNumBit+frameMinNumBit
        > bitReservSize) {
      padNumBit = frameAvailNumBit-usedNumBit-frameNumBit+frameMinNumBit
        -bitReservSize;

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

      usedNumBit += padNumBit;
      totPadNumBit += padNumBit;
    }

    /* advance in bit stream */
    if (frameDecNumBit)
      usedNumBit = frameNumBit;
    if (BsGetSkip(bitStream,usedNumBit))
      CommonExit(1,"Decode: error advancing in bit stream data");
    frameAvailNumBit -= usedNumBit;

    /* variable bit rate: don't exceed bit reservoir size */
    if (frameAvailNumBit > bitReservSize)
      frameAvailNumBit = bitReservSize;

    frameAvailNumBit += frameNumBit;

    if (decodeFrame) {
      if(SpeedControlMode) {
        PicolaDecodeFlag = 0;
        PicolaNumInputSample = frameBufNumSample;
        totNumSample += frameBufNumSample;
        
        while(0==PicolaDecodeFlag) {
          mod_picola(sampleBuf[0], PicolaNumInputSample, 
                     PicolaBuffer[0], speedFact, 
                     &PicolaDecodeFlag, &PicolaOutputFlag);
          PicolaNumInputSample = 0;
          if(PicolaOutputFlag) {
            /* write audio file */
            numSample = frameMaxNumSample;
            AudioWriteData(audioFile,PicolaBuffer,numSample);
            PicolaNumSample += numSample;
          }
        }

      }else {
        /* write audio file */
        AudioWriteData(audioFile,sampleBuf,frameBufNumSample);
        totNumSample += frameBufNumSample;
      }
    }	 /* if (decodeFrame) */
#ifdef FHG_DEBUGPLOT
  plotDisplay();
#endif
   
    frame++;
  }     /* while (!BsEof(...)) */

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

  totDataNumBit = BsCurrentBit(bitStream)-headerNumBit-totPadNumBit;

  frameAvailNumBit -= frameNumBit;

  if (mainDebugLevel >= 3) {
    printf("totNumFrame=%d\n",frame);
    printf("totNumBit=%ld\n",BsCurrentBit(bitStream));
    if (!testBitStream) {
      printf("totDataNumBit=%ld  (%.3f bit/frame)\n",
             totDataNumBit,totDataNumBit/(float)frame);
      printf("bitRate=%.3f bit/sec  (%.6f sec/frame)\n",
             bitRate,totDataNumBit/(float)frame/bitRate);
      printf("totPadNumBit=%ld\n",totPadNumBit);
      printf("frameRemainNumBit=%d\n",frameAvailNumBit);
      if(SpeedControlMode) {
        printf("totNumSample=%ld  (%.3f sec)\n",
             PicolaNumSample,PicolaNumSample/fSample);
        printf("Speed Control Ratio: Target=%.3f, Result=%.3f\n", 
            speedFact, totNumSample/(float)PicolaNumSample);
      }else {
        printf("totNumSample=%ld  (%.3f sec)\n",
             totNumSample,totNumSample/fSample);
      }
      printf("delayNumSample=%d  (%.3f sec)\n",
             delayNumSample,delayNumSample/fSample);
      printf("fileNumSample=%ld  (%.3f sec)\n",
             totNumSample-delayNumSample,
             (totNumSample-delayNumSample)/fSample);
      printf("frameAvgNumSample=%.3f  (%.6f sec/frame)\n",
             totNumSample/(float)frame,
             totNumSample/(float)frame/fSample);
    }
  }

  /* free decoder memory */
  switch (mode) {
  case MODE_PAR:
    DecParFree();
    break;
  case MODE_LPC:
    DecLpcFree();
    break;
  case MODE_G729:
    printf("DecG729Free to be defined.\n");
    break;
  case MODE_G723:
    printf("DecG723Free to be defined.\n");
    break;
  case MODE_TF:
    DecTfFree();
    break;
  }

  if(SpeedControlMode)
    PicolaFree();

  /* close bit stream file */
  if (BsClose(bitStream))
    CommonExit(1,"Decode: error closing bit stream file");

  if (!testBitStream) {
    /* close audio file */
    AudioClose(audioFile);
  
    /* free buffers */
    if (numChannel>1)
      for (ch=0; ch<numChannel; ch++)
        free(sampleBuf[ch]);
    free(sampleBuf);
    BsFreeBuffer(bitBuf);

    /* for speed control (PICOLA) */
    if (numChannel>1)
      for (ch=0; ch<numChannel; ch++)
        free(*(PicolaBuffer+ch));
    free(PicolaBuffer);
  }     /* if (!testBitStream) */

  return 0;
}

#ifdef VERSION2

void MakeInfoFileName(char *name, char *infoName) 
{
  int i;
  
  i = 0;
  strcpy(infoName, name);
  while( (infoName[i] != '.') && (infoName[i] != '\0') ) 
    i++;
  if( infoName[i] == '.' ) {
     infoName[i+1] = 'i';
     infoName[i+2] = 'n';
     infoName[i+3] = 'f';
  }
  else {
     strcat(infoName, ".inf");
  }
    
}  

#endif /* VERSION2 */

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

int main (int argc, char *argv[])
{
  char *progName = "<no program name>";
  int  result;
  char bitFileName[STRLEN];
  char decFileName[STRLEN];
  char *decFormat;
  int  fileIdx;

  /* 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);
      DecParInfo(stdout);
      DecLpcInfo(stdout);
      DecTfInfo(stdout);
      DecG729Info(stdout);
      DecG723Info(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",DecParInfo(NULL));
    printf("%s\n",DecLpcInfo(NULL));
    printf("%s\n",DecTfInfo(NULL));
    printf("%s\n",DecG723Info(NULL));   
    printf("%s\n",DecG729Info(NULL));
  }

  CmdLineInit(cmdDebugLevel);

  /* calc variable default values */
  if (!decBitRateUsed)
    decBitRate = 0;
  if (!regionDuratUsed)
    regionDurat = -1;
  if (!bitPathUsed)
    bitPath = getenv(MP4_BIT_PATH_ENV);
  if (!decPathUsed)
    decPath = getenv(MP4_DEC_PATH_ENV);
  if (!decExtUsed)
    decExt = getenv(MP4_DEC_FMT_ENV);
  if (decExt==NULL)
    decExt = MP4_DEC_EXT;

  /* check command line options */
  if (decBitRate < 0)
    CommonExit(1,"decoder bit rate < 0");
  if (numChannelOut < 0)
    CommonExit(1,"number of audio channels < 0");
  if (fSampleOut < 0)
    CommonExit(1,"sampling frequency < 0");
  if (speedFact <= 0)
    CommonExit(1,"speech change factor <= 0");
  if (pitchFact <= 0)
    CommonExit(1,"pitch change factor <= 0");
  if (regionStart < 0)
    CommonExit(1,"start of region < 0");
  if (regionDuratUsed && regionDurat < 0)
    CommonExit(1,"duration  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");
  
  /* process all files on command line */
  fileIdx = 0;
  while (varArgIdx[fileIdx] >= 0) {

    /* compose file names */
    if (ComposeFileName(argv[varArgIdx[fileIdx]],0,bitPath,bitExt,bitFileName,
                        STRLEN))
      CommonExit(1,"composed file name too long");
#ifdef VERSION2
    if ( epFlag )
      MakeInfoFileName(bitFileName, infoFileName);
#endif
    if ( outFileNameUsed ) {
      if (ComposeFileName(outFileName,0,decPath,decExt,decFileName,STRLEN))
        CommonExit(1,"composed file name too long");
    }
    else
      if (ComposeFileName(argv[varArgIdx[fileIdx]],1,decPath,decExt,
                          decFileName,STRLEN))
        CommonExit(1,"composed file name too long");

    /* extract file format (extension) */
    if (strcmp(decFileName,"-") == 0)
      decFormat = decExt;
    else {
      decFormat = decFileName+strlen(decFileName);
      do
        decFormat--;
      while (decFormat>decFileName && *decFormat!='.');
    }

    /* decode file */
    if (mainDebugLevel >= 1)
      printf("decoding %s -> %s\n",bitFileName,decFileName);

    if (Decode(bitFileName,decFileName,decFormat,decBitRate,testBitStream,
               numChannelOut,fSampleOut,epFlag,speedFact,pitchFact,decPara,
               regionStart,regionDurat
#ifdef VERSION2
                ,infoFileName, 
                 predFileName
#endif
        ))
      CommonWarning("error decoding audio file %s",bitFileName);

    fileIdx++;
  }

  CmdLineEvalFree(paraList);

#ifdef COREHACK
/* Use   make all CFLAGS=-DCOREHACK   to enable this hack */
/* It must not be enabled when using   make all TF=0   HP 980212 */
  {
    /* dirty hack for now. What we need is a de-init function for the tf / scalable decoder module */
    extern void CloseCoreDecoder( void );
    CloseCoreDecoder();  /* flush the core bitstream */
  }
#endif

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

  return 0;
}

/* end of mp4dec.c */

