/*
 ** Copyright 2003-2010, VisualOn, Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
/*******************************************************************************
	File:		psy_configuration.c

	Content:	Psychoaccoustic configuration functions

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

#include "basic_op.h"
#include "oper_32b.h"
#include "psy_configuration.h"
#include "adj_thr.h"
#include "aac_rom.h"



#define BARC_SCALE	100 /* integer barc values are scaled with 100 */
#define LOG2_1000	301 /* log2*1000 */
#define PI2_1000	1571 /* pi/2*1000*/
#define ATAN_COEF1	3560 /* 1000/0.280872f*/
#define ATAN_COEF2	281 /* 1000*0.280872f*/


typedef struct{
  Word32 sampleRate;
  const UWord8 *paramLong;
  const UWord8 *paramShort;
}SFB_INFO_TAB;

static const Word16 ABS_LEV = 20;
static const Word16 BARC_THR_QUIET[] = {15, 10,  7,  2,  0,  0,  0,  0,  0,  0,
                                         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
                                         3,  5, 10, 20, 30};



static const Word16 max_bark = 24; /* maximum bark-value */
static const Word16 maskLow  = 30; /* in 1dB/bark */
static const Word16 maskHigh = 15; /* in 1*dB/bark */
static const Word16 c_ratio  = 0x0029; /* pow(10.0f, -(29.0f/10.0f)) */

static const Word16 maskLowSprEnLong = 30;       /* in 1dB/bark */
static const Word16 maskHighSprEnLong = 20;      /* in 1dB/bark */
static const Word16 maskHighSprEnLongLowBr = 15; /* in 1dB/bark */
static const Word16 maskLowSprEnShort = 20;      /* in 1dB/bark */
static const Word16 maskHighSprEnShort = 15;     /* in 1dB/bark */
static const Word16 c_minRemainingThresholdFactor = 0x0148;    /* 0.01 *(1 << 15)*/
static const Word32 c_maxsnr = 0x66666666;		 /* upper limit is -1 dB */
static const Word32 c_minsnr = 0x00624dd3;		 /* lower limit is -25 dB */

static const Word32 c_maxClipEnergyLong = 0x77359400;  /* 2.0e9f*/
static const Word32 c_maxClipEnergyShort = 0x01dcd650; /* 2.0e9f/(AACENC_TRANS_FAC*AACENC_TRANS_FAC)*/


Word32 GetSRIndex(Word32 sampleRate)
{
    if (92017 <= sampleRate) return 0;
    if (75132 <= sampleRate) return 1;
    if (55426 <= sampleRate) return 2;
    if (46009 <= sampleRate) return 3;
    if (37566 <= sampleRate) return 4;
    if (27713 <= sampleRate) return 5;
    if (23004 <= sampleRate) return 6;
    if (18783 <= sampleRate) return 7;
    if (13856 <= sampleRate) return 8;
    if (11502 <= sampleRate) return 9;
    if (9391 <= sampleRate) return 10;

    return 11;
}


/*********************************************************************************
*
* function name: atan_1000
* description:  calculates 1000*atan(x/1000)
*               based on atan approx for x > 0
*				atan(x) = x/((float)1.0f+(float)0.280872f*x*x)  if x < 1
*						= pi/2 - x/((float)0.280872f +x*x)	    if x >= 1
* return:       1000*atan(x/1000)
*
**********************************************************************************/
static Word16 atan_1000(Word32 val)
{
  Word32 y;


  if(L_sub(val, 1000) < 0) {
    y = extract_l(((1000 * val) / (1000 + ((val * val) / ATAN_COEF1))));
  }
  else {
    y = PI2_1000 - ((1000 * val) / (ATAN_COEF2 + ((val * val) / 1000)));
  }

  return extract_l(y);
}


