/* ***** BEGIN LICENSE BLOCK *****
    Source last modified: $Id: aacdec.c,v 1.1 2005/02/26 01:47:31 jrecker 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), Ken Cooke (kenc@real.com)
    February 2005

    aacdec.c - platform-independent top level decoder API
 **************************************************************************************/

#include "aaccommon.h"

//#include "profile.h"

#define PROFILE_START(x)
#define PROFILE_END()

/**************************************************************************************
    Function:    AACInitDecoder

    Description: allocate memory for platform-specific data
                clear all the user-accessible fields
                initialize SBR decoder if enabled

    Inputs:      none

    Outputs:     none

    Return:      handle to AAC decoder instance, 0 if malloc fails
 **************************************************************************************/
HAACDecoder AACInitDecoder(void) {
    AACDecInfo *aacDecInfo;

    aacDecInfo = AllocateBuffers();
    if (!aacDecInfo) {
        return 0;
    }

#ifdef AAC_ENABLE_SBR
    if (InitSBR(aacDecInfo)) {
        AACFreeDecoder(aacDecInfo);
        return 0;
    }
#endif

    return (HAACDecoder)aacDecInfo;
}

HAACDecoder AACInitDecoderPre(void *ptr, int sz) {
    AACDecInfo *aacDecInfo;

    aacDecInfo = AllocateBuffersPre(&ptr, &sz);
    if (!aacDecInfo) {
        return 0;
    }

#ifdef AAC_ENABLE_SBR
    if (InitSBRPre(aacDecInfo, &ptr, &sz)) {
        return 0;
    }
#endif

    return (HAACDecoder)aacDecInfo;
}

/**************************************************************************************
    Function:    AACFreeDecoder

    Description: free platform-specific data allocated by AACInitDecoder
                free SBR decoder if enabled

    Inputs:      valid AAC decoder instance pointer (HAACDecoder)

    Outputs:     none

    Return:      none
 **************************************************************************************/
void AACFreeDecoder(HAACDecoder hAACDecoder) {
    AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

    if (!aacDecInfo) {
        return;
    }

#ifdef AAC_ENABLE_SBR
    FreeSBR(aacDecInfo);
#endif
    FreeBuffers(aacDecInfo);
}

/**************************************************************************************
    Function:    AACFindSyncWord

    Description: locate the next byte-alinged sync word in the raw AAC stream

    Inputs:      buffer to search for sync word
                max number of bytes to search in buffer

    Outputs:     none

    Return:      offset to first sync word (bytes from start of buf)
                -1 if sync not found after searching nBytes
 **************************************************************************************/
int AACFindSyncWord(unsigned char *buf, int nBytes) {
    int i;

    /* find byte-aligned syncword (12 bits = 0xFFF) */
    for (i = 0; i < nBytes - 1; i++) {
        if ((buf[i + 0] & SYNCWORDH) == SYNCWORDH && (buf[i + 1] & SYNCWORDL) == SYNCWORDL) {
            return i;
        }
    }

    return -1;
}

/**************************************************************************************
    Function:    AACGetLastFrameInfo

    Description: get info about last AAC frame decoded (number of samples decoded,
                  sample rate, bit rate, etc.)

    Inputs:      valid AAC decoder instance pointer (HAACDecoder)
                pointer to AACFrameInfo struct

    Outputs:     filled-in AACFrameInfo struct

    Return:      none

    Notes:       call this right after calling AACDecode()
 **************************************************************************************/
