/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

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

	iLBC Speech Coder ANSI-C Source Code

        iLBC_test.c

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

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

#include "modules/audio_coding/codecs/ilbc/defines.h"
#include "modules/audio_coding/codecs/ilbc/nit_encode.h"
#include "modules/audio_coding/codecs/ilbc/encode.h"
#include "modules/audio_coding/codecs/ilbc/init_decode.h"
#include "modules/audio_coding/codecs/ilbc/decode.h"
#include "modules/audio_coding/codecs/ilbc/constants.h"
#include "modules/audio_coding/codecs/ilbc/ilbc.h"

#define ILBCNOOFWORDS_MAX (NO_OF_BYTES_30MS)/2

/* Runtime statistics */
#include <time.h>
/* #define CLOCKS_PER_SEC  1000 */

/*----------------------------------------------------------------*
 *  Encoder interface function
 *---------------------------------------------------------------*/

short encode(                         /* (o) Number of bytes encoded */
    IlbcEncoder *iLBCenc_inst,    /* (i/o) Encoder instance */
    int16_t *encoded_data,      /* (o) The encoded bytes */
    int16_t *data               /* (i) The signal block to encode */
                                                        ){

  /* do the actual encoding */
  WebRtcIlbcfix_Encode((uint16_t *)encoded_data, data, iLBCenc_inst);

  return (iLBCenc_inst->no_of_bytes);
}

/*----------------------------------------------------------------*
 *  Decoder interface function
 *---------------------------------------------------------------*/

short decode( /* (o) Number of decoded samples */
    IlbcDecoder *iLBCdec_inst, /* (i/o) Decoder instance */
    short *decoded_data, /* (o) Decoded signal block */
    short *encoded_data, /* (i) Encoded bytes */
    short mode           /* (i) 0=PL, 1=Normal */
              ){

  /* check if mode is valid */

  if (mode<0 || mode>1) {
    printf("\nERROR - Wrong mode - 0, 1 allowed\n"); exit(3);}

  /* do actual decoding of block */

  WebRtcIlbcfix_Decode(decoded_data, (uint16_t *)encoded_data,
                       iLBCdec_inst, mode);

  return (iLBCdec_inst->blockl);
}

/*----------------------------------------------------------------*
 *  Main program to test iLBC encoding and decoding
 *
 *  Usage:
 *		exefile_name.exe <infile> <bytefile> <outfile> <channelfile>
 *
 *---------------------------------------------------------------*/

#define MAXFRAMES   10000
#define MAXFILELEN (BLOCKL_MAX*MAXFRAMES)

