mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 04:57:06 +03:00
477 lines
15 KiB
C
477 lines
15 KiB
C
/* ***** 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;
|
|
}
|
|
|