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

This software module was originally developed by
Nokia in the course of development of the MPEG-2 AAC/MPEG-4 
Audio standard ISO/IEC13818-7, 14496-1, 2 and 3.
This software module is an implementation of a part
of one or more MPEG-2 AAC/MPEG-4 Audio tools as specified by the
MPEG-2 aac/MPEG-4 Audio standard. ISO/IEC  gives users of the
MPEG-2aac/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 aac/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, 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 aac/MPEG-4
Audio conforming products. The original developer retains full right to
use the code for the developer's own purpose, assign or donate the code to a
third party and to inhibit third party from using the code for non
MPEG-2 aac/MPEG-4 Audio conforming products. This copyright notice
must be included in all copies or derivative works.
Copyright (c)1997.  

***************************************************************************/
/**************************************************************************
  nok_lt_prediction.c  -  Performs Long Term Prediction for the MPEG-4
  			  T/F Decoder.
  Author(s): Mikko Suonio, Juha Ojanperä
  	     Nokia Research Center, Speech and Audio Systems, 1997.
  *************************************************************************/


/**************************************************************************
  Version Control Information			Method: CVS
  Identifiers:
  $Revision: 1.8 $
  $Date: 1997/11/13 16:50:19 $ (check in)
  $Author: purnhage $
  *************************************************************************/



/**************************************************************************
  External Objects Needed
  *************************************************************************/
/*
  Standard library declarations.  */
#include <stdio.h>

/*
  Interface to related modules.  */
#include "all.h"
#include "tf_main.h"
#include "block.h"

/*
  Macro:	PRINT
  Purpose:	Interface to function for printing error messages.  */

/*
  Enum constant:MAX_SHORT_WINDOWS
  Purpose:	Gives the maximum number of subblocks (short windows).
  Explanation:	-  */

/*
  Purpose:	Provided debug options.
  Explanation:	If element debug['p'] is true, we give debug information
  		on long term prediction.  */
extern int debug[];

/*
  Purpose:	Modified discrete cosine transform
  Input:	p_in_data[]	- input signal (length: 2*shift length)
		p_overlap[]	- buffer containing the previous frame
				  in OVERLAPPED-mode (length: 2*shift length)
		block_type	- selects the type of window to use
  		wfun_select	- select window function
  		nlong		- shift length for long windows
                nmed		- shift length for medium windows
		nshort		- shift length for short windows
		overlap_select	- OVERLAPPED-mode select
		num_short_win	- number of short windows to transform
		save_window	- if true, save the block_type
				  for future use
  Output:	p_out_mdct[]	- transformed signal (length: shift length)
		p_overlap[]	- a copy of p_in_data in OVERLAPPED-mode
  				  (length: shift length)
  Explanation:	-  */
extern void buffer2freq (double p_in_data[], double p_out_mdct[],
			 double p_overlap[], WINDOW_TYPE block_type,
			 Window_shape wfun_select, int nlong, int nmed,
			 int nshort, Mdct_in overlap_select,
			 int num_short_win, int save_window);

/*
  Purpose:	Inverse of modified discrete cosine transform
  Input:	p_in_data[]	- input signal (length: shift length)
		p_overlap[]	- the overlap buffer; does not have
				  an effect in NON_OVERLAPPED-mode,
				  but have to be allocated
				  (length: shift length)
		block_type	- selects the type of window to use
  		nlong		- shift length for long windows
                nmed		- shift length for medium windows
		nshort		- shift length for short windows
  		wfun_select	- select window function
		overlap_select	- OVERLAPPED-mode select
		num_short_win	- number of short windows to transform
 		save_window	- if true, save block_type for future use
  Output:	p_out_data[]	- transformed signal (length: 2*shift length)
		p_overlap[]	- the overlap buffer; always modified
				  (length: shift length)
  Explanation:	-  */