/*****************************************************************************
*
* function name: BarcLineValue
* description:  Calculates barc value for one frequency line
* returns:      barc value of line * BARC_SCALE
* input:        number of lines in transform, index of line to check, Fs
* output:
*
*****************************************************************************/
static Word16 BarcLineValue(Word16 noOfLines, Word16 fftLine, Word32 samplingFreq)
{
  Word32 center_freq, temp, bvalFFTLine;

  /* center frequency of fft line */
  center_freq = (fftLine * samplingFreq) / (noOfLines << 1);
  temp =  atan_1000((center_freq << 2) / (3*10));
  bvalFFTLine =
    (26600 * atan_1000((center_freq*76) / 100) + 7*temp*temp) / (2*1000*1000 / BARC_SCALE);

  return saturate(bvalFFTLine);
}

/*****************************************************************************
*
* function name: initThrQuiet
* description:  init thredhold in quiet
*
*****************************************************************************/
static void initThrQuiet(Word16  numPb,
                         const Word16 *pbOffset,
                         Word16 *pbBarcVal,
                         Word32 *pbThresholdQuiet) {
  Word16 i;
  Word16 barcThrQuiet;

  for(i=0; i<numPb; i++) {
    Word16 bv1, bv2;


    if (i>0)
      bv1 = (pbBarcVal[i] + pbBarcVal[i-1]) >> 1;
    else
      bv1 = pbBarcVal[i] >> 1;


    if (i < (numPb - 1))
      bv2 = (pbBarcVal[i] + pbBarcVal[i+1]) >> 1;
    else {
      bv2 = pbBarcVal[i];
    }

    bv1 = min((bv1 / BARC_SCALE), max_bark);
    bv2 = min((bv2 / BARC_SCALE), max_bark);

    barcThrQuiet = min(BARC_THR_QUIET[bv1], BARC_THR_QUIET[bv2]);


    /*
      we calculate
      pow(10.0f,(float)(barcThrQuiet - ABS_LEV)*0.1)*(float)ABS_LOW*(pbOffset[i+1] - pbOffset[i]);
    */

    pbThresholdQuiet[i] = pow2_xy((((barcThrQuiet - ABS_LEV) * 100) +
                          LOG2_1000*(14+2*LOG_NORM_PCM)), LOG2_1000) * (pbOffset[i+1] - pbOffset[i]);
  }
}


/*****************************************************************************
*
* function name: initSpreading
* description:  init energy spreading parameter
*
*****************************************************************************/
static void initSpreading(Word16  numPb,
                          Word16 *pbBarcValue,
                          Word16 *pbMaskLoFactor,
                          Word16 *pbMaskHiFactor,
                          Word16 *pbMaskLoFactorSprEn,
                          Word16 *pbMaskHiFactorSprEn,
                          const Word32 bitrate,
                          const Word16 blockType)
{
  Word16 i;
  Word16 maskLowSprEn, maskHighSprEn;


  if (sub(blockType, SHORT_WINDOW) != 0) {
    maskLowSprEn = maskLowSprEnLong;

    if (bitrate > 22000)
      maskHighSprEn = maskHighSprEnLong;
    else
      maskHighSprEn = maskHighSprEnLongLowBr;
  }
  else {
    maskLowSprEn = maskLowSprEnShort;
    maskHighSprEn = maskHighSprEnShort;
  }

  for(i=0; i<numPb; i++) {

    if (i > 0) {
      Word32 dbVal;
      Word16 dbark = pbBarcValue[i] - pbBarcValue[i-1];

      /*
        we calulate pow(10.0f, -0.1*dbVal/BARC_SCALE)
      */
      dbVal = (maskHigh * dbark);
      pbMaskHiFactor[i] = round16(pow2_xy(L_negate(dbVal), (Word32)LOG2_1000));             /* 0.301 log10(2) */

      dbVal = (maskLow * dbark);
      pbMaskLoFactor[i-1] = round16(pow2_xy(L_negate(dbVal),(Word32)LOG2_1000));


      dbVal = (maskHighSprEn * dbark);
      pbMaskHiFactorSprEn[i] =  round16(pow2_xy(L_negate(dbVal),(Word32)LOG2_1000));
      dbVal = (maskLowSprEn * dbark);
      pbMaskLoFactorSprEn[i-1] = round16(pow2_xy(L_negate(dbVal),(Word32)LOG2_1000));
    }
    else {
      pbMaskHiFactor[i] = 0;
      pbMaskLoFactor[numPb-1] = 0;

      pbMaskHiFactorSprEn[i] = 0;
      pbMaskLoFactorSprEn[numPb-1] = 0;
    }
  }

}


/*****************************************************************************
*
* function name: initBarcValues
* description:  init bark value
*
*****************************************************************************/
static void initBarcValues(Word16  numPb,
                           const Word16 *pbOffset,
                           Word16  numLines,
                           Word32  samplingFrequency,
                           Word16 *pbBval)
{
  Word16 i;
  Word16 pbBval0, pbBval1;

  pbBval0 = 0;

  for(i=0; i<numPb; i++){
    pbBval1 = BarcLineValue(numLines, pbOffset[i+1], samplingFrequency);
    pbBval[i] = (pbBval0 + pbBval1) >> 1;
    pbBval0 = pbBval1;
  }
}


/*****************************************************************************
*
* function name: initMinSnr
* description:  calculate min snr parameter
*				minSnr(n) = 1/(2^sfbPemin(n)/w(n) - 1.5)
*
*****************************************************************************/
static void initMinSnr(const Word32  bitrate,
                       const Word32  samplerate,
                       const Word16  numLines,
                       const Word16 *sfbOffset,
                       const Word16 *pbBarcVal,
                       const Word16  sfbActive,
                       Word16       *sfbMinSnr)
{
  Word16 sfb;
  Word16 barcWidth;
  Word16 pePerWindow;
  Word32 pePart;
  Word32 snr;
  Word16 pbVal0, pbVal1, shift;

  /* relative number of active barks */


  pePerWindow = bits2pe(extract_l((bitrate * numLines) / samplerate));

  pbVal0 = 0;

  for (sfb=0; sfb<sfbActive; sfb++) {

    pbVal1 = (pbBarcVal[sfb] << 1) - pbVal0;
    barcWidth = pbVal1 - pbVal0;
    pbVal0 = pbVal1;

    /* allow at least 2.4% of pe for each active barc */
	pePart = ((pePerWindow * 24) * (max_bark * barcWidth)) /
        (pbBarcVal[sfbActive-1] * (sfbOffset[sfb+1] - sfbOffset[sfb]));


    pePart = min(pePart, 8400);
    pePart = max(pePart, 1400);

    /* minSnr(n) = 1/(2^sfbPemin(n)/w(n) - 1.5)*/
	/* we add an offset of 2^16 to the pow functions */
	/* 0xc000 = 1.5*(1 << 15)*/

    snr = pow2_xy((pePart - 16*1000),1000) - 0x0000c000;

    if(snr > 0x00008000)
	{
		shift = norm_l(snr);
		snr = Div_32(0x00008000 << shift, snr << shift);
	}
	else
	{
		snr = 0x7fffffff;
	}

    /* upper limit is -1 dB */
    snr = min(snr, c_maxsnr);
    /* lower limit is -25 dB */
    snr = max(snr, c_minsnr);
    sfbMinSnr[sfb] = round16(snr);
  }

}

