/* ***** BEGIN LICENSE BLOCK *****
    Source last modified: $Id: tns.c,v 1.2 2005/05/24 16:01:55 albertofloyd Exp $

    Portions Copyright (c) 1995-2005 RealNetworks, Inc. All Rights Reserved.

    The contents of this file, and the files included with this file,
    are subject to the current version of the RealNetworks Public
    Source License (the "RPSL") available at
    http://www.helixcommunity.org/content/rpsl unless you have licensed
    the file under the current version of the RealNetworks Community
    Source License (the "RCSL") available at
    http://www.helixcommunity.org/content/rcsl, in which case the RCSL
    will apply. You may also obtain the license terms directly from
    RealNetworks.  You may not use this file except in compliance with
    the RPSL or, if you have a valid RCSL with RealNetworks applicable
    to this file, the RCSL.  Please see the applicable RPSL or RCSL for
    the rights, obligations and limitations governing use of the
    contents of the file.

    This file is part of the Helix DNA Technology. RealNetworks is the
    developer of the Original Code and owns the copyrights in the
    portions it created.

    This file, and the files included with this file, is distributed
    and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
    ENJOYMENT OR NON-INFRINGEMENT.

    Technology Compatibility Kit Test Suite(s) Location:
      http://www.helixcommunity.org/content/tck

    Contributor(s):

 * ***** END LICENSE BLOCK ***** */

/**************************************************************************************
    Fixed-point HE-AAC decoder
    Jon Recker (jrecker@real.com)
    February 2005

    tns.c - apply TNS to spectrum
 **************************************************************************************/

#include "coder.h"
#include "assembly.h"

#define FBITS_LPC_COEFS	20

//fb
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnarrowing"

/*  inverse quantization tables for TNS filter coefficients, format = Q31
    see bottom of file for table generation
    negative (vs. spec) since we use MADD for filter kernel
*/
static const int invQuant3[16] PROGMEM = {
    0x00000000, 0xc8767f65, 0x9becf22c, 0x83358feb, 0x83358feb, 0x9becf22c, 0xc8767f65, 0x00000000,
    0x2bc750e9, 0x5246dd49, 0x6ed9eba1, 0x7e0e2e32, 0x7e0e2e32, 0x6ed9eba1, 0x5246dd49, 0x2bc750e9,
};

static const int invQuant4[16] PROGMEM = {
    0x00000000, 0xe5632654, 0xcbf00dbe, 0xb4c373ee, 0xa0e0a15f, 0x9126145f, 0x8643c7b3, 0x80b381ac,
    0x7f7437ad, 0x7b1d1a49, 0x7294b5f2, 0x66256db2, 0x563ba8aa, 0x4362210e, 0x2e3d2abb, 0x17851aad,
};

#pragma GCC diagnostic pop

/**************************************************************************************
    Function:    DecodeLPCCoefs

    Description: decode LPC coefficients for TNS

    Inputs:      order of TNS filter
                resolution of coefficients (3 or 4 bits)
                coefficients unpacked from bitstream
                scratch buffer (b) of size >= order

    Outputs:     LPC coefficients in Q(FBITS_LPC_COEFS), in 'a'

    Return:      none

    Notes:       assumes no guard bits in input transform coefficients
                a[i] = Q(FBITS_LPC_COEFS), don't store a0 = 1.0
                  (so a[0] = first delay tap, etc.)
                max abs(a[i]) < log2(order), so for max order = 20 a[i] < 4.4
                  (up to 3 bits of gain) so a[i] has at least 31 - FBITS_LPC_COEFS - 3
                  guard bits
                to ensure no intermediate overflow in all-pole filter, set
                  FBITS_LPC_COEFS such that number of guard bits >= log2(max order)
 **************************************************************************************/
static void DecodeLPCCoefs(int order, int res, signed char *filtCoef, int *a, int *b) {
    int i, m, t;
    const int *invQuantTab;

    if (res == 3) {
        invQuantTab = invQuant3;
    } else if (res == 4) {
        invQuantTab = invQuant4;
    } else {
        return;
    }

    for (m = 0; m < order; m++) {
        t = invQuantTab[filtCoef[m] & 0x0f];	/* t = Q31 */
        for (i = 0; i < m; i++) {
            b[i] = a[i] - (MULSHIFT32(t, a[m - i - 1]) << 1);
        }
        for (i = 0; i < m; i++) {
            a[i] = b[i];
        }
        a[m] = t >> (31 - FBITS_LPC_COEFS);
    }
}

/**************************************************************************************
    Function:    FilterRegion

    Description: apply LPC filter to one region of coefficients

    Inputs:      number of transform coefficients in this region
                direction flag (forward = 1, backward = -1)
                order of filter
                'size' transform coefficients
                'order' LPC coefficients in Q(FBITS_LPC_COEFS)
                scratch buffer for history (must be >= order samples long)

    Outputs:     filtered transform coefficients

    Return:      guard bit mask (OR of abs value of all filtered transform coefs)

    Notes:       assumes no guard bits in input transform coefficients
                gains 0 int bits
                history buffer does not need to be preserved between regions
 **************************************************************************************/
static int FilterRegion(int size, int dir, int order, int *audioCoef, int *a, int *hist) {
    int i, j, y, hi32, inc, gbMask;
    U64 sum64;

    /* init history to 0 every time */
    for (i = 0; i < order; i++) {
        hist[i] = 0;
    }

    sum64.w64 = 0;     /* avoid warning */
    gbMask = 0;
    inc = (dir ? -1 : 1);
    do {
        /* sum64 = a0*y[n] = 1.0*y[n] */
        y = *audioCoef;
        sum64.r.hi32 = y >> (32 - FBITS_LPC_COEFS);
        sum64.r.lo32 = y << FBITS_LPC_COEFS;

        /* sum64 += (a1*y[n-1] + a2*y[n-2] + ... + a[order-1]*y[n-(order-1)]) */
        for (j = order - 1; j > 0; j--) {
            sum64.w64 = MADD64(sum64.w64, hist[j], a[j]);
            hist[j] = hist[j - 1];
        }
        sum64.w64 = MADD64(sum64.w64, hist[0], a[0]);
        y = (sum64.r.hi32 << (32 - FBITS_LPC_COEFS)) | (sum64.r.lo32 >> FBITS_LPC_COEFS);

        /* clip output (rare) */
        hi32 = sum64.r.hi32;
        if ((hi32 >> 31) != (hi32 >> (FBITS_LPC_COEFS - 1))) {
            y = (hi32 >> 31) ^ 0x7fffffff;
        }

        hist[0] = y;
        *audioCoef = y;
        audioCoef += inc;
        gbMask |= FASTABS(y);
    } while (--size);

    return gbMask;
}

/**************************************************************************************
    Function:    TNSFilter

    Description: apply temporal noise shaping, if enabled

    Inputs:      valid AACDecInfo struct
                index of current channel

    Outputs:     updated transform coefficients
                updated minimum guard bit count for this channel

    Return:      0 if successful, -1 if error
 **************************************************************************************/