void freq2buffer (double p_in_data[], double p_out_data[], double p_overlap[],
		  WINDOW_TYPE block_type, int nlong, int nmed, int nshort,
		  Window_shape wfun_select, Window_shape wfun_select_prev,
		  Imdct_out overlap_select, int num_short_win);

/*
  Function:	getbits
  Purpose:	Reads specified number of bits from the open
  		input bitstream.  */



/**************************************************************************
  External Objects Provided
  *************************************************************************/
#include "nok_lt_prediction.h"



/**************************************************************************
  Internal Objects
  *************************************************************************/
/*
  Purpose:	Length of the bitstream element ltp_data_present.  */
#define	LEN_LTP_DATA_PRESENT 1

/*
  Purpose:	Length of the bitstream element ltp_lag.  */
#define	LEN_LTP_LAG 11

/*
  Purpose:	Length of the bitstream element ltp_coef.  */
#define	LEN_LTP_COEF 3

/*
  Purpose:	Length of the bitstream element ltp_short_used.  */
#define	LEN_LTP_SHORT_USED 1

/*
  Purpose:	Length of the bitstream element ltp_short_lag_present.  */
#define	LEN_LTP_SHORT_LAG_PRESENT 1

/*
  Purpose:	Length of the bitstream element ltp_short_lag.  */
#define	LEN_LTP_SHORT_LAG 5

/*
  Purpose:	Offset of the lags written in the bitstream.  */
#define	NOK_LTP_LAG_OFFSET 16

/*
  Purpose:	Length of the bitstream element ltp_long_used.  */
#define	LEN_LTP_LONG_USED 1

/*
  Purpose:	Upper limit for the number of scalefactor bands
   		which can use lt prediction with long windows.
  Explanation:	Bands 0..NOK_MAX_LT_PRED_SFB-1 can use lt prediction.  */
#define	NOK_MAX_LT_PRED_LONG_SFB 40

/*
  Purpose:	Upper limit for the number of scalefactor bands
   		which can use lt prediction with short windows.
  Explanation:	Bands 0..NOK_MAX_LT_PRED_SFB-1 can use lt prediction.  */
#define	NOK_MAX_LT_PRED_SHORT_SFB 13

/*
  Purpose:	Buffer offset which causes overlapping.
  Explanation:	This is only used for a short window sequence.  */
#define	SHORT_SEQ_OFFSET (BLOCK_LEN_LONG-(BLOCK_LEN_SHORT*4+BLOCK_LEN_SHORT/2))

/*
  Purpose:	Number of codes for LTP weight. */
#define CODESIZE 8

/*
  Purpose:	Codebook for LTP weight coefficients.  */
static double codebook[CODESIZE] =
{
  {0.570829},
  {0.696616},
  {0.813004},
  {0.911304},
  {0.984900},
  {1.067894},
  {1.194601},
  {1.369533}
};

static short double_to_int (double sig_in);



/**************************************************************************
  Object Definitions
  *************************************************************************/



/**************************************************************************
  Title:	nok_init_lt_pred

  Purpose:	Initialize the history buffer for long term prediction

  Usage:	nok_init_lt_pred (lt_status)


  Input:	lt_status
  			- buffer: history buffer

  Output:	lt_status
  			- buffer: filled with 0

  References:	-

  Explanation:	-

  Author(s):	Mikko Suonio
  *************************************************************************/
void
nok_init_lt_pred (NOK_LT_PRED_STATUS *lt_status)
{
  int i;

  for (i = 0; i < NOK_LT_BLEN; i++)
    lt_status->buffer[i] = 0;
}



/**************************************************************************
  Title:	nok_lt_prediction

  Purpose:	Performs long term prediction using given coefficients.

  Usage:        nok_lt_predict (profile, info, win_type, win_shape,
  				sbk_prediction_used, sfb_prediction_used,
				lt_status, weight, delay, current_frame)

  Input:	profile	- currently not used	
  		info	- information on the current frame
			  nsbk: number of subblocks (subwindows)
			        in a block (window sequence)
			  bins_per_sbk: number of spectral coefficients
			  		in each subblock; currently
					we assume that they are of
					equal size
			  sfb_per_bk: total number of scalefactor bands
			  	       in a block
			  sfb_per_sbk: number of scalefactor bands
			  	       in each subblock
		win_type
			- window sequence (frame, block) type
		win_shape
			- shape of the mdct window
  		sbk_prediction_used
			- first item toggles prediction on(1)/off(0) for
			  all subblocks, next items toggle it on/off on
			  each subblock separately
  		sfb_prediction_used
			- first item is not used, but the following items
			  toggle prediction on(1)/off(0) on each
			  scalefactor-band of every subblock
		lt_status
			- history buffer for prediction
		weight	- a weight factor to apply to all subblocks
		delay	- array of delays to apply to each subblock
		current_frame	- the dequantized spectral coeffients and
			  prediction errors where prediction is used
		block_size_long
			- size of the long block
		block_size_medium
			- size of the medium block
		block_size_short
			- size of the short block

  Output:	current_frame
  			- the dequantized spectrum possible with a
			  prediction vector added

  References:	1.) buffer2freq
		2.) double_to_int
  		3.) freq2buffer

  Explanation:	-

  Author(s):	Mikko Suonio, Juha Ojanperä
  *************************************************************************/
