/* ***** BEGIN LICENSE BLOCK *****
    Source last modified: $Id: stproc.c,v 1.3 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

    stproc.c - mid-side and intensity stereo processing
 **************************************************************************************/

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

/*  pow14[0][i] = -pow(2, i/4.0)
    pow14[1][i] = +pow(2, i/4.0)

    i = [0,1,2,3]
    format = Q30
*/


/**************************************************************************************
    Function:    StereoProcessGroup

    Description: apply mid-side and intensity stereo to group of transform coefficients

    Inputs:      dequantized transform coefficients for both channels
                pointer to appropriate scalefactor band table
                mid-side mask enabled flag
                buffer with mid-side mask (one bit for each scalefactor band)
                bit offset into mid-side mask buffer
                max coded scalefactor band
                buffer of codebook indices for right channel
                buffer of scalefactors for right channel, range = [0, 256]

    Outputs:     updated transform coefficients in Q(FBITS_OUT_DQ_OFF)
                updated minimum guard bit count for both channels

    Return:      none

    Notes:       assume no guard bits in input
                gains 0 int bits
 **************************************************************************************/
static void StereoProcessGroup(int *coefL, int *coefR, const /*short*/ int *sfbTab,
                               int msMaskPres, unsigned char *msMaskPtr, int msMaskOffset, int maxSFB,
                               unsigned char *cbRight, short *sfRight, int *gbCurrent) {
    //fb
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnarrowing"
    static const int pow14[2][4] PROGMEM = {
        { 0xc0000000, 0xb3e407d7, 0xa57d8666, 0x945d819b },
        { 0x40000000, 0x4c1bf829, 0x5a82799a, 0x6ba27e65 }
    };
#pragma GCC diagnostic pop

    int sfb, width, cbIdx, sf, cl, cr, scalef, scalei;
    int gbMaskL, gbMaskR;
    unsigned char msMask;

    msMask = (*msMaskPtr++) >> msMaskOffset;
    gbMaskL = 0;
    gbMaskR = 0;

    for (sfb = 0; sfb < maxSFB; sfb++) {
        width = sfbTab[sfb + 1] - sfbTab[sfb];	/* assume >= 0 (see sfBandTabLong/sfBandTabShort) */
        cbIdx = cbRight[sfb];

        if (cbIdx == 14 || cbIdx == 15) {
            /* intensity stereo */
            if (msMaskPres == 1 && (msMask & 0x01)) {
                cbIdx ^= 0x01;    /* invert_intensity(): 14 becomes 15, or 15 becomes 14 */
            }
            sf = -sfRight[sfb];				/* negative since we use identity 0.5^(x) = 2^(-x) (see spec) */
            cbIdx &= 0x01;					/* choose - or + scale factor */
            scalef = pow14[cbIdx][sf & 0x03];
            scalei = (sf >> 2) + 2;			/* +2 to compensate for scalef = Q30 */

            if (scalei > 0) {
                if (scalei > 30) {
                    scalei = 30;
                }
                do {
                    cr = MULSHIFT32(*coefL++, scalef);
                    CLIP_2N(cr, 31 - scalei);
                    cr <<= scalei;
                    gbMaskR |= FASTABS(cr);
                    *coefR++ = cr;
                } while (--width);
            } else {
                scalei = -scalei;
                if (scalei > 31) {
                    scalei = 31;
                }
                do {
                    cr = MULSHIFT32(*coefL++, scalef) >> scalei;
                    gbMaskR |= FASTABS(cr);
                    *coefR++ = cr;
                } while (--width);
            }
        } else if (cbIdx != 13 && ((msMaskPres == 1 && (msMask & 0x01)) || msMaskPres == 2)) {
            /* mid-side stereo (assumes no GB in inputs) */
            do {
                cl = *coefL;
                cr = *coefR;

                if ((FASTABS(cl) | FASTABS(cr)) >> 30) {
                    /* avoid overflow (rare) */
                    cl >>= 1;
                    sf = cl + (cr >> 1);	CLIP_2N(sf, 30); 	sf <<= 1;
                    cl = cl - (cr >> 1);	CLIP_2N(cl, 30); 	cl <<= 1;
                } else {
                    /* usual case */
                    sf = cl + cr;
                    cl -= cr;
                }

                *coefL++ = sf;
                gbMaskL |= FASTABS(sf);
                *coefR++ = cl;
                gbMaskR |= FASTABS(cl);
            } while (--width);

        } else {
            /* nothing to do */
            coefL += width;
            coefR += width;
        }

        /* get next mask bit (should be branchless on ARM) */
        msMask >>= 1;
        if (++msMaskOffset == 8) {
            msMask = *msMaskPtr++;
            msMaskOffset = 0;
        }
    }

    cl = CLZ(gbMaskL) - 1;
    if (gbCurrent[0] > cl) {
        gbCurrent[0] = cl;
    }

    cr = CLZ(gbMaskR) - 1;
    if (gbCurrent[1] > cr) {
        gbCurrent[1] = cr;
    }

    return;
}

