| /* ----------------------------------------------------------------------------- |
| 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 |
| ----------------------------------------------------------------------------- */ |
| |
| /************************* MPEG-D DRC decoder library ************************** |
| |
| Author(s): Andreas Hoelzer |
| |
| Description: DRC Set Selection |
| |
| *******************************************************************************/ |
| |
| #include "drcDec_selectionProcess.h" |
| #include "drcDec_tools.h" |
| |
| #define UNDEFINED_LOUDNESS_VALUE (FIXP_DBL) MAXVAL_DBL |
| |
| typedef enum { |
| DETR_NONE = 0, |
| DETR_NIGHT = 1, |
| DETR_NOISY = 2, |
| DETR_LIMITED = 3, |
| DETR_LOWLEVEL = 4, |
| DETR_DIALOG = 5, |
| DETR_GENERAL_COMPR = 6, |
| DETR_EXPAND = 7, |
| DETR_ARTISTIC = 8, |
| DETR_COUNT |
| } DRC_EFFECT_TYPE_REQUEST; |
| |
| typedef enum { |
| DFRT_EFFECT_TYPE, |
| DFRT_DYNAMIC_RANGE, |
| DFRT_DRC_CHARACTERISTIC |
| } DRC_FEATURE_REQUEST_TYPE; |
| |
| typedef enum { |
| MDR_DEFAULT = 0, |
| MDR_PROGRAM_LOUDNESS = 1, |
| MDR_ANCHOR_LOUDNESS = 2 |
| } METHOD_DEFINITION_REQUEST; |
| |
| typedef enum { |
| MSR_DEFAULT = 0, |
| MSR_BS_1770_4 = 1, |
| MSR_USER = 2, |
| MSR_EXPERT_PANEL = 3, |
| MSR_RESERVED_A = 4, |
| MSR_RESERVED_B = 5, |
| MSR_RESERVED_C = 6, |
| MSR_RESERVED_D = 7, |
| MSR_RESERVED_E = 8 |
| } MEASUREMENT_SYSTEM_REQUEST; |
| |
| typedef enum { |
| LPR_DEFAULT = 0, |
| LPR_OFF = 1, |
| LPR_HIGHPASS = 2 |
| } LOUDNESS_PREPROCESSING_REQUEST; |
| |
| typedef enum { |
| DRMRT_SHORT_TERM_LOUDNESS_TO_AVG = 0, |
| DRMRT_MOMENTARY_LOUDNESS_TO_AVG = 1, |
| DRMRT_TOP_OF_LOUDNESS_RANGE_TO_AVG = 2 |
| } DYN_RANGE_MEASUREMENT_REQUEST_TYPE; |
| |
| typedef enum { |
| TCRT_DOWNMIX_ID = 0, |
| TCRT_TARGET_LAYOUT = 1, |
| TCRT_TARGET_CHANNEL_COUNT = 2 |
| } TARGET_CONFIG_REQUEST_TYPE; |
| |
| typedef shouldBeUnion { |
| struct { |
| UCHAR numRequests; |
| UCHAR numRequestsDesired; |
| DRC_EFFECT_TYPE_REQUEST request[MAX_REQUESTS_DRC_EFFECT_TYPE]; |
| } drcEffectType; |
| struct { |
| DYN_RANGE_MEASUREMENT_REQUEST_TYPE measurementRequestType; |
| UCHAR requestedIsRange; |
| FIXP_DBL requestValue; /* e = 7 */ |
| FIXP_DBL requestValueMin; /* e = 7 */ |
| FIXP_DBL requestValueMax; /* e = 7 */ |
| } dynamicRange; |
| UCHAR drcCharacteristic; |
| } |
| DRC_FEATURE_REQUEST; |
| |
| typedef struct { |
| /* system parameters */ |
| SCHAR baseChannelCount; |
| SCHAR baseLayout; /* not supported */ |
| TARGET_CONFIG_REQUEST_TYPE targetConfigRequestType; |
| UCHAR numDownmixIdRequests; |
| UCHAR downmixIdRequested[MAX_REQUESTS_DOWNMIX_ID]; |
| UCHAR targetLayoutRequested; |
| UCHAR targetChannelCountRequested; |
| LONG audioSampleRate; /* needed for complexity estimation, currently not |
| supported */ |
| |
| /* loudness normalization parameters */ |
| UCHAR loudnessNormalizationOn; |
| FIXP_DBL targetLoudness; /* e = 7 */ |
| UCHAR albumMode; |
| UCHAR peakLimiterPresent; |
| UCHAR loudnessDeviationMax; /* resolution: 1 dB */ |
| METHOD_DEFINITION_REQUEST loudnessMeasurementMethod; |
| MEASUREMENT_SYSTEM_REQUEST loudnessMeasurementSystem; |
| LOUDNESS_PREPROCESSING_REQUEST loudnessMeasurementPreProc; /* not supported */ |
| LONG deviceCutOffFrequency; /* not supported */ |
| FIXP_DBL loudnessNormalizationGainDbMax; /* e = 7 */ |
| FIXP_DBL loudnessNormalizationGainModificationDb; /* e = 7 */ |
| FIXP_DBL outputPeakLevelMax; /* e = 7 */ |
| |
| /* dynamic range control parameters */ |
| UCHAR dynamicRangeControlOn; |
| UCHAR numDrcFeatureRequests; |
| DRC_FEATURE_REQUEST_TYPE drcFeatureRequestType[MAX_REQUESTS_DRC_FEATURE]; |
| DRC_FEATURE_REQUEST drcFeatureRequest[MAX_REQUESTS_DRC_FEATURE]; |
| |
| /* other */ |
| FIXP_SGL boost; /* e = 1 */ |
| FIXP_SGL compress; /* e = 1 */ |
| UCHAR drcCharacteristicTarget; /* not supported */ |
| } SEL_PROC_INPUT, *HANDLE_SEL_PROC_INPUT; |
| |
| /* Table E.1 of ISO/IEC DIS 23003-4: Recommended order of fallback effect type |
| * requests */ |
| static DRC_EFFECT_TYPE_REQUEST fallbackEffectTypeRequests[6][5] = { |
| /* Night */ {DETR_GENERAL_COMPR, DETR_NOISY, DETR_LIMITED, DETR_LOWLEVEL, |
| DETR_DIALOG}, |
| /* Noisy */ |
| {DETR_GENERAL_COMPR, DETR_NIGHT, DETR_LIMITED, DETR_LOWLEVEL, DETR_DIALOG}, |
| /* Limited */ |
| {DETR_GENERAL_COMPR, DETR_NIGHT, DETR_NOISY, DETR_LOWLEVEL, DETR_DIALOG}, |
| /* LowLevel */ |
| {DETR_GENERAL_COMPR, DETR_NOISY, DETR_NIGHT, DETR_LIMITED, DETR_DIALOG}, |
| /* Dialog */ |
| {DETR_GENERAL_COMPR, DETR_NIGHT, DETR_NOISY, DETR_LIMITED, DETR_LOWLEVEL}, |
| /* General */ |
| {DETR_NIGHT, DETR_NOISY, DETR_LIMITED, DETR_LOWLEVEL, DETR_DIALOG}}; |
| |
| /*******************************************/ |
| typedef struct { |
| UCHAR selectionFlag; |
| UCHAR downmixIdRequestIndex; |
| FIXP_DBL outputPeakLevel; /* e = 7 */ |
| FIXP_DBL loudnessNormalizationGainDbAdjusted; /* e = 7 */ |
| FIXP_DBL outputLoudness; /* e = 7 */ |
| DRC_INSTRUCTIONS_UNI_DRC* pInst; |
| |
| } DRCDEC_SELECTION_DATA; |
| |
| typedef struct { |
| UCHAR numData; |
| DRCDEC_SELECTION_DATA data[(12 + 1 + 6)]; |
| |
| } DRCDEC_SELECTION; |
| |
| /*******************************************/ |
| /* helper functions */ |
| /*******************************************/ |
| |
| static int _isError(int x) { |
| if (x < DRCDEC_SELECTION_PROCESS_WARNING) { |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* compare and assign */ |
| static inline int _compAssign(UCHAR* dest, const UCHAR src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = src; |
| return diff; |
| } |
| |
| static inline int _compAssign(SCHAR* dest, const SCHAR src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = src; |
| return diff; |
| } |
| |
| static inline int _compAssign(FIXP_DBL* dest, const FIXP_DBL src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = src; |
| return diff; |
| } |
| |
| static inline int _compAssign(FIXP_SGL* dest, const FIXP_SGL src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = src; |
| return diff; |
| } |
| |
| static inline int _compAssign(TARGET_CONFIG_REQUEST_TYPE* dest, const int src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = (TARGET_CONFIG_REQUEST_TYPE)src; |
| return diff; |
| } |
| |
| static inline int _compAssign(METHOD_DEFINITION_REQUEST* dest, const int src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = (METHOD_DEFINITION_REQUEST)src; |
| return diff; |
| } |
| |
| static inline int _compAssign(DRC_FEATURE_REQUEST_TYPE* dest, const int src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = (DRC_FEATURE_REQUEST_TYPE)src; |
| return diff; |
| } |
| |
| static inline int _compAssign(DRC_EFFECT_TYPE_REQUEST* dest, const int src) { |
| int diff = 0; |
| if (*dest != src) diff = 1; |
| *dest = (DRC_EFFECT_TYPE_REQUEST)src; |
| return diff; |
| } |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_addNew( |
| DRCDEC_SELECTION* pSelection); |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_add( |
| DRCDEC_SELECTION* pSelection, DRCDEC_SELECTION_DATA* pDataIn); |
| |
| static int _drcdec_selection_clear(DRCDEC_SELECTION* pSelection); |
| |
| static int _drcdec_selection_getNumber(DRCDEC_SELECTION* pSelection); |
| |
| static int _drcdec_selection_setNumber(DRCDEC_SELECTION* pSelection, int num); |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_getAt( |
| DRCDEC_SELECTION* pSelection, int at); |
| |
| static int _swapSelectionAndClear(DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected); |
| |
| static int _swapSelection(DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected); |
| |
| /*******************************************/ |
| /* declarations of static functions */ |
| /*******************************************/ |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _initDefaultParams( |
| HANDLE_SEL_PROC_INPUT hSelProcInput); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _initCodecModeParams( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, const SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetPreSelection( |
| SEL_PROC_INPUT* hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected, SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_peakValue0( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _dynamicRangeMeasurement( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, DRC_INSTRUCTIONS_UNI_DRC* pInst, |
| UCHAR downmixIdRequested, |
| DYN_RANGE_MEASUREMENT_REQUEST_TYPE dynamicRangeMeasurementType, |
| int albumMode, int* peakToAveragePresent, FIXP_DBL* peakToAverage); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _channelLayoutToDownmixIdMapping( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _generateVirtualDrcSets( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetRequestSelection( |
| SEL_PROC_INPUT* hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected, SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _generateOutputInfo( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_SEL_PROC_OUTPUT hSelProcOutput, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION_DATA* pSelectionData, SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectDownmixMatrix( |
| HANDLE_SEL_PROC_OUTPUT hSelProcOutput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getLoudness( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int albumMode, |
| METHOD_DEFINITION_REQUEST measurementMethodRequested, |
| MEASUREMENT_SYSTEM_REQUEST measurementSystemRequested, |
| FIXP_DBL targetLoudness, int drcSetId, int downmixIdRequested, |
| FIXP_DBL* pLoudnessNormalizationGain, FIXP_DBL* pLoudness); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getMixingLevel( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int downmixIdRequested, |
| int drcSetIdRequested, int albumMode, FIXP_DBL* pMixingLevel); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getSignalPeakLevel( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, DRC_INSTRUCTIONS_UNI_DRC* pInst, |
| int downmixIdRequested, int* explicitPeakInformationPresent, |
| FIXP_DBL* signalPeakLevelOut, /* e = 7 */ |
| SEL_PROC_CODEC_MODE codecMode); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _extractLoudnessPeakToAverageValue( |
| LOUDNESS_INFO* loudnessInfo, |
| DYN_RANGE_MEASUREMENT_REQUEST_TYPE dynamicRangeMeasurementType, |
| int* pLoudnessPeakToAverageValuePresent, |
| FIXP_DBL* pLoudnessPeakToAverageValue); |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectAlbumLoudness( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected); |
| |
| static int _findMethodDefinition(LOUDNESS_INFO* pLoudnessInfo, |
| int methodDefinition, int startIndex); |
| |
| /*******************************************/ |
| /* public functions */ |
| /*******************************************/ |
| |
| struct s_drcdec_selection_process { |
| SEL_PROC_CODEC_MODE codecMode; |
| SEL_PROC_INPUT selProcInput; |
| DRCDEC_SELECTION |
| selectionData[2]; /* 2 instances, one before and one after selection */ |
| }; |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_Create(HANDLE_DRC_SELECTION_PROCESS* phInstance) { |
| HANDLE_DRC_SELECTION_PROCESS hInstance; |
| hInstance = (HANDLE_DRC_SELECTION_PROCESS)FDKcalloc( |
| 1, sizeof(struct s_drcdec_selection_process)); |
| |
| if (!hInstance) return DRCDEC_SELECTION_PROCESS_OUTOFMEMORY; |
| |
| hInstance->codecMode = SEL_PROC_CODEC_MODE_UNDEFINED; |
| |
| *phInstance = hInstance; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_Init(HANDLE_DRC_SELECTION_PROCESS hInstance) { |
| if (!hInstance) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| _initDefaultParams(&hInstance->selProcInput); |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_SetCodecMode(HANDLE_DRC_SELECTION_PROCESS hInstance, |
| const SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| if (!hInstance) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| switch (codecMode) { |
| case SEL_PROC_MPEG_4_AAC: |
| case SEL_PROC_MPEG_D_USAC: |
| case SEL_PROC_TEST_TIME_DOMAIN: |
| case SEL_PROC_TEST_QMF_DOMAIN: |
| case SEL_PROC_TEST_STFT_DOMAIN: |
| hInstance->codecMode = codecMode; |
| break; |
| |
| case SEL_PROC_CODEC_MODE_UNDEFINED: |
| default: |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| retVal = _initCodecModeParams(&(hInstance->selProcInput), |
| hInstance->codecMode = codecMode); |
| |
| return retVal; |
| } |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_SetParam(HANDLE_DRC_SELECTION_PROCESS hInstance, |
| const SEL_PROC_USER_PARAM requestType, |
| FIXP_DBL requestValue, int* pDiff) { |
| INT requestValueInt = (INT)requestValue; |
| int i, diff = 0; |
| SEL_PROC_INPUT* pSelProcInput = &(hInstance->selProcInput); |
| |
| switch (requestType) { |
| case SEL_PROC_LOUDNESS_NORMALIZATION_ON: |
| if ((requestValueInt != 0) && (requestValueInt != 1)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= |
| _compAssign(&pSelProcInput->loudnessNormalizationOn, requestValueInt); |
| break; |
| case SEL_PROC_TARGET_LOUDNESS: |
| /* Lower boundary: drcSetTargetLoudnessValueLower default value. |
| Upper boundary: drcSetTargetLoudnessValueUpper default value */ |
| if ((requestValue < FL2FXCONST_DBL(-63.0f / (float)(1 << 7))) || |
| (requestValue > (FIXP_DBL)0)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| if (requestValue > |
| FL2FXCONST_DBL(-10.0f / |
| (float)(1 << 7))) /* recommended maximum value */ |
| requestValue = FL2FXCONST_DBL(-10.0f / (float)(1 << 7)); |
| diff |= _compAssign(&pSelProcInput->targetLoudness, requestValue); |
| break; |
| case SEL_PROC_EFFECT_TYPE: |
| if ((requestValueInt < -1) || (requestValueInt >= DETR_COUNT)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| /* Caution. This overrides all drcFeatureRequests requested so far! */ |
| if (requestValueInt == -1) { |
| diff |= _compAssign(&pSelProcInput->dynamicRangeControlOn, 0); |
| } else if (requestValueInt == DETR_NONE) { |
| diff |= _compAssign(&pSelProcInput->dynamicRangeControlOn, 1); |
| diff |= _compAssign(&pSelProcInput->numDrcFeatureRequests, 0); |
| } else { |
| diff |= _compAssign(&pSelProcInput->dynamicRangeControlOn, 1); |
| diff |= _compAssign(&pSelProcInput->numDrcFeatureRequests, 1); |
| diff |= _compAssign(&pSelProcInput->drcFeatureRequestType[0], |
| DFRT_EFFECT_TYPE); |
| diff |= _compAssign(&pSelProcInput->drcFeatureRequest[0] |
| .drcEffectType.numRequestsDesired, |
| 1); |
| diff |= _compAssign( |
| &pSelProcInput->drcFeatureRequest[0].drcEffectType.request[0], |
| requestValueInt); |
| if ((requestValueInt > DETR_NONE) && |
| (requestValueInt <= DETR_GENERAL_COMPR)) { |
| /* use fallback effect type requests */ |
| for (i = 0; i < 5; i++) { |
| diff |= |
| _compAssign(&pSelProcInput->drcFeatureRequest[0] |
| .drcEffectType.request[i + 1], |
| fallbackEffectTypeRequests[requestValueInt - 1][i]); |
| } |
| diff |= _compAssign( |
| &pSelProcInput->drcFeatureRequest[0].drcEffectType.numRequests, |
| 6); |
| } else { |
| diff |= _compAssign( |
| &pSelProcInput->drcFeatureRequest[0].drcEffectType.numRequests, |
| 1); |
| } |
| } |
| break; |
| case SEL_PROC_LOUDNESS_MEASUREMENT_METHOD: |
| if ((requestValueInt < 0) || (requestValueInt > 2)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->loudnessMeasurementMethod, |
| requestValueInt); |
| break; |
| case SEL_PROC_DOWNMIX_ID: |
| diff |= |
| _compAssign(&pSelProcInput->targetConfigRequestType, TCRT_DOWNMIX_ID); |
| if (requestValueInt < 0) { /* negative requests signal no downmixId */ |
| diff |= _compAssign(&pSelProcInput->numDownmixIdRequests, 0); |
| } else { |
| diff |= _compAssign(&pSelProcInput->numDownmixIdRequests, 1); |
| diff |= |
| _compAssign(&pSelProcInput->downmixIdRequested[0], requestValueInt); |
| } |
| break; |
| case SEL_PROC_TARGET_LAYOUT: |
| /* Request target layout according to ChannelConfiguration in ISO/IEC |
| * 23001-8 (CICP) */ |
| if ((requestValueInt < 1) || (requestValueInt > 63)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->targetConfigRequestType, |
| TCRT_TARGET_LAYOUT); |
| diff |= |
| _compAssign(&pSelProcInput->targetLayoutRequested, requestValueInt); |
| break; |
| case SEL_PROC_TARGET_CHANNEL_COUNT: |
| if ((requestValueInt < 1) || (requestValueInt > 8)) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->targetConfigRequestType, |
| TCRT_TARGET_CHANNEL_COUNT); |
| diff |= _compAssign(&pSelProcInput->targetChannelCountRequested, |
| requestValueInt); |
| break; |
| case SEL_PROC_BASE_CHANNEL_COUNT: |
| if (requestValueInt < 0) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->baseChannelCount, requestValueInt); |
| break; |
| case SEL_PROC_SAMPLE_RATE: |
| if (requestValueInt < 0) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->audioSampleRate, requestValueInt); |
| break; |
| case SEL_PROC_BOOST: |
| if ((requestValue < (FIXP_DBL)0) || |
| (requestValue > FL2FXCONST_DBL(1.0f / (float)(1 << 1)))) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= _compAssign(&pSelProcInput->boost, FX_DBL2FX_SGL(requestValue)); |
| break; |
| case SEL_PROC_COMPRESS: |
| if ((requestValue < (FIXP_DBL)0) || |
| (requestValue > FL2FXCONST_DBL(1.0f / (float)(1 << 1)))) |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; |
| diff |= |
| _compAssign(&pSelProcInput->compress, FX_DBL2FX_SGL(requestValue)); |
| break; |
| default: |
| return DRCDEC_SELECTION_PROCESS_INVALID_PARAM; |
| } |
| |
| if (pDiff != NULL) { |
| *pDiff |= diff; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| FIXP_DBL |
| drcDec_SelectionProcess_GetParam(HANDLE_DRC_SELECTION_PROCESS hInstance, |
| const SEL_PROC_USER_PARAM requestType) { |
| SEL_PROC_INPUT* pSelProcInput = &(hInstance->selProcInput); |
| |
| switch (requestType) { |
| case SEL_PROC_LOUDNESS_NORMALIZATION_ON: |
| return (FIXP_DBL)pSelProcInput->loudnessNormalizationOn; |
| case SEL_PROC_DYNAMIC_RANGE_CONTROL_ON: |
| return (FIXP_DBL)pSelProcInput->dynamicRangeControlOn; |
| default: |
| return (FIXP_DBL)0; |
| } |
| } |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_Delete(HANDLE_DRC_SELECTION_PROCESS* phInstance) { |
| if (phInstance == NULL || *phInstance == NULL) |
| return DRCDEC_SELECTION_PROCESS_INVALID_HANDLE; |
| |
| FDKfree(*phInstance); |
| *phInstance = NULL; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| DRCDEC_SELECTION_PROCESS_RETURN |
| drcDec_SelectionProcess_Process(HANDLE_DRC_SELECTION_PROCESS hInstance, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| HANDLE_SEL_PROC_OUTPUT hSelProcOutput) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| DRCDEC_SELECTION* pCandidatesSelected; |
| DRCDEC_SELECTION* pCandidatesPotential; |
| |
| if (hInstance == NULL) return DRCDEC_SELECTION_PROCESS_INVALID_HANDLE; |
| |
| pCandidatesSelected = &(hInstance->selectionData[0]); |
| pCandidatesPotential = &(hInstance->selectionData[1]); |
| _drcdec_selection_setNumber(pCandidatesSelected, 0); |
| _drcdec_selection_setNumber(pCandidatesPotential, 0); |
| |
| retVal = _generateVirtualDrcSets(&(hInstance->selProcInput), hUniDrcConfig, |
| hInstance->codecMode); |
| if (retVal) return (retVal); |
| |
| if (hInstance->selProcInput.baseChannelCount != |
| hUniDrcConfig->channelLayout.baseChannelCount) { |
| hInstance->selProcInput.baseChannelCount = |
| hUniDrcConfig->channelLayout.baseChannelCount; |
| } |
| |
| if ((hInstance->selProcInput.targetConfigRequestType != 0) || |
| (hInstance->selProcInput.targetConfigRequestType == 0 && |
| hInstance->selProcInput.numDownmixIdRequests == 0)) { |
| retVal = _channelLayoutToDownmixIdMapping(&(hInstance->selProcInput), |
| hUniDrcConfig); |
| |
| if (_isError(retVal)) return (retVal); |
| } |
| |
| retVal = _drcSetPreSelection(&(hInstance->selProcInput), hUniDrcConfig, |
| hLoudnessInfoSet, &pCandidatesPotential, |
| &pCandidatesSelected, hInstance->codecMode); |
| if (retVal) return (retVal); |
| |
| if (hInstance->selProcInput.albumMode) { |
| _swapSelectionAndClear(&pCandidatesPotential, &pCandidatesSelected); |
| |
| retVal = _selectAlbumLoudness(hLoudnessInfoSet, pCandidatesPotential, |
| pCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if (_drcdec_selection_getNumber(pCandidatesSelected) == 0) { |
| _swapSelection(&pCandidatesPotential, &pCandidatesSelected); |
| } |
| } |
| |
| _swapSelectionAndClear(&pCandidatesPotential, &pCandidatesSelected); |
| |
| retVal = _drcSetRequestSelection(&(hInstance->selProcInput), hUniDrcConfig, |
| hLoudnessInfoSet, &pCandidatesPotential, |
| &pCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| retVal = _drcSetFinalSelection(&(hInstance->selProcInput), hUniDrcConfig, |
| &pCandidatesPotential, &pCandidatesSelected, |
| hInstance->codecMode); |
| if (retVal) return (retVal); |
| |
| retVal = _generateOutputInfo( |
| &(hInstance->selProcInput), hSelProcOutput, hUniDrcConfig, |
| hLoudnessInfoSet, &(pCandidatesSelected->data[0]), hInstance->codecMode); |
| |
| if (_isError(retVal)) return (retVal); |
| |
| retVal = _selectDownmixMatrix(hSelProcOutput, hUniDrcConfig); |
| if (retVal) return (retVal); |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /*******************************************/ |
| /* static functions */ |
| /*******************************************/ |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _initDefaultParams( |
| HANDLE_SEL_PROC_INPUT hSelProcInput) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| if (hSelProcInput == NULL) return DRCDEC_SELECTION_PROCESS_INVALID_HANDLE; |
| |
| /* system parameters */ |
| hSelProcInput->baseChannelCount = -1; |
| hSelProcInput->baseLayout = -1; |
| hSelProcInput->targetConfigRequestType = TCRT_DOWNMIX_ID; |
| hSelProcInput->numDownmixIdRequests = 0; |
| |
| /* loudness normalization parameters */ |
| hSelProcInput->albumMode = 0; |
| hSelProcInput->peakLimiterPresent = 0; |
| hSelProcInput->loudnessNormalizationOn = 1; |
| hSelProcInput->targetLoudness = FL2FXCONST_DBL(-24.0f / (float)(1 << 7)); |
| hSelProcInput->loudnessDeviationMax = DEFAULT_LOUDNESS_DEVIATION_MAX; |
| hSelProcInput->loudnessMeasurementMethod = MDR_DEFAULT; |
| hSelProcInput->loudnessMeasurementSystem = MSR_DEFAULT; |
| hSelProcInput->loudnessMeasurementPreProc = LPR_DEFAULT; |
| hSelProcInput->deviceCutOffFrequency = 500; |
| hSelProcInput->loudnessNormalizationGainDbMax = |
| (FIXP_DBL)MAXVAL_DBL; /* infinity as default */ |
| hSelProcInput->loudnessNormalizationGainModificationDb = (FIXP_DBL)0; |
| hSelProcInput->outputPeakLevelMax = (FIXP_DBL)0; |
| if (hSelProcInput->peakLimiterPresent == 1) { |
| hSelProcInput->outputPeakLevelMax = FL2FXCONST_DBL(6.0f / (float)(1 << 7)); |
| } |
| |
| /* dynamic range control parameters */ |
| hSelProcInput->dynamicRangeControlOn = 1; |
| |
| hSelProcInput->numDrcFeatureRequests = 0; |
| |
| /* other parameters */ |
| hSelProcInput->boost = FL2FXCONST_SGL(1.f / (float)(1 << 1)); |
| hSelProcInput->compress = FL2FXCONST_SGL(1.f / (float)(1 << 1)); |
| hSelProcInput->drcCharacteristicTarget = 0; |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _initCodecModeParams( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, const SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| if (hSelProcInput == NULL) return DRCDEC_SELECTION_PROCESS_INVALID_HANDLE; |
| |
| switch (codecMode) { |
| case SEL_PROC_MPEG_H_3DA: |
| hSelProcInput->loudnessDeviationMax = 0; |
| hSelProcInput->peakLimiterPresent = 1; /* peak limiter is mandatory */ |
| /* The peak limiter also has to catch overshoots due to user |
| interactivity, downmixing etc. Therefore the maximum output peak level is |
| reduced to 0 dB. */ |
| hSelProcInput->outputPeakLevelMax = (FIXP_DBL)0; |
| break; |
| case SEL_PROC_MPEG_4_AAC: |
| case SEL_PROC_MPEG_D_USAC: |
| hSelProcInput->loudnessDeviationMax = DEFAULT_LOUDNESS_DEVIATION_MAX; |
| hSelProcInput->peakLimiterPresent = 1; |
| /* A peak limiter is present at the end of the decoder, therefore we can |
| * allow for a maximum output peak level greater than full scale |
| */ |
| hSelProcInput->outputPeakLevelMax = |
| FL2FXCONST_DBL(6.0f / (float)(1 << 7)); |
| break; |
| case SEL_PROC_TEST_TIME_DOMAIN: |
| case SEL_PROC_TEST_QMF_DOMAIN: |
| case SEL_PROC_TEST_STFT_DOMAIN: |
| /* for testing, adapt to default settings in reference software */ |
| hSelProcInput->loudnessNormalizationOn = 0; |
| hSelProcInput->dynamicRangeControlOn = 0; |
| break; |
| case SEL_PROC_CODEC_MODE_UNDEFINED: |
| default: |
| hSelProcInput->loudnessDeviationMax = DEFAULT_LOUDNESS_DEVIATION_MAX; |
| hSelProcInput->peakLimiterPresent = 0; |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _channelLayoutToDownmixIdMapping( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| DOWNMIX_INSTRUCTIONS* pDown = NULL; |
| |
| int i; |
| |
| hSelProcInput->numDownmixIdRequests = 0; |
| |
| switch (hSelProcInput->targetConfigRequestType) { |
| case TCRT_DOWNMIX_ID: |
| if (hSelProcInput->numDownmixIdRequests == 0) { |
| hSelProcInput->downmixIdRequested[0] = 0; |
| hSelProcInput->numDownmixIdRequests = 1; |
| } |
| |
| break; |
| |
| case TCRT_TARGET_LAYOUT: |
| if (hSelProcInput->targetLayoutRequested == hSelProcInput->baseLayout) { |
| hSelProcInput->downmixIdRequested[0] = 0; |
| hSelProcInput->numDownmixIdRequests = 1; |
| } |
| |
| if (hSelProcInput->numDownmixIdRequests == 0) { |
| for (i = 0; i < hUniDrcConfig->downmixInstructionsCount; i++) { |
| pDown = &(hUniDrcConfig->downmixInstructions[i]); |
| |
| if (hSelProcInput->targetLayoutRequested == pDown->targetLayout) { |
| hSelProcInput |
| ->downmixIdRequested[hSelProcInput->numDownmixIdRequests] = |
| pDown->downmixId; |
| hSelProcInput->numDownmixIdRequests++; |
| } |
| } |
| } |
| |
| if (hSelProcInput->baseLayout == -1) { |
| retVal = DRCDEC_SELECTION_PROCESS_WARNING; |
| } |
| |
| if (hSelProcInput->numDownmixIdRequests == 0) { |
| hSelProcInput->downmixIdRequested[0] = 0; |
| hSelProcInput->numDownmixIdRequests = 1; |
| retVal = DRCDEC_SELECTION_PROCESS_WARNING; |
| } |
| |
| break; |
| |
| case TCRT_TARGET_CHANNEL_COUNT: |
| if (hSelProcInput->targetChannelCountRequested == |
| hSelProcInput->baseChannelCount) { |
| hSelProcInput->downmixIdRequested[0] = 0; |
| hSelProcInput->numDownmixIdRequests = 1; |
| } |
| |
| if (hSelProcInput->numDownmixIdRequests == 0) { |
| for (i = 0; i < hUniDrcConfig->downmixInstructionsCount; i++) { |
| pDown = &(hUniDrcConfig->downmixInstructions[i]); |
| |
| if (hSelProcInput->targetChannelCountRequested == |
| pDown->targetChannelCount) { |
| hSelProcInput |
| ->downmixIdRequested[hSelProcInput->numDownmixIdRequests] = |
| pDown->downmixId; |
| hSelProcInput->numDownmixIdRequests++; |
| } |
| } |
| } |
| |
| if (hSelProcInput->baseChannelCount == -1) { |
| retVal = DRCDEC_SELECTION_PROCESS_WARNING; |
| } |
| |
| if (hSelProcInput->numDownmixIdRequests == 0) { |
| retVal = DRCDEC_SELECTION_PROCESS_WARNING; |
| hSelProcInput->downmixIdRequested[0] = 0; |
| hSelProcInput->numDownmixIdRequests = 1; |
| } |
| |
| break; |
| |
| default: |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| return retVal; |
| } |
| |
| /*******************************************/ |
| |
| /* Note: Numbering of DRC pre-selection steps according to MPEG-D Part-4 DRC |
| * Amd1 */ |
| |
| /* #1: DownmixId of DRC set matches the requested downmixId. |
| #2: Output channel layout of DRC set matches the requested layout. |
| #3: Channel count of DRC set matches the requested channel count. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement123( |
| int nRequestedDownmixId, DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, |
| int* pMatchFound) { |
| int i; |
| *pMatchFound = 0; |
| |
| for (i = 0; i < pDrcInstructionUniDrc->downmixIdCount; i++) { |
| if ((pDrcInstructionUniDrc->downmixId[i] == nRequestedDownmixId) || |
| (pDrcInstructionUniDrc->downmixId[i] == DOWNMIX_ID_ANY_DOWNMIX) || |
| ((pDrcInstructionUniDrc->downmixId[i] == DOWNMIX_ID_BASE_LAYOUT) && |
| (pDrcInstructionUniDrc->drcSetId > 0))) { |
| *pMatchFound = 1; |
| break; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /* #4: The DRC set is not a "Fade-" or "Ducking-" only DRC set. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement4( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstruction, int nDynamicRangeControlOn, |
| int* pMatchFound) { |
| *pMatchFound = 0; |
| |
| if (nDynamicRangeControlOn == 1) { |
| if ((pDrcInstruction->drcSetEffect != EB_FADE) && |
| (pDrcInstruction->drcSetEffect != EB_DUCK_OTHER) && |
| (pDrcInstruction->drcSetEffect != EB_DUCK_SELF) && |
| (pDrcInstruction->drcSetEffect != 0 || pDrcInstruction->drcSetId < 0)) { |
| *pMatchFound = 1; |
| } |
| } else { |
| *pMatchFound = 1; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /* #5: The number of DRC bands is supported. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement5( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, |
| DRC_COEFFICIENTS_UNI_DRC* pCoef, int* pMatchFound) { |
| int i; |
| |
| *pMatchFound = 1; |
| |
| if (pCoef == NULL) /* check for parametricDRC */ |
| { |
| *pMatchFound = 1; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| for (i = 0; i < pDrcInstructionUniDrc->nDrcChannelGroups; i++) { |
| int indexDrcCoeff = pDrcInstructionUniDrc->gainSetIndexForChannelGroup[i]; |
| int bandCount = 0; |
| |
| if (indexDrcCoeff > pCoef->gainSetCount - 1) /* check for parametricDRC */ |
| { |
| *pMatchFound = 1; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| GAIN_SET* gainSet = &(pCoef->gainSet[indexDrcCoeff]); |
| bandCount = gainSet->bandCount; |
| |
| if (bandCount > 4) { |
| *pMatchFound = 0; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /* #6: Independent use of DRC set is permitted.*/ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement6( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, int* pMatchFound) { |
| *pMatchFound = 0; |
| |
| if (((pDrcInstructionUniDrc->dependsOnDrcSetPresent == 0) && |
| (pDrcInstructionUniDrc->noIndependentUse == 0)) || |
| (pDrcInstructionUniDrc->dependsOnDrcSetPresent == 1)) { |
| *pMatchFound = 1; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /* #7: DRC sets that require EQ are only permitted if EQ is supported. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement7( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, int* pMatchFound) { |
| *pMatchFound = 1; |
| |
| if (pDrcInstructionUniDrc->requiresEq) { |
| /* EQ is not supported */ |
| *pMatchFound = 0; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static void _setSelectionDataInfo( |
| DRCDEC_SELECTION_DATA* pData, FIXP_DBL loudness, /* e = 7 */ |
| FIXP_DBL loudnessNormalizationGainDb, /* e = 7 */ |
| FIXP_DBL loudnessNormalizationGainDbMax, /* e = 7 */ |
| FIXP_DBL loudnessDeviationMax, /* e = 7 */ |
| FIXP_DBL signalPeakLevel, /* e = 7 */ |
| FIXP_DBL outputPeakLevelMax, /* e = 7 */ |
| int applyAdjustment) { |
| FIXP_DBL adjustment = 0; /* e = 8 */ |
| |
| /* use e = 8 for all function parameters to prevent overflow */ |
| loudness >>= 1; |
| loudnessNormalizationGainDb >>= 1; |
| loudnessNormalizationGainDbMax >>= 1; |
| loudnessDeviationMax >>= 1; |
| signalPeakLevel >>= 1; |
| outputPeakLevelMax >>= 1; |
| |
| if (applyAdjustment) { |
| adjustment = |
| fMax((FIXP_DBL)0, signalPeakLevel + loudnessNormalizationGainDb - |
| outputPeakLevelMax); |
| adjustment = fMin(adjustment, fMax((FIXP_DBL)0, loudnessDeviationMax)); |
| } |
| |
| pData->loudnessNormalizationGainDbAdjusted = fMin( |
| loudnessNormalizationGainDb - adjustment, loudnessNormalizationGainDbMax); |
| pData->outputLoudness = loudness + pData->loudnessNormalizationGainDbAdjusted; |
| pData->outputPeakLevel = |
| signalPeakLevel + pData->loudnessNormalizationGainDbAdjusted; |
| |
| /* shift back to e = 7 using saturation */ |
| pData->loudnessNormalizationGainDbAdjusted = SATURATE_LEFT_SHIFT( |
| pData->loudnessNormalizationGainDbAdjusted, 1, DFRACT_BITS); |
| pData->outputLoudness = |
| SATURATE_LEFT_SHIFT(pData->outputLoudness, 1, DFRACT_BITS); |
| pData->outputPeakLevel = |
| SATURATE_LEFT_SHIFT(pData->outputPeakLevel, 1, DFRACT_BITS); |
| } |
| |
| static int _targetLoudnessInRange( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, FIXP_DBL targetLoudness) { |
| int retVal = 0; |
| |
| FIXP_DBL drcSetTargetLoudnessValueUpper = |
| ((FIXP_DBL)pDrcInstructionUniDrc->drcSetTargetLoudnessValueUpper) |
| << (DFRACT_BITS - 1 - 7); |
| FIXP_DBL drcSetTargetLoudnessValueLower = |
| ((FIXP_DBL)pDrcInstructionUniDrc->drcSetTargetLoudnessValueLower) |
| << (DFRACT_BITS - 1 - 7); |
| |
| if (pDrcInstructionUniDrc->drcSetTargetLoudnessPresent && |
| drcSetTargetLoudnessValueUpper >= targetLoudness && |
| drcSetTargetLoudnessValueLower < targetLoudness) { |
| retVal = 1; |
| } |
| |
| return retVal; |
| } |
| |
| /* #8: The range of the target loudness specified for a DRC set has to include |
| * the requested decoder target loudness. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement8( |
| SEL_PROC_INPUT* hSelProcInput, int downmixIdIndex, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected, SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int explicitPeakInformationPresent; |
| FIXP_DBL signalPeakLevel; |
| int addToCandidate = 0; |
| |
| FIXP_DBL loudnessNormalizationGainDb; |
| FIXP_DBL loudness; |
| |
| FIXP_DBL loudnessDeviationMax = |
| ((FIXP_DBL)hSelProcInput->loudnessDeviationMax) << (DFRACT_BITS - 1 - 7); |
| ; |
| |
| if (hSelProcInput->loudnessNormalizationOn) { |
| retVal = _getLoudness(hLoudnessInfoSet, hSelProcInput->albumMode, |
| hSelProcInput->loudnessMeasurementMethod, |
| hSelProcInput->loudnessMeasurementSystem, |
| hSelProcInput->targetLoudness, |
| pDrcInstructionUniDrc->drcSetId, |
| hSelProcInput->downmixIdRequested[downmixIdIndex], |
| &loudnessNormalizationGainDb, &loudness); |
| if (retVal) return (retVal); |
| } else { |
| loudnessNormalizationGainDb = (FIXP_DBL)0; |
| loudness = UNDEFINED_LOUDNESS_VALUE; |
| } |
| |
| retVal = _getSignalPeakLevel( |
| hSelProcInput, hUniDrcConfig, hLoudnessInfoSet, pDrcInstructionUniDrc, |
| hSelProcInput->downmixIdRequested[downmixIdIndex], |
| &explicitPeakInformationPresent, &signalPeakLevel, codecMode |
| |
| ); |
| if (retVal) return (retVal); |
| |
| if (hSelProcInput->dynamicRangeControlOn) { |
| if (explicitPeakInformationPresent == 0) { |
| if (pDrcInstructionUniDrc->drcSetTargetLoudnessPresent && |
| ((hSelProcInput->loudnessNormalizationOn && |
| _targetLoudnessInRange(pDrcInstructionUniDrc, |
| hSelProcInput->targetLoudness)) || |
| !hSelProcInput->loudnessNormalizationOn)) { |
| DRCDEC_SELECTION_DATA* pData = |
| _drcdec_selection_addNew(pCandidatesSelected); |
| if (pData == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| _setSelectionDataInfo(pData, loudness, loudnessNormalizationGainDb, |
| hSelProcInput->loudnessNormalizationGainDbMax, |
| loudnessDeviationMax, signalPeakLevel, |
| hSelProcInput->outputPeakLevelMax, 0); |
| pData->downmixIdRequestIndex = downmixIdIndex; |
| pData->pInst = pDrcInstructionUniDrc; |
| pData->selectionFlag = |
| 1; /* signal pre-selection step dealing with drcSetTargetLoudness */ |
| |
| if (hSelProcInput->loudnessNormalizationOn) { |
| pData->outputPeakLevel = |
| hSelProcInput->targetLoudness - |
| (((FIXP_DBL)pData->pInst->drcSetTargetLoudnessValueUpper) |
| << (DFRACT_BITS - 1 - 7)); |
| } else { |
| pData->outputPeakLevel = (FIXP_DBL)0; |
| } |
| } else { |
| if ((!hSelProcInput->loudnessNormalizationOn) || |
| (!pDrcInstructionUniDrc->drcSetTargetLoudnessPresent) || |
| (hSelProcInput->loudnessNormalizationOn && |
| _targetLoudnessInRange(pDrcInstructionUniDrc, |
| hSelProcInput->targetLoudness))) { |
| addToCandidate = 1; |
| } |
| } |
| } else { |
| addToCandidate = 1; |
| } |
| |
| if (addToCandidate) { |
| DRCDEC_SELECTION_DATA* pData = |
| _drcdec_selection_addNew(pCandidatesPotential); |
| if (pData == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| _setSelectionDataInfo(pData, loudness, loudnessNormalizationGainDb, |
| hSelProcInput->loudnessNormalizationGainDbMax, |
| loudnessDeviationMax, signalPeakLevel, |
| hSelProcInput->outputPeakLevelMax, 0); |
| pData->downmixIdRequestIndex = downmixIdIndex; |
| pData->pInst = pDrcInstructionUniDrc; |
| pData->selectionFlag = 0; |
| } |
| } else { |
| if (pDrcInstructionUniDrc->drcSetId < 0) { |
| DRCDEC_SELECTION_DATA* pData = |
| _drcdec_selection_addNew(pCandidatesSelected); |
| if (pData == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| _setSelectionDataInfo(pData, loudness, loudnessNormalizationGainDb, |
| hSelProcInput->loudnessNormalizationGainDbMax, |
| loudnessDeviationMax, signalPeakLevel, |
| hSelProcInput->outputPeakLevelMax, 1); |
| |
| pData->downmixIdRequestIndex = downmixIdIndex; |
| pData->pInst = pDrcInstructionUniDrc; |
| pData->selectionFlag = 0; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /* #9: Clipping is minimized. */ |
| static DRCDEC_SELECTION_PROCESS_RETURN _preSelectionRequirement9( |
| SEL_PROC_INPUT* hSelProcInput, DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| if (pCandidate->outputPeakLevel <= hSelProcInput->outputPeakLevelMax) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetPreSelectionSingleInstruction( |
| SEL_PROC_INPUT* hSelProcInput, int downmixIdIndex, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc, |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected, SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int matchFound = 0; |
| DRC_COEFFICIENTS_UNI_DRC* pCoef = |
| selectDrcCoefficients(hUniDrcConfig, LOCATION_SELECTED); |
| |
| retVal = _preSelectionRequirement123( |
| hSelProcInput->downmixIdRequested[downmixIdIndex], pDrcInstructionUniDrc, |
| &matchFound); |
| |
| if (!retVal && matchFound) |
| retVal = _preSelectionRequirement4(pDrcInstructionUniDrc, |
| hSelProcInput->dynamicRangeControlOn, |
| &matchFound); |
| |
| if (!retVal && matchFound) |
| retVal = |
| _preSelectionRequirement5(pDrcInstructionUniDrc, pCoef, &matchFound); |
| |
| if (!retVal && matchFound) |
| retVal = _preSelectionRequirement6(pDrcInstructionUniDrc, &matchFound); |
| |
| if (!retVal && matchFound) |
| retVal = _preSelectionRequirement7(pDrcInstructionUniDrc, &matchFound); |
| |
| if (!retVal && matchFound) |
| retVal = _preSelectionRequirement8( |
| hSelProcInput, downmixIdIndex, hUniDrcConfig, hLoudnessInfoSet, |
| pDrcInstructionUniDrc, pCandidatesPotential, pCandidatesSelected, |
| codecMode); |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetSelectionAddCandidates( |
| SEL_PROC_INPUT* hSelProcInput, DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int nHitCount = 0; |
| int i; |
| |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pDrcInstructionUniDrc = pCandidate->pInst; |
| |
| if (_targetLoudnessInRange(pDrcInstructionUniDrc, |
| hSelProcInput->targetLoudness)) { |
| nHitCount++; |
| } |
| } |
| |
| if (nHitCount != 0) { |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pDrcInstructionUniDrc = pCandidate->pInst; |
| |
| if (_targetLoudnessInRange(pDrcInstructionUniDrc, |
| hSelProcInput->targetLoudness)) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } else { |
| FIXP_DBL lowestPeakLevel = MAXVAL_DBL; /* e = 7 */ |
| FIXP_DBL peakLevel = 0; /* e = 7 */ |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| peakLevel = pCandidate->outputPeakLevel; |
| |
| if (peakLevel < lowestPeakLevel) { |
| lowestPeakLevel = peakLevel; |
| } |
| } |
| |
| /* add all with lowest peak level or max 1dB above */ |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| FIXP_DBL loudnessDeviationMax = |
| ((FIXP_DBL)hSelProcInput->loudnessDeviationMax) |
| << (DFRACT_BITS - 1 - 7); /* e = 7 */ |
| |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| peakLevel = pCandidate->outputPeakLevel; |
| |
| if (peakLevel == lowestPeakLevel || |
| peakLevel <= |
| lowestPeakLevel + FL2FXCONST_DBL(1.0f / (float)(1 << 7))) { |
| FIXP_DBL adjustment = |
| fMax((FIXP_DBL)0, peakLevel - hSelProcInput->outputPeakLevelMax); |
| adjustment = fMin(adjustment, fMax((FIXP_DBL)0, loudnessDeviationMax)); |
| |
| pCandidate->loudnessNormalizationGainDbAdjusted -= adjustment; |
| pCandidate->outputPeakLevel -= adjustment; |
| pCandidate->outputLoudness -= adjustment; |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _dependentDrcInstruction( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, DRC_INSTRUCTIONS_UNI_DRC* pInst, |
| DRC_INSTRUCTIONS_UNI_DRC** ppDrcInstructionsDependent) { |
| int i; |
| DRC_INSTRUCTIONS_UNI_DRC* pDependentDrc = NULL; |
| |
| for (i = 0; i < hUniDrcConfig->drcInstructionsUniDrcCount; i++) { |
| pDependentDrc = |
| (DRC_INSTRUCTIONS_UNI_DRC*)&(hUniDrcConfig->drcInstructionsUniDrc[i]); |
| |
| if (pDependentDrc->drcSetId == pInst->dependsOnDrcSet) { |
| break; |
| } |
| } |
| |
| if (i == hUniDrcConfig->drcInstructionsUniDrcCount) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| if (pDependentDrc->dependsOnDrcSetPresent == 1) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| *ppDrcInstructionsDependent = pDependentDrc; |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectDrcSetEffectNone( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| if ((pCandidate->pInst->drcSetEffect & 0xff) == 0) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectSingleEffectType( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, DRC_EFFECT_TYPE_REQUEST effectType, |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| DRC_INSTRUCTIONS_UNI_DRC* pInst; |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionsDependent; |
| |
| if (effectType == DETR_NONE) { |
| retVal = _selectDrcSetEffectNone(hUniDrcConfig, pCandidatesPotential, |
| pCandidatesSelected); |
| if (retVal) return (retVal); |
| } else { |
| int effectBitPosition = 1 << (effectType - 1); |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pInst = pCandidate->pInst; |
| |
| if (!pInst->dependsOnDrcSetPresent) { |
| if ((pInst->drcSetEffect & effectBitPosition)) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } else { |
| retVal = _dependentDrcInstruction(hUniDrcConfig, pInst, |
| &pDrcInstructionsDependent); |
| if (retVal) return (retVal); |
| |
| if (((pInst->drcSetEffect & effectBitPosition)) || |
| ((pDrcInstructionsDependent->drcSetEffect & effectBitPosition))) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectEffectTypeFeature( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, DRC_FEATURE_REQUEST drcFeatureRequest, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int i; |
| int desiredEffectTypeFound = 0; |
| |
| for (i = 0; i < drcFeatureRequest.drcEffectType.numRequestsDesired; i++) { |
| retVal = _selectSingleEffectType( |
| hUniDrcConfig, drcFeatureRequest.drcEffectType.request[i], |
| *ppCandidatesPotential, *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected)) { |
| desiredEffectTypeFound = 1; |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| } |
| } |
| |
| if (!desiredEffectTypeFound) { |
| for (i = drcFeatureRequest.drcEffectType.numRequestsDesired; |
| i < drcFeatureRequest.drcEffectType.numRequests; i++) { |
| retVal = _selectSingleEffectType( |
| hUniDrcConfig, drcFeatureRequest.drcEffectType.request[i], |
| *ppCandidatesPotential, *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected)) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| break; |
| } |
| } |
| } |
| |
| _swapSelection(ppCandidatesPotential, ppCandidatesSelected); |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectDynamicRange( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRC_FEATURE_REQUEST drcFeatureRequest, UCHAR* pDownmixIdRequested, |
| int albumMode, DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* ppCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int i; |
| int peakToAveragePresent; |
| FIXP_DBL peakToAverage; |
| |
| FIXP_DBL minVal = MAXVAL_DBL; |
| FIXP_DBL val = 0; |
| |
| int numSelectedCandidates = _drcdec_selection_getNumber(ppCandidatesSelected); |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| retVal = _dynamicRangeMeasurement( |
| hLoudnessInfoSet, pCandidate->pInst, |
| pDownmixIdRequested[pCandidate->downmixIdRequestIndex], |
| drcFeatureRequest.dynamicRange.measurementRequestType, albumMode, |
| &peakToAveragePresent, &peakToAverage); |
| if (retVal) return (retVal); |
| |
| if (peakToAveragePresent) { |
| if (!drcFeatureRequest.dynamicRange.requestedIsRange) { |
| val = fAbs(drcFeatureRequest.dynamicRange.requestValue - peakToAverage); |
| |
| if (minVal > val) { |
| minVal = val; |
| |
| _drcdec_selection_setNumber(ppCandidatesSelected, |
| numSelectedCandidates); |
| } |
| if (_drcdec_selection_add(ppCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } else { |
| if ((peakToAverage >= drcFeatureRequest.dynamicRange.requestValueMin) && |
| (peakToAverage <= drcFeatureRequest.dynamicRange.requestValueMax)) { |
| if (_drcdec_selection_add(ppCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectSingleDrcCharacteristic( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, int requestedDrcCharacteristic, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| int i, j, b; |
| int hit = 0; |
| |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = NULL; |
| DRC_COEFFICIENTS_UNI_DRC* pCoef = NULL; |
| GAIN_SET* pGainSet = NULL; |
| |
| if (requestedDrcCharacteristic < 1) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| pCoef = selectDrcCoefficients(hUniDrcConfig, LOCATION_SELECTED); |
| |
| if (pCoef == NULL) /* check for parametricDRC */ |
| { |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| for (i = 0; i < _drcdec_selection_getNumber(*ppCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(*ppCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pInst = pCandidate->pInst; |
| |
| hit = 0; |
| |
| for (j = 0; j < pInst->nDrcChannelGroups; j++) { |
| int bandCount = 0; |
| int indexDrcCoeff = pInst->gainSetIndexForChannelGroup[j]; |
| |
| if (indexDrcCoeff > pCoef->gainSetCount - 1) /* check for parametricDRC */ |
| { |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| pGainSet = &(pCoef->gainSet[indexDrcCoeff]); |
| bandCount = pGainSet->bandCount; |
| |
| for (b = 0; b < bandCount; b++) { |
| if ((pGainSet->drcCharacteristic[b].isCICP) && |
| (pGainSet->drcCharacteristic[b].cicpIndex == |
| requestedDrcCharacteristic)) { |
| hit = 1; |
| break; |
| } |
| } |
| |
| if (hit) break; |
| } |
| |
| if (hit) { |
| if (_drcdec_selection_add(*ppCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected)) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectDrcCharacteristic( |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, int drcCharacteristicRequested, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| const int secondTry[12] = {0, 2, 3, 4, 5, 6, 5, 9, 10, 7, 8, 10}; |
| |
| retVal = _selectSingleDrcCharacteristic( |
| hUniDrcConfig, drcCharacteristicRequested, ppCandidatesPotential, |
| ppCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if ((drcCharacteristicRequested <= 11) && |
| (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0)) { |
| retVal = _selectSingleDrcCharacteristic( |
| hUniDrcConfig, secondTry[drcCharacteristicRequested], |
| ppCandidatesPotential, ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0) { |
| if ((drcCharacteristicRequested >= 2) && |
| (drcCharacteristicRequested <= 5)) { |
| retVal = _selectSingleDrcCharacteristic( |
| hUniDrcConfig, drcCharacteristicRequested - 1, ppCandidatesPotential, |
| ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } else if (drcCharacteristicRequested == 11) { |
| retVal = _selectSingleDrcCharacteristic( |
| hUniDrcConfig, 9, ppCandidatesPotential, ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| } |
| |
| _swapSelection(ppCandidatesPotential, ppCandidatesSelected); |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_peakValue0( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| DRCDEC_SELECTION_DATA* pCandidate = |
| _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| if (pCandidate->outputPeakLevel <= FIXP_DBL(0)) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_downmixId( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| int i, j; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(*ppCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(*ppCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pInst = pCandidate->pInst; |
| |
| for (j = 0; j < pInst->downmixIdCount; j++) { |
| if (DOWNMIX_ID_BASE_LAYOUT != pInst->downmixId[j] && |
| DOWNMIX_ID_ANY_DOWNMIX != pInst->downmixId[j] && |
| hSelProcInput |
| ->downmixIdRequested[pCandidate->downmixIdRequestIndex] == |
| pInst->downmixId[j]) { |
| if (_drcdec_selection_add(*ppCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0) { |
| _swapSelection(ppCandidatesPotential, ppCandidatesSelected); |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static int _crossSum(int value) { |
| int sum = 0; |
| |
| while (value != 0) { |
| if ((value & 1) == 1) { |
| sum++; |
| } |
| |
| value >>= 1; |
| } |
| |
| return sum; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_effectTypes( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| int minNumEffects = 1000; |
| int numEffects = 0; |
| int effects = 0; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pInst = pCandidate->pInst; |
| |
| effects = pInst->drcSetEffect; |
| effects &= 0xffff ^ (EB_GENERAL_COMPR); |
| numEffects = _crossSum(effects); |
| |
| if (numEffects < minNumEffects) { |
| minNumEffects = numEffects; |
| } |
| } |
| |
| /* add all with minimum number of effects */ |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pInst = pCandidate->pInst; |
| |
| effects = pInst->drcSetEffect; |
| effects &= 0xffff ^ (EB_GENERAL_COMPR); |
| numEffects = _crossSum(effects); |
| |
| if (numEffects == minNumEffects) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectSmallestTargetLoudnessValueUpper( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| SCHAR minVal = 0x7F; |
| SCHAR val = 0; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| val = pCandidate->pInst->drcSetTargetLoudnessValueUpper; |
| |
| if (val < minVal) { |
| minVal = val; |
| } |
| } |
| |
| /* add all with same smallest drcSetTargetLoudnessValueUpper */ |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| val = pCandidate->pInst->drcSetTargetLoudnessValueUpper; |
| |
| if (val == minVal) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_targetLoudness( |
| FIXP_DBL targetLoudness, DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int i; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| if (pCandidate->selectionFlag == 0) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| if (_drcdec_selection_getNumber(pCandidatesSelected) == 0) { |
| retVal = _selectSmallestTargetLoudnessValueUpper(pCandidatesPotential, |
| pCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(pCandidatesSelected) > 1) { |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstructionUniDrc = NULL; |
| |
| _swapSelectionAndClear(&pCandidatesPotential, &pCandidatesSelected); |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| pDrcInstructionUniDrc = pCandidate->pInst; |
| |
| if (_targetLoudnessInRange(pDrcInstructionUniDrc, targetLoudness)) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| if (_drcdec_selection_getNumber(pCandidatesSelected) > 1) { |
| _swapSelectionAndClear(&pCandidatesPotential, &pCandidatesSelected); |
| |
| retVal = _selectSmallestTargetLoudnessValueUpper(pCandidatesPotential, |
| pCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_peakValueLargest( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| FIXP_DBL largestPeakLevel = MINVAL_DBL; |
| FIXP_DBL peakLevel = 0; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| peakLevel = pCandidate->outputPeakLevel; |
| |
| if (peakLevel > largestPeakLevel) { |
| largestPeakLevel = peakLevel; |
| } |
| } |
| |
| /* add all with same largest peak level */ |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| peakLevel = pCandidate->outputPeakLevel; |
| |
| if (peakLevel == largestPeakLevel) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection_drcSetId( |
| DRCDEC_SELECTION* pCandidatesPotential, |
| DRCDEC_SELECTION* pCandidatesSelected) { |
| int i; |
| int largestId = -1000; |
| int id = 0; |
| DRCDEC_SELECTION_DATA* pCandidate = NULL; |
| DRCDEC_SELECTION_DATA* pCandidateSelected = NULL; |
| |
| for (i = 0; i < _drcdec_selection_getNumber(pCandidatesPotential); i++) { |
| pCandidate = _drcdec_selection_getAt(pCandidatesPotential, i); |
| if (pCandidate == NULL) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| id = pCandidate->pInst->drcSetId; |
| |
| if (id > largestId) { |
| largestId = id; |
| pCandidateSelected = pCandidate; |
| } |
| } |
| |
| if (pCandidateSelected != NULL) { |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidateSelected) == NULL) |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } else { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetFinalSelection( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected, SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesPotential) == 0) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } else if (_drcdec_selection_getNumber(*ppCandidatesPotential) == 1) { |
| _swapSelection(ppCandidatesPotential, ppCandidatesSelected); |
| /* finished */ |
| } else /* > 1 */ |
| { |
| retVal = _drcSetFinalSelection_peakValue0(*ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 1) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| retVal = _drcSetFinalSelection_downmixId( |
| hSelProcInput, ppCandidatesPotential, ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 1) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| retVal = _drcSetFinalSelection_effectTypes(*ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 1) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| retVal = _drcSetFinalSelection_targetLoudness( |
| hSelProcInput->targetLoudness, *ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 1) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| retVal = _drcSetFinalSelection_peakValueLargest(*ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 1) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| retVal = _drcSetFinalSelection_drcSetId(*ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| } |
| } |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _generateVirtualDrcSets( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| SEL_PROC_CODEC_MODE codecMode) { |
| int i; |
| int nMixes = hUniDrcConfig->downmixInstructionsCount + 1; |
| int index = hUniDrcConfig->drcInstructionsUniDrcCount; |
| int indexVirtual = -1; |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstruction = |
| &(hUniDrcConfig->drcInstructionsUniDrc[index]); |
| |
| if (codecMode == SEL_PROC_MPEG_H_3DA) { |
| nMixes = 1; |
| } |
| |
| if ((index + nMixes) > (12 + 1 + 6)) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| FDKmemset(pDrcInstruction, 0, sizeof(DRC_INSTRUCTIONS_UNI_DRC)); |
| |
| pDrcInstruction->drcSetId = indexVirtual; |
| index++; |
| indexVirtual--; |
| pDrcInstruction->downmixIdCount = 1; |
| |
| if ((codecMode == SEL_PROC_MPEG_H_3DA) && |
| (hSelProcInput->numDownmixIdRequests)) { |
| pDrcInstruction->downmixId[0] = hSelProcInput->downmixIdRequested[0]; |
| } else { |
| pDrcInstruction->downmixId[0] = DOWNMIX_ID_BASE_LAYOUT; |
| } |
| |
| for (i = 1; i < nMixes; i++) { |
| pDrcInstruction = &(hUniDrcConfig->drcInstructionsUniDrc[index]); |
| FDKmemset(pDrcInstruction, 0, sizeof(DRC_INSTRUCTIONS_UNI_DRC)); |
| pDrcInstruction->drcSetId = indexVirtual; |
| pDrcInstruction->downmixId[0] = |
| hUniDrcConfig->downmixInstructions[i - 1].downmixId; |
| pDrcInstruction->downmixIdCount = 1; |
| index++; |
| indexVirtual--; |
| } |
| |
| hUniDrcConfig->drcInstructionsCountInclVirtual = |
| hUniDrcConfig->drcInstructionsUniDrcCount + nMixes; |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _generateOutputInfo( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_SEL_PROC_OUTPUT hSelProcOutput, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION_DATA* pSelectionData, SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| int i, j; |
| int hasDependend = 0; |
| int hasFading = 0; |
| int hasDucking = 0; |
| int selectedDrcSetIds; |
| int selectedDownmixIds; |
| FIXP_DBL mixingLevel = 0; |
| int albumMode = hSelProcInput->albumMode; |
| UCHAR* pDownmixIdRequested = hSelProcInput->downmixIdRequested; |
| FIXP_SGL boost = hSelProcInput->boost; |
| FIXP_SGL compress = hSelProcInput->compress; |
| |
| hSelProcOutput->numSelectedDrcSets = 1; |
| hSelProcOutput->selectedDrcSetIds[0] = pSelectionData->pInst->drcSetId; |
| hSelProcOutput->selectedDownmixIds[0] = |
| pSelectionData->pInst->drcApplyToDownmix == 1 |
| ? pSelectionData->pInst->downmixId[0] |
| : 0; |
| hSelProcOutput->loudnessNormalizationGainDb = |
| pSelectionData->loudnessNormalizationGainDbAdjusted + |
| hSelProcInput->loudnessNormalizationGainModificationDb; |
| hSelProcOutput->outputPeakLevelDb = pSelectionData->outputPeakLevel; |
| |
| hSelProcOutput->boost = boost; |
| hSelProcOutput->compress = compress; |
| hSelProcOutput->baseChannelCount = |
| hUniDrcConfig->channelLayout.baseChannelCount; |
| hSelProcOutput->targetChannelCount = |
| hUniDrcConfig->channelLayout.baseChannelCount; |
| hSelProcOutput->activeDownmixId = |
| pDownmixIdRequested[pSelectionData->downmixIdRequestIndex]; |
| |
| _getMixingLevel(hLoudnessInfoSet, *pDownmixIdRequested, |
| hSelProcOutput->selectedDrcSetIds[0], albumMode, |
| &mixingLevel); |
| hSelProcOutput->mixingLevel = mixingLevel; |
| |
| /*dependent*/ |
| if (pSelectionData->pInst->dependsOnDrcSetPresent) { |
| int dependsOnDrcSetID = pSelectionData->pInst->dependsOnDrcSet; |
| |
| for (i = 0; i < hUniDrcConfig->drcInstructionsCountInclVirtual; i++) { |
| if (hUniDrcConfig->drcInstructionsUniDrc[i].drcSetId == |
| dependsOnDrcSetID) { |
| hSelProcOutput->selectedDrcSetIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcSetId; |
| hSelProcOutput->selectedDownmixIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcApplyToDownmix == 1 |
| ? hUniDrcConfig->drcInstructionsUniDrc[i].downmixId[0] |
| : 0; |
| hSelProcOutput->numSelectedDrcSets++; |
| hasDependend = 1; |
| break; |
| } |
| } |
| } |
| |
| /* fading */ |
| if (hSelProcInput->albumMode == 0) { |
| for (i = 0; i < hUniDrcConfig->drcInstructionsUniDrcCount; i++) { |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = |
| &(hUniDrcConfig->drcInstructionsUniDrc[i]); |
| |
| if (pInst->drcSetEffect & EB_FADE) { |
| if (pInst->downmixId[0] == DOWNMIX_ID_ANY_DOWNMIX) { |
| hSelProcOutput->numSelectedDrcSets = hasDependend + 1; |
| hSelProcOutput |
| ->selectedDrcSetIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcSetId; |
| hSelProcOutput |
| ->selectedDownmixIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcApplyToDownmix == 1 |
| ? hUniDrcConfig->drcInstructionsUniDrc[i].downmixId[0] |
| : 0; |
| hSelProcOutput->numSelectedDrcSets++; |
| hasFading = 1; |
| |
| } else { |
| retVal = DRCDEC_SELECTION_PROCESS_NOT_OK; |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| } |
| |
| /* ducking */ |
| for (i = 0; i < hUniDrcConfig->drcInstructionsUniDrcCount; i++) { |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = |
| &(hUniDrcConfig->drcInstructionsUniDrc[i]); |
| |
| if (pInst->drcSetEffect & (EB_DUCK_OTHER | EB_DUCK_SELF)) { |
| for (j = 0; j < pInst->downmixIdCount; j++) { |
| if (pInst->downmixId[j] == hSelProcOutput->activeDownmixId) { |
| hSelProcOutput->numSelectedDrcSets = |
| hasDependend + 1; /* ducking overrides fading */ |
| |
| hSelProcOutput |
| ->selectedDrcSetIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcSetId; |
| /* force ducking DRC set to be processed on base layout */ |
| hSelProcOutput |
| ->selectedDownmixIds[hSelProcOutput->numSelectedDrcSets] = 0; |
| hSelProcOutput->numSelectedDrcSets++; |
| hasDucking = 1; |
| } |
| } |
| } |
| } |
| |
| /* repeat for DOWNMIX_ID_BASE_LAYOUT if no ducking found*/ |
| |
| if (!hasDucking) { |
| for (i = 0; i < hUniDrcConfig->drcInstructionsUniDrcCount; i++) { |
| DRC_INSTRUCTIONS_UNI_DRC* pInst = |
| &(hUniDrcConfig->drcInstructionsUniDrc[i]); |
| |
| if (pInst->drcSetEffect & (EB_DUCK_OTHER | EB_DUCK_SELF)) { |
| for (j = 0; j < pInst->downmixIdCount; j++) { |
| if (pInst->downmixId[j] == DOWNMIX_ID_BASE_LAYOUT) { |
| hSelProcOutput->numSelectedDrcSets = hasDependend + hasFading + 1; |
| hSelProcOutput |
| ->selectedDrcSetIds[hSelProcOutput->numSelectedDrcSets] = |
| hUniDrcConfig->drcInstructionsUniDrc[i].drcSetId; |
| /* force ducking DRC set to be processed on base layout */ |
| hSelProcOutput |
| ->selectedDownmixIds[hSelProcOutput->numSelectedDrcSets] = 0; |
| hSelProcOutput->numSelectedDrcSets++; |
| } |
| } |
| } |
| } |
| } |
| |
| if (hSelProcOutput->numSelectedDrcSets > 3) { |
| /* maximum permitted number of applied DRC sets is 3, see section 6.3.5 of |
| * ISO/IEC 23003-4 */ |
| hSelProcOutput->numSelectedDrcSets = 0; |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| /* sorting: Ducking/Fading -> Dependent -> Selected */ |
| if (hSelProcOutput->numSelectedDrcSets == 3) { |
| selectedDrcSetIds = hSelProcOutput->selectedDrcSetIds[0]; |
| selectedDownmixIds = hSelProcOutput->selectedDownmixIds[0]; |
| hSelProcOutput->selectedDrcSetIds[0] = hSelProcOutput->selectedDrcSetIds[2]; |
| hSelProcOutput->selectedDownmixIds[0] = |
| hSelProcOutput->selectedDownmixIds[2]; |
| hSelProcOutput->selectedDrcSetIds[2] = selectedDrcSetIds; |
| hSelProcOutput->selectedDownmixIds[2] = selectedDownmixIds; |
| } else if (hSelProcOutput->numSelectedDrcSets == 2) { |
| selectedDrcSetIds = hSelProcOutput->selectedDrcSetIds[0]; |
| selectedDownmixIds = hSelProcOutput->selectedDownmixIds[0]; |
| hSelProcOutput->selectedDrcSetIds[0] = hSelProcOutput->selectedDrcSetIds[1]; |
| hSelProcOutput->selectedDownmixIds[0] = |
| hSelProcOutput->selectedDownmixIds[1]; |
| hSelProcOutput->selectedDrcSetIds[1] = selectedDrcSetIds; |
| hSelProcOutput->selectedDownmixIds[1] = selectedDownmixIds; |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectDownmixMatrix( |
| HANDLE_SEL_PROC_OUTPUT hSelProcOutput, |
| HANDLE_UNI_DRC_CONFIG hUniDrcConfig) { |
| int i; |
| hSelProcOutput->baseChannelCount = |
| hUniDrcConfig->channelLayout.baseChannelCount; |
| hSelProcOutput->targetChannelCount = |
| hUniDrcConfig->channelLayout.baseChannelCount; |
| hSelProcOutput->targetLayout = -1; |
| hSelProcOutput->downmixMatrixPresent = 0; |
| |
| if (hSelProcOutput->activeDownmixId != 0) { |
| for (i = 0; i < hUniDrcConfig->downmixInstructionsCount; i++) { |
| DOWNMIX_INSTRUCTIONS* pDown = &(hUniDrcConfig->downmixInstructions[i]); |
| |
| if (hSelProcOutput->activeDownmixId == pDown->downmixId) { |
| hSelProcOutput->targetChannelCount = pDown->targetChannelCount; |
| hSelProcOutput->targetLayout = pDown->targetLayout; |
| |
| if (pDown->downmixCoefficientsPresent) { |
| int j, k; |
| FIXP_DBL downmixOffset = getDownmixOffset( |
| pDown, hSelProcOutput->baseChannelCount); /* e = 1 */ |
| |
| for (j = 0; j < hSelProcOutput->baseChannelCount; j++) { |
| for (k = 0; k < hSelProcOutput->targetChannelCount; k++) { |
| hSelProcOutput->downmixMatrix[j][k] = |
| fMultDiv2( |
| downmixOffset, |
| pDown->downmixCoefficient[j + k * hSelProcOutput |
| ->baseChannelCount]) |
| << 2; |
| } |
| } |
| |
| hSelProcOutput->downmixMatrixPresent = 1; |
| } |
| break; |
| } |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetPreSelection( |
| SEL_PROC_INPUT* hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected, SEL_PROC_CODEC_MODE codecMode) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int i, j; |
| |
| for (i = 0; i < hSelProcInput->numDownmixIdRequests; i++) { |
| for (j = 0; j < hUniDrcConfig->drcInstructionsCountInclVirtual; j++) { |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstruction = |
| &(hUniDrcConfig->drcInstructionsUniDrc[j]); |
| retVal = _drcSetPreSelectionSingleInstruction( |
| hSelProcInput, i, hUniDrcConfig, hLoudnessInfoSet, pDrcInstruction, |
| *ppCandidatesPotential, *ppCandidatesSelected, codecMode); |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| |
| retVal = _preSelectionRequirement9(hSelProcInput, *ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0) { |
| retVal = _drcSetSelectionAddCandidates( |
| hSelProcInput, *ppCandidatesPotential, *ppCandidatesSelected); |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| return retVal; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _drcSetRequestSelection( |
| SEL_PROC_INPUT* hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal; |
| int i; |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesPotential) == 0) { |
| retVal = DRCDEC_SELECTION_PROCESS_NOT_OK; |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| if (hSelProcInput->dynamicRangeControlOn) { |
| if (hSelProcInput->numDrcFeatureRequests == 0) { |
| retVal = _selectDrcSetEffectNone(hUniDrcConfig, *ppCandidatesPotential, |
| *ppCandidatesSelected); |
| if (retVal) return (retVal); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) == 0) { |
| DRC_FEATURE_REQUEST fallbackRequest; |
| fallbackRequest.drcEffectType.numRequests = 5; |
| fallbackRequest.drcEffectType.numRequestsDesired = 5; |
| fallbackRequest.drcEffectType.request[0] = DETR_GENERAL_COMPR; |
| fallbackRequest.drcEffectType.request[1] = DETR_NIGHT; |
| fallbackRequest.drcEffectType.request[2] = DETR_NOISY; |
| fallbackRequest.drcEffectType.request[3] = DETR_LIMITED; |
| fallbackRequest.drcEffectType.request[4] = DETR_LOWLEVEL; |
| |
| retVal = _selectEffectTypeFeature(hUniDrcConfig, fallbackRequest, |
| ppCandidatesPotential, |
| ppCandidatesSelected); |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| } else { |
| for (i = 0; i < hSelProcInput->numDrcFeatureRequests; i++) { |
| if (hSelProcInput->drcFeatureRequestType[i] == DFRT_EFFECT_TYPE) { |
| retVal = _selectEffectTypeFeature( |
| hUniDrcConfig, hSelProcInput->drcFeatureRequest[i], |
| ppCandidatesPotential, ppCandidatesSelected); |
| |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| else if (hSelProcInput->drcFeatureRequestType[i] == |
| DFRT_DYNAMIC_RANGE) { |
| retVal = _selectDynamicRange( |
| hUniDrcConfig, hLoudnessInfoSet, |
| hSelProcInput->drcFeatureRequest[i], |
| hSelProcInput->downmixIdRequested, hSelProcInput->albumMode, |
| *ppCandidatesPotential, *ppCandidatesSelected); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 0) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| } |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } else if (hSelProcInput->drcFeatureRequestType[i] == |
| DFRT_DRC_CHARACTERISTIC) { |
| retVal = _selectDrcCharacteristic( |
| hUniDrcConfig, |
| hSelProcInput->drcFeatureRequest[i].drcCharacteristic, |
| ppCandidatesPotential, ppCandidatesSelected); |
| |
| if (_drcdec_selection_getNumber(*ppCandidatesSelected) > 0) { |
| _swapSelectionAndClear(ppCandidatesPotential, ppCandidatesSelected); |
| } |
| if (retVal) return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| } |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /*******************************************/ |
| static DRCDEC_SELECTION_PROCESS_RETURN _dynamicRangeMeasurement( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, DRC_INSTRUCTIONS_UNI_DRC* pInst, |
| UCHAR downmixIdRequested, |
| DYN_RANGE_MEASUREMENT_REQUEST_TYPE dynamicRangeMeasurementType, |
| int albumMode, int* pPeakToAveragePresent, FIXP_DBL* pPeakToAverage) { |
| int i; |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| int drcSetId = fMax(0, pInst->drcSetId); |
| |
| *pPeakToAveragePresent = 0; |
| |
| if (albumMode) { |
| for (i = 0; i < hLoudnessInfoSet->loudnessInfoAlbumCount; i++) { |
| LOUDNESS_INFO* pLoudnessInfo = &(hLoudnessInfoSet->loudnessInfoAlbum[i]); |
| |
| if (drcSetId == pLoudnessInfo->drcSetId) { |
| if (downmixIdRequested == pLoudnessInfo->downmixId) { |
| retVal = _extractLoudnessPeakToAverageValue( |
| pLoudnessInfo, dynamicRangeMeasurementType, pPeakToAveragePresent, |
| pPeakToAverage); |
| if (retVal) return (retVal); |
| } |
| } |
| } |
| } |
| |
| if (*pPeakToAveragePresent == 0) { |
| for (i = 0; i < hLoudnessInfoSet->loudnessInfoCount; i++) { |
| LOUDNESS_INFO* pLoudnessInfo = &(hLoudnessInfoSet->loudnessInfo[i]); |
| |
| if (drcSetId == pLoudnessInfo->drcSetId) { |
| if (downmixIdRequested == pLoudnessInfo->downmixId) { |
| retVal = _extractLoudnessPeakToAverageValue( |
| pLoudnessInfo, dynamicRangeMeasurementType, pPeakToAveragePresent, |
| pPeakToAverage); |
| if (retVal) return (retVal); |
| } |
| } |
| } |
| } |
| |
| return retVal; |
| } |
| /*******************************************/ |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_addNew( |
| DRCDEC_SELECTION* pSelection) { |
| if (pSelection->numData < (12 + 1 + 6)) { |
| DRCDEC_SELECTION_DATA* pData = &(pSelection->data[pSelection->numData]); |
| FDKmemset(pData, 0, sizeof(DRCDEC_SELECTION_DATA)); |
| pSelection->numData++; |
| |
| return pData; |
| } else { |
| return NULL; |
| } |
| } |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_add( |
| DRCDEC_SELECTION* pSelection, DRCDEC_SELECTION_DATA* pDataIn) { |
| if (pSelection->numData < (12 + 1 + 6)) { |
| DRCDEC_SELECTION_DATA* pData = &(pSelection->data[pSelection->numData]); |
| FDKmemcpy(pData, pDataIn, sizeof(DRCDEC_SELECTION_DATA)); |
| pSelection->numData++; |
| return pData; |
| } else { |
| return NULL; |
| } |
| } |
| |
| static int _drcdec_selection_clear(DRCDEC_SELECTION* pSelection) { |
| return pSelection->numData = 0; |
| } |
| |
| static int _drcdec_selection_getNumber(DRCDEC_SELECTION* pSelection) { |
| return pSelection->numData; |
| } |
| |
| static int _drcdec_selection_setNumber(DRCDEC_SELECTION* pSelection, int num) { |
| if (num >= 0 && num < pSelection->numData) { |
| return pSelection->numData = num; |
| } else { |
| return pSelection->numData; |
| } |
| } |
| |
| static DRCDEC_SELECTION_DATA* _drcdec_selection_getAt( |
| DRCDEC_SELECTION* pSelection, int at) { |
| if (at >= 0 && at < (12 + 1 + 6)) { |
| return &(pSelection->data[at]); |
| } else { |
| return NULL; |
| } |
| } |
| |
| static int _swapSelectionAndClear(DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| DRCDEC_SELECTION* pTmp = *ppCandidatesPotential; |
| *ppCandidatesPotential = *ppCandidatesSelected; |
| *ppCandidatesSelected = pTmp; |
| _drcdec_selection_clear(*ppCandidatesSelected); |
| return 0; |
| } |
| |
| static int _swapSelection(DRCDEC_SELECTION** ppCandidatesPotential, |
| DRCDEC_SELECTION** ppCandidatesSelected) { |
| DRCDEC_SELECTION* pTmp = *ppCandidatesPotential; |
| *ppCandidatesPotential = *ppCandidatesSelected; |
| *ppCandidatesSelected = pTmp; |
| return 0; |
| } |
| |
| /*******************************************/ |
| |
| static LOUDNESS_INFO* _getLoudnessInfoStructure( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int drcSetId, int downmixId, |
| int albumMode) { |
| int i, j; |
| int count; |
| |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| if (albumMode) { |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; |
| } else { |
| count = hLoudnessInfoSet->loudnessInfoCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((pLoudnessInfo[i].drcSetId == drcSetId) && |
| (pLoudnessInfo[i].downmixId == downmixId)) { |
| for (j = 0; j < pLoudnessInfo[i].measurementCount; j++) { |
| if ((pLoudnessInfo[i].loudnessMeasurement[j].methodDefinition == 1) || |
| (pLoudnessInfo[i].loudnessMeasurement[j].methodDefinition == 2)) { |
| return &pLoudnessInfo[i]; |
| } |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static LOUDNESS_INFO* _getApplicableLoudnessInfoStructure( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int drcSetId, |
| int downmixIdRequested, int albumMode) { |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| /* default value */ |
| pLoudnessInfo = _getLoudnessInfoStructure(hLoudnessInfoSet, drcSetId, |
| downmixIdRequested, albumMode); |
| |
| /* fallback values */ |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, drcSetId, 0x7F, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = _getLoudnessInfoStructure(hLoudnessInfoSet, 0x3F, |
| downmixIdRequested, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = _getLoudnessInfoStructure(hLoudnessInfoSet, 0, |
| downmixIdRequested, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, 0x3F, 0x7F, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, 0, 0x7F, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, drcSetId, 0, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, 0x3F, 0, albumMode); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| pLoudnessInfo = |
| _getLoudnessInfoStructure(hLoudnessInfoSet, 0, 0, albumMode); |
| } |
| |
| return pLoudnessInfo; |
| } |
| |
| /*******************************************/ |
| |
| typedef struct { |
| FIXP_DBL value; |
| int order; |
| } VALUE_ORDER; |
| |
| void _initValueOrder(VALUE_ORDER* pValue) { |
| pValue->value = (FIXP_DBL)0; |
| pValue->order = -1; |
| } |
| |
| enum { |
| MS_BONUS0 = 0, |
| MS_BONUS1770, |
| MS_BONUSUSER, |
| MS_BONUSEXPERT, |
| MS_RESA, |
| MS_RESB, |
| MS_RESC, |
| MS_RESD, |
| MS_RESE, |
| MS_PROGRAMLOUDNESS, |
| MS_PEAKLOUDNESS |
| }; |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getMethodValue( |
| VALUE_ORDER* pValueOrder, FIXP_DBL value, int measurementSystem, |
| int measurementSystemRequested) { |
| const int rows = 11; |
| const int columns = 12; |
| const int pOrdering[rows][columns] = { |
| {0, 0, 8, 0, 1, 3, 0, 5, 6, 7, 4, 2}, /* default = bonus1770 */ |
| {0, 0, 8, 0, 1, 3, 0, 5, 6, 7, 4, 2}, /* bonus1770 */ |
| {0, 0, 1, 0, 8, 5, 0, 2, 3, 4, 6, 7}, /* bonusUser */ |
| {0, 0, 3, 0, 1, 8, 0, 4, 5, 6, 7, 2}, /* bonusExpert */ |
| {0, 0, 5, 0, 1, 3, 0, 8, 6, 7, 4, 2}, /* ResA */ |
| {0, 0, 5, 0, 1, 3, 0, 6, 8, 7, 4, 2}, /* ResB */ |
| {0, 0, 5, 0, 1, 3, 0, 6, 7, 8, 4, 2}, /* ResC */ |
| {0, 0, 3, 0, 1, 7, 0, 4, 5, 6, 8, 2}, /* ResD */ |
| {0, 0, 1, 0, 7, 5, 0, 2, 3, 4, 6, 8}, /* ResE */ |
| {0, 0, 1, 0, 0, 0, 0, 2, 3, 4, 0, 0}, /* ProgramLoudness */ |
| {0, 7, 0, 0, 0, 0, 6, 5, 4, 3, 2, 1} /* PeakLoudness */ |
| }; |
| |
| if (measurementSystemRequested < 0 || measurementSystemRequested >= rows || |
| measurementSystem < 0 || measurementSystem >= columns) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| if (pOrdering[measurementSystemRequested][measurementSystem] > |
| pValueOrder->order) { |
| pValueOrder->order = |
| pOrdering[measurementSystemRequested][measurementSystem]; |
| pValueOrder->value = value; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /*******************************************/ |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getLoudness( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int albumMode, |
| METHOD_DEFINITION_REQUEST measurementMethodRequested, |
| MEASUREMENT_SYSTEM_REQUEST measurementSystemRequested, |
| FIXP_DBL targetLoudness, /* e = 7 */ |
| int drcSetId, int downmixIdRequested, |
| FIXP_DBL* pLoudnessNormalizationGain, /* e = 7 */ |
| FIXP_DBL* pLoudness) /* e = 7 */ |
| { |
| int index; |
| |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| VALUE_ORDER valueOrder; |
| |
| /* map MDR_DEFAULT to MDR_PROGRAM_LOUDNESS */ |
| METHOD_DEFINITION_REQUEST requestedMethodDefinition = |
| measurementMethodRequested < MDR_ANCHOR_LOUDNESS ? MDR_PROGRAM_LOUDNESS |
| : MDR_ANCHOR_LOUDNESS; |
| |
| if (measurementMethodRequested > MDR_ANCHOR_LOUDNESS) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| _initValueOrder(&valueOrder); |
| |
| *pLoudness = UNDEFINED_LOUDNESS_VALUE; |
| *pLoudnessNormalizationGain = (FIXP_DBL)0; |
| |
| if (drcSetId < 0) { |
| drcSetId = 0; |
| } |
| |
| pLoudnessInfo = _getApplicableLoudnessInfoStructure( |
| hLoudnessInfoSet, drcSetId, downmixIdRequested, albumMode); |
| |
| if (albumMode && (pLoudnessInfo == NULL)) { |
| pLoudnessInfo = _getApplicableLoudnessInfoStructure( |
| hLoudnessInfoSet, drcSetId, downmixIdRequested, 0); |
| } |
| |
| if (pLoudnessInfo == NULL) { |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| index = -1; |
| |
| do { |
| index = _findMethodDefinition(pLoudnessInfo, requestedMethodDefinition, |
| index + 1); |
| |
| if (index >= 0) { |
| _getMethodValue( |
| &valueOrder, pLoudnessInfo->loudnessMeasurement[index].methodValue, |
| pLoudnessInfo->loudnessMeasurement[index].measurementSystem, |
| measurementSystemRequested); |
| } |
| } while (index >= 0); |
| |
| /* repeat with other method definition */ |
| if (valueOrder.order == -1) { |
| index = -1; |
| |
| do { |
| index = _findMethodDefinition( |
| pLoudnessInfo, |
| requestedMethodDefinition == MDR_PROGRAM_LOUDNESS |
| ? MDR_ANCHOR_LOUDNESS |
| : MDR_PROGRAM_LOUDNESS, |
| index + 1); |
| |
| if (index >= 0) { |
| _getMethodValue( |
| &valueOrder, pLoudnessInfo->loudnessMeasurement[index].methodValue, |
| pLoudnessInfo->loudnessMeasurement[index].measurementSystem, |
| measurementSystemRequested); |
| } |
| } while (index >= 0); |
| } |
| |
| if (valueOrder.order == -1) { |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } else { |
| *pLoudnessNormalizationGain = targetLoudness - valueOrder.value; |
| *pLoudness = valueOrder.value; |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| /*******************************************/ |
| |
| static int _truePeakLevelIsPresent(HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| int drcSetId, int downmixId, int albumMode) { |
| int i; |
| int count; |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| if (albumMode) { |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; |
| } else { |
| count = hLoudnessInfoSet->loudnessInfoCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((pLoudnessInfo[i].drcSetId == drcSetId) && |
| (pLoudnessInfo[i].downmixId == downmixId)) { |
| if (pLoudnessInfo[i].truePeakLevelPresent) return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getTruePeakLevel( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int drcSetId, int downmixId, |
| int albumMode, FIXP_DBL* pTruePeakLevel) { |
| int i; |
| int count; |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| if (albumMode) { |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; |
| } else { |
| count = hLoudnessInfoSet->loudnessInfoCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((pLoudnessInfo[i].drcSetId == drcSetId) && |
| (pLoudnessInfo[i].downmixId == downmixId)) { |
| if (pLoudnessInfo[i].truePeakLevelPresent) { |
| *pTruePeakLevel = pLoudnessInfo[i].truePeakLevel; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| static int _samplePeakLevelIsPresent(HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, |
| int drcSetId, int downmixId, |
| int albumMode) { |
| int i; |
| int count; |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| if (albumMode) { |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; |
| } else { |
| count = hLoudnessInfoSet->loudnessInfoCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((pLoudnessInfo[i].drcSetId == drcSetId) && |
| (pLoudnessInfo[i].downmixId == downmixId)) { |
| if (pLoudnessInfo[i].samplePeakLevelPresent) return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getSamplePeakLevel( |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int drcSetId, int downmixId, |
| int albumMode, FIXP_DBL* pSamplePeakLevel /* e = 7 */ |
| ) { |
| int i; |
| int count; |
| LOUDNESS_INFO* pLoudnessInfo = NULL; |
| |
| if (albumMode) { |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; |
| } else { |
| count = hLoudnessInfoSet->loudnessInfoCount; |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if ((pLoudnessInfo[i].drcSetId == drcSetId) && |
| (pLoudnessInfo[i].downmixId == downmixId)) { |
| if (pLoudnessInfo[i].samplePeakLevelPresent) { |
| *pSamplePeakLevel = pLoudnessInfo[i].samplePeakLevel; |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| static int _limiterPeakTargetIsPresent( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstruction, int drcSetId, int downmixId) { |
| int i; |
| |
| if (pDrcInstruction->limiterPeakTargetPresent) { |
| if ((pDrcInstruction->downmixId[0] == downmixId) || |
| (pDrcInstruction->downmixId[0] == 0x7F)) { |
| return 1; |
| } |
| |
| for (i = 0; i < pDrcInstruction->downmixIdCount; i++) { |
| if (pDrcInstruction->downmixId[i] == downmixId) { |
| return 1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getLimiterPeakTarget( |
| DRC_INSTRUCTIONS_UNI_DRC* pDrcInstruction, int drcSetId, int downmixId, |
| FIXP_DBL* pLimiterPeakTarget) { |
| int i; |
| |
| if (pDrcInstruction->limiterPeakTargetPresent) { |
| if ((pDrcInstruction->downmixId[0] == downmixId) || |
| (pDrcInstruction->downmixId[0] == 0x7F)) { |
| *pLimiterPeakTarget = |
| ((FX_SGL2FX_DBL(pDrcInstruction->limiterPeakTarget) >> 2)); |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| |
| for (i = 0; i < pDrcInstruction->downmixIdCount; i++) { |
| if (pDrcInstruction->downmixId[i] == downmixId) { |
| *pLimiterPeakTarget = |
| ((FX_SGL2FX_DBL(pDrcInstruction->limiterPeakTarget) >> 2)); |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| } |
| } |
| } |
| |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; |
| } |
| |
| static int _downmixCoefficientsArePresent(HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| int downmixId, int* pIndex) { |
| int i; |
| *pIndex = -1; |
| |
| for (i = 0; i < hUniDrcConfig->downmixInstructionsCount; i++) { |
| if (hUniDrcConfig->downmixInstructions[i].downmixId == downmixId) { |
| if (hUniDrcConfig->downmixInstructions[i].downmixCoefficientsPresent) { |
| *pIndex = i; |
| return 1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static DRCDEC_SELECTION_PROCESS_RETURN _getSignalPeakLevel( |
| HANDLE_SEL_PROC_INPUT hSelProcInput, HANDLE_UNI_DRC_CONFIG hUniDrcConfig, |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, DRC_INSTRUCTIONS_UNI_DRC* pInst, |
| int downmixIdRequested, int* explicitPeakInformationPresent, |
| FIXP_DBL* signalPeakLevelOut, /* e = 7 */ |
| SEL_PROC_CODEC_MODE codecMode |
| |
| ) { |
| DRCDEC_SELECTION_PROCESS_RETURN retVal = DRCDEC_SELECTION_PROCESS_NO_ERROR; |
| |
| int albumMode = hSelProcInput->albumMode; |
| |
| FIXP_DBL signalPeakLevelTmp = (FIXP_DBL)0; |
| FIXP_DBL signalPeakLevel = FIXP_DBL(0); |
| |
| int dmxId = downmixIdRequested; |
| |
| int drcSetId = pInst->drcSetId; |
| |
| if (drcSetId < 0) { |
| drcSetId = 0; |
| } |
| |
| *explicitPeakInformationPresent = 1; |
| |
| if (_truePeakLevelIsPresent(hLoudnessInfoSet, drcSetId, dmxId, albumMode)) { |
| retVal = _getTruePeakLevel(hLoudnessInfoSet, drcSetId, dmxId, albumMode, |
| &signalPeakLevel); |
| if (retVal) return (retVal); |
| } else if (_samplePeakLevelIsPresent(hLoudnessInfoSet, drcSetId, dmxId, |
| albumMode)) { |
| retVal = _getSamplePeakLevel(hLoudnessInfoSet, drcSetId, dmxId, albumMode, |
| &signalPeakLevel); |
| if (retVal) return (retVal); |
| } else if (_truePeakLevelIsPresent(hLoudnessInfoSet, 0x3F, dmxId, |
| albumMode)) { |
| retVal = _getTruePeakLevel(hLoudnessInfoSet, 0x3F, dmxId, albumMode, |
| &signalPeakLevel); |
| if (retVal) return (retVal); |
| } else if (_samplePeakLevelIsPresent(hLoudnessInfoSet, 0x3F, dmxId, |
| albumMode)) { |
| retVal = _getSamplePeakLevel(hLoudnessInfoSet, 0x3F, dmxId, albumMode, |
| &signalPeakLevel); |
| if (retVal) return (retVal); |
| } else if (_limiterPeakTargetIsPresent(pInst, drcSetId, dmxId)) { |
| retVal = _getLimiterPeakTarget(pInst, drcSetId, dmxId, &signalPeakLevel); |
| if (retVal) return (retVal); |
| } else if (dmxId != 0) { |
| int downmixInstructionIndex = 0; |
| FIXP_DBL downmixPeakLevelDB = 0; |
| |
| *explicitPeakInformationPresent = 0; |
| |
| signalPeakLevelTmp = FIXP_DBL(0); |
| |
| if (_downmixCoefficientsArePresent(hUniDrcConfig, dmxId, |
| &downmixInstructionIndex)) { |
| FIXP_DBL dB_m; |
| int dB_e; |
| FIXP_DBL coeff; |
| FIXP_DBL sum, maxSum; /* e = 7, so it is possible to sum up up to 32 |
| downmix coefficients (with e = 2) */ |
| int i, j; |
| DOWNMIX_INSTRUCTIONS* pDown = |
| &(hUniDrcConfig->downmixInstructions[downmixInstructionIndex]); |
| FIXP_DBL downmixOffset = getDownmixOffset( |
| pDown, hUniDrcConfig->channelLayout.baseChannelCount); /* e = 1 */ |
| maxSum = (FIXP_DBL)0; |
| |
| for (i = 0; i < pDown->targetChannelCount; i++) { |
| sum = (FIXP_DBL)0; |
| for (j = 0; j < hUniDrcConfig->channelLayout.baseChannelCount; j++) { |
| coeff = pDown->downmixCoefficient[j + i * hUniDrcConfig->channelLayout |
| .baseChannelCount]; |
| sum += coeff >> 5; |
| } |
| if (maxSum < sum) maxSum = sum; |
| } |
| |
| maxSum = fMultDiv2(maxSum, downmixOffset) << 2; |
| |
| if (maxSum == FL2FXCONST_DBL(1.0f / (float)(1 << 7))) { |
| downmixPeakLevelDB = (FIXP_DBL)0; |
| } else { |
| dB_m = lin2dB(maxSum, 7, &dB_e); /* e_maxSum = 7 */ |
| downmixPeakLevelDB = |
| scaleValue(dB_m, dB_e - 7); /* e_downmixPeakLevelDB = 7 */ |
| } |
| } |
| |
| if (_truePeakLevelIsPresent(hLoudnessInfoSet, drcSetId, 0, albumMode)) { |
| retVal = _getTruePeakLevel(hLoudnessInfoSet, drcSetId, 0, albumMode, |
| &signalPeakLevelTmp); |
| if (retVal) return (retVal); |
| } else if (_samplePeakLevelIsPresent(hLoudnessInfoSet, drcSetId, 0, |
| albumMode)) { |
| retVal = _getSamplePeakLevel(hLoudnessInfoSet, drcSetId, 0, albumMode, |
| &signalPeakLevelTmp); |
| if (retVal) return ( |