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

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.11 $
  $Date: 1998/05/05 15:47:44 $ (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"
#include "nok_ltp_common.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:	-  */

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

/*
  Function:	buffer2freq
  Call:		buffer2freq (p_in_data, p_out_mdct, p_overlap, block_type,
  			     wfun_select, nlong, nmed, nshort, overlap_select,
			     num_short_win, save_window);
  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:	-  */

/*
  Function:	freq2buffer
  Call:		freq2buffer (p_in_data, p_out_data, p_overlap, block_type,
  			     nlong, nmed, nshort, wfun_select, fun_select_prev,
			     overlap_select, num_short_win);
  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:	-  */

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


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


/**************************************************************************
  Internal Objects
  *************************************************************************/
#include "nok_ltp_common_internal.h"

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:
    case LONG_MEDIUM_WINDOW:
    case MEDIUM_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_SQ_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,
                     HANDLE_RESILIENCE hResilience,
                     HANDLE_BUFFER     hVm,
                     HANDLE_EP_INFO    hEPInfo)
{
  int i, j, last_band;
  int prev_subblock;
  

  if ((sbk_prediction_used[0] = GetBits ( NOK_DATA,
                                          LEN_LTP_DATA_PRESENT,
                                          hResilience,
                                          hVm, 
                                          hEPInfo )))
    {
      delay[0] = GetBits ( NOK_DATA,
                           LEN_LTP_LAG,
                           hResilience,
                           hVm, 
                           hEPInfo );
      *weight = codebook[GetBits ( NOK_DATA,
                                   LEN_LTP_COEF,
                                   hResilience,
                                   hVm, 
                                   hEPInfo )];
    
      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 ( NOK_DATA,
                                               LEN_LTP_LONG_USED,
                                               hResilience,
                                               hVm, 
                                               hEPInfo );
	  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 ( NOK_DATA,
                                                        LEN_LTP_SHORT_USED,
                                                        hResilience,
                                                        hVm, 
                                                        hEPInfo )))
		{
		  if(prev_subblock == -1)
		    {
		      delay[i] = delay[0];
		      prev_subblock = i;
		    }
		  else
		    {
		      if (GetBits (  NOK_DATA,
                                     LEN_LTP_SHORT_LAG_PRESENT,
                                     hResilience,
                                     hVm, 
                                     hEPInfo ))
			{
			  delay[i] = GetBits (  NOK_DATA,
                                                LEN_LTP_SHORT_LAG,
                                                hResilience,
                                                hVm, 
                                                hEPInfo );
			  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");
	}
    }
}