int main(int argc, char* argv[])
{

  /* Runtime statistics */

  float starttime1, starttime2;
  float runtime1, runtime2;
  float outtime;

  FILE *ifileid,*efileid,*ofileid, *chfileid;
  short *inputdata, *encodeddata, *decodeddata;
  short *channeldata;
  int blockcount = 0, noOfBlocks=0, i, noOfLostBlocks=0;
  short mode;
  IlbcEncoder Enc_Inst;
  IlbcDecoder Dec_Inst;

  short frameLen;
  short count;
#ifdef SPLIT_10MS
  short size;
#endif

  inputdata=(short*) malloc(MAXFILELEN*sizeof(short));
  if (inputdata==NULL) {
    fprintf(stderr,"Could not allocate memory for vector\n");
    exit(0);
  }
  encodeddata=(short*) malloc(ILBCNOOFWORDS_MAX*MAXFRAMES*sizeof(short));
  if (encodeddata==NULL) {
    fprintf(stderr,"Could not allocate memory for vector\n");
    free(inputdata);
    exit(0);
  }
  decodeddata=(short*) malloc(MAXFILELEN*sizeof(short));
  if (decodeddata==NULL) {
    fprintf(stderr,"Could not allocate memory for vector\n");
    free(inputdata);
    free(encodeddata);
    exit(0);
  }
  channeldata=(short*) malloc(MAXFRAMES*sizeof(short));
  if (channeldata==NULL) {
    fprintf(stderr,"Could not allocate memory for vector\n");
    free(inputdata);
    free(encodeddata);
    free(decodeddata);
    exit(0);
  }

  /* get arguments and open files */

  if (argc != 6 ) {
    fprintf(stderr, "%s mode inputfile bytefile outputfile channelfile\n",
            argv[0]);
    fprintf(stderr, "Example:\n");
    fprintf(stderr, "%s <30,20> in.pcm byte.dat out.pcm T30.0.dat\n", argv[0]);
    exit(1);
  }
  mode=atoi(argv[1]);
  if (mode != 20 && mode != 30) {
    fprintf(stderr,"Wrong mode %s, must be 20, or 30\n", argv[1]);
    exit(2);
  }
  if ( (ifileid=fopen(argv[2],"rb")) == NULL) {
    fprintf(stderr,"Cannot open input file %s\n", argv[2]);
    exit(2);}
  if ( (efileid=fopen(argv[3],"wb")) == NULL) {
    fprintf(stderr, "Cannot open channelfile file %s\n",
            argv[3]); exit(3);}
  if( (ofileid=fopen(argv[4],"wb")) == NULL) {
    fprintf(stderr, "Cannot open output file %s\n",
            argv[4]); exit(3);}
  if ( (chfileid=fopen(argv[5],"rb")) == NULL) {
    fprintf(stderr,"Cannot open channel file file %s\n", argv[5]);
    exit(2);}


  /* print info */
#ifndef PRINT_MIPS
  fprintf(stderr, "\n");
  fprintf(stderr,
          "*---------------------------------------------------*\n");
  fprintf(stderr,
          "*                                                   *\n");
  fprintf(stderr,
          "*      iLBCtest                                     *\n");
  fprintf(stderr,
          "*                                                   *\n");
  fprintf(stderr,
          "*                                                   *\n");
  fprintf(stderr,
          "*---------------------------------------------------*\n");
#ifdef SPLIT_10MS
  fprintf(stderr,"\n10ms split with raw mode: %2d ms\n", mode);
#else
  fprintf(stderr,"\nMode          : %2d ms\n", mode);
#endif
  fprintf(stderr,"\nInput file    : %s\n", argv[2]);
  fprintf(stderr,"Coded file    : %s\n", argv[3]);
  fprintf(stderr,"Output file   : %s\n\n", argv[4]);
  fprintf(stderr,"Channel file  : %s\n\n", argv[5]);
#endif

  /* Initialization */

  WebRtcIlbcfix_EncoderInit(&Enc_Inst, mode);
  WebRtcIlbcfix_DecoderInit(&Dec_Inst, mode, 1);

  /* extract the input file and channel file */

#ifdef SPLIT_10MS
  frameLen = (mode==20)? 80:160;
  fread(Enc_Inst.past_samples, sizeof(short), frameLen, ifileid);
  Enc_Inst.section = 0;

  while( fread(&inputdata[noOfBlocks*80], sizeof(short),
               80, ifileid) == 80 ) {
    noOfBlocks++;
  }

  noOfBlocks += frameLen/80;
  frameLen = 80;
#else
  frameLen = Enc_Inst.blockl;

  while( fread(&inputdata[noOfBlocks*Enc_Inst.blockl],sizeof(short),
               Enc_Inst.blockl,ifileid)==(uint16_t)Enc_Inst.blockl){
    noOfBlocks++;
  }
#endif


  while ((fread(&channeldata[blockcount],sizeof(short), 1,chfileid)==1)
            && ( blockcount < noOfBlocks/(Enc_Inst.blockl/frameLen) )) {
    blockcount++;
  }

  if ( blockcount < noOfBlocks/(Enc_Inst.blockl/frameLen) ) {
    fprintf(stderr,"Channel file %s is too short\n", argv[4]);
    free(inputdata);
    free(encodeddata);
    free(decodeddata);
    free(channeldata);
    exit(0);
  }

  count=0;

  /* Runtime statistics */

  starttime1 = clock()/(float)CLOCKS_PER_SEC;

  /* Encoding loop */
#ifdef PRINT_MIPS
  printf("-1 -1\n");
#endif

#ifdef SPLIT_10MS
  /* "Enc_Inst.section != 0" is to make sure we run through full
     lengths of all vectors for 10ms split mode.
  */
  //   while( (count < noOfBlocks) || (Enc_Inst.section != 0) )    {
  while( count < blockcount * (Enc_Inst.blockl/frameLen) )    {

    encode(&Enc_Inst, &encodeddata[Enc_Inst.no_of_words *
                                   (count/(Enc_Inst.nsub/2))],
           &inputdata[frameLen * count] );
#else
    while (count < noOfBlocks) {
      encode( &Enc_Inst, &encodeddata[Enc_Inst.no_of_words * count],
              &inputdata[frameLen * count] );
#endif

#ifdef PRINT_MIPS
      printf("-1 -1\n");
#endif

      count++;
    }

    count=0;

    /* Runtime statistics */

    starttime2=clock()/(float)CLOCKS_PER_SEC;
    runtime1 = (float)(starttime2-starttime1);

    /* Decoding loop */

    while (count < blockcount) {
      if (channeldata[count]==1) {
        /* Normal decoding */
        decode(&Dec_Inst, &decodeddata[count * Dec_Inst.blockl],
               &encodeddata[Dec_Inst.no_of_words * count], 1);
      } else if (channeldata[count]==0) {
        /* PLC */
        short emptydata[ILBCNOOFWORDS_MAX];
        memset(emptydata, 0, Dec_Inst.no_of_words*sizeof(short));
        decode(&Dec_Inst, &decodeddata[count*Dec_Inst.blockl],
               emptydata, 0);
        noOfLostBlocks++;
      } else {
        printf("Error in channel file (values have to be either 1 or 0)\n");
        exit(0);
      }
#ifdef PRINT_MIPS
      printf("-1 -1\n");
#endif

      count++;
    }

    /* Runtime statistics */

    runtime2 = (float)(clock()/(float)CLOCKS_PER_SEC-starttime2);

    outtime = (float)((float)blockcount*
                      (float)mode/1000.0);

#ifndef PRINT_MIPS
    printf("\nLength of speech file: %.1f s\n", outtime);
    printf("Lost frames          : %.1f%%\n\n", 100*(float)noOfLostBlocks/(float)blockcount);

    printf("Time to run iLBC_encode+iLBC_decode:");
    printf(" %.1f s (%.1f%% of realtime)\n", runtime1+runtime2,
           (100*(runtime1+runtime2)/outtime));

    printf("Time in iLBC_encode                :");
    printf(" %.1f s (%.1f%% of total runtime)\n",
           runtime1, 100.0*runtime1/(runtime1+runtime2));

    printf("Time in iLBC_decode                :");
    printf(" %.1f s (%.1f%% of total runtime)\n\n",
           runtime2, 100.0*runtime2/(runtime1+runtime2));
#endif

    /* Write data to files */
    for (i=0; i<blockcount; i++) {
      fwrite(&encodeddata[i*Enc_Inst.no_of_words], sizeof(short),
             Enc_Inst.no_of_words, efileid);
    }
    for (i=0;i<blockcount;i++) {
      fwrite(&decodeddata[i*Enc_Inst.blockl],sizeof(short),Enc_Inst.blockl,ofileid);
    }

    /* return memory and close files */

    free(inputdata);
    free(encodeddata);
    free(decodeddata);
    free(channeldata);
    fclose(ifileid);  fclose(efileid); fclose(ofileid);
    return(0);
  }