void AACGetLastFrameInfo(HAACDecoder hAACDecoder, AACFrameInfo *aacFrameInfo) {
    AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

    if (!aacDecInfo) {
        aacFrameInfo->bitRate =       0;
        aacFrameInfo->nChans =        0;
        aacFrameInfo->sampRateCore =  0;
        aacFrameInfo->sampRateOut =   0;
        aacFrameInfo->bitsPerSample = 0;
        aacFrameInfo->outputSamps =   0;
        aacFrameInfo->profile =       0;
        aacFrameInfo->tnsUsed =       0;
        aacFrameInfo->pnsUsed =       0;
    } else {
        aacFrameInfo->bitRate =       aacDecInfo->bitRate;
        aacFrameInfo->nChans =        aacDecInfo->nChans;
        aacFrameInfo->sampRateCore =  aacDecInfo->sampRate;
        aacFrameInfo->sampRateOut =   aacDecInfo->sampRate * (aacDecInfo->sbrEnabled ? 2 : 1);
        aacFrameInfo->bitsPerSample = 16;
        aacFrameInfo->outputSamps =   aacDecInfo->nChans * AAC_MAX_NSAMPS * (aacDecInfo->sbrEnabled ? 2 : 1);
        aacFrameInfo->profile =       aacDecInfo->profile;
        aacFrameInfo->tnsUsed =       aacDecInfo->tnsUsed;
        aacFrameInfo->pnsUsed =       aacDecInfo->pnsUsed;
    }
}

/**************************************************************************************
    Function:    AACSetRawBlockParams

    Description: set internal state variables for decoding a stream of raw data blocks

    Inputs:      valid AAC decoder instance pointer (HAACDecoder)
                flag indicating source of parameters
                AACFrameInfo struct, with the members nChans, sampRate, and profile
                  optionally filled-in

    Outputs:     updated codec state

    Return:      0 if successful, error code (< 0) if error

    Notes:       if copyLast == 1, then the codec sets up its internal state (for
                  decoding raw blocks) based on previously-decoded ADTS header info
                if copyLast == 0, then the codec uses the values passed in
                  aacFrameInfo to configure its internal state (useful when the
                  source is MP4 format, for example)
 **************************************************************************************/
int AACSetRawBlockParams(HAACDecoder hAACDecoder, int copyLast, AACFrameInfo *aacFrameInfo) {
    AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

    if (!aacDecInfo) {
        return ERR_AAC_NULL_POINTER;
    }

    aacDecInfo->format = AAC_FF_RAW;
    if (copyLast) {
        return SetRawBlockParams(aacDecInfo, 1, 0, 0, 0);
    } else {
        return SetRawBlockParams(aacDecInfo, 0, aacFrameInfo->nChans, aacFrameInfo->sampRateCore, aacFrameInfo->profile);
    }
}

/**************************************************************************************
    Function:    AACFlushCodec

    Description: flush internal codec state (after seeking, for example)

    Inputs:      valid AAC decoder instance pointer (HAACDecoder)

    Outputs:     updated state variables in aacDecInfo

    Return:      0 if successful, error code (< 0) if error
 **************************************************************************************/
int AACFlushCodec(HAACDecoder hAACDecoder) {
    int ch;
    AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;

    if (!aacDecInfo) {
        return ERR_AAC_NULL_POINTER;
    }

    /*  reset common state variables which change per-frame
        don't touch state variables which are (usually) constant for entire clip
         (nChans, sampRate, profile, format, sbrEnabled)
    */
    aacDecInfo->prevBlockID = AAC_ID_INVALID;
    aacDecInfo->currBlockID = AAC_ID_INVALID;
    aacDecInfo->currInstTag = -1;
    for (ch = 0; ch < MAX_NCHANS_ELEM; ch++) {
        aacDecInfo->sbDeinterleaveReqd[ch] = 0;
    }
    aacDecInfo->adtsBlocksLeft = 0;
    aacDecInfo->tnsUsed = 0;
    aacDecInfo->pnsUsed = 0;

    /* reset internal codec state (flush overlap buffers, etc.) */
    FlushCodec(aacDecInfo);
#ifdef AAC_ENABLE_SBR
    FlushCodecSBR(aacDecInfo);
#endif

    return ERR_AAC_NONE;
}

