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

       iLBC Speech Coder ANSI-C Source Code

       iLBC_encode.c

       Copyright (C) The Internet Society (2004).
       All Rights Reserved.

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

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

#include "iLBC_define.h"
#include "LPCencode.h"
#include "FrameClassify.h"
#include "StateSearchW.h"
#include "StateConstructW.h"
#include "helpfun.h"
#include "constants.h"
#include "packing.h"
#include "iCBSearch.h"
#include "iCBConstruct.h"
#include "hpInput.h"
#include "anaFilter.h"
#include "syntFilter.h"

   /*----------------------------------------------------------------*
    *  Initiation of encoder instance.
    *---------------------------------------------------------------*/

short initEncode(               /* (o) Number of bytes
                                   encoded */
  iLBC_Enc_Inst_t * iLBCenc_inst,       /* (i/o) Encoder instance */
  int mode                      /* (i) frame size mode */
  )
{
  iLBCenc_inst->mode = mode;
  if (mode == 30) {
    iLBCenc_inst->blockl = BLOCKL_30MS;
    iLBCenc_inst->nsub = NSUB_30MS;
    iLBCenc_inst->nasub = NASUB_30MS;
    iLBCenc_inst->lpc_n = LPC_N_30MS;
    iLBCenc_inst->no_of_bytes = NO_OF_BYTES_30MS;
    iLBCenc_inst->no_of_words = NO_OF_WORDS_30MS;





    iLBCenc_inst->state_short_len = STATE_SHORT_LEN_30MS;
    /* ULP init */
    iLBCenc_inst->ULP_inst = &ULP_30msTbl;
  } else if (mode == 20) {
    iLBCenc_inst->blockl = BLOCKL_20MS;
    iLBCenc_inst->nsub = NSUB_20MS;
    iLBCenc_inst->nasub = NASUB_20MS;
    iLBCenc_inst->lpc_n = LPC_N_20MS;
    iLBCenc_inst->no_of_bytes = NO_OF_BYTES_20MS;
    iLBCenc_inst->no_of_words = NO_OF_WORDS_20MS;
    iLBCenc_inst->state_short_len = STATE_SHORT_LEN_20MS;
    /* ULP init */
    iLBCenc_inst->ULP_inst = &ULP_20msTbl;
  } else {
    exit(2);
  }

  memset((*iLBCenc_inst).anaMem, 0, LPC_FILTERORDER * sizeof(float));
  memcpy((*iLBCenc_inst).lsfold, lsfmeanTbl,
    LPC_FILTERORDER * sizeof(float));
  memcpy((*iLBCenc_inst).lsfdeqold, lsfmeanTbl,
    LPC_FILTERORDER * sizeof(float));
  memset((*iLBCenc_inst).lpc_buffer, 0,
    (LPC_LOOKBACK + BLOCKL_MAX) * sizeof(float));
  memset((*iLBCenc_inst).hpimem, 0, 4 * sizeof(float));

  return (iLBCenc_inst->no_of_bytes);
}

   /*----------------------------------------------------------------*
    *  main encoder function
    *---------------------------------------------------------------*/

