Commit 603f48ab authored by Jean-Michel Trivi's avatar Jean-Michel Trivi

AAC Decoder: introduce time domain limiter

Introduce time domain limiter. The module is per default enabled for all
  AAC-LC and HE-AAC v1/2 streams. For all ER-AAC-LD and ER-AAC-ELD streams
  the limiter is disabled per default. The feature can be en- or disabled
  via dynamic API parameter. Note that the limiter introduces an additional
  output delay which depends on the module parameters and the streams
  sampling rate.

Bug 9428126

Change-Id: I299a072340b33e2c324facbd347a72c8de3d380e
parent 629f60c0
......@@ -436,6 +436,16 @@ typedef enum
2: Create a dual mono output signal from channel 2. \n
3: Create a dual mono output signal by mixing both channels (L' = R' = 0.5*Ch1 + 0.5*Ch2). */
AAC_PCM_OUTPUT_CHANNEL_MAPPING = 0x0003, /*!< Output buffer channel ordering. 0: MPEG PCE style order, 1: WAV file channel order (default). */
AAC_PCM_LIMITER_ENABLE = 0x0004, /*!< Enable signal level limiting. \n
-1: Auto-config. Enable limiter for all non-lowdelay configurations by default. \n
0: Disable limiter in general. \n
1: Enable limiter always.
It is recommended to call the decoder with a AACDEC_CLRHIST flag to reset all states when
the limiter switch is changed explicitly. */
AAC_PCM_LIMITER_ATTACK_TIME = 0x0005, /*!< Signal level limiting attack time in ms.
Default confguration is 15 ms. Adjustable range from 1 ms to 15 ms. */
AAC_PCM_LIMITER_RELEAS_TIME = 0x0006, /*!< Signal level limiting release time in ms.
Default configuration is 50 ms. Adjustable time must be larger than 0 ms. */
AAC_PCM_MIN_OUTPUT_CHANNELS = 0x0011, /*!< Minimum number of PCM output channels. If higher than the number of encoded audio channels,
a simple channel extension is applied. \n
-1, 0: Disable channel extenstion feature. The decoder output contains the same number of
......
......@@ -130,7 +130,6 @@ void aacDecoder_drcInit (
/* init control fields */
self->enable = 0;
self->numThreads = 0;
self->digitalNorm = 0;
/* init params */
pParams = &self->params;
......@@ -139,8 +138,9 @@ void aacDecoder_drcInit (
pParams->usrCut = FL2FXCONST_DBL(0.0f);
pParams->boost = FL2FXCONST_DBL(0.0f);
pParams->usrBoost = FL2FXCONST_DBL(0.0f);
pParams->targetRefLevel = AACDEC_DRC_DEFAULT_REF_LEVEL;
pParams->targetRefLevel = -1;
pParams->expiryFrame = AACDEC_DRC_DFLT_EXPIRY_FRAMES;
pParams->applyDigitalNorm = 0;
pParams->applyHeavyCompression = 0;
/* initial program ref level = target ref level */
......@@ -222,11 +222,12 @@ AAC_DECODER_ERROR aacDecoder_drcSetParam (
return AAC_DEC_INVALID_HANDLE;
}
if (value < 0) {
self->digitalNorm = 0;
self->params.applyDigitalNorm = 0;
self->params.targetRefLevel = -1;
}
else {
/* ref_level must be between 0 and MAX_REFERENCE_LEVEL, inclusive */
self->digitalNorm = 1;
self->params.applyDigitalNorm = 1;
if (self->params.targetRefLevel != (SCHAR)value) {
self->params.targetRefLevel = (SCHAR)value;
self->progRefLevel = (SCHAR)value; /* Always set the program reference level equal to the
......@@ -234,6 +235,16 @@ AAC_DECODER_ERROR aacDecoder_drcSetParam (
}
}
break;
case APPLY_NORMALIZATION:
if (value < 0 || value > 1) {
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 < 0 || value > 1) {
return AAC_DEC_SET_PARAM_FAIL;
......@@ -278,7 +289,7 @@ AAC_DECODER_ERROR aacDecoder_drcSetParam (
self->enable = ( (self->params.boost > (FIXP_DBL)0)
|| (self->params.cut > (FIXP_DBL)0)
|| (self->params.applyHeavyCompression != 0)
|| (self->digitalNorm == 1) );
|| (self->params.targetRefLevel >= 0) );
return ErrorStatus;
......@@ -827,6 +838,7 @@ void aacDecoder_drcApply (
void *pSbrDec,
CAacDecoderChannelInfo *pAacDecoderChannelInfo,
CDrcChannelData *pDrcChData,
FIXP_DBL *extGain,
int ch, /* needed only for SBR */
int aacFrameSize,
int bSbrPresent )
......@@ -838,8 +850,8 @@ void aacDecoder_drcApply (
FIXP_DBL max_mantissa;
INT max_exponent;
FIXP_DBL norm_mantissa = FL2FXCONST_DBL(0.0f);
INT norm_exponent = 0;
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];
......@@ -861,6 +873,15 @@ void aacDecoder_drcApply (
if (!self->enable) {
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;
}
......@@ -876,7 +897,7 @@ void aacDecoder_drcApply (
reduced DAC SNR (if signal is attenuated) or clipping (if signal is
boosted) */
if (self->digitalNorm == 1)
if (pParams->targetRefLevel >= 0)
{
/* 0.5^((targetRefLevel - progRefLevel)/24) */
norm_mantissa = fLdPow(
......@@ -886,7 +907,18 @@ void aacDecoder_drcApply (
3,
&norm_exponent );
}
else {
/* 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 == 0) {
/* Reset normalization gain since this module must not apply it */
norm_mantissa = FL2FXCONST_DBL(0.5f);
norm_exponent = 1;
}
......
......@@ -98,7 +98,6 @@ amm-info@iis.fraunhofer.de
#include "channel.h"
#include "FDK_bitstream.h"
#define AACDEC_DRC_DEFAULT_REF_LEVEL ( 108 ) /* -27 dB below full scale (typical for movies) */
#define AACDEC_DRC_DFLT_EXPIRY_FRAMES ( 50 ) /* Default DRC data expiry time in AAC frames */
/**
......@@ -111,6 +110,7 @@ typedef enum
TARGET_REF_LEVEL,
DRC_BS_DELAY,
DRC_DATA_EXPIRY_FRAME,
APPLY_NORMALIZATION,
APPLY_HEAVY_COMPRESSION
} AACDEC_DRC_PARAM;
......@@ -149,6 +149,8 @@ int aacDecoder_drcProlog (
* \param pSbrDec pointer to SBR decoder instance
* \param pAacDecoderChannelInfo AAC decoder channel instance to be processed
* \param pDrcDat DRC channel data
* \param extGain Pointer to a FIXP_DBL where a externally applyable gain will be stored into (independently on whether it will be apply internally or not).
* At function call the buffer must hold the scale (0 >= scale < DFRACT_BITS) to be applied on the gain value.
* \param ch channel index
* \param aacFrameSize AAC frame size
* \param bSbrPresent flag indicating that SBR is present, in which case DRC is handed over to the SBR instance pSbrDec
......@@ -158,6 +160,7 @@ void aacDecoder_drcApply (
void *pSbrDec,
CAacDecoderChannelInfo *pAacDecoderChannelInfo,
CDrcChannelData *pDrcDat,
FIXP_DBL *extGain,
int ch,
int aacFrameSize,
int bSbrPresent );
......
......@@ -140,6 +140,7 @@ typedef struct
UINT expiryFrame;
SCHAR targetRefLevel;
UCHAR bsDelayEnable;
UCHAR applyDigitalNorm;
UCHAR applyHeavyCompression;
} CDrcParams;
......
......@@ -1653,6 +1653,8 @@ LINKSPEC_CPP AAC_DECODER_ERROR CAacDecoder_DecodeFrame(
{
int stride, offset, c;
/* Turn on/off DRC modules level normalization in digital domain depending on the limiter status. */
aacDecoder_drcSetParam( self->hDrcInfo, APPLY_NORMALIZATION, (self->limiterEnableCurr) ? 0 : 1 );
/* Extract DRC control data and map it to channels (without bitstream delay) */
aacDecoder_drcProlog (
self->hDrcInfo,
......@@ -1703,12 +1705,15 @@ LINKSPEC_CPP AAC_DECODER_ERROR CAacDecoder_DecodeFrame(
/* Reset DRC control data for this channel */
aacDecoder_drcInitChannelData ( &self->pAacDecoderStaticChannelInfo[c]->drcData );
}
/* The DRC module demands to be called with the gain field holding the gain scale. */
self->extGain[0] = (FIXP_DBL)TDL_GAIN_SCALING;
/* DRC processing */
aacDecoder_drcApply (
self->hDrcInfo,
self->hSbrDecoder,
pAacDecoderChannelInfo,
&self->pAacDecoderStaticChannelInfo[c]->drcData,
self->extGain,
c,
self->streamInfo.aacSamplesPerFrame,
self->sbrEnabled
......@@ -1726,6 +1731,7 @@ LINKSPEC_CPP AAC_DECODER_ERROR CAacDecoder_DecodeFrame(
(self->frameOK && !(flags&AACDEC_CONCEAL)),
self->aacCommonData.workBufferCore1->mdctOutTemp
);
self->extGainDelay = self->streamInfo.aacSamplesPerFrame;
break;
case AACDEC_RENDER_ELDFB:
CBlock_FrequencyToTimeLowDelay(
......@@ -1735,6 +1741,7 @@ LINKSPEC_CPP AAC_DECODER_ERROR CAacDecoder_DecodeFrame(
self->streamInfo.aacSamplesPerFrame,
stride
);
self->extGainDelay = (self->streamInfo.aacSamplesPerFrame*2 - self->streamInfo.aacSamplesPerFrame/2 - 1)/2;
break;
default:
ErrorStatus = AAC_DEC_UNKNOWN;
......
......@@ -111,6 +111,7 @@ amm-info@iis.fraunhofer.de
#include "aacdec_drc.h"
#include "pcmutils_lib.h"
#include "limiter.h"
/* Capabilities flags */
......@@ -215,6 +216,12 @@ struct AAC_DECODER_INSTANCE {
CAncData ancData; /*!< structure to handle ancillary data */
HANDLE_PCM_DOWNMIX hPcmUtils; /*!< privat data for the PCM utils. */
TDLimiterPtr hLimiter; /*!< Handle of time domain limiter. */
UCHAR limiterEnableUser; /*!< The limiter configuration requested by the library user */
UCHAR limiterEnableCurr; /*!< The current limiter configuration. */
FIXP_DBL extGain[1]; /*!< Gain that must be applied to the output signal. */
UINT extGainDelay; /*!< Delay that must be accounted for extGain. */
};
......
......@@ -110,7 +110,7 @@ amm-info@iis.fraunhofer.de
/* Decoder library info */
#define AACDECODER_LIB_VL0 2
#define AACDECODER_LIB_VL1 5
#define AACDECODER_LIB_VL2 6
#define AACDECODER_LIB_VL2 7
#define AACDECODER_LIB_TITLE "AAC Decoder Lib"
#define AACDECODER_LIB_BUILD_DATE __DATE__
#define AACDECODER_LIB_BUILD_TIME __TIME__
......@@ -397,12 +397,14 @@ aacDecoder_SetParam ( const HANDLE_AACDECODER self, /*!< Handle of the decode
CConcealParams *pConcealData = NULL;
HANDLE_AAC_DRC hDrcInfo = NULL;
HANDLE_PCM_DOWNMIX hPcmDmx = NULL;
TDLimiterPtr hPcmTdl = NULL;
/* check decoder handle */
if (self != NULL) {
pConcealData = &self->concealCommonData;
hDrcInfo = self->hDrcInfo;
hPcmDmx = self->hPcmUtils;
hPcmTdl = self->hLimiter;
} else {
errorStatus = AAC_DEC_INVALID_HANDLE;
}
......@@ -486,6 +488,47 @@ aacDecoder_SetParam ( const HANDLE_AACDECODER self, /*!< Handle of the decode
}
break;
case AAC_PCM_LIMITER_ENABLE:
if (value < -1 || value > 1) {
return AAC_DEC_SET_PARAM_FAIL;
}
if (self == NULL) {
return AAC_DEC_INVALID_HANDLE;
}
self->limiterEnableUser = value;
break;
case AAC_PCM_LIMITER_ATTACK_TIME:
if (value <= 0) { /* module function converts value to unsigned */
return AAC_DEC_SET_PARAM_FAIL;
}
switch (setLimiterAttack(hPcmTdl, value)) {
case TDLIMIT_OK:
break;
case TDLIMIT_INVALID_HANDLE:
return AAC_DEC_INVALID_HANDLE;
case TDLIMIT_INVALID_PARAMETER:
default:
return AAC_DEC_SET_PARAM_FAIL;
}
break;
case AAC_PCM_LIMITER_RELEAS_TIME:
if (value <= 0) { /* module function converts value to unsigned */
return AAC_DEC_SET_PARAM_FAIL;
}
switch (setLimiterRelease(hPcmTdl, value)) {
case TDLIMIT_OK:
break;
case TDLIMIT_INVALID_HANDLE:
return AAC_DEC_INVALID_HANDLE;
case TDLIMIT_INVALID_PARAMETER:
default:
return AAC_DEC_SET_PARAM_FAIL;
}
break;
case AAC_PCM_OUTPUT_CHANNEL_MAPPING:
switch (value) {
case 0:
......@@ -632,6 +675,14 @@ LINKSPEC_CPP HANDLE_AACDECODER aacDecoder_Open(TRANSPORT_TYPE transportFmt, UINT
goto bail;
}
aacDec->hLimiter = createLimiter(TDL_ATTACK_DEFAULT_MS, TDL_RELEASE_DEFAULT_MS, SAMPLE_MAX, (8), 96000);
if (NULL == aacDec->hLimiter) {
err = -1;
goto bail;
}
aacDec->limiterEnableUser = (UCHAR)-1;
aacDec->limiterEnableCurr = 0;
/* Assure that all modules have same delay */
......@@ -807,6 +858,17 @@ LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_DecodeFrame(
self->streamInfo.numTotalBytes = 0;
}
if (self->limiterEnableUser==(UCHAR)-1) {
/* Enbale limiter for all non-lowdelay AOT's. */
self->limiterEnableCurr = ( self->flags & (AC_LD|AC_ELD) ) ? 0 : 1;
}
else {
/* Use limiter configuration as requested. */
self->limiterEnableCurr = self->limiterEnableUser;
}
/* reset limiter gain on a per frame basis */
self->extGain[0] = FL2FXCONST_DBL(1.0f/(float)(1<<TDL_GAIN_SCALING));
ErrorStatus = CAacDecoder_DecodeFrame(self,
flags | (fTpConceal ? AACDEC_CONCEAL : 0),
......@@ -909,6 +971,7 @@ LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_DecodeFrame(
{
INT pcmLimiterScale = 0;
PCMDMX_ERROR dmxErr = PCMDMX_OK;
if ( flags & (AACDEC_INTR | AACDEC_CLRHIST) ) {
/* delete data from the past (e.g. mixdown coeficients) */
......@@ -924,13 +987,34 @@ LINKSPEC_CPP AAC_DECODER_ERROR aacDecoder_DecodeFrame(
self->channelType,
self->channelIndices,
self->channelOutputMapping,
NULL
(self->limiterEnableCurr) ? &pcmLimiterScale : NULL
);
if (dmxErr == PCMDMX_INVALID_MODE) {
/* Announce the framework that the current combination of channel configuration and downmix
* settings are not know to produce a predictable behavior and thus maybe produce strange output. */
ErrorStatus = AAC_DEC_DECODE_FRAME_ERROR;
}
if ( flags & AACDEC_CLRHIST ) {
/* Delete the delayed signal. */
resetLimiter(self->hLimiter);
}
if (self->limiterEnableCurr)
{
/* Set actual signal parameters */
setLimiterNChannels(self->hLimiter, self->streamInfo.numChannels);
setLimiterSampleRate(self->hLimiter, self->streamInfo.sampleRate);
applyLimiter(
self->hLimiter,
pTimeData,
self->extGain,
&pcmLimiterScale,
1,
self->extGainDelay,
self->streamInfo.frameSize
);
}
}
......@@ -956,6 +1040,9 @@ LINKSPEC_CPP void aacDecoder_Close ( HANDLE_AACDECODER self )
return;
if (self->hLimiter != NULL) {
destroyLimiter(self->hLimiter);
}
if (self->hPcmUtils != NULL) {
pcmDmx_Close( &self->hPcmUtils );
......
/* -----------------------------------------------------------------------------------------------------------
Software License for The Fraunhofer FDK AAC Codec Library for Android
© Copyright 1995 - 2013 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
----------------------------------------------------------------------------------------------------------- */
/************************ FDK PCM postprocessor module *********************
Author(s): Matthias Neusinger
Description: Hard limiter for clipping prevention
*******************************************************************************/
#ifndef _LIMITER_H_
#define _LIMITER_H_
#include "common_fix.h"
#define TDL_ATTACK_DEFAULT_MS (15) /* default attack time in ms */
#define TDL_RELEASE_DEFAULT_MS (50) /* default release time in ms */
#define TDL_GAIN_SCALING (15) /* scaling of gain value. */
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
TDLIMIT_OK = 0,
__error_codes_start = -100,
TDLIMIT_INVALID_HANDLE,
TDLIMIT_INVALID_PARAMETER,
__error_codes_end
} TDLIMITER_ERROR;
struct TDLimiter;
typedef struct TDLimiter* TDLimiterPtr;
/******************************************************************************
* createLimiter *
* maxAttackMs: maximum and initial attack/lookahead time in milliseconds *
* releaseMs: release time in milliseconds (90% time constant) *
* threshold: limiting threshold *
* maxChannels: maximum and initial number of channels *
* maxSampleRate: maximum and initial sampling rate in Hz *
* returns: limiter handle *
******************************************************************************/
TDLimiterPtr createLimiter(unsigned int maxAttackMs,
unsigned int releaseMs,
INT_PCM threshold,
unsigned int maxChannels,
unsigned int maxSampleRate);
/******************************************************************************
* resetLimiter *
* limiter: limiter handle *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR resetLimiter(TDLimiterPtr limiter);
/******************************************************************************
* destroyLimiter *
* limiter: limiter handle *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR destroyLimiter(TDLimiterPtr limiter);
/******************************************************************************
* applyLimiter *
* limiter: limiter handle *
* pGain : pointer to gains to be applied to the signal before limiting, *
* which are downscaled by TDL_GAIN_SCALING bit. *
* These gains are delayed by gain_delay, and smoothed. *
* Smoothing is done by a butterworth lowpass filter with a cutoff *
* frequency which is fixed with respect to the sampling rate. *
* It is a substitute for the smoothing due to windowing and *
* overlap/add, if a gain is applied in frequency domain. *
* gain_scale: pointer to scaling exponents to be applied to the signal before *
* limiting, without delay and without smoothing *
* gain_size: number of elements in pGain, currently restricted to 1 *
* gain_delay: delay [samples] with which the gains in pGain shall be applied *
* gain_delay <= nSamples *
* samples: input/output buffer containing interleaved samples *
* precision of output will be DFRACT_BITS-TDL_GAIN_SCALING bits *
* nSamples: number of samples per channel *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR applyLimiter(TDLimiterPtr limiter,
INT_PCM* samples,
FIXP_DBL* pGain,
const INT* gain_scale,
const UINT gain_size,
const UINT gain_delay,
const UINT nSamples);
/******************************************************************************
* getLimiterDelay *
* limiter: limiter handle *
* returns: exact delay caused by the limiter in samples *
******************************************************************************/
unsigned int getLimiterDelay(TDLimiterPtr limiter);
/******************************************************************************
* setLimiterNChannels *
* limiter: limiter handle *
* nChannels: number of channels ( <= maxChannels specified on create) *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR setLimiterNChannels(TDLimiterPtr limiter, unsigned int nChannels);
/******************************************************************************
* setLimiterSampleRate *
* limiter: limiter handle *
* sampleRate: sampling rate in Hz ( <= maxSampleRate specified on create) *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR setLimiterSampleRate(TDLimiterPtr limiter, unsigned int sampleRate);
/******************************************************************************
* setLimiterAttack *
* limiter: limiter handle *
* attackMs: attack time in ms ( <= maxAttackMs specified on create) *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR setLimiterAttack(TDLimiterPtr limiter, unsigned int attackMs);
/******************************************************************************
* setLimiterRelease *
* limiter: limiter handle *
* releaseMs: release time in ms *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR setLimiterRelease(TDLimiterPtr limiter, unsigned int releaseMs);
/******************************************************************************
* setLimiterThreshold *
* limiter: limiter handle *
* threshold: limiter threshold *
* returns: error code *
******************************************************************************/
TDLIMITER_ERROR setLimiterThreshold(TDLimiterPtr limiter, INT_PCM threshold);
#ifdef __cplusplus
}
#endif
#endif //#ifndef _LIMITER_H_
/* -----------------------------------------------------------------------------------------------------------
Software License for The Fraunhofer FDK AAC Codec Library for Android
© Copyright 1995 - 2013 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
----------------------------------------------------------------------------------------------------------- */
/************************ FDK PCM postprocessor module *********************
Author(s): Matthias Neusinger
Description: Hard limiter for clipping prevention
*******************************************************************************/
#include "limiter.h"
struct TDLimiter {
unsigned int attack;
FIXP_DBL attackConst, releaseConst;
unsigned int attackMs, releaseMs, maxAttackMs;
FIXP_PCM threshold;
unsigned int channels, maxChannels;
unsigned int sampleRate, maxSampleRate;
FIXP_DBL cor, max;
FIXP_DBL* maxBuf;
FIXP_DBL* delayBuf;
unsigned int maxBufIdx, delayBufIdx;
FIXP_DBL smoothState0;
FIXP_DBL minGain;
FIXP_DBL additionalGainPrev;
FIXP_DBL additionalGainFilterState;
FIXP_DBL additionalGainFilterState1;
};
/* create limiter */
TDLimiterPtr createLimiter(
unsigned int maxAttackMs,
unsigned int releaseMs,
INT_PCM threshold,
unsigned int maxChannels,
unsigned int maxSampleRate
)
{
TDLimiterPtr limiter = NULL;
unsigned int attack, release;
FIXP_DBL attackConst, releaseConst, exponent;
INT e_ans;
/* calc attack and release time in samples */
attack = (unsigned int)(maxAttackMs * maxSampleRate / 1000);
release = (unsigned int)(releaseMs * maxSampleRate / 1000);
/* alloc limiter struct */
limiter = (TDLimiterPtr)FDKcalloc(1, sizeof(struct TDLimiter));
if (!limiter) return NULL;
/* alloc max and delay buffers */
limiter->maxBuf = (FIXP_DBL*)FDKcalloc(attack + 1, sizeof(FIXP_DBL));
limiter->delayBuf = (FIXP_DBL*)FDKcalloc(attack * maxChannels, sizeof(FIXP_DBL));
if (!limiter->maxBuf || !limiter->delayBuf) {
destroyLimiter(limiter);
return NULL;
}
/* attackConst = pow(0.1, 1.0 / (attack + 1)) */
exponent = invFixp(attack+1);
attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
attackConst = scaleValue(attackConst, e_ans);
/* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */
exponent = invFixp(release + 1);
releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
releaseConst = scaleValue(releaseConst, e_ans);
/* init parameters */
limiter->attackMs = maxAttackMs;
limiter->maxAttackMs = maxAttackMs;
limiter->releaseMs = releaseMs;
limiter->attack = attack;
limiter->attackConst = attackConst;
limiter->releaseConst = releaseConst;
limiter->threshold = (FIXP_PCM)threshold;
limiter->channels = maxChannels;
limiter->maxChannels = maxChannels;
limiter->sampleRate = maxSampleRate;
limiter->maxSampleRate = maxSampleRate;
resetLimiter(limiter);
return limiter;
}
/* reset limiter */
TDLIMITER_ERROR resetLimiter(TDLimiterPtr limiter)
{
if (limiter != NULL) {
limiter->maxBufIdx = 0;
limiter->delayBufIdx = 0;
limiter->max = (FIXP_DBL)0;
limiter->cor = FL2FXCONST_DBL(1.0f/(1<<1));
limiter->smoothState0 = FL2FXCONST_DBL(1.0f/(1<<1));
limiter->minGain = FL2FXCONST_DBL(1.0f/(1<<1));
limiter->additionalGainPrev = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING));
limiter->additionalGainFilterState = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING));
limiter->additionalGainFilterState1 = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING));
FDKmemset(limiter->maxBuf, 0, (limiter->attack + 1) * sizeof(FIXP_DBL) );
FDKmemset(limiter->delayBuf, 0, limiter->attack * limiter->channels * sizeof(FIXP_DBL) );
}
else {
return TDLIMIT_INVALID_HANDLE;
}
return TDLIMIT_OK;
}
/* destroy limiter */
TDLIMITER_ERROR destroyLimiter(TDLimiterPtr limiter)
{
if (limiter != NULL) {
FDKfree(limiter->maxBuf);
FDKfree(limiter->delayBuf);
FDKfree(limiter);
}
else {
return TDLIMIT_INVALID_HANDLE;
}
return TDLIMIT_OK;
}
/* apply limiter */
TDLIMITER_ERROR applyLimiter(TDLimiterPtr limiter,
INT_PCM* samples,
FIXP_DBL* pGain,
const INT* gain_scale,
const UINT gain_size,
const UINT gain_delay,
const UINT nSamples)
{
unsigned int i, j;
FIXP_PCM tmp1, tmp2;
FIXP_DBL tmp, old, gain, additionalGain, additionalGainUnfiltered;
FIXP_DBL minGain = FL2FXCONST_DBL(1.0f/(1<<1));
FDK_ASSERT(gain_size == 1);
FDK_ASSERT(gain_delay <= nSamples);
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
{
unsigned int channels = limiter->channels;
unsigned int attack = limiter->attack;
FIXP_DBL attackConst = limiter->attackConst;
FIXP_DBL releaseConst = limiter->releaseConst;
FIXP_DBL threshold = FX_PCM2FX_DBL(limiter->threshold)>>TDL_GAIN_SCALING;
FIXP_DBL max = limiter->max;
FIXP_DBL* maxBuf = limiter->maxBuf;
unsigned int maxBufIdx = limiter->maxBufIdx;
FIXP_DBL cor = limiter->cor;
FIXP_DBL* delayBuf = limiter->delayBuf;
unsigned int delayBufIdx = limiter->delayBufIdx;
FIXP_DBL smoothState0 = limiter->smoothState0;
FIXP_DBL additionalGainSmoothState = limiter->additionalGainFilterState;
FIXP_DBL additionalGainSmoothState1 = limiter->additionalGainFilterState1;
for (i = 0; i < nSamples; i++) {
if (i < gain_delay) {
additionalGainUnfiltered = limiter->additionalGainPrev;
} else {
additionalGainUnfiltered = pGain[0];
}
/* Smooth additionalGain */
/* [b,a] = butter(1, 0.01) */
static const FIXP_SGL b[] = { FL2FXCONST_SGL(0.015466*2.0), FL2FXCONST_SGL( 0.015466*2.0) };
static const FIXP_SGL a[] = { FL2FXCONST_SGL(1.000000), FL2FXCONST_SGL(-0.96907) };
/* [b,a] = butter(1, 0.001) */
//static const FIXP_SGL b[] = { FL2FXCONST_SGL(0.0015683*2.0), FL2FXCONST_SGL( 0.0015683*2.0) };
//static const FIXP_SGL a[] = { FL2FXCONST_SGL(1.0000000), FL2FXCONST_SGL(-0.99686) };
additionalGain = - fMult(additionalGainSmoothState, a[1]) + fMultDiv2( additionalGainUnfiltered, b[0]) + fMultDiv2(additionalGainSmoothState1, b[1]);
additionalGainSmoothState1 = additionalGainUnfiltered;
additionalGainSmoothState = additionalGain;
/* Apply the additional scaling that has no delay and no smoothing */
if (gain_scale[0] > 0) {
additionalGain <<= gain_scale[0];
} else {
additionalGain >>= gain_scale[0];
}
/* get maximum absolute sample value of all channels, including the additional gain. */
tmp1 = (FIXP_PCM)0;
for (j = 0; j < channels; j++) {
tmp2 = (FIXP_PCM)samples[i * channels + j];
if (tmp2 == (FIXP_PCM)SAMPLE_MIN) /* protect fAbs from -1.0 value */
tmp2 = (FIXP_PCM)(SAMPLE_MIN+1);
tmp1 = fMax(tmp1, fAbs(tmp2));
}
tmp = SATURATE_LEFT_SHIFT(fMultDiv2(tmp1, additionalGain), 1, DFRACT_BITS);
/* set threshold as lower border to save calculations in running maximum algorithm */
tmp = fMax(tmp, threshold);
/* running maximum */
old = maxBuf[maxBufIdx];
maxBuf[maxBufIdx] = tmp;
if (tmp >= max) {
/* new sample is greater than old maximum, so it is the new maximum */
max = tmp;
}
else if (old < max) {
/* maximum does not change, as the sample, which has left the window was
not the maximum */
}
else {
/* the old maximum has left the window, we have to search the complete
buffer for the new max */
max = maxBuf[0];
for (j = 1; j <= attack; j++) {
if (maxBuf[j] > max) max = maxBuf[j];
}
}
maxBufIdx++;
if (maxBufIdx >= attack+1) maxBufIdx = 0;
/* calc gain */
/* gain is downscaled by one, so that gain = 1.0 can be represented */
if (max > threshold) {
gain = fDivNorm(threshold, max)>>1;
}
else {
gain = FL2FXCONST_DBL(1.0f/(1<<1));
}
/* gain smoothing, method: TDL_EXPONENTIAL */
/* first order IIR filter with attack correction to avoid overshoots */
/* correct the 'aiming' value of the exponential attack to avoid the remaining overshoot */
if (gain < smoothState0) {
cor = fMin(cor, fMultDiv2((gain - fMultDiv2(FL2FXCONST_SGL(0.1f*(1<<1)),smoothState0)), FL2FXCONST_SGL(1.11111111f/(1<<1)))<<2);
}
else {
cor = gain;
}
/* smoothing filter */
if (cor < smoothState0) {
smoothState0 = fMult(attackConst,(smoothState0 - cor)) + cor; /* attack */
smoothState0 = fMax(smoothState0, gain); /* avoid overshooting target */
}
else {
/* sign inversion twice to round towards +infinity,
so that gain can converge to 1.0 again,
for bit-identical output when limiter is not active */
smoothState0 = -fMult(releaseConst,-(smoothState0 - cor)) + cor; /* release */
}
gain = smoothState0;
/* lookahead delay, apply gain */
for (j = 0; j < channels; j++) {
tmp = delayBuf[delayBufIdx * channels + j];
delayBuf[delayBufIdx * channels + j] = fMult((FIXP_PCM)samples[i * channels + j], additionalGain);
/* Apply gain to delayed signal */
if (gain < FL2FXCONST_DBL(1.0f/(1<<1)))
tmp = fMult(tmp,gain<<1);
samples[i * channels + j] = FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT(tmp,TDL_GAIN_SCALING,DFRACT_BITS));
}
delayBufIdx++;
if (delayBufIdx >= attack) delayBufIdx = 0;
/* save minimum gain factor */
if (gain < minGain) minGain = gain;
}
limiter->max = max;
limiter->maxBufIdx = maxBufIdx;
limiter->cor = cor;
limiter->delayBufIdx = delayBufIdx;
limiter->smoothState0 = smoothState0;
limiter->additionalGainFilterState = additionalGainSmoothState;
limiter->additionalGainFilterState1 = additionalGainSmoothState1;
limiter->minGain = minGain;
limiter->additionalGainPrev = pGain[0];
return TDLIMIT_OK;
}
}
/* get delay in samples */
unsigned int getLimiterDelay(TDLimiterPtr limiter)
{
FDK_ASSERT(limiter != NULL);
return limiter->attack;
}
/* set number of channels */
TDLIMITER_ERROR setLimiterNChannels(TDLimiterPtr limiter, unsigned int nChannels)
{
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
if (nChannels > limiter->maxChannels) return TDLIMIT_INVALID_PARAMETER;
limiter->channels = nChannels;
//resetLimiter(limiter);
return TDLIMIT_OK;
}
/* set sampling rate */
TDLIMITER_ERROR setLimiterSampleRate(TDLimiterPtr limiter, unsigned int sampleRate)
{
unsigned int attack, release;
FIXP_DBL attackConst, releaseConst, exponent;
INT e_ans;
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
if (sampleRate > limiter->maxSampleRate) return TDLIMIT_INVALID_PARAMETER;
/* update attack and release time in samples */
attack = (unsigned int)(limiter->attackMs * sampleRate / 1000);
release = (unsigned int)(limiter->releaseMs * sampleRate / 1000);
/* attackConst = pow(0.1, 1.0 / (attack + 1)) */
exponent = invFixp(attack+1);
attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
attackConst = scaleValue(attackConst, e_ans);
/* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */
exponent = invFixp(release + 1);
releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
releaseConst = scaleValue(releaseConst, e_ans);
limiter->attack = attack;
limiter->attackConst = attackConst;
limiter->releaseConst = releaseConst;
limiter->sampleRate = sampleRate;
/* reset */
//resetLimiter(limiter);
return TDLIMIT_OK;
}
/* set attack time */
TDLIMITER_ERROR setLimiterAttack(TDLimiterPtr limiter, unsigned int attackMs)
{
unsigned int attack;
FIXP_DBL attackConst, exponent;
INT e_ans;
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
if (attackMs > limiter->maxAttackMs) return TDLIMIT_INVALID_PARAMETER;
/* calculate attack time in samples */
attack = (unsigned int)(attackMs * limiter->sampleRate / 1000);
/* attackConst = pow(0.1, 1.0 / (attack + 1)) */
exponent = invFixp(attack+1);
attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
attackConst = scaleValue(attackConst, e_ans);
limiter->attack = attack;
limiter->attackConst = attackConst;
limiter->attackMs = attackMs;
return TDLIMIT_OK;
}
/* set release time */
TDLIMITER_ERROR setLimiterRelease(TDLimiterPtr limiter, unsigned int releaseMs)
{
unsigned int release;
FIXP_DBL releaseConst, exponent;
INT e_ans;
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
/* calculate release time in samples */
release = (unsigned int)(releaseMs * limiter->sampleRate / 1000);
/* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */
exponent = invFixp(release + 1);
releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
releaseConst = scaleValue(releaseConst, e_ans);
limiter->releaseConst = releaseConst;
limiter->releaseMs = releaseMs;
return TDLIMIT_OK;
}
/* set limiter threshold */
TDLIMITER_ERROR setLimiterThreshold(TDLimiterPtr limiter, INT_PCM threshold)
{
if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE;
limiter->threshold = (FIXP_PCM)threshold;
return TDLIMIT_OK;
}
......@@ -148,7 +148,7 @@ amm-info@iis.fraunhofer.de
/* Decoder library info */
#define PCMDMX_LIB_VL0 2
#define PCMDMX_LIB_VL1 4
#define PCMDMX_LIB_VL2 1
#define PCMDMX_LIB_VL2 2
#define PCMDMX_LIB_TITLE "PCM Downmix Lib"
#define PCMDMX_LIB_BUILD_DATE __DATE__
#define PCMDMX_LIB_BUILD_TIME __TIME__
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment