/* ***** 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
 */
static const int pow14[2][4] = { 
	{ 0xc0000000, 0xb3e407d7, 0xa57d8666, 0x945d819b }, 
	{ 0x40000000, 0x4c1bf829, 0x5a82799a, 0x6ba27e65 }
};

/**************************************************************************************
 * 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 *sfbTab, 
							  int msMaskPres, unsigned char *msMaskPtr, int msMaskOffset, int maxSFB, 
							  unsigned char *cbRight, short *sfRight, int *gbCurrent)
{
	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 *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;
}