/*****************************************************************************
*
* function name: InitPsyConfigurationLong
* description:  init long block psychoacoustic configuration
*
*****************************************************************************/
Word16 InitPsyConfigurationLong(Word32 bitrate,
                                Word32 samplerate,
                                Word16 bandwidth,
                                PSY_CONFIGURATION_LONG *psyConf)
{
  Word32 samplerateindex;
  Word16 sfbBarcVal[MAX_SFB_LONG];
  Word16 sfb;

  /*
    init sfb table
  */
  samplerateindex = GetSRIndex(samplerate);
  psyConf->sfbCnt = sfBandTotalLong[samplerateindex];
  psyConf->sfbOffset = sfBandTabLong + sfBandTabLongOffset[samplerateindex];
  psyConf->sampRateIdx = samplerateindex;

  /*
    calculate barc values for each pb
  */
  initBarcValues(psyConf->sfbCnt,
                 psyConf->sfbOffset,
                 psyConf->sfbOffset[psyConf->sfbCnt],
                 samplerate,
                 sfbBarcVal);

  /*
    init thresholds in quiet
  */
  initThrQuiet(psyConf->sfbCnt,
               psyConf->sfbOffset,
               sfbBarcVal,
               psyConf->sfbThresholdQuiet);

  /*
    calculate spreading function
  */
  initSpreading(psyConf->sfbCnt,
                sfbBarcVal,
                psyConf->sfbMaskLowFactor,
                psyConf->sfbMaskHighFactor,
                psyConf->sfbMaskLowFactorSprEn,
                psyConf->sfbMaskHighFactorSprEn,
                bitrate,
                LONG_WINDOW);

  /*
    init ratio
  */
  psyConf->ratio = c_ratio;

  psyConf->maxAllowedIncreaseFactor = 2;
  psyConf->minRemainingThresholdFactor = c_minRemainingThresholdFactor;    /* 0.01 *(1 << 15)*/

  psyConf->clipEnergy = c_maxClipEnergyLong;
  psyConf->lowpassLine = extract_l((bandwidth<<1) * FRAME_LEN_LONG / samplerate);

  for (sfb = 0; sfb < psyConf->sfbCnt; sfb++) {
    if (sub(psyConf->sfbOffset[sfb], psyConf->lowpassLine) >= 0)
      break;
  }
  psyConf->sfbActive = sfb;

  /*
    calculate minSnr
  */
  initMinSnr(bitrate,
             samplerate,
             psyConf->sfbOffset[psyConf->sfbCnt],
             psyConf->sfbOffset,
             sfbBarcVal,
             psyConf->sfbActive,
             psyConf->sfbMinSnr);


  return(0);
}

/*****************************************************************************
*
* function name: InitPsyConfigurationShort
* description:  init short block psychoacoustic configuration
*
*****************************************************************************/
Word16 InitPsyConfigurationShort(Word32 bitrate,
                                 Word32 samplerate,
                                 Word16 bandwidth,
                                 PSY_CONFIGURATION_SHORT *psyConf)
{
  Word32 samplerateindex;
  Word16 sfbBarcVal[MAX_SFB_SHORT];
  Word16 sfb;
  /*
    init sfb table
  */
  samplerateindex = GetSRIndex(samplerate);
  psyConf->sfbCnt = sfBandTotalShort[samplerateindex];
  psyConf->sfbOffset = sfBandTabShort + sfBandTabShortOffset[samplerateindex];
  psyConf->sampRateIdx = samplerateindex;
  /*
    calculate barc values for each pb
  */
  initBarcValues(psyConf->sfbCnt,
                 psyConf->sfbOffset,
                 psyConf->sfbOffset[psyConf->sfbCnt],
                 samplerate,
                 sfbBarcVal);

  /*
    init thresholds in quiet
  */
  initThrQuiet(psyConf->sfbCnt,
               psyConf->sfbOffset,
               sfbBarcVal,
               psyConf->sfbThresholdQuiet);

  /*
    calculate spreading function
  */
  initSpreading(psyConf->sfbCnt,
                sfbBarcVal,
                psyConf->sfbMaskLowFactor,
                psyConf->sfbMaskHighFactor,
                psyConf->sfbMaskLowFactorSprEn,
                psyConf->sfbMaskHighFactorSprEn,
                bitrate,
                SHORT_WINDOW);

  /*
    init ratio
  */
  psyConf->ratio = c_ratio;

  psyConf->maxAllowedIncreaseFactor = 2;
  psyConf->minRemainingThresholdFactor = c_minRemainingThresholdFactor;

  psyConf->clipEnergy = c_maxClipEnergyShort;

  psyConf->lowpassLine = extract_l(((bandwidth << 1) * FRAME_LEN_SHORT) / samplerate);

  for (sfb = 0; sfb < psyConf->sfbCnt; sfb++) {

    if (psyConf->sfbOffset[sfb] >= psyConf->lowpassLine)
      break;
  }
  psyConf->sfbActive = sfb;

  /*
    calculate minSnr
  */
  initMinSnr(bitrate,
             samplerate,
             psyConf->sfbOffset[psyConf->sfbCnt],
             psyConf->sfbOffset,
             sfbBarcVal,
             psyConf->sfbActive,
             psyConf->sfbMinSnr);

  return(0);
}