void
nok_lt_predict (int profile, Info *info, WINDOW_TYPE win_type,
		Window_shape win_shape,
		int *sbk_prediction_used,
		int *sfb_prediction_used, NOK_LT_PRED_STATUS *lt_status,
		Float weight, int *delay, Float *current_frame,
		int block_size_long, int block_size_medium,
		int block_size_short
		)
{
  int i, j, subblock;
  double current_frame_double[NOK_MAX_BLOCK_LEN_LONG];
  double mdct_predicted[2 * NOK_MAX_BLOCK_LEN_LONG];
  double overlap_buffer[2 * NOK_MAX_BLOCK_LEN_LONG];
  double predicted_samples[2 * NOK_MAX_BLOCK_LEN_LONG];
  static Window_shape win_shape_prev[2]={WS_FHG, WS_FHG};	/* YB : 971113 */

  /* printf ("long: %d, med: %d, short: %d.\n", block_size_long,
     block_size_medium, block_size_short); */
  
  switch(win_type)
    {
    case ONLY_LONG_WINDOW:
    case LONG_SHORT_WINDOW:
    case SHORT_LONG_WINDOW:
      if (sbk_prediction_used[0])
	{
	  /* Prediction for time domain signal */
	  j = NOK_LT_BLEN - 2 * block_size_long - delay[0];
	  for (i = 0; i < 2 * block_size_long; i++)
	    predicted_samples[i] = weight * lt_status->buffer[i + j];

	  /* Transform prediction to frequency domain. */
	  buffer2freq (predicted_samples, mdct_predicted, NULL, win_type,
		       win_shape, block_size_long, block_size_medium,
		       block_size_short, NON_OVERLAPPED, info->nsbk, 0);
 
	  /* Clean those sfb's where prediction is not used. */
	  for (i = 0, j = 0; i < info->sfb_per_bk; i++)
	    if (sfb_prediction_used[i + 1] == 0)
	      for (; j < info->sbk_sfb_top[0][i]; j++)
		mdct_predicted[j] = 0.0;
	    else
	      j = info->sbk_sfb_top[0][i];

	  /* Add the prediction to dequantized spectrum. */
	  for (i = 0; i < block_size_long; i++)
	    current_frame[i] = current_frame[i] + mdct_predicted[i];
	}

      for (i = 0; i < block_size_long; i++)
        current_frame_double[i] = current_frame[i];

      /* Finally update the time domain history buffer. */
      freq2buffer (current_frame_double,
		   predicted_samples,
		   overlap_buffer,
		   win_type,
		   block_size_long,
		   block_size_medium,
		   block_size_short,
		   win_shape,
		   win_shape_prev[0],
		   NON_OVERLAPPED,
		   info->nsbk 
                   );

      for (i = 0; i < NOK_LT_BLEN - block_size_long; i++)
	lt_status->buffer[i] = lt_status->buffer[i + block_size_long];

      j = NOK_LT_BLEN - 2 * block_size_long;
      for (i = 0; i < block_size_long; i++)
	{
	  lt_status->buffer[i + j] = double_to_int (predicted_samples[i] + lt_status->buffer[i + j]);
	  lt_status->buffer[NOK_LT_BLEN - block_size_long + i] =
	    double_to_int (predicted_samples[i + block_size_long]);
	}
      break;

    case ONLY_SHORT_WINDOW:      
      /* Prepare the buffer for all forthcoming short windows at once.
         This could be done as the long window case, but that would
         be inefficient. We have to take this into account when
         referencing the buffer.  */
      for (i = 0; i < NOK_LT_BLEN - block_size_long; i++)
	lt_status->buffer[i] = lt_status->buffer[i + block_size_long];

      for (i = NOK_LT_BLEN - block_size_long; i < NOK_LT_BLEN; i++)
	lt_status->buffer[i] = 0;

      for (subblock = 0; subblock < info->nsbk; subblock++)
	{
	  if (sbk_prediction_used[0] && sbk_prediction_used[subblock + 1])
	    { 
	      /* Prediction for time domain signal. The buffer shift
		 causes an additional delay depending on the subblock
		 number.  */
	      j = NOK_LT_BLEN - block_size_long - 2 * block_size_short - delay[subblock] ;
	      for (i = 0; i < 2 * block_size_short; i++)
		predicted_samples[i] = weight * lt_status->buffer[i + j];

	      /* Transform prediction to frequency domain.  */
	      buffer2freq (predicted_samples, mdct_predicted, NULL, win_type,
			   win_shape, block_size_long, block_size_medium,
			   block_size_short, NON_OVERLAPPED, 1, 0);

	      /* Clean those sfb's where prediction is not used.  */
	      for (i = 0, j = 0; i < info->sfb_per_sbk[subblock]; i++)
		if (sfb_prediction_used[subblock * info->sfb_per_sbk[subblock] + i] == 0)
		  for (; j < info->sbk_sfb_top[subblock][i]; j++)
		    mdct_predicted[j] = 0.0;
		else
		  j = info->sbk_sfb_top[subblock][i];

	      /* Add the prediction to dequantized spectrum.  */
	      for (i = 0; i < info->bins_per_sbk[subblock]; i++)
		current_frame[subblock * block_size_short + i] += mdct_predicted[i];
	    }

	  for (i = 0; i < info->bins_per_sbk[subblock]; i++)
	    current_frame_double[i] = current_frame[subblock * block_size_short + i];

	  /* Finally update the time domain history buffer by adding
	     this window.  */
	  freq2buffer (current_frame_double, predicted_samples, overlap_buffer,
		       win_type, block_size_long, block_size_medium,
		       block_size_short, win_shape, win_shape_prev[0], NON_OVERLAPPED, 1);

	  i = NOK_LT_BLEN - 2 * block_size_long + SHORT_SEQ_OFFSET + subblock * block_size_short;
	  for (j = 0; j < 2 * block_size_short; j++)
	    lt_status->buffer[i + j] = double_to_int (predicted_samples[j] + lt_status->buffer[i + j]);
	}
      break;
    default:
      break;
    }
	win_shape_prev[0] = win_shape;

}



/**************************************************************************
  Title:	double_to_int

  Purpose:      Converts floating point format to integer (16-bit).

  Usage:	y = double_to_int(sig_in)

  Input:	sig_in  - floating point number

  Output:	y  - integer number

  References:	-

  Explanation:  -

  Author(s):	Juha Ojanpera
  *************************************************************************/

