| /* ----------------------------------------------------------------------------- | |
| Software License for The Fraunhofer FDK AAC Codec Library for Android | |
| © Copyright 1995 - 2019 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_ALBUM_MODE: | |
| if ((requestValueInt < 0) || (requestValueInt > 1)) | |
| return DRCDEC_SELECTION_PROCESS_PARAM_OUT_OF_RANGE; | |
| diff |= _compAssign(&pSelProcInput->albumMode, 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 + | |
| (FIXP_DBL)(1 << 15))); /* convert to FIXP_SGL with rounding */ | |
| 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 + | |
| (FIXP_DBL)(1 << 15))); /* convert to FIXP_SGL with rounding */ | |
| 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 (pDown->targetChannelCount > 8) { | |
| continue; | |
| } | |
| 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) { | |
| if (hUniDrcConfig->downmixInstructions[i].targetChannelCount > 8) | |
| return 0; | |
| *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 (retVal); | |
| } else if (_truePeakLevelIsPresent(hLoudnessInfoSet, 0x3F, 0, albumMode)) { | |
| retVal = _getTruePeakLevel(hLoudnessInfoSet, 0x3F, 0, albumMode, | |
| &signalPeakLevelTmp); | |
| if (retVal) return (retVal); | |
| } else if (_samplePeakLevelIsPresent(hLoudnessInfoSet, 0x3F, 0, | |
| albumMode)) { | |
| retVal = _getSamplePeakLevel(hLoudnessInfoSet, 0x3F, 0, albumMode, | |
| &signalPeakLevelTmp); | |
| if (retVal) return (retVal); | |
| } else if (_limiterPeakTargetIsPresent(pInst, drcSetId, 0)) { | |
| retVal = _getLimiterPeakTarget(pInst, drcSetId, 0, &signalPeakLevelTmp); | |
| if (retVal) return (retVal); | |
| } | |
| signalPeakLevel = signalPeakLevelTmp + downmixPeakLevelDB; | |
| } else { | |
| signalPeakLevel = FIXP_DBL(0); /* worst case estimate */ | |
| *explicitPeakInformationPresent = FIXP_DBL(0); | |
| } | |
| *signalPeakLevelOut = signalPeakLevel; | |
| return retVal; | |
| } | |
| static DRCDEC_SELECTION_PROCESS_RETURN _extractLoudnessPeakToAverageValue( | |
| LOUDNESS_INFO* loudnessInfo, | |
| DYN_RANGE_MEASUREMENT_REQUEST_TYPE dynamicRangeMeasurementType, | |
| int* pLoudnessPeakToAverageValuePresent, | |
| FIXP_DBL* pLoudnessPeakToAverageValue) { | |
| int i; | |
| VALUE_ORDER valueOrderLoudness; | |
| VALUE_ORDER valueOrderPeakLoudness; | |
| _initValueOrder(&valueOrderLoudness); | |
| _initValueOrder(&valueOrderPeakLoudness); | |
| LOUDNESS_MEASUREMENT* pLoudnessMeasure = NULL; | |
| *pLoudnessPeakToAverageValuePresent = 0; | |
| for (i = 0; i < loudnessInfo->measurementCount; i++) { | |
| pLoudnessMeasure = &(loudnessInfo->loudnessMeasurement[i]); | |
| if (pLoudnessMeasure->methodDefinition == MD_PROGRAM_LOUDNESS) { | |
| _getMethodValue(&valueOrderLoudness, pLoudnessMeasure->methodValue, | |
| pLoudnessMeasure->measurementSystem, MS_PROGRAMLOUDNESS); | |
| } | |
| if ((dynamicRangeMeasurementType == DRMRT_SHORT_TERM_LOUDNESS_TO_AVG) && | |
| (pLoudnessMeasure->methodDefinition == MD_SHORT_TERM_LOUDNESS_MAX)) { | |
| _getMethodValue(&valueOrderPeakLoudness, pLoudnessMeasure->methodValue, | |
| pLoudnessMeasure->measurementSystem, MS_PEAKLOUDNESS); | |
| } | |
| if ((dynamicRangeMeasurementType == DRMRT_MOMENTARY_LOUDNESS_TO_AVG) && | |
| (pLoudnessMeasure->methodDefinition == MD_MOMENTARY_LOUDNESS_MAX)) { | |
| _getMethodValue(&valueOrderPeakLoudness, pLoudnessMeasure->methodValue, | |
| pLoudnessMeasure->measurementSystem, MS_PEAKLOUDNESS); | |
| } | |
| if ((dynamicRangeMeasurementType == DRMRT_TOP_OF_LOUDNESS_RANGE_TO_AVG) && | |
| (pLoudnessMeasure->methodDefinition == MD_MAX_OF_LOUDNESS_RANGE)) { | |
| _getMethodValue(&valueOrderPeakLoudness, pLoudnessMeasure->methodValue, | |
| pLoudnessMeasure->measurementSystem, MS_PEAKLOUDNESS); | |
| } | |
| } | |
| if ((valueOrderLoudness.order > -1) && (valueOrderPeakLoudness.order > -1)) { | |
| *pLoudnessPeakToAverageValue = | |
| valueOrderPeakLoudness.value - valueOrderLoudness.value; | |
| *pLoudnessPeakToAverageValuePresent = 1; | |
| } | |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; | |
| } | |
| /*******************************************/ | |
| static DRCDEC_SELECTION_PROCESS_RETURN _selectAlbumLoudness( | |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, | |
| DRCDEC_SELECTION* pCandidatesPotential, | |
| DRCDEC_SELECTION* pCandidatesSelected) { | |
| int i, j; | |
| 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; | |
| for (j = 0; j < hLoudnessInfoSet->loudnessInfoAlbumCount; j++) { | |
| if (pCandidate->pInst->drcSetId == | |
| hLoudnessInfoSet->loudnessInfoAlbum[j].drcSetId) { | |
| if (_drcdec_selection_add(pCandidatesSelected, pCandidate) == NULL) | |
| return DRCDEC_SELECTION_PROCESS_NOT_OK; | |
| } | |
| } | |
| } | |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; | |
| } | |
| /*******************************************/ | |
| static int _findMethodDefinition(LOUDNESS_INFO* pLoudnessInfo, | |
| int methodDefinition, int startIndex) { | |
| int i; | |
| int index = -1; | |
| for (i = startIndex; i < pLoudnessInfo->measurementCount; i++) { | |
| if (pLoudnessInfo->loudnessMeasurement[i].methodDefinition == | |
| methodDefinition) { | |
| index = i; | |
| break; | |
| } | |
| } | |
| return index; | |
| } | |
| /*******************************************/ | |
| static DRCDEC_SELECTION_PROCESS_RETURN _getMixingLevel( | |
| HANDLE_LOUDNESS_INFO_SET hLoudnessInfoSet, int downmixIdRequested, | |
| int drcSetIdRequested, int albumMode, FIXP_DBL* pMixingLevel) { | |
| const FIXP_DBL mixingLevelDefault = FL2FXCONST_DBL(85.0f / (float)(1 << 7)); | |
| int i; | |
| int count; | |
| LOUDNESS_INFO* pLoudnessInfo = NULL; | |
| *pMixingLevel = mixingLevelDefault; | |
| if (drcSetIdRequested < 0) { | |
| drcSetIdRequested = 0; | |
| } | |
| if (albumMode) { | |
| count = hLoudnessInfoSet->loudnessInfoAlbumCount; | |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfoAlbum; | |
| } else { | |
| count = hLoudnessInfoSet->loudnessInfoCount; | |
| pLoudnessInfo = hLoudnessInfoSet->loudnessInfo; | |
| } | |
| for (i = 0; i < count; i++) { | |
| if ((drcSetIdRequested == pLoudnessInfo[i].drcSetId) && | |
| ((downmixIdRequested == pLoudnessInfo[i].downmixId) || | |
| (DOWNMIX_ID_ANY_DOWNMIX == pLoudnessInfo[i].downmixId))) { | |
| int index = _findMethodDefinition(&pLoudnessInfo[i], MD_MIXING_LEVEL, 0); | |
| if (index >= 0) { | |
| *pMixingLevel = pLoudnessInfo[i].loudnessMeasurement[index].methodValue; | |
| break; | |
| } | |
| } | |
| } | |
| return DRCDEC_SELECTION_PROCESS_NO_ERROR; | |
| } | |
| /*******************************************/ |