/**************************************************************************************
    Function:    StereoProcess

    Description: apply mid-side and intensity stereo, if enabled

    Inputs:      valid AACDecInfo struct (including dequantized transform coefficients)

    Outputs:     updated transform coefficients in Q(FBITS_OUT_DQ_OFF)
                updated minimum guard bit count for both channels

    Return:      0 if successful, -1 if error
 **************************************************************************************/
int StereoProcess(AACDecInfo *aacDecInfo) {
    PSInfoBase *psi;
    ICSInfo *icsInfo;
    int gp, win, nSamps, msMaskOffset;
    int *coefL, *coefR;
    unsigned char *msMaskPtr;
    const /*short*/ int *sfbTab;

    /* validate pointers */
    if (!aacDecInfo || !aacDecInfo->psInfoBase) {
        return -1;
    }
    psi = (PSInfoBase *)(aacDecInfo->psInfoBase);

    /* mid-side and intensity stereo require common_window == 1 (see MPEG4 spec, Correction 2, 2004) */
    if (psi->commonWin != 1 || aacDecInfo->currBlockID != AAC_ID_CPE) {
        return 0;
    }

    /* nothing to do */
    if (!psi->msMaskPresent && !psi->intensityUsed[1]) {
        return 0;
    }

    icsInfo = &(psi->icsInfo[0]);
    if (icsInfo->winSequence == 2) {
        sfbTab = sfBandTabShort + sfBandTabShortOffset[psi->sampRateIdx];
        nSamps = NSAMPS_SHORT;
    } else {
        sfbTab = sfBandTabLong + sfBandTabLongOffset[psi->sampRateIdx];
        nSamps = NSAMPS_LONG;
    }
    coefL = psi->coef[0];
    coefR = psi->coef[1];

    /* do fused mid-side/intensity processing for each block (one long or eight short) */
    msMaskOffset = 0;
    msMaskPtr = psi->msMaskBits;
    for (gp = 0; gp < icsInfo->numWinGroup; gp++) {
        for (win = 0; win < icsInfo->winGroupLen[gp]; win++) {
            StereoProcessGroup(coefL, coefR, sfbTab, psi->msMaskPresent,
                               msMaskPtr, msMaskOffset, icsInfo->maxSFB, psi->sfbCodeBook[1] + gp * icsInfo->maxSFB,
                               psi->scaleFactors[1] + gp * icsInfo->maxSFB, psi->gbCurrent);
            coefL += nSamps;
            coefR += nSamps;
        }
        /* we use one bit per sfb, so there are maxSFB bits for each window group */
        msMaskPtr += (msMaskOffset + icsInfo->maxSFB) >> 3;
        msMaskOffset = (msMaskOffset + icsInfo->maxSFB) & 0x07;
    }

    ASSERT(coefL == psi->coef[0] + 1024);
    ASSERT(coefR == psi->coef[1] + 1024);

    return 0;
}