static short
double_to_int (double sig_in)
{
  short sig_out;


  if (sig_in > 32767)
    sig_out = 32767;
  else if (sig_in < -32768)
    sig_out = -32768;
  else if (sig_in > 0.0)
    sig_out = (short) (sig_in + 0.5);
  else if (sig_in <= 0.0)
    sig_out = (short) (sig_in - 0.5);

  return (sig_out);
}



/**************************************************************************
  Title:	nok_lt_decode

  Purpose:	Decode the bitstream elements for long term prediction

  Usage:	nok_lt_decode (win_type, max_sfb, sbk_prediction_used,
  				sfb_prediction_used, weight, delay)


  Input:	win_type
			- window sequence (frame, block) type
		max_sfb - number of scalefactor bands used in
			  the current frame
  Output:	sbk_prediction_used
			- first item toggles prediction on(1)/off(0) for
			  all subblocks, next items toggle it on/off on
			  each subblock separately
  		sfb_prediction_used
			- first item is not used, but the following items
			  toggle prediction on(1)/off(0) on each
			  scalefactor-band of every subblock
		weight	- a weight factor to apply to all subblocks
		delay	- array of delays to apply to each subblock

  References:	1.) getbits
  		2.) PRINT

  Explanation:	-

  Author(s):	Mikko Suonio
  *************************************************************************/
void
nok_lt_decode (WINDOW_TYPE win_type, int max_sfb, int *sbk_prediction_used,
	       int *sfb_prediction_used, Float *weight, int *delay)
{
  int i, j, last_band;
  int prev_subblock;
  

  if ((sbk_prediction_used[0] = getbits (LEN_LTP_DATA_PRESENT)))
    {
      delay[0] = getbits (LEN_LTP_LAG);
      *weight = codebook[getbits (LEN_LTP_COEF)];
    
      if (win_type != ONLY_SHORT_WINDOW)
	{
	  last_band = (max_sfb < NOK_MAX_LT_PRED_LONG_SFB
		       ? max_sfb : NOK_MAX_LT_PRED_LONG_SFB) + 1;
      
	  sfb_prediction_used[0] = sbk_prediction_used[0];
	  for (i = 1; i < last_band; i++)
	    sfb_prediction_used[i] = getbits (LEN_LTP_LONG_USED);
	  for (; i < max_sfb + 1; i++)
	    sfb_prediction_used[i] = 0;
	}
      else
	{
	  last_band = (max_sfb < NOK_MAX_LT_PRED_SHORT_SFB) ? max_sfb : NOK_MAX_LT_PRED_SHORT_SFB;
      
	  prev_subblock = -1;
	  for (i = 0; i < NSHORT; i++)
	    {
	      if ((sbk_prediction_used[i+1] = getbits (LEN_LTP_SHORT_USED)))
		{
		  if(prev_subblock == -1)
		    {
		      delay[i] = delay[0];
		      prev_subblock = i;
		    }
		  else
		    {
		      if (getbits (LEN_LTP_SHORT_LAG_PRESENT))
			{
			  delay[i] = getbits (LEN_LTP_SHORT_LAG);
			  delay[i] -= NOK_LTP_LAG_OFFSET;
			  delay[i] = delay[prev_subblock] - delay[i];
			}
		      else
			delay[i] = delay[prev_subblock];
		    }
          
		  for (j = 0; j < last_band; j++)
		    sfb_prediction_used[i * last_band + j] = 1;
		}
	    }
	}
    }
  
  if (debug['p'])
    {
      if (sbk_prediction_used[0])
	{
	  PRINT (SE, "long term prediction enabled (%2d):  ", last_band);
	  for (i = 1; i < max_sfb + 1; i++)
	    PRINT (SE, " %d", sfb_prediction_used[i]);
	  PRINT (SE, "\n");
	  for (i = 1; i < MAX_SHORT_WINDOWS + 1; i++)
	    PRINT (SE, " %d", sbk_prediction_used[i]);
	  PRINT (SE, "\n");
	}
    }
}