/**************************************************************************************
    Function:    AACDecode

    Description: decode AAC frame

    Inputs:      valid AAC decoder instance pointer (HAACDecoder)
                double pointer to buffer of AAC data
                pointer to number of valid bytes remaining in inbuf
                pointer to outbuf, big enough to hold one frame of decoded PCM samples
                  (outbuf must be double-sized if SBR enabled)

    Outputs:     PCM data in outbuf, interleaved LRLRLR... if stereo
                  number of output samples = 1024 per channel (2048 if SBR enabled)
                updated inbuf pointer
                updated bytesLeft

    Return:      0 if successful, error code (< 0) if error

    Notes:       inbuf pointer and bytesLeft are not updated until whole frame is
                  successfully decoded, so if ERR_AAC_INDATA_UNDERFLOW is returned
                  just call AACDecode again with more data in inbuf
 **************************************************************************************/
int AACDecode(HAACDecoder hAACDecoder, unsigned char **inbuf, int *bytesLeft, short *outbuf) {
    int err, offset, bitOffset, bitsAvail;
    int ch, baseChan, elementChans;
    unsigned char *inptr;
    AACDecInfo *aacDecInfo = (AACDecInfo *)hAACDecoder;
#ifdef AAC_ENABLE_SBR
    int baseChanSBR, elementChansSBR;
#endif

    if (!aacDecInfo) {
        return ERR_AAC_NULL_POINTER;
    }

    /* make local copies (see "Notes" above) */
    inptr = *inbuf;
    bitOffset = 0;
    bitsAvail = (*bytesLeft) << 3;

    /* first time through figure out what the file format is */
    if (aacDecInfo->format == AAC_FF_Unknown) {
        if (bitsAvail < 32) {
            return ERR_AAC_INDATA_UNDERFLOW;
        }

        if (IS_ADIF(inptr)) {
            /* unpack ADIF header */
            aacDecInfo->format = AAC_FF_ADIF;
            err = UnpackADIFHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
            if (err) {
                return err;
            }
        } else {
            /* assume ADTS by default */
            aacDecInfo->format = AAC_FF_ADTS;
        }
    }



    /* if ADTS, search for start of next frame */
    if (aacDecInfo->format == AAC_FF_ADTS) {
        /* can have 1-4 raw data blocks per ADTS frame (header only present for first one) */
        if (aacDecInfo->adtsBlocksLeft == 0) {
            offset = AACFindSyncWord(inptr, bitsAvail >> 3);
            if (offset < 0) {
                return ERR_AAC_INDATA_UNDERFLOW;
            }
            inptr += offset;
            bitsAvail -= (offset << 3);

            err = UnpackADTSHeader(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
            if (err) {
                return err;
            }

            if (aacDecInfo->nChans == -1) {
                /* figure out implicit channel mapping if necessary */
                err = GetADTSChannelMapping(aacDecInfo, inptr, bitOffset, bitsAvail);
                if (err) {
                    return err;
                }
            }
        }
        aacDecInfo->adtsBlocksLeft--;
    } else if (aacDecInfo->format == AAC_FF_RAW) {
        err = PrepareRawBlock(aacDecInfo);
        if (err) {
            return err;
        }
    }



    /* check for valid number of channels */
    if (aacDecInfo->nChans > AAC_MAX_NCHANS || aacDecInfo->nChans <= 0) {
        return ERR_AAC_NCHANS_TOO_HIGH;
    }

    /* will be set later if active in this frame */
    aacDecInfo->tnsUsed = 0;
    aacDecInfo->pnsUsed = 0;

    bitOffset = 0;
    baseChan = 0;
#ifdef AAC_ENABLE_SBR
    baseChanSBR = 0;
#endif
    do {



        /* parse next syntactic element */
        err = DecodeNextElement(aacDecInfo, &inptr, &bitOffset, &bitsAvail);
        if (err) {
            return err;
        }

        elementChans = elementNumChans[aacDecInfo->currBlockID];
        if (baseChan + elementChans > AAC_MAX_NCHANS) {
            return ERR_AAC_NCHANS_TOO_HIGH;
        }

        /* noiseless decoder and dequantizer */
        for (ch = 0; ch < elementChans; ch++) {
            PROFILE_START("noiseless decoder");
            err = DecodeNoiselessData(aacDecInfo, &inptr, &bitOffset, &bitsAvail, ch);
            PROFILE_END();

            if (err) {
                return err;
            }

            PROFILE_START("dequant");
            if (Dequantize(aacDecInfo, ch)) {
                return ERR_AAC_DEQUANT;
            }
            PROFILE_END();
        }

        PROFILE_START("mid-side and intensity stereo");
        /* mid-side and intensity stereo */
        if (aacDecInfo->currBlockID == AAC_ID_CPE) {
            if (StereoProcess(aacDecInfo)) {
                return ERR_AAC_STEREO_PROCESS;
            }
        }
        PROFILE_END();


        /* PNS, TNS, inverse transform */
        for (ch = 0; ch < elementChans; ch++) {
            PROFILE_START("PNS");
            if (PNS(aacDecInfo, ch)) {
                return ERR_AAC_PNS;
            }
            PROFILE_END();

            if (aacDecInfo->sbDeinterleaveReqd[ch]) {
                /* deinterleave short blocks, if required */
                if (DeinterleaveShortBlocks(aacDecInfo, ch)) {
                    return ERR_AAC_SHORT_BLOCK_DEINT;
                }
                aacDecInfo->sbDeinterleaveReqd[ch] = 0;
            }

            PROFILE_START("TNS");
            if (TNSFilter(aacDecInfo, ch)) {
                return ERR_AAC_TNS;
            }
            PROFILE_END();

            PROFILE_START("IMDCT");
            if (IMDCT(aacDecInfo, ch, baseChan + ch, outbuf)) {
                return ERR_AAC_IMDCT;
            }
            PROFILE_END();
        }

#ifdef AAC_ENABLE_SBR
        if (aacDecInfo->sbrEnabled && (aacDecInfo->currBlockID == AAC_ID_FIL || aacDecInfo->currBlockID == AAC_ID_LFE)) {
            if (aacDecInfo->currBlockID == AAC_ID_LFE) {
                elementChansSBR = elementNumChans[AAC_ID_LFE];
            } else if (aacDecInfo->currBlockID == AAC_ID_FIL && (aacDecInfo->prevBlockID == AAC_ID_SCE || aacDecInfo->prevBlockID == AAC_ID_CPE)) {
                elementChansSBR = elementNumChans[aacDecInfo->prevBlockID];
            } else {
                elementChansSBR = 0;
            }

            if (baseChanSBR + elementChansSBR > AAC_MAX_NCHANS) {
                return ERR_AAC_SBR_NCHANS_TOO_HIGH;
            }

            /* parse SBR extension data if present (contained in a fill element) */
            if (DecodeSBRBitstream(aacDecInfo, baseChanSBR)) {
                return ERR_AAC_SBR_BITSTREAM;
            }

            /* apply SBR */
            if (DecodeSBRData(aacDecInfo, baseChanSBR, outbuf)) {
                return ERR_AAC_SBR_DATA;
            }

            baseChanSBR += elementChansSBR;
        }
#endif

        baseChan += elementChans;
    } while (aacDecInfo->currBlockID != AAC_ID_END);

    /* byte align after each raw_data_block */
    if (bitOffset) {
        inptr++;
        bitsAvail -= (8 - bitOffset);
        bitOffset = 0;
        if (bitsAvail < 0) {
            return ERR_AAC_INDATA_UNDERFLOW;
        }
    }

    /* update pointers */
    aacDecInfo->frameCount++;
    *bytesLeft -= (inptr - *inbuf);
    *inbuf = inptr;

    return ERR_AAC_NONE;
}