void iLBC_encode(unsigned char *bytes,  /* (o) encoded data bits iLBC */
  float *block,                 /* (o) speech vector to
                                   encode */
  iLBC_Enc_Inst_t * iLBCenc_inst        /* (i/o) the general encoder
                                           state */
  )
{

  float data[BLOCKL_MAX];
  float residual[BLOCKL_MAX], reverseResidual[BLOCKL_MAX];

  int start, idxForMax, idxVec[STATE_LEN];





  float reverseDecresidual[BLOCKL_MAX], mem[CB_MEML];
  int n, k, meml_gotten, Nfor, Nback, i, pos;
  int gain_index[CB_NSTAGES * NASUB_MAX], extra_gain_index[CB_NSTAGES];
  int cb_index[CB_NSTAGES * NASUB_MAX], extra_cb_index[CB_NSTAGES];
  int lsf_i[LSF_NSPLIT * LPC_N_MAX];
  unsigned char *pbytes;
  int diff, start_pos, state_first;
  float en1, en2;
  int index, ulp, firstpart;
  int subcount, subframe;
  float weightState[LPC_FILTERORDER];
  float syntdenum[NSUB_MAX * (LPC_FILTERORDER + 1)];
  float weightdenum[NSUB_MAX * (LPC_FILTERORDER + 1)];
  float decresidual[BLOCKL_MAX];

  /* high pass filtering of input signal if such is not done
     prior to calling this function */

  hpInput(block, iLBCenc_inst->blockl, data, (*iLBCenc_inst).hpimem);

  /* otherwise simply copy */

  /*memcpy(data,block,iLBCenc_inst->blockl*sizeof(float)); */

  /* LPC of hp filtered input data */

  LPCencode(syntdenum, weightdenum, lsf_i, data, iLBCenc_inst);


  /* inverse filter to get residual */

  for (n = 0; n < iLBCenc_inst->nsub; n++) {
    anaFilter(&data[n * SUBL], &syntdenum[n * (LPC_FILTERORDER + 1)],
      SUBL, &residual[n * SUBL], iLBCenc_inst->anaMem);
  }

  /* find state location */

  start = FrameClassify(iLBCenc_inst, residual);

  /* check if state should be in first or last part of the
     two subframes */

  diff = STATE_LEN - iLBCenc_inst->state_short_len;
  en1 = 0;
  index = (start - 1) * SUBL;





  for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
    en1 += residual[index + i] * residual[index + i];
  }
  en2 = 0;
  index = (start - 1) * SUBL + diff;
  for (i = 0; i < iLBCenc_inst->state_short_len; i++) {
    en2 += residual[index + i] * residual[index + i];
  }


  if (en1 > en2) {
    state_first = 1;
    start_pos = (start - 1) * SUBL;
  } else {
    state_first = 0;
    start_pos = (start - 1) * SUBL + diff;
  }

  /* scalar quantization of state */

  StateSearchW(iLBCenc_inst, &residual[start_pos],
    &syntdenum[(start - 1) * (LPC_FILTERORDER + 1)],
    &weightdenum[(start - 1) * (LPC_FILTERORDER + 1)], &idxForMax,
    idxVec, iLBCenc_inst->state_short_len, state_first);

  StateConstructW(idxForMax, idxVec,
    &syntdenum[(start - 1) * (LPC_FILTERORDER + 1)],
    &decresidual[start_pos], iLBCenc_inst->state_short_len);

  /* predictive quantization in state */

  if (state_first) {            /* put adaptive part in the end */

    /* setup memory */

    memset(mem, 0,
      (CB_MEML - iLBCenc_inst->state_short_len) * sizeof(float));
    memcpy(mem + CB_MEML - iLBCenc_inst->state_short_len,
      decresidual + start_pos,
      iLBCenc_inst->state_short_len * sizeof(float));
    memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

    /* encode sub-frames */

    iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
      &residual[start_pos + iLBCenc_inst->state_short_len],
      mem + CB_MEML - stMemLTbl,
      stMemLTbl, diff, CB_NSTAGES,
      &weightdenum[start * (LPC_FILTERORDER + 1)], weightState, 0);

    /* construct decoded vector */

    iCBConstruct(&decresidual[start_pos +
        iLBCenc_inst->state_short_len], extra_cb_index,
      extra_gain_index, mem + CB_MEML - stMemLTbl, stMemLTbl, diff,
      CB_NSTAGES);

  } else {                      /* put adaptive part in the beginning */

    /* create reversed vectors for prediction */

    for (k = 0; k < diff; k++) {
      reverseResidual[k] = residual[(start + 1) * SUBL - 1
        - (k + iLBCenc_inst->state_short_len)];
    }

    /* setup memory */

    meml_gotten = iLBCenc_inst->state_short_len;
    for (k = 0; k < meml_gotten; k++) {
      mem[CB_MEML - 1 - k] = decresidual[start_pos + k];
    }
    memset(mem, 0, (CB_MEML - k) * sizeof(float));
    memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

    /* encode sub-frames */

    iCBSearch(iLBCenc_inst, extra_cb_index, extra_gain_index,
      reverseResidual, mem + CB_MEML - stMemLTbl, stMemLTbl,
      diff, CB_NSTAGES,
      &weightdenum[(start - 1) * (LPC_FILTERORDER + 1)],
      weightState, 0);

    /* construct decoded vector */

    iCBConstruct(reverseDecresidual, extra_cb_index,
      extra_gain_index, mem + CB_MEML - stMemLTbl, stMemLTbl,
      diff, CB_NSTAGES);

    /* get decoded residual from reversed vector */

    for (k = 0; k < diff; k++) {
      decresidual[start_pos - 1 - k] = reverseDecresidual[k];





    }
  }

  /* counter for predicted sub-frames */

  subcount = 0;

  /* forward prediction of sub-frames */

  Nfor = iLBCenc_inst->nsub - start - 1;


  if (Nfor > 0) {

    /* setup memory */

    memset(mem, 0, (CB_MEML - STATE_LEN) * sizeof(float));
    memcpy(mem + CB_MEML - STATE_LEN, decresidual + (start - 1) * SUBL,
      STATE_LEN * sizeof(float));
    memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

    /* loop over sub-frames to encode */

    for (subframe = 0; subframe < Nfor; subframe++) {

      /* encode sub-frame */

      iCBSearch(iLBCenc_inst, cb_index + subcount * CB_NSTAGES,
        gain_index + subcount * CB_NSTAGES,
        &residual[(start + 1 + subframe) * SUBL],
        mem + CB_MEML - memLfTbl[subcount],
        memLfTbl[subcount], SUBL, CB_NSTAGES,
        &weightdenum[(start + 1 + subframe) *
          (LPC_FILTERORDER + 1)], weightState, subcount + 1);

      /* construct decoded vector */

      iCBConstruct(&decresidual[(start + 1 + subframe) * SUBL],
        cb_index + subcount * CB_NSTAGES,
        gain_index + subcount * CB_NSTAGES,
        mem + CB_MEML - memLfTbl[subcount],
        memLfTbl[subcount], SUBL, CB_NSTAGES);

      /* update memory */

      memcpy(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(float));
      memcpy(mem + CB_MEML - SUBL,
        &decresidual[(start + 1 + subframe) * SUBL],
        SUBL * sizeof(float));
      memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

      subcount++;
    }
  }


  /* backward prediction of sub-frames */

  Nback = start - 1;


  if (Nback > 0) {

    /* create reverse order vectors */

    for (n = 0; n < Nback; n++) {
      for (k = 0; k < SUBL; k++) {
        reverseResidual[n * SUBL + k] =
          residual[(start - 1) * SUBL - 1 - n * SUBL - k];
        reverseDecresidual[n * SUBL + k] =
          decresidual[(start - 1) * SUBL - 1 - n * SUBL - k];
      }
    }

    /* setup memory */

    meml_gotten = SUBL * (iLBCenc_inst->nsub + 1 - start);


    if (meml_gotten > CB_MEML) {
      meml_gotten = CB_MEML;
    }
    for (k = 0; k < meml_gotten; k++) {
      mem[CB_MEML - 1 - k] = decresidual[(start - 1) * SUBL + k];
    }
    memset(mem, 0, (CB_MEML - k) * sizeof(float));
    memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

    /* loop over sub-frames to encode */

    for (subframe = 0; subframe < Nback; subframe++) {

      /* encode sub-frame */

      iCBSearch(iLBCenc_inst, cb_index + subcount * CB_NSTAGES,
        gain_index + subcount * CB_NSTAGES,
        &reverseResidual[subframe * SUBL],
        mem + CB_MEML - memLfTbl[subcount],
        memLfTbl[subcount], SUBL, CB_NSTAGES,
        &weightdenum[(start - 2 - subframe) *
          (LPC_FILTERORDER + 1)], weightState, subcount + 1);

      /* construct decoded vector */

      iCBConstruct(&reverseDecresidual[subframe * SUBL],
        cb_index + subcount * CB_NSTAGES,
        gain_index + subcount * CB_NSTAGES,
        mem + CB_MEML - memLfTbl[subcount],
        memLfTbl[subcount], SUBL, CB_NSTAGES);

      /* update memory */

      memcpy(mem, mem + SUBL, (CB_MEML - SUBL) * sizeof(float));
      memcpy(mem + CB_MEML - SUBL,
        &reverseDecresidual[subframe * SUBL], SUBL * sizeof(float));
      memset(weightState, 0, LPC_FILTERORDER * sizeof(float));

      subcount++;

    }

    /* get decoded residual from reversed vector */

    for (i = 0; i < SUBL * Nback; i++) {
      decresidual[SUBL * Nback - i - 1] = reverseDecresidual[i];
    }
  }
  /* end encoding part */

  /* adjust index */
  index_conv_enc(cb_index);

  /* pack bytes */

  pbytes = bytes;
  pos = 0;

  /* loop over the 3 ULP classes */

  for (ulp = 0; ulp < 3; ulp++) {






    /* LSF */
    for (k = 0; k < LSF_NSPLIT * iLBCenc_inst->lpc_n; k++) {
      packsplit(&lsf_i[k], &firstpart, &lsf_i[k],
        iLBCenc_inst->ULP_inst->lsf_bits[k][ulp],
        iLBCenc_inst->ULP_inst->lsf_bits[k][ulp] +
        iLBCenc_inst->ULP_inst->lsf_bits[k][ulp + 1] +
        iLBCenc_inst->ULP_inst->lsf_bits[k][ulp + 2]);
      dopack(&pbytes, firstpart,
        iLBCenc_inst->ULP_inst->lsf_bits[k][ulp], &pos);
    }

    /* Start block info */

    packsplit(&start, &firstpart, &start,
      iLBCenc_inst->ULP_inst->start_bits[ulp],
      iLBCenc_inst->ULP_inst->start_bits[ulp] +
      iLBCenc_inst->ULP_inst->start_bits[ulp + 1] +
      iLBCenc_inst->ULP_inst->start_bits[ulp + 2]);
    dopack(&pbytes, firstpart,
      iLBCenc_inst->ULP_inst->start_bits[ulp], &pos);

    packsplit(&state_first, &firstpart, &state_first,
      iLBCenc_inst->ULP_inst->startfirst_bits[ulp],
      iLBCenc_inst->ULP_inst->startfirst_bits[ulp] +
      iLBCenc_inst->ULP_inst->startfirst_bits[ulp + 1] +
      iLBCenc_inst->ULP_inst->startfirst_bits[ulp + 2]);
    dopack(&pbytes, firstpart,
      iLBCenc_inst->ULP_inst->startfirst_bits[ulp], &pos);

    packsplit(&idxForMax, &firstpart, &idxForMax,
      iLBCenc_inst->ULP_inst->scale_bits[ulp],
      iLBCenc_inst->ULP_inst->scale_bits[ulp] +
      iLBCenc_inst->ULP_inst->scale_bits[ulp + 1] +
      iLBCenc_inst->ULP_inst->scale_bits[ulp + 2]);
    dopack(&pbytes, firstpart,
      iLBCenc_inst->ULP_inst->scale_bits[ulp], &pos);

    for (k = 0; k < iLBCenc_inst->state_short_len; k++) {
      packsplit(idxVec + k, &firstpart, idxVec + k,
        iLBCenc_inst->ULP_inst->state_bits[ulp],
        iLBCenc_inst->ULP_inst->state_bits[ulp] +
        iLBCenc_inst->ULP_inst->state_bits[ulp + 1] +
        iLBCenc_inst->ULP_inst->state_bits[ulp + 2]);
      dopack(&pbytes, firstpart,
        iLBCenc_inst->ULP_inst->state_bits[ulp], &pos);
    }






    /* 23/22 (20ms/30ms) sample block */

    for (k = 0; k < CB_NSTAGES; k++) {
      packsplit(extra_cb_index + k, &firstpart,
        extra_cb_index + k,
        iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp],
        iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp] +
        iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp + 1] +
        iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp + 2]);
      dopack(&pbytes, firstpart,
        iLBCenc_inst->ULP_inst->extra_cb_index[k][ulp], &pos);
    }

    for (k = 0; k < CB_NSTAGES; k++) {
      packsplit(extra_gain_index + k, &firstpart,
        extra_gain_index + k,
        iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp],
        iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp] +
        iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp + 1] +
        iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp + 2]);
      dopack(&pbytes, firstpart,
        iLBCenc_inst->ULP_inst->extra_cb_gain[k][ulp], &pos);
    }

    /* The two/four (20ms/30ms) 40 sample sub-blocks */

    for (i = 0; i < iLBCenc_inst->nasub; i++) {
      for (k = 0; k < CB_NSTAGES; k++) {
        packsplit(cb_index + i * CB_NSTAGES + k, &firstpart,
          cb_index + i * CB_NSTAGES + k,
          iLBCenc_inst->ULP_inst->cb_index[i][k][ulp],
          iLBCenc_inst->ULP_inst->cb_index[i][k][ulp] +
          iLBCenc_inst->ULP_inst->cb_index[i][k][ulp + 1] +
          iLBCenc_inst->ULP_inst->cb_index[i][k][ulp + 2]);
        dopack(&pbytes, firstpart,
          iLBCenc_inst->ULP_inst->cb_index[i][k][ulp], &pos);
      }
    }

    for (i = 0; i < iLBCenc_inst->nasub; i++) {
      for (k = 0; k < CB_NSTAGES; k++) {
        packsplit(gain_index + i * CB_NSTAGES + k, &firstpart,
          gain_index + i * CB_NSTAGES + k,
          iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp],
          iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp] +
          iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp + 1] +
          iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp + 2]);
        dopack(&pbytes, firstpart,
          iLBCenc_inst->ULP_inst->cb_gain[i][k][ulp], &pos);
      }
    }
  }

  /* set the last bit to zero (otherwise the decoder
     will treat it as a lost frame) */
  dopack(&pbytes, 0, 1, &pos);
}