int TNSFilter(AACDecInfo *aacDecInfo, int ch) {
    int win, winLen, nWindows, nSFB, filt, bottom, top, order, maxOrder, dir;
    int start, end, size, tnsMaxBand, numFilt, gbMask;
    int *audioCoef;
    unsigned char *filtLength, *filtOrder, *filtRes, *filtDir;
    signed char *filtCoef;
    const unsigned /*char*/ int *tnsMaxBandTab;
    const /*short*/ int *sfbTab;
    ICSInfo *icsInfo;
    TNSInfo *ti;
    PSInfoBase *psi;

    /* validate pointers */
    if (!aacDecInfo || !aacDecInfo->psInfoBase) {
        return -1;
    }
    psi = (PSInfoBase *)(aacDecInfo->psInfoBase);
    icsInfo = (ch == 1 && psi->commonWin == 1) ? &(psi->icsInfo[0]) : &(psi->icsInfo[ch]);
    ti = &psi->tnsInfo[ch];

    if (!ti->tnsDataPresent) {
        return 0;
    }

    if (icsInfo->winSequence == 2) {
        nWindows = NWINDOWS_SHORT;
        winLen = NSAMPS_SHORT;
        nSFB = sfBandTotalShort[psi->sampRateIdx];
        maxOrder = tnsMaxOrderShort[aacDecInfo->profile];
        sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx];
        tnsMaxBandTab = tnsMaxBandsShort + tnsMaxBandsShortOffset[aacDecInfo->profile];
        tnsMaxBand = tnsMaxBandTab[psi->sampRateIdx];
    } else {
        nWindows = NWINDOWS_LONG;
        winLen = NSAMPS_LONG;
        nSFB = sfBandTotalLong[psi->sampRateIdx];
        maxOrder = tnsMaxOrderLong[aacDecInfo->profile];
        sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx];
        tnsMaxBandTab = tnsMaxBandsLong + tnsMaxBandsLongOffset[aacDecInfo->profile];
        tnsMaxBand = tnsMaxBandTab[psi->sampRateIdx];
    }

    if (tnsMaxBand > icsInfo->maxSFB) {
        tnsMaxBand = icsInfo->maxSFB;
    }

    filtRes =    ti->coefRes;
    filtLength = ti->length;
    filtOrder =  ti->order;
    filtDir =    ti->dir;
    filtCoef =   ti->coef;

    gbMask = 0;
    audioCoef =  psi->coef[ch];
    for (win = 0; win < nWindows; win++) {
        bottom = nSFB;
        numFilt = ti->numFilt[win];
        for (filt = 0; filt < numFilt; filt++) {
            top = bottom;
            bottom = top - *filtLength++;
            bottom = MAX(bottom, 0);
            order = *filtOrder++;
            order = MIN(order, maxOrder);

            if (order) {
                start = sfbTab[MIN(bottom, tnsMaxBand)];
                end   = sfbTab[MIN(top, tnsMaxBand)];
                size = end - start;
                if (size > 0) {
                    dir = *filtDir++;
                    if (dir) {
                        start = end - 1;
                    }

                    DecodeLPCCoefs(order, filtRes[win], filtCoef, psi->tnsLPCBuf, psi->tnsWorkBuf);
                    gbMask |= FilterRegion(size, dir, order, audioCoef + start, psi->tnsLPCBuf, psi->tnsWorkBuf);
                }
                filtCoef += order;
            }
        }
        audioCoef += winLen;
    }

    /* update guard bit count if necessary */
    size = CLZ(gbMask) - 1;
    if (psi->gbCurrent[ch] > size) {
        psi->gbCurrent[ch] = size;
    }

    return 0;
}

/*	 Code to generate invQuantXXX[] tables
    {
      int res, i, t;
      double powScale, iqfac, iqfac_m, d;

      powScale = pow(2.0, 31) * -1.0;	/ ** make coefficients negative for using MADD in kernel ** /
      for (res = 3; res <= 4; res++) {
        iqfac =   ( ((1 << (res-1)) - 0.5) * (2.0 / M_PI) );
        iqfac_m = ( ((1 << (res-1)) + 0.5) * (2.0 / M_PI) );
        printf("static const int invQuant%d[16] = {\n", res);
        for (i = 0; i < 16; i++) {
          / ** extend bottom 4 bits into signed, 2's complement number ** /
          t = (i << 28) >> 28;

          if (t >= 0)   d = sin(t / iqfac);
          else          d = sin(t / iqfac_m);

          d *= powScale;
          printf("0x%08x, ", (int)(d > 0 ? d + 0.5 : d - 0.5));
          if ((i & 0x07) == 0x07)
             printf("\n");
        }
        printf("};\n\n");
 	  }
    }
*/

