diff options
Diffstat (limited to 'fdk-aac/libAACdec/src/aacdec_drc.cpp')
-rw-r--r-- | fdk-aac/libAACdec/src/aacdec_drc.cpp | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/fdk-aac/libAACdec/src/aacdec_drc.cpp b/fdk-aac/libAACdec/src/aacdec_drc.cpp new file mode 100644 index 0000000..922a09e --- /dev/null +++ b/fdk-aac/libAACdec/src/aacdec_drc.cpp @@ -0,0 +1,1355 @@ +/* ----------------------------------------------------------------------------- +Software License for The Fraunhofer FDK AAC Codec Library for Android + +© Copyright 1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten +Forschung e.V. All rights reserved. + + 1. INTRODUCTION +The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software +that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding +scheme for digital audio. This FDK AAC Codec software is intended to be used on +a wide variety of Android devices. + +AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient +general perceptual audio codecs. AAC-ELD is considered the best-performing +full-bandwidth communications codec by independent studies and is widely +deployed. AAC has been standardized by ISO and IEC as part of the MPEG +specifications. + +Patent licenses for necessary patent claims for the FDK AAC Codec (including +those of Fraunhofer) may be obtained through Via Licensing +(www.vialicensing.com) or through the respective patent owners individually for +the purpose of encoding or decoding bit streams in products that are compliant +with the ISO/IEC MPEG audio standards. Please note that most manufacturers of +Android devices already license these patent claims through Via Licensing or +directly from the patent owners, and therefore FDK AAC Codec software may +already be covered under those patent licenses when it is used for those +licensed purposes only. + +Commercially-licensed AAC software libraries, including floating-point versions +with enhanced sound quality, are also available from Fraunhofer. Users are +encouraged to check the Fraunhofer website for additional applications +information and documentation. + +2. COPYRIGHT LICENSE + +Redistribution and use in source and binary forms, with or without modification, +are permitted without payment of copyright license fees provided that you +satisfy the following conditions: + +You must retain the complete text of this software license in redistributions of +the FDK AAC Codec or your modifications thereto in source code form. + +You must retain the complete text of this software license in the documentation +and/or other materials provided with redistributions of the FDK AAC Codec or +your modifications thereto in binary form. You must make available free of +charge copies of the complete source code of the FDK AAC Codec and your +modifications thereto to recipients of copies in binary form. + +The name of Fraunhofer may not be used to endorse or promote products derived +from this library without prior written permission. + +You may not charge copyright license fees for anyone to use, copy or distribute +the FDK AAC Codec software or your modifications thereto. + +Your modified versions of the FDK AAC Codec must carry prominent notices stating +that you changed the software and the date of any change. For modified versions +of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android" +must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK +AAC Codec Library for Android." + +3. NO PATENT LICENSE + +NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without +limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE. +Fraunhofer provides no warranty of patent non-infringement with respect to this +software. + +You may use this FDK AAC Codec software or modifications thereto only for +purposes that are authorized by appropriate patent licenses. + +4. DISCLAIMER + +This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright +holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +including but not limited to the implied warranties of merchantability and +fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, +or consequential damages, including but not limited to procurement of substitute +goods or services; loss of use, data, or profits, or business interruption, +however caused and on any theory of liability, whether in contract, strict +liability, or tort (including negligence), arising in any way out of the use of +this software, even if advised of the possibility of such damage. + +5. CONTACT INFORMATION + +Fraunhofer Institute for Integrated Circuits IIS +Attention: Audio and Multimedia Departments - FDK AAC LL +Am Wolfsmantel 33 +91058 Erlangen, Germany + +www.iis.fraunhofer.de/amm +amm-info@iis.fraunhofer.de +----------------------------------------------------------------------------- */ + +/**************************** AAC decoder library ****************************** + + Author(s): Christian Griebel + + Description: Dynamic range control (DRC) decoder tool for AAC + +*******************************************************************************/ + +#include "aacdec_drc.h" + +#include "channelinfo.h" +#include "aac_rom.h" + +#include "sbrdecoder.h" + +/* + * Dynamic Range Control + */ + +/* For parameter conversion */ +#define DRC_PARAMETER_BITS (7) +#define DRC_MAX_QUANT_STEPS (1 << DRC_PARAMETER_BITS) +#define DRC_MAX_QUANT_FACTOR (DRC_MAX_QUANT_STEPS - 1) +#define DRC_PARAM_QUANT_STEP \ + (FL2FXCONST_DBL(1.0f / (float)DRC_MAX_QUANT_FACTOR)) +#define DRC_PARAM_SCALE (1) +#define DRC_SCALING_MAX \ + ((FIXP_DBL)((INT)(DRC_PARAM_QUANT_STEP >> DRC_PARAM_SCALE) * (INT)127)) + +#define DRC_BLOCK_LEN (1024) +#define DRC_BAND_MULT (4) +#define DRC_BLOCK_LEN_DIV_BAND_MULT (DRC_BLOCK_LEN / DRC_BAND_MULT) + +#define MAX_REFERENCE_LEVEL (127) + +#define DRC_HEAVY_THRESHOLD_DB (10) + +#define DVB_ANC_DATA_SYNC_BYTE (0xBC) /* DVB ancillary data sync byte. */ + +#define OFF 0 +#define ON 1 + +static INT convert_drcParam(FIXP_DBL param_dbl) { + /* converts an internal DRC boost/cut scaling factor in FIXP_DBL + (which is downscaled by DRC_PARAM_SCALE) + back to an integer value between 0 and 127. */ + LONG param_long; + + param_long = (LONG)param_dbl >> 7; + param_long = param_long * (INT)DRC_MAX_QUANT_FACTOR; + param_long >>= 31 - 7 - DRC_PARAM_SCALE - 1; + param_long += 1; /* for rounding */ + param_long >>= 1; + + return (INT)param_long; +} + +/*! + \brief Initialize DRC information + + \self Handle of DRC info + + \return none +*/ +void aacDecoder_drcInit(HANDLE_AAC_DRC self) { + CDrcParams *pParams; + + if (self == NULL) { + return; + } + + /* init control fields */ + self->enable = OFF; + self->numThreads = 0; + + /* init params */ + pParams = &self->params; + pParams->bsDelayEnable = 0; + pParams->cut = FL2FXCONST_DBL(0.0f); + pParams->usrCut = FL2FXCONST_DBL(0.0f); + pParams->boost = FL2FXCONST_DBL(0.0f); + pParams->usrBoost = FL2FXCONST_DBL(0.0f); + pParams->targetRefLevel = -1; + pParams->expiryFrame = AACDEC_DRC_DFLT_EXPIRY_FRAMES; + pParams->applyDigitalNorm = OFF; + pParams->applyHeavyCompression = OFF; + pParams->usrApplyHeavyCompression = OFF; + + pParams->defaultPresentationMode = DISABLED_PARAMETER_HANDLING; + pParams->encoderTargetLevel = MAX_REFERENCE_LEVEL; /* worst case assumption */ + + self->update = 1; + self->numOutChannels = 0; + self->prevAacNumChannels = 0; + + /* initial program ref level = target ref level */ + self->progRefLevel = pParams->targetRefLevel; + self->progRefLevelPresent = 0; + self->presMode = -1; + self->uniDrcPrecedence = 0; +} + +/*! + \brief Initialize DRC control data for one channel + + \self Handle of DRC info + + \return none +*/ +void aacDecoder_drcInitChannelData(CDrcChannelData *pDrcChData) { + if (pDrcChData != NULL) { + pDrcChData->expiryCount = 0; + pDrcChData->numBands = 1; + pDrcChData->bandTop[0] = DRC_BLOCK_LEN_DIV_BAND_MULT - 1; + pDrcChData->drcValue[0] = 0; + pDrcChData->drcInterpolationScheme = 0; + pDrcChData->drcDataType = UNKNOWN_PAYLOAD; + } +} + +/*! + \brief Set one single DRC parameter + + \self Handle of DRC info. + \param Parameter to be set. + \value Value to be set. + + \return an error code. +*/ +AAC_DECODER_ERROR aacDecoder_drcSetParam(HANDLE_AAC_DRC self, + AACDEC_DRC_PARAM param, INT value) { + AAC_DECODER_ERROR ErrorStatus = AAC_DEC_OK; + + switch (param) { + case DRC_CUT_SCALE: + /* set attenuation scale factor */ + if ((value < 0) || (value > DRC_MAX_QUANT_FACTOR)) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.usrCut = (FIXP_DBL)( + (INT)(DRC_PARAM_QUANT_STEP >> DRC_PARAM_SCALE) * (INT)value); + self->update = 1; + break; + case DRC_BOOST_SCALE: + /* set boost factor */ + if ((value < 0) || (value > DRC_MAX_QUANT_FACTOR)) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.usrBoost = (FIXP_DBL)( + (INT)(DRC_PARAM_QUANT_STEP >> DRC_PARAM_SCALE) * (INT)value); + self->update = 1; + break; + case TARGET_REF_LEVEL: + if (value > MAX_REFERENCE_LEVEL || value < -MAX_REFERENCE_LEVEL) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + if (value < 0) { + self->params.applyDigitalNorm = OFF; + self->params.targetRefLevel = -1; + } else { + /* ref_level must be between 0 and MAX_REFERENCE_LEVEL, inclusive */ + self->params.applyDigitalNorm = ON; + if (self->params.targetRefLevel != (SCHAR)value) { + self->params.targetRefLevel = (SCHAR)value; + self->progRefLevel = (SCHAR)value; /* Always set the program reference + level equal to the target level + according to 4.5.2.7.3 of + ISO/IEC 14496-3. */ + } + self->update = 1; + } + break; + case APPLY_NORMALIZATION: + if ((value != OFF) && (value != ON)) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + /* Store new parameter value */ + self->params.applyDigitalNorm = (UCHAR)value; + break; + case APPLY_HEAVY_COMPRESSION: + if ((value != OFF) && (value != ON)) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + /* Store new parameter value */ + self->params.usrApplyHeavyCompression = (UCHAR)value; + self->update = 1; + break; + case DEFAULT_PRESENTATION_MODE: + if (value < AAC_DRC_PARAMETER_HANDLING_DISABLED || + value > AAC_DRC_PRESENTATION_MODE_2_DEFAULT) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.defaultPresentationMode = + (AACDEC_DRC_PARAMETER_HANDLING)value; + self->update = 1; + break; + case ENCODER_TARGET_LEVEL: + if (value > MAX_REFERENCE_LEVEL || value < 0) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.encoderTargetLevel = (UCHAR)value; + self->update = 1; + break; + case DRC_BS_DELAY: + if (value < 0 || value > 1) { + return AAC_DEC_SET_PARAM_FAIL; + } + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.bsDelayEnable = value; + break; + case DRC_DATA_EXPIRY_FRAME: + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->params.expiryFrame = (value > 0) ? (UINT)value : 0; + break; + case MAX_OUTPUT_CHANNELS: + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->numOutChannels = (INT)value; + self->update = 1; + break; + case UNIDRC_PRECEDENCE: + if (self == NULL) { + return AAC_DEC_INVALID_HANDLE; + } + self->uniDrcPrecedence = (UCHAR)value; + break; + default: + return AAC_DEC_SET_PARAM_FAIL; + } /* switch(param) */ + + return ErrorStatus; +} + +static int parseExcludedChannels(UINT *excludedChnsMask, + HANDLE_FDK_BITSTREAM bs) { + UINT excludeMask = 0; + UINT i, j; + int bitCnt = 9; + + for (i = 0, j = 1; i < 7; i++, j <<= 1) { + if (FDKreadBits(bs, 1)) { + excludeMask |= j; + } + } + + /* additional_excluded_chns */ + while (FDKreadBits(bs, 1)) { + for (i = 0; i < 7; i++, j <<= 1) { + if (FDKreadBits(bs, 1)) { + excludeMask |= j; + } + } + bitCnt += 9; + FDK_ASSERT(j < (UINT)-1); + } + + *excludedChnsMask = excludeMask; + + return (bitCnt); +} + +/*! + \brief Save DRC payload bitstream position + + \self Handle of DRC info + \bs Handle of FDK bitstream + + \return The number of DRC payload bits +*/ +int aacDecoder_drcMarkPayload(HANDLE_AAC_DRC self, HANDLE_FDK_BITSTREAM bs, + AACDEC_DRC_PAYLOAD_TYPE type) { + UINT bsStartPos; + int i, numBands = 1, bitCnt = 0; + + if (self == NULL) { + return 0; + } + + bsStartPos = FDKgetValidBits(bs); + + switch (type) { + case MPEG_DRC_EXT_DATA: { + bitCnt = 4; + + if (FDKreadBits(bs, 1)) { /* pce_tag_present */ + FDKreadBits(bs, 8); /* pce_instance_tag + drc_tag_reserved_bits */ + bitCnt += 8; + } + + if (FDKreadBits(bs, 1)) { /* excluded_chns_present */ + FDKreadBits(bs, 7); /* exclude mask [0..7] */ + bitCnt += 8; + while (FDKreadBits(bs, 1)) { /* additional_excluded_chns */ + FDKreadBits(bs, 7); /* exclude mask [x..y] */ + bitCnt += 8; + } + } + + if (FDKreadBits(bs, 1)) { /* drc_bands_present */ + numBands += FDKreadBits(bs, 4); /* drc_band_incr */ + FDKreadBits(bs, 4); /* reserved */ + bitCnt += 8; + for (i = 0; i < numBands; i++) { + FDKreadBits(bs, 8); /* drc_band_top[i] */ + bitCnt += 8; + } + } + + if (FDKreadBits(bs, 1)) { /* prog_ref_level_present */ + FDKreadBits(bs, 8); /* prog_ref_level + prog_ref_level_reserved_bits */ + bitCnt += 8; + } + + for (i = 0; i < numBands; i++) { + FDKreadBits(bs, 8); /* dyn_rng_sgn[i] + dyn_rng_ctl[i] */ + bitCnt += 8; + } + + if ((self->numPayloads < MAX_DRC_THREADS) && + ((INT)FDKgetValidBits(bs) >= 0)) { + self->drcPayloadPosition[self->numPayloads++] = bsStartPos; + } + } break; + + case DVB_DRC_ANC_DATA: + bitCnt += 8; + /* check sync word */ + if (FDKreadBits(bs, 8) == DVB_ANC_DATA_SYNC_BYTE) { + int dmxLevelsPresent, compressionPresent; + int coarseGrainTcPresent, fineGrainTcPresent; + + /* bs_info field */ + FDKreadBits( + bs, + 8); /* mpeg_audio_type, dolby_surround_mode, presentation_mode */ + bitCnt += 8; + + /* Evaluate ancillary_data_status */ + FDKreadBits(bs, 3); /* reserved, set to 0 */ + dmxLevelsPresent = + FDKreadBits(bs, 1); /* downmixing_levels_MPEG4_status */ + FDKreadBits(bs, 1); /* reserved, set to 0 */ + compressionPresent = + FDKreadBits(bs, 1); /* audio_coding_mode_and_compression status */ + coarseGrainTcPresent = + FDKreadBits(bs, 1); /* coarse_grain_timecode_status */ + fineGrainTcPresent = + FDKreadBits(bs, 1); /* fine_grain_timecode_status */ + bitCnt += 8; + + /* MPEG4 downmixing levels */ + if (dmxLevelsPresent) { + FDKreadBits(bs, 8); /* downmixing_levels_MPEG4 */ + bitCnt += 8; + } + /* audio coding mode and compression status */ + if (compressionPresent) { + FDKreadBits(bs, 16); /* audio_coding_mode, Compression_value */ + bitCnt += 16; + } + /* coarse grain timecode */ + if (coarseGrainTcPresent) { + FDKreadBits(bs, 16); /* coarse_grain_timecode */ + bitCnt += 16; + } + /* fine grain timecode */ + if (fineGrainTcPresent) { + FDKreadBits(bs, 16); /* fine_grain_timecode */ + bitCnt += 16; + } + if (!self->dvbAncDataAvailable && ((INT)FDKgetValidBits(bs) >= 0)) { + self->dvbAncDataPosition = bsStartPos; + self->dvbAncDataAvailable = 1; + } + } + break; + + default: + break; + } + + return (bitCnt); +} + +/*! + \brief Parse DRC parameters from bitstream + + \bs Handle of FDK bitstream (in) + \pDrcBs Pointer to DRC payload data container (out) + \payloadPosition Bitstream position of MPEG DRC data chunk (in) + + \return Flag telling whether new DRC data has been found or not. +*/ +static int aacDecoder_drcParse(HANDLE_FDK_BITSTREAM bs, CDrcPayload *pDrcBs, + UINT payloadPosition) { + int i, numBands; + + /* Move to the beginning of the DRC payload field */ + FDKpushBiDirectional(bs, (INT)FDKgetValidBits(bs) - (INT)payloadPosition); + + /* pce_tag_present */ + if (FDKreadBits(bs, 1)) { + pDrcBs->pceInstanceTag = FDKreadBits(bs, 4); /* pce_instance_tag */ + /* only one program supported */ + FDKreadBits(bs, 4); /* drc_tag_reserved_bits */ + } else { + pDrcBs->pceInstanceTag = -1; /* not present */ + } + + if (FDKreadBits(bs, 1)) { /* excluded_chns_present */ + /* get excluded_chn_mask */ + parseExcludedChannels(&pDrcBs->excludedChnsMask, bs); + } else { + pDrcBs->excludedChnsMask = 0; + } + + numBands = 1; + if (FDKreadBits(bs, 1)) /* drc_bands_present */ + { + /* get band_incr */ + numBands += FDKreadBits(bs, 4); /* drc_band_incr */ + pDrcBs->channelData.drcInterpolationScheme = + FDKreadBits(bs, 4); /* drc_interpolation_scheme */ + /* band_top */ + for (i = 0; i < numBands; i++) { + pDrcBs->channelData.bandTop[i] = FDKreadBits(bs, 8); /* drc_band_top[i] */ + } + } else { + pDrcBs->channelData.bandTop[0] = DRC_BLOCK_LEN_DIV_BAND_MULT - + 1; /* ... comprising the whole spectrum. */ + ; + } + + pDrcBs->channelData.numBands = numBands; + + if (FDKreadBits(bs, 1)) /* prog_ref_level_present */ + { + pDrcBs->progRefLevel = FDKreadBits(bs, 7); /* prog_ref_level */ + FDKreadBits(bs, 1); /* prog_ref_level_reserved_bits */ + } else { + pDrcBs->progRefLevel = -1; + } + + for (i = 0; i < numBands; i++) { + pDrcBs->channelData.drcValue[i] = FDKreadBits(bs, 1) + << 7; /* dyn_rng_sgn[i] */ + pDrcBs->channelData.drcValue[i] |= + FDKreadBits(bs, 7) & 0x7F; /* dyn_rng_ctl[i] */ + } + + /* Set DRC payload type */ + pDrcBs->channelData.drcDataType = MPEG_DRC_EXT_DATA; + + return (1); +} + +/*! + \brief Parse heavy compression value transported in DSEs of DVB streams with + MPEG-4 content. + + \bs Handle of FDK bitstream (in) + \pDrcBs Pointer to DRC payload data container (out) + \payloadPosition Bitstream position of DVB ancillary data chunk + + \return Flag telling whether new DRC data has been found or not. +*/ +#define DVB_COMPRESSION_SCALE (8) /* 48,164 dB */ + +static int aacDecoder_drcReadCompression(HANDLE_FDK_BITSTREAM bs, + CDrcPayload *pDrcBs, + UINT payloadPosition) { + int foundDrcData = 0; + int dmxLevelsPresent, compressionPresent; + + /* Move to the beginning of the DRC payload field */ + FDKpushBiDirectional(bs, (INT)FDKgetValidBits(bs) - (INT)payloadPosition); + + /* Sanity checks */ + if (FDKgetValidBits(bs) < 24) { + return 0; + } + + /* Check sync word */ + if (FDKreadBits(bs, 8) != DVB_ANC_DATA_SYNC_BYTE) { + return 0; + } + + /* Evaluate bs_info field */ + if (FDKreadBits(bs, 2) != 3) { /* mpeg_audio_type */ + /* No MPEG-4 audio data */ + return 0; + } + FDKreadBits(bs, 2); /* dolby_surround_mode */ + pDrcBs->presMode = FDKreadBits(bs, 2); /* presentation_mode */ + FDKreadBits(bs, 1); /* stereo_downmix_mode */ + if (FDKreadBits(bs, 1) != 0) { /* reserved, set to 0 */ + return 0; + } + + /* Evaluate ancillary_data_status */ + if (FDKreadBits(bs, 3) != 0) { /* reserved, set to 0 */ + return 0; + } + dmxLevelsPresent = FDKreadBits(bs, 1); /* downmixing_levels_MPEG4_status */ + /*extensionPresent =*/FDKreadBits(bs, + 1); /* ancillary_data_extension_status; */ + compressionPresent = + FDKreadBits(bs, 1); /* audio_coding_mode_and_compression status */ + /*coarseGrainTcPresent =*/FDKreadBits(bs, + 1); /* coarse_grain_timecode_status */ + /*fineGrainTcPresent =*/FDKreadBits(bs, 1); /* fine_grain_timecode_status */ + + if (dmxLevelsPresent) { + FDKreadBits(bs, 8); /* downmixing_levels_MPEG4 */ + } + + /* audio_coding_mode_and_compression_status */ + if (compressionPresent) { + UCHAR compressionOn, compressionValue; + + /* audio_coding_mode */ + if (FDKreadBits(bs, 7) != 0) { /* The reserved bits shall be set to "0". */ + return 0; + } + compressionOn = (UCHAR)FDKreadBits(bs, 1); /* compression_on */ + compressionValue = (UCHAR)FDKreadBits(bs, 8); /* Compression_value */ + + if (compressionOn) { + /* A compression value is available so store the data just like MPEG DRC + * data */ + pDrcBs->channelData.numBands = 1; /* One band ... */ + pDrcBs->channelData.drcValue[0] = + compressionValue; /* ... with one value ... */ + pDrcBs->channelData.bandTop[0] = + DRC_BLOCK_LEN_DIV_BAND_MULT - + 1; /* ... comprising the whole spectrum. */ + ; + pDrcBs->pceInstanceTag = -1; /* Not present */ + pDrcBs->progRefLevel = -1; /* Not present */ + pDrcBs->channelData.drcDataType = + DVB_DRC_ANC_DATA; /* Set DRC payload type to DVB. */ + foundDrcData = 1; + } + } + + return (foundDrcData); +} + +/* + * Extract DRC payload from bitstream and map it to channels. + * Valid return values are: + * -1 : An unexpected error occured. + * 0 : No error and no valid DRC data available. + * 1 : No error and valid DRC data has been mapped. + */ +static int aacDecoder_drcExtractAndMap( + HANDLE_AAC_DRC self, HANDLE_FDK_BITSTREAM hBs, + CAacDecoderStaticChannelInfo *pAacDecoderStaticChannelInfo[], + UCHAR pceInstanceTag, + UCHAR channelMapping[], /* Channel mapping translating drcChannel index to + canonical channel index */ + int validChannels) { + CDrcPayload threadBs[MAX_DRC_THREADS]; + CDrcPayload *validThreadBs[MAX_DRC_THREADS]; + CDrcParams *pParams; + UINT backupBsPosition; + int result = 0; + int i, thread, validThreads = 0; + + FDK_ASSERT(self != NULL); + FDK_ASSERT(hBs != NULL); + FDK_ASSERT(pAacDecoderStaticChannelInfo != NULL); + + pParams = &self->params; + + self->numThreads = 0; + backupBsPosition = FDKgetValidBits(hBs); + + for (i = 0; i < self->numPayloads && self->numThreads < MAX_DRC_THREADS; + i++) { + /* Init payload data chunk. The memclear is very important because it + initializes the most values. Without it the module wouldn't work properly + or crash. */ + FDKmemclear(&threadBs[self->numThreads], sizeof(CDrcPayload)); + threadBs[self->numThreads].channelData.bandTop[0] = + DRC_BLOCK_LEN_DIV_BAND_MULT - 1; + + /* Extract payload */ + self->numThreads += aacDecoder_drcParse(hBs, &threadBs[self->numThreads], + self->drcPayloadPosition[i]); + } + self->numPayloads = 0; + + if (self->dvbAncDataAvailable && + self->numThreads < MAX_DRC_THREADS) { /* Append a DVB heavy compression + payload thread if available. */ + + /* Init payload data chunk. The memclear is very important because it + initializes the most values. Without it the module wouldn't work properly + or crash. */ + FDKmemclear(&threadBs[self->numThreads], sizeof(CDrcPayload)); + threadBs[self->numThreads].channelData.bandTop[0] = + DRC_BLOCK_LEN_DIV_BAND_MULT - 1; + + /* Extract payload */ + self->numThreads += aacDecoder_drcReadCompression( + hBs, &threadBs[self->numThreads], self->dvbAncDataPosition); + } + self->dvbAncDataAvailable = 0; + + /* Reset the bitbufffer */ + FDKpushBiDirectional(hBs, (INT)FDKgetValidBits(hBs) - (INT)backupBsPosition); + + /* calculate number of valid bits in excl_chn_mask */ + + /* coupling channels not supported */ + + /* check for valid threads */ + for (thread = 0; thread < self->numThreads; thread++) { + CDrcPayload *pThreadBs = &threadBs[thread]; + int numExclChns = 0; + + switch ((AACDEC_DRC_PAYLOAD_TYPE)pThreadBs->channelData.drcDataType) { + default: + continue; + case MPEG_DRC_EXT_DATA: + case DVB_DRC_ANC_DATA: + break; + } + + if (pThreadBs->pceInstanceTag >= 0) { /* if PCE tag present */ + if (pThreadBs->pceInstanceTag != pceInstanceTag) { + continue; /* don't accept */ + } + } + + /* calculate number of excluded channels */ + if (pThreadBs->excludedChnsMask > 0) { + INT exclMask = pThreadBs->excludedChnsMask; + int ch; + for (ch = 0; ch < validChannels; ch++) { + numExclChns += exclMask & 0x1; + exclMask >>= 1; + } + } + if (numExclChns < validChannels) { + validThreadBs[validThreads] = pThreadBs; + validThreads++; + } + } + + /* map DRC bitstream information onto DRC channel information */ + for (thread = 0; thread < validThreads; thread++) { + CDrcPayload *pThreadBs = validThreadBs[thread]; + INT exclMask = pThreadBs->excludedChnsMask; + AACDEC_DRC_PAYLOAD_TYPE drcPayloadType = + (AACDEC_DRC_PAYLOAD_TYPE)pThreadBs->channelData.drcDataType; + int ch; + + /* last progRefLevel transmitted is the one that is used + * (but it should really only be transmitted once per block!) + */ + if (pThreadBs->progRefLevel >= 0) { + self->progRefLevel = pThreadBs->progRefLevel; + self->progRefLevelPresent = 1; + self->prlExpiryCount = 0; /* Got a new value -> Reset counter */ + } + + if (drcPayloadType == DVB_DRC_ANC_DATA) { + /* Announce the presentation mode of this valid thread. */ + self->presMode = pThreadBs->presMode; + } + + /* SCE, CPE and LFE */ + for (ch = 0; ch < validChannels; ch++) { + AACDEC_DRC_PAYLOAD_TYPE prvPayloadType = UNKNOWN_PAYLOAD; + int mapedChannel = channelMapping[ch]; + + if ((mapedChannel >= validChannels) || + ((exclMask & (1 << mapedChannel)) != 0)) + continue; + + if ((pParams->expiryFrame <= 0) || + (pAacDecoderStaticChannelInfo[ch]->drcData.expiryCount < + pParams->expiryFrame)) { + prvPayloadType = + (AACDEC_DRC_PAYLOAD_TYPE)pAacDecoderStaticChannelInfo[ch] + ->drcData.drcDataType; + } + if (((drcPayloadType == MPEG_DRC_EXT_DATA) && + (prvPayloadType != DVB_DRC_ANC_DATA)) || + ((drcPayloadType == DVB_DRC_ANC_DATA) && + (pParams->applyHeavyCompression == + ON))) { /* copy thread to channel */ + pAacDecoderStaticChannelInfo[ch]->drcData = pThreadBs->channelData; + result = 1; + } + } + /* CCEs not supported by now */ + } + + /* Increment and check expiry counter for the program reference level: */ + if ((pParams->expiryFrame > 0) && + (self->prlExpiryCount++ > + pParams->expiryFrame)) { /* The program reference level is too old, so + set it back to the target level. */ + self->progRefLevelPresent = 0; + self->progRefLevel = pParams->targetRefLevel; + self->prlExpiryCount = 0; + } + + return result; +} + +void aacDecoder_drcApply(HANDLE_AAC_DRC self, void *pSbrDec, + CAacDecoderChannelInfo *pAacDecoderChannelInfo, + CDrcChannelData *pDrcChData, FIXP_DBL *extGain, + int ch, /* needed only for SBR */ + int aacFrameSize, int bSbrPresent) { + int band, bin, numBands; + int bottom = 0; + int modifyBins = 0; + + FIXP_DBL max_mantissa; + INT max_exponent; + + FIXP_DBL norm_mantissa = FL2FXCONST_DBL(0.5f); + INT norm_exponent = 1; + + FIXP_DBL fact_mantissa[MAX_DRC_BANDS]; + INT fact_exponent[MAX_DRC_BANDS]; + + CDrcParams *pParams = &self->params; + + FIXP_DBL *pSpectralCoefficient = + SPEC_LONG(pAacDecoderChannelInfo->pSpectralCoefficient); + CIcsInfo *pIcsInfo = &pAacDecoderChannelInfo->icsInfo; + SHORT *pSpecScale = pAacDecoderChannelInfo->specScale; + + int winSeq = pIcsInfo->WindowSequence; + + /* Increment and check expiry counter */ + if ((pParams->expiryFrame > 0) && + (++pDrcChData->expiryCount > + pParams->expiryFrame)) { /* The DRC data is too old, so delete it. */ + aacDecoder_drcInitChannelData(pDrcChData); + } + + if (self->enable != ON) { + sbrDecoder_drcDisable((HANDLE_SBRDECODER)pSbrDec, ch); + if (extGain != NULL) { + INT gainScale = (INT)*extGain; + /* The gain scaling must be passed to the function in the buffer pointed + * on by extGain. */ + if (gainScale >= 0 && gainScale <= DFRACT_BITS) { + *extGain = scaleValue(norm_mantissa, norm_exponent - gainScale); + } else { + FDK_ASSERT(0); + } + } + return; + } + + numBands = pDrcChData->numBands; + + /* If program reference normalization is done in the digital domain, + modify factor to perform normalization. prog_ref_level can + alternatively be passed to the system for modification of the level in + the analog domain. Analog level modification avoids problems with + reduced DAC SNR (if signal is attenuated) or clipping (if signal is + boosted) */ + + if (pParams->targetRefLevel >= 0) { + /* 0.5^((targetRefLevel - progRefLevel)/24) */ + norm_mantissa = + fLdPow(FL2FXCONST_DBL(-1.0), /* log2(0.5) */ + 0, + (FIXP_DBL)((INT)(FL2FXCONST_DBL(1.0f / 24.0) >> 3) * + (INT)(pParams->targetRefLevel - self->progRefLevel)), + 3, &norm_exponent); + } + /* Always export the normalization gain (if possible). */ + if (extGain != NULL) { + INT gainScale = (INT)*extGain; + /* The gain scaling must be passed to the function in the buffer pointed on + * by extGain. */ + if (gainScale >= 0 && gainScale <= DFRACT_BITS) { + *extGain = scaleValue(norm_mantissa, norm_exponent - gainScale); + } else { + FDK_ASSERT(0); + } + } + if (self->params.applyDigitalNorm == OFF) { + /* Reset normalization gain since this module must not apply it */ + norm_mantissa = FL2FXCONST_DBL(0.5f); + norm_exponent = 1; + } + + /* calc scale factors */ + for (band = 0; band < numBands; band++) { + UCHAR drcVal = pDrcChData->drcValue[band]; + + fact_mantissa[band] = FL2FXCONST_DBL(0.5f); + fact_exponent[band] = 1; + + if ((pParams->applyHeavyCompression == ON) && + ((AACDEC_DRC_PAYLOAD_TYPE)pDrcChData->drcDataType == + DVB_DRC_ANC_DATA)) { + INT compressionFactorVal_e; + int valX, valY; + + valX = drcVal >> 4; + valY = drcVal & 0x0F; + + /* calculate the unscaled heavy compression factor. + compressionFactor = 48.164 - 6.0206*valX - 0.4014*valY dB + range: -48.166 dB to 48.164 dB */ + if (drcVal != 0x7F) { + fact_mantissa[band] = fPowInt( + FL2FXCONST_DBL(0.95483867181), /* -0.4014dB = 0.95483867181 */ + 0, valY, &compressionFactorVal_e); + + /* -0.0008dB (48.164 - 6.0206*8 = -0.0008) */ + fact_mantissa[band] = + fMult(FL2FXCONST_DBL(0.99990790084), fact_mantissa[band]); + + fact_exponent[band] = + DVB_COMPRESSION_SCALE - valX + compressionFactorVal_e; + } + } else if ((AACDEC_DRC_PAYLOAD_TYPE)pDrcChData->drcDataType == + MPEG_DRC_EXT_DATA) { + /* apply the scaled dynamic range control words to factor. + * if scaling drc_cut (or drc_boost), or control word drc_mantissa is 0 + * then there is no dynamic range compression + * + * if pDrcChData->drcSgn[band] is + * 1 then gain is < 1 : factor = 2^(-self->cut * + * pDrcChData->drcMag[band] / 24) 0 then gain is > 1 : factor = 2^( + * self->boost * pDrcChData->drcMag[band] / 24) + */ + + if ((drcVal & 0x7F) > 0) { + FIXP_DBL tParamVal = (drcVal & 0x80) ? -pParams->cut : pParams->boost; + + fact_mantissa[band] = f2Pow( + (FIXP_DBL)((INT)fMult(FL2FXCONST_DBL(1.0f / 192.0f), tParamVal) * + (drcVal & 0x7F)), + 3 + DRC_PARAM_SCALE, &fact_exponent[band]); + } + } + + fact_mantissa[band] = fMult(fact_mantissa[band], norm_mantissa); + fact_exponent[band] += norm_exponent; + + } /* end loop over bands */ + + /* normalizations */ + { + int res; + + max_mantissa = FL2FXCONST_DBL(0.0f); + max_exponent = 0; + for (band = 0; band < numBands; band++) { + max_mantissa = fixMax(max_mantissa, fact_mantissa[band]); + max_exponent = fixMax(max_exponent, fact_exponent[band]); + } + + /* left shift factors to gain accurancy */ + res = CntLeadingZeros(max_mantissa) - 1; + + /* above topmost DRC band gain factor is 1 */ + if (((pDrcChData->bandTop[fMax(0, numBands - 1)] + 1) << 2) < aacFrameSize) + res = 0; + + if (res > 0) { + res = fixMin(res, max_exponent); + max_exponent -= res; + + for (band = 0; band < numBands; band++) { + fact_mantissa[band] <<= res; + fact_exponent[band] -= res; + } + } + + /* normalize magnitudes to one scale factor */ + for (band = 0; band < numBands; band++) { + if (fact_exponent[band] < max_exponent) { + fact_mantissa[band] >>= max_exponent - fact_exponent[band]; + } + if (fact_mantissa[band] != FL2FXCONST_DBL(0.5f)) { + modifyBins = 1; + } + } + if (max_exponent != 1) { + modifyBins = 1; + } + } + + /* apply factor to spectral lines + * short blocks must take care that bands fall on + * block boundaries! + */ + if (!bSbrPresent) { + bottom = 0; + + if (!modifyBins) { + /* We don't have to modify the spectral bins because the fractional part + of all factors is 0.5. In order to keep accurancy we don't apply the + factor but decrease the exponent instead. */ + max_exponent -= 1; + } else { + for (band = 0; band < numBands; band++) { + int top = fixMin((int)((pDrcChData->bandTop[band] + 1) << 2), + aacFrameSize); /* ... * DRC_BAND_MULT; */ + + for (bin = bottom; bin < top; bin++) { + pSpectralCoefficient[bin] = + fMult(pSpectralCoefficient[bin], fact_mantissa[band]); + } + + bottom = top; + } + } + + /* above topmost DRC band gain factor is 1 */ + if (max_exponent > 0) { + for (bin = bottom; bin < aacFrameSize; bin += 1) { + pSpectralCoefficient[bin] >>= max_exponent; + } + } + + /* adjust scaling */ + pSpecScale[0] += max_exponent; + + if (winSeq == BLOCK_SHORT) { + int win; + for (win = 1; win < 8; win++) { + pSpecScale[win] += max_exponent; + } + } + } else { + HANDLE_SBRDECODER hSbrDecoder = (HANDLE_SBRDECODER)pSbrDec; + numBands = pDrcChData->numBands; + + /* feed factors into SBR decoder for application in QMF domain. */ + sbrDecoder_drcFeedChannel(hSbrDecoder, ch, numBands, fact_mantissa, + max_exponent, pDrcChData->drcInterpolationScheme, + winSeq, pDrcChData->bandTop); + } + + return; +} + +/* + * DRC parameter and presentation mode handling + */ +static void aacDecoder_drcParameterHandling(HANDLE_AAC_DRC self, + INT aacNumChannels, + SCHAR prevDrcProgRefLevel, + SCHAR prevDrcPresMode) { + int isDownmix, isMonoDownmix, isStereoDownmix; + int dDmx, dHr; + AACDEC_DRC_PARAMETER_HANDLING drcParameterHandling; + CDrcParams *p; + + FDK_ASSERT(self != NULL); + + p = &self->params; + + if (self->progRefLevel != prevDrcProgRefLevel) self->update = 1; + + if (self->presMode != prevDrcPresMode) self->update = 1; + + if (self->prevAacNumChannels != aacNumChannels) self->update = 1; + + /* return if no relevant parameter has changed */ + if (!self->update) { + return; + } + + /* derive downmix property. aacNumChannels: number of channels in aac stream, + * numOutChannels: number of output channels */ + isDownmix = (aacNumChannels > self->numOutChannels); + isDownmix = (isDownmix && (self->numOutChannels > 0)); + isMonoDownmix = (isDownmix && (self->numOutChannels == 1)); + isStereoDownmix = (isDownmix && (self->numOutChannels == 2)); + + if ((self->presMode == 1) || (self->presMode == 2)) { + drcParameterHandling = (AACDEC_DRC_PARAMETER_HANDLING)self->presMode; + } else { /* no presentation mode -> use parameter handling specified by + AAC_DRC_DEFAULT_PRESENTATION_MODE */ + drcParameterHandling = p->defaultPresentationMode; + } + + /* by default, do as desired */ + p->cut = p->usrCut; + p->boost = p->usrBoost; + p->applyHeavyCompression = p->usrApplyHeavyCompression; + + switch (drcParameterHandling) { + case DISABLED_PARAMETER_HANDLING: + default: + /* use drc parameters as requested */ + break; + + case ENABLED_PARAMETER_HANDLING: + /* dDmx: estimated headroom reduction due to downmix, format: -1/4*dB + dDmx = floor(-4*20*log10(aacNumChannels/numOutChannels)) */ + if (isDownmix) { + FIXP_DBL dmxTmp; + int e_log, e_mult; + dmxTmp = fDivNorm(self->numOutChannels, + aacNumChannels); /* inverse division -> + negative sign after + logarithm */ + dmxTmp = fLog2(dmxTmp, 0, &e_log); + dmxTmp = fMultNorm( + dmxTmp, FL2FXCONST_DBL(4.0f * 20.0f * 0.30103f / (float)(1 << 5)), + &e_mult); /* e = e_log + e_mult + 5 */ + dDmx = (int)scaleValue(dmxTmp, e_log + e_mult + 5 - (DFRACT_BITS - 1)); + } else { + dDmx = 0; + } + + /* dHr: Full estimated (decoder) headroom reduction due to loudness + * normalisation (DTL - PRL) and downmix. Format: -1/4*dB */ + if (p->targetRefLevel >= 0) { /* if target level is provided */ + dHr = p->targetRefLevel + dDmx - self->progRefLevel; + } else { + dHr = dDmx; + } + + if (dHr < 0) { /* if headroom is reduced */ + /* Use compression, but as little as possible. */ + /* eHr: Headroom provided by encoder, format: -1/4 dB */ + int eHr = fixMin(p->encoderTargetLevel - self->progRefLevel, 0); + if (eHr < + dHr) { /* if encoder provides more headroom than decoder needs */ + /* derive scaling of light DRC */ + FIXP_DBL calcFactor_norm; + INT calcFactor; /* fraction of DRC gains that is minimally needed for + clipping prevention */ + calcFactor_norm = + fDivNorm(-dHr, -eHr); /* 0.0 < calcFactor_norm < 1.0 */ + calcFactor_norm = calcFactor_norm >> DRC_PARAM_SCALE; + /* quantize to 128 steps */ + calcFactor = convert_drcParam( + calcFactor_norm); /* convert to integer value between 0 and 127 */ + calcFactor_norm = (FIXP_DBL)( + (INT)(DRC_PARAM_QUANT_STEP >> DRC_PARAM_SCALE) * calcFactor); + p->cut = (calcFactor_norm > p->cut) + ? calcFactor_norm + : p->cut; /* use calcFactor_norm as lower limit */ + } else { + /* encoder provides equal or less headroom than decoder needs */ + /* the time domain limiter must always be active in this case. It is + * assumed that the framework activates it by default */ + p->cut = DRC_SCALING_MAX; + if ((dHr - eHr) <= + -4 * DRC_HEAVY_THRESHOLD_DB) { /* use heavy compression if + headroom deficit is equal or + higher than + DRC_HEAVY_THRESHOLD_DB */ + p->applyHeavyCompression = ON; + } + } + } else { /* dHr >= 0 */ + /* no restrictions required, as headroom is not reduced. */ + /* p->cut = p->usrCut; */ + } + break; + + /* presentation mode 1 and 2 according to ETSI TS 101 154: + Digital Video Broadcasting (DVB); Specification for the use of Video + and Audio Coding in Broadcasting Applications based on the MPEG-2 + Transport Stream, section C.5.4., "Decoding", and Table C.33. Also + according to amendment 4 to ISO/IEC 14496-3, section 4.5.2.14.2.4, and + Table AMD4.11. ISO DRC -> applyHeavyCompression = OFF (Use + light compression, MPEG-style) Compression_value -> + applyHeavyCompression = ON (Use heavy compression, DVB-style) scaling + restricted -> p->cut = DRC_SCALING_MAX */ + + case DRC_PRESENTATION_MODE_1: /* presentation mode 1, Light:-31/Heavy:-23 */ + if ((p->targetRefLevel >= 0) && + (p->targetRefLevel < + 124)) { /* if target level is provided and > -31 dB */ + /* playback up to -23 dB */ + p->applyHeavyCompression = ON; + } else { /* target level <= -31 dB or not provided */ + /* playback -31 dB */ + if (isMonoDownmix || isStereoDownmix) { /* stereo or mono downmixing */ + p->cut = DRC_SCALING_MAX; + } + } + break; + + case DRC_PRESENTATION_MODE_2: /* presentation mode 2, Light:-23/Heavy:-23 */ + if ((p->targetRefLevel >= 0) && + (p->targetRefLevel < + 124)) { /* if target level is provided and > -31 dB */ + /* playback up to -23 dB */ + if (isMonoDownmix) { /* if mono downmix */ + p->applyHeavyCompression = ON; + } else { + p->applyHeavyCompression = OFF; + p->cut = DRC_SCALING_MAX; + } + } else { /* target level <= -31 dB or not provided */ + /* playback -31 dB */ + p->applyHeavyCompression = OFF; + if (isMonoDownmix || isStereoDownmix) { /* stereo or mono downmixing */ + p->cut = DRC_SCALING_MAX; + } + } + break; + } /* switch (drcParameterHandling) */ + + /* With heavy compression, there is no scaling. + Scaling factors are set for notification only. */ + if (p->applyHeavyCompression == ON) { + p->boost = DRC_SCALING_MAX; + p->cut = DRC_SCALING_MAX; + } + + /* switch on/off processing */ + self->enable = ((p->boost > (FIXP_DBL)0) || (p->cut > (FIXP_DBL)0) || + (p->applyHeavyCompression == ON) || (p->targetRefLevel >= 0)); + self->enable = (self->enable && !self->uniDrcPrecedence); + + self->prevAacNumChannels = aacNumChannels; + self->update = 0; +} + +/* + * Prepare DRC processing + * Valid return values are: + * -1 : An unexpected error occured. + * 0 : No error and no valid DRC data available. + * 1 : No error and valid DRC data has been mapped. + */ +int aacDecoder_drcProlog( + HANDLE_AAC_DRC self, HANDLE_FDK_BITSTREAM hBs, + CAacDecoderStaticChannelInfo *pAacDecoderStaticChannelInfo[], + UCHAR pceInstanceTag, + UCHAR channelMapping[], /* Channel mapping translating drcChannel index to + canonical channel index */ + int validChannels) { + int result = 0; + + if (self == NULL) { + return -1; + } + + if (!self->params.bsDelayEnable) { + /* keep previous progRefLevel and presMode for update flag in + * drcParameterHandling */ + INT prevPRL, prevPM = 0; + prevPRL = self->progRefLevel; + prevPM = self->presMode; + + result = aacDecoder_drcExtractAndMap( + self, hBs, pAacDecoderStaticChannelInfo, pceInstanceTag, channelMapping, + validChannels); + + if (result < 0) { + return result; + } + + /* Drc parameter handling */ + aacDecoder_drcParameterHandling(self, validChannels, prevPRL, prevPM); + } + + return result; +} + +/* + * Finalize DRC processing + * Valid return values are: + * -1 : An unexpected error occured. + * 0 : No error and no valid DRC data available. + * 1 : No error and valid DRC data has been mapped. + */ +int aacDecoder_drcEpilog( + HANDLE_AAC_DRC self, HANDLE_FDK_BITSTREAM hBs, + CAacDecoderStaticChannelInfo *pAacDecoderStaticChannelInfo[], + UCHAR pceInstanceTag, + UCHAR channelMapping[], /* Channel mapping translating drcChannel index to + canonical channel index */ + int validChannels) { + int result = 0; + + if (self == NULL) { + return -1; + } + + if (self->params.bsDelayEnable) { + /* keep previous progRefLevel and presMode for update flag in + * drcParameterHandling */ + INT prevPRL, prevPM = 0; + prevPRL = self->progRefLevel; + prevPM = self->presMode; + + result = aacDecoder_drcExtractAndMap( + self, hBs, pAacDecoderStaticChannelInfo, pceInstanceTag, channelMapping, + validChannels); + + if (result < 0) { + return result; + } + + /* Drc parameter handling */ + aacDecoder_drcParameterHandling(self, validChannels, prevPRL, prevPM); + } + + return result; +} + +/* + * Export relevant metadata info from bitstream payload. + */ +void aacDecoder_drcGetInfo(HANDLE_AAC_DRC self, SCHAR *pPresMode, + SCHAR *pProgRefLevel) { + if (self != NULL) { + if (pPresMode != NULL) { + *pPresMode = self->presMode; + } + if (pProgRefLevel != NULL) { + if (self->progRefLevelPresent) { + *pProgRefLevel = self->progRefLevel; + } else { + *pProgRefLevel = -1; + } + } + } +